# Story: Theme clair/sombre — activation fonctionnelle du bouton toggle

## 1. Contexte (Context)

Le composant `theme-toggle` (pill avec deux boutons icône soleil/flocon) existe déjà dans le header et le footer (`templates/components/_theme_toggle.html.twig`, `assets/styles/components/_theme-toggle.scss`), livré à la suite de la spec footer du 2026-07-11, mais c'est aujourd'hui un placeholder visuel sans aucune logique JS : cliquer ne fait rien. Cette story câble le comportement réel de bascule clair/sombre sur l'ensemble du site public (l'admin n'a pas de bouton toggle et n'est pas concerné). Le changement de thème n'affecte que la palette de couleurs (aucun changement de layout, spacing ou typographie), est persisté côté client dans `localStorage`, et doit s'appliquer avant le premier paint pour éviter un flash de la mauvaise version au chargement. L'implémentation introduit une couche de CSS custom properties (`--color-*`) sur laquelle les variables Sass `$color-*` existantes (`assets/styles/base/_variables.scss`) sont repointées, ce qui évite de toucher aux sites d'appel de ces variables dans le reste du codebase.

## 2. Le besoin (User Story)

```
En tant que visiteur du site public
Je souhaite pouvoir basculer entre un thème clair et un thème sombre via le bouton toggle déjà présent dans le header et le footer
Afin de consulter le site avec la palette de couleurs qui me convient, avec mon choix mémorisé d'une page à l'autre et d'une visite à l'autre
```

## 3. Liens utiles (Useful Links)

- **Spec technique complète**: `spec-driven-development/2026-07-12-theme-switch.md` (architecture détaillée, liste exhaustive des sites d'appel `rgba($color-*, …)` à migrer, palette dark proposée)
- **Figma**: aucune maquette dark mode fournie à ce jour — la palette dark de cette story est une proposition technique (inversion de luminance en conservant la teinte de marque), **à valider visuellement avant merge définitif**. Figma clair de référence : https://www.figma.com/design/nxwV8yGIZ2J9XHvQ4CpWsS/Galta_2026
- **Code existant**:
  - Composant toggle (markup déjà fonctionnel, ne nécessite aucun changement) : `templates/components/_theme_toggle.html.twig`
  - Styles du composant : `assets/styles/components/_theme-toggle.scss`
  - Variables Sass à repointer : `assets/styles/base/_variables.scss:11-26`
  - Entry point à modifier : `assets/app.ts` (pattern existant : `Header`, `BurgerMenu`)
  - Layout à modifier (script anti-flash) : `templates/base.html.twig`
- **Story liée**: spec footer du 2026-07-11 (`spec-driven-development/2026-07-11-site-footer.md`) — a introduit le composant toggle non câblé
- **Architecture**:
  - CLAUDE.md — section "Docker Command Execution" (build via `docker-compose exec node npm run build`)
  - CLAUDE.md — section "TypeScript Only" (`ThemeManager.ts` doit être strict TypeScript, alias `@core/theme/*`)
  - `spec-driven-development/rules/typescript-only.md`

## 4. Critères d'acceptation (Acceptance Criteria)

### Scénario 1 : Bascule visuelle du thème via le bouton header
```gherkin
Scénario: Le visiteur bascule en thème sombre depuis le header
  Étant donné que je visite une page du site public en thème clair (par défaut)
  Quand je clique sur le bouton "sombre" (icône flocon) du toggle dans le header
  Alors la palette de couleurs de toute la page change (header, fond de page, cartes, texte, boutons CTA)
  Et aucun élément de layout, d'espacement ou de typographie ne change
  Et le bouton "sombre" affiche l'état visuel actif (classe `is-active`, `aria-pressed="true"`)
```

### Scénario 2 : Synchronisation header/footer sans clic supplémentaire
```gherkin
Scénario: Les deux instances du toggle restent synchronisées
  Étant donné que je viens de basculer en thème sombre via le toggle du header
  Quand je fais défiler la page jusqu'au footer
  Alors le bouton "sombre" du toggle présent dans le footer affiche déjà l'état actif
  Et je n'ai eu besoin de cliquer qu'une seule fois (un seul ThemeManager pilote les deux instances)
```

### Scénario 3 : Persistance du choix en localStorage
```gherkin
Scénario: Le thème choisi est mémorisé après un clic
  Étant donné que je clique sur le bouton "sombre" du toggle
  Quand j'inspecte le localStorage du navigateur
  Alors la clé "galta-theme" contient la valeur "dark"
  Quand je clique ensuite sur le bouton "clair"
  Alors la clé "galta-theme" contient la valeur "light"
```

### Scénario 4 : Persistance cross-page
```gherkin
Scénario: Le thème choisi est conservé en naviguant sur le site
  Étant donné que j'ai sélectionné le thème sombre sur la page d'accueil
  Quand je navigue vers une autre page du site public (ex. le hub actualités)
  Alors le thème sombre est déjà appliqué sur la nouvelle page dès son affichage
  Et je n'ai pas besoin de re-cliquer sur le toggle
```

### Scénario 5 : Absence de flash au chargement (anti-FOUC)
```gherkin
Scénario: Aucun flash de la version claire ne doit apparaître au rechargement
  Étant donné que j'ai précédemment choisi le thème sombre (clé "galta-theme" = "dark" en localStorage)
  Quand je recharge complètement la page (F5), y compris avec un throttling réseau/CPU activé dans les devtools
  Alors la page s'affiche directement en thème sombre dès le premier paint
  Et aucune version claire n'est visible même brièvement avant la bascule
```

### Scénario 6 : Valeur par défaut sans choix explicite
```gherkin
Scénario: Un nouveau visiteur voit le thème clair par défaut
  Étant donné qu'aucune entrée "galta-theme" n'existe en localStorage (première visite ou storage vidé)
  Quand je visite n'importe quelle page du site public
  Alors le thème clair (comportement actuel) est affiché
  Et aucun bouton du toggle n'affiche l'état actif par défaut sauf celui correspondant au thème clair
```

### Scénario 7 : Admin non affecté
```gherkin
Scénario: Le back-office reste inchangé quel que soit le thème choisi côté public
  Étant donné que j'ai sélectionné le thème sombre sur le site public
  Quand j'accède au dashboard admin (/admin) ou à la page de login admin
  Alors ces pages s'affichent avec leur palette habituelle, sans aucun changement visuel
  Et l'attribut `data-theme` n'est jamais positionné sur ces pages (elles n'étendent pas `base.html.twig`)
```

### Scénario 8 : Footer non themé (fond fixe)
```gherkin
Scénario: Le fond du footer reste identique dans les deux thèmes
  Étant donné que je bascule entre thème clair et thème sombre
  Quand j'observe le fond du bloc footer (`.site-footer`)
  Alors sa couleur de fond reste la même valeur littérale fixe dans les deux thèmes (déjà un bloc sombre par design)
  Et seuls les tokens de couleur du corps de page (header, fond de page, cartes, texte) varient réellement entre les deux thèmes
```

### Scénario 9 : Build sans régression Sass
```gherkin
Scénario: Le build de production ne produit aucun CSS invalide après migration des couleurs
  Étant donné que les variables Sass `$color-*` pointent désormais vers des `var(--color-*)`
  Et que tous les appels `rgba($color-x, alpha)` recensés dans la spec technique ont été convertis en `rgba(var(--color-x-rgb), alpha)`
  Quand j'exécute `docker-compose exec node npm run build`
  Alors le build se termine sans erreur
  Et le CSS compilé ne contient aucune règle invalide (ex. `rgba(var(--color-x), alpha)` sans triplet RGB, ou un `$color-x` littéral oublié)
```

### Scénario 10 : Accessibilité — contraste en mode sombre
```gherkin
Scénario: Le contraste texte/fond reste suffisant en thème sombre
  Étant donné que le thème sombre est actif
  Quand je vérifie le contraste sur le header, une carte de contenu, et un bouton CTA
  Alors le ratio de contraste texte/fond atteint au moins 4.5:1 (WCAG AA, texte normal)
  Et si un token de la palette proposée ne respecte pas ce ratio, il est ajusté avant le passage en "Ready to merge"
```

## Notes de validation avant merge

- La palette dark de `assets/styles/base/_theme.scss` (bloc `[data-theme='dark']`) est une proposition technique sans maquette Figma dédiée — une validation visuelle/design explicite est requise en plus de la review de code habituelle (voir spec technique, section "Review Workflow").
- La liste exhaustive des sites d'appel `rgba($color-*, …)` à convertir est détaillée dans `spec-driven-development/2026-07-12-theme-switch.md` (section "Implementation Tasks" > "Frontend — SCSS") et doit être re-vérifiée par grep avant merge, la liste pouvant avoir bougé.
- Aucun test automatisé PHPUnit pertinent : comportement 100% client-side/CSS, aucune logique serveur introduite.
