diff --git a/PLAN_NAVIGATION_SLIDES.md b/PLAN_NAVIGATION_SLIDES.md deleted file mode 100644 index 5a2dbd9..0000000 --- a/PLAN_NAVIGATION_SLIDES.md +++ /dev/null @@ -1,325 +0,0 @@ -# Plan : Navigation par slides horizontaux - -## Contexte - -Le site source (world.game) utilise fullpage.js v4 avec un plugin scrollHorizontally propriétaire. Toutes les pages sont dans le DOM simultanément et la navigation fait glisser un conteneur avec `transform: translate3d`. On reproduit ce comportement en Svelte 5 sans dépendance externe. - -**Approche retenue :** -- Charger d'abord la page demandée (chargement initial rapide) -- Charger les autres en arrière-plan en parallèle (HTTP/2, payloads légers) -- Si l'utilisateur navigue avant qu'une page soit chargée → attendre qu'elle le soit -- Hack back/forward inspiré de decarb.one - -**Pourquoi le chargement progressif plutôt qu'un endpoint unique `/all-pages.json` ?** -Un endpoint unique bloquerait le premier rendu jusqu'à ce que Kirby ait tout construit. Le chargement progressif affiche la page initiale immédiatement, les 5 autres chargent en parallèle. Avec HTTP/2 et des payloads JSON légers (6 pages), le coût est négligeable. Chaque page peut aussi être mise en cache indépendamment. - ---- - -## Ordre des slides - -**Dynamique, tiré de `site->pages->listed` (Kirby).** Ne pas hardcoder l'ordre. - -Au chargement initial, la réponse JSON d'une page inclut `data.site.navigation` (déjà exposé par le template Kirby et stocké dans `site.svelte.js`). Ce tableau donne l'ordre et les chemins des pages listées. C'est lui qui construit le tableau `SLIDES_CONFIG` au runtime. - -Exemple de ce que Kirby expose déjà : -```json -{ - "site": { - "navigation": [ - { "id": "home", "url": "/home", "title": "Home" }, - { "id": "expertise", "url": "/expertise", "title": "Expertise" }, - ... - ] - } -} -``` - -Le store `slides` sera initialisé depuis ce tableau dynamiquement après le premier fetch. - ---- - -## Architecture cible - -### Conteneur slides (App.svelte) -``` -[body: overflow hidden] - [Header - fixed] - [.slides-wrapper: display flex, width: N*100vw, transition 1000ms] - [.slide × N: width 100vw, height 100vh, flex-shrink 0] - [] ← rendu seulement si data chargée -``` - -`transform: translateX(-{activeIndex * 100}vw)` géré via style inline réactif. - ---- - -## Fichiers à créer - -### `src/state/slides.svelte.js` (nouveau) - -Store central de l'état des slides : - -```js -// Initialisé dynamiquement depuis site.navigation -// slidesData: Array<{ id, path, template, data, loaded, loading }> -// activeIndex: number -// pendingPath: string | null - -export const slides = { - get all(), get activeIndex(), get active(), get pendingPath(), - - init(siteNavigation), // construit slidesData depuis les pages listées de Kirby - setData(path, data), // stocke les données + loaded=true d'une slide - setLoading(path, bool), - slideTo(path), // anime si chargée, sinon set pendingPath - resolvePending(), // appelé quand une slide finit de charger - getIndexByPath(path), -} -``` - -### `src/router/index.js` (réécriture quasi-complète) - -Remplace navaid par une logique custom : - -```js -// loadSlide(path) : -// fetch(`${path}.json`) → slides.setData() + site/locale init (1er appel seulement) -// -// loadAllSlidesInBackground(exceptPath) : -// slides.all.filter(s => s.path !== exceptPath).forEach(s => loadSlide(s.path)) -// -// slideTo(path, { skipHistory = false }) : -// - slide déjà chargée → slides.slideTo() + history.pushState si !skipHistory -// - slide non chargée → slides.setPending(path) (la nav se déclenche quand loaded) -// -// initRouter() : -// 1. path initial = window.location.pathname (ou '/home' si '/') -// 2. loadSlide(initialPath) : -// → initialise slides.init(site.navigation) après le 1er fetch -// → set active slide -// 3. loadAllSlidesInBackground(exceptPath: initialPath) -// 4. popstate listener : slideTo(window.location.pathname, { skipHistory: true }) -// 5. Intercepter clics internes : slideTo() -``` - -**Hack popstate :** -```js -window.addEventListener('popstate', () => { - slideTo(window.location.pathname, { skipHistory: true }) -}) -``` -Le navigateur change déjà l'URL sur ← → on lit simplement `window.location.pathname` et on slide sans re-pousser dans l'historique. - -**Export : `initRouter`, `slideTo`** - ---- - -## Fichiers à modifier - -### `src/App.svelte` - -```svelte - - -
- -
-
-
- {#each slides.all as slide} -
- {#if slide.loaded} - - {/if} -
- {/each} -
-
-
- - -``` - -Footer : intégré dans le slide Blog (comme dans le source). Supprimer le Footer global. - -### `src/components/layout/Header.svelte` -- `import { slideTo } from '@router'` (remplace `navigateTo`) -- Appels `navigateTo(path)` → `slideTo(path)` -- État actif du lien menu : basé sur `slides.active.id` - ---- - -## Fichiers inchangés -- `src/views/*.svelte` — aucune modification -- `src/state/site.svelte.js` -- `src/state/locale.svelte.js` -- `src/state/navigation.svelte.js` -- `src/main.js` -- `src/state/page.svelte.js` — peut être supprimé après migration (remplacé par slides store) - ---- - -## Accessibilité (RGAA / WCAG 2.1 AA) - -### Navigation clavier du système de slides - -Le système de slides horizontal est le point le plus délicat : par défaut, un utilisateur au clavier tabulerait dans les slides hors-écran. Il faut : - -- **Slides inactives** : `aria-hidden="true"` + tous les éléments focusables à `tabindex="-1"` → invisibles pour le clavier et les lecteurs d'écran -- **Slide active** : `aria-hidden="false"` + tabindex restauré -- **Navigation au clavier entre slides** : touches `←` `→` déclenchent `slideTo()` (à l'instar des carousels ARIA) -- **Focus management** : à chaque changement de slide, déplacer le focus sur le `

` (ou premier élément focusable) de la nouvelle slide -- **Lien "Aller au contenu"** (`