1212 * document, routé par classe CSS. Zéro allocation de closure dans des boucles.
1313 * – ASSET_REGISTRY data-driven : table de dépendances évaluée en un seul passage
1414 * DOM (sélecteurs concaténés → un seul appel moteur), scripts et styles injectés
15- * sans doublon, exécution différée dans les cycles d'inactivité du thread
16- * (requestIdleCallback) pour ne pas bloquer le First Contentful Paint .
15+ * sans doublon. Les scripts sont async : injectés après parsing, ils ne bloquent
16+ * jamais le rendu — aucune deferral supplémentaire n'est nécessaire .
1717 * – Scroll-to-top via scroll event passif ({ passive: true }) : lecture de scrollY
1818 * seule, sans lecture de layout (getBoundingClientRect, offsetTop) — zéro reflow.
1919 * – Image fallback réactif : écoute globale en phase de capture sur window.error —
3232 * – Le timeout de NavigationSystem est déclaré dans la portée du système et non dans
3333 * le handler resize : corrige le bug silencieux de l'original où clearTimeout était
3434 * inopérant (nouvelle variable à chaque appel).
35+ * – aria-hidden sur sub-nav est conditionnel au viewport, pas fixé à true au boot :
36+ * en mode desktop, le sub-nav est visible via CSS et doit être accessible aux AT.
37+ * Le breakpoint est lu depuis la CSS custom property --size-nav (getComputedStyle) —
38+ * source de vérité unique, pas de valeur dupliquée en dur dans le JS.
39+ * – injectSvgSprite est exposée sur window (non confinée dans l'IIFE) : elle est
40+ * appelée directement depuis main.js et more.js qui s'exécutent dans des bundles
41+ * distincts et n'ont pas accès à la portée de l'IIFE. Un namespace window.App
42+ * aurait requis une mise à jour de tous les appelants sans bénéfice architectural.
3543 * – initScrollButton utilise un scroll event { passive: true } plutôt qu'un
3644 * IntersectionObserver sur élément sentinel injecté. La lecture de scrollY seule
3745 * ne provoque aucun reflow — le thrashing n'existe que lorsqu'une lecture de layout
@@ -137,9 +145,12 @@ const AppPipeline = (() => {
137145 this . _sizeNav = parseFloat ( getComputedStyle ( DOM . html ) . getPropertyValue ( '--size-nav' ) )
138146 this . _htmlFontSize = parseFloat ( getComputedStyle ( DOM . html ) . getPropertyValue ( 'font-size' ) )
139147
140- // État initial : menu fermé
148+ // État initial : dépend du viewport courant.
149+ // En mode desktop (viewport > sizeNav), le sub-nav est visible en CSS
150+ // et ne doit pas être masqué aux AT via aria-hidden.
151+ const isMobile = window . innerWidth / this . _htmlFontSize < this . _sizeNav
141152 this . _btn . setAttribute ( 'aria-expanded' , 'false' )
142- this . _subNav . setAttribute ( 'aria-hidden' , 'true' )
153+ this . _subNav . setAttribute ( 'aria-hidden' , isMobile ? 'true' : 'false ')
143154 } ,
144155
145156 toggle ( ) {
@@ -154,12 +165,13 @@ const AppPipeline = (() => {
154165 onResize ( ) {
155166 clearTimeout ( this . _resizeTimer )
156167 this . _resizeTimer = setTimeout ( ( ) => {
157- if (
158- this . _btn &&
159- this . _sizeNav < window . innerWidth / this . _htmlFontSize &&
160- this . _btn . getAttribute ( 'aria-expanded' ) === 'true'
161- ) {
162- this . toggle ( )
168+ if ( ! this . _btn ) return
169+ const isDesktop = window . innerWidth / this . _htmlFontSize > this . _sizeNav
170+ if ( isDesktop ) {
171+ // Passage en desktop : ferme le menu burger si ouvert et restaure
172+ // aria-hidden à false — le sub-nav est désormais visible en CSS.
173+ if ( this . _btn . getAttribute ( 'aria-expanded' ) === 'true' ) this . toggle ( )
174+ this . _subNav . setAttribute ( 'aria-hidden' , 'false' )
163175 }
164176 } , 200 )
165177 }
@@ -216,7 +228,9 @@ const AppPipeline = (() => {
216228 }
217229
218230 // — SVG Sprites —
219- const injectSvgSprite = ( targetElement , spriteId , svgFile = 'util' ) => {
231+ // Exposée globalement : appelée depuis main.js et more.js (hors IIFE).
232+ // TODO : refactoriser pour éviter l'exposition globale.
233+ window . injectSvgSprite = ( targetElement , spriteId , svgFile = 'util' ) => {
220234 targetElement . insertAdjacentHTML (
221235 'beforeend' ,
222236 `<svg role="img" focusable="false"><use href="/sprites/${ svgFile } .svg#${ spriteId } "></use></svg>`
@@ -365,10 +379,8 @@ const AppPipeline = (() => {
365379 // 6.3 Scroll To Top
366380 initScrollButton ( )
367381
368- // 6.4 Assets (différé — ne doit pas concurrencer le FCP)
369- 'requestIdleCallback' in window
370- ? requestIdleCallback ( ( ) => AssetSystem . resolve ( ) )
371- : setTimeout ( ( ) => AssetSystem . resolve ( ) , 1 )
382+ // 6.4 Assets
383+ AssetSystem . resolve ( )
372384
373385 // 6.5 Service Worker
374386 if ( 'serviceWorker' in navigator ) {
0 commit comments