From b1c6a2f28052564d505dd05b363169cb97fb81b3 Mon Sep 17 00:00:00 2001 From: Yvv Date: Sat, 18 Jul 2026 09:51:42 +0200 Subject: [PATCH] feat(useMood): options darkClass et persist pour le pattern dark/light MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - darkClass: bascule une classe unique sur selon isDark (variants dark: UnoCSS/Tailwind — besoin GrateWizard) - setMood(id, { persist: false }): application éphémère (embed, prefers-color-scheme) - initMood ne persiste plus le défaut, seulement un choix déjà stocké - README: section GrateWizard + avertissement distribution (extends relatif hors repo = build Docker/CI cassé, à trancher avant merge) Co-Authored-By: Claude Fable 5 --- README.md | 26 +++++++++++++++++++++++--- app/composables/useMood.ts | 29 ++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f8e4ae4..667c163 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,12 @@ Fournit le **mécanisme** commun, jamais les couleurs (chaque projet garde son i Supporte les 2 patterns existants sans réécrire le CSS d'un projet : - **classes `.mood-`** + variables `--mood-*` définies en CSS (style libreDecision) ; - **variables CSS injectées en JS** via `MoodDef.vars` (style librodrome `--color-*`) ; - - option `lightDarkClasses` pour basculer aussi `.palette-light/.palette-dark`. + - option `lightDarkClasses` pour basculer aussi `.palette-light/.palette-dark` ; + - option `darkClass: 'dark'` pour basculer une classe unique selon `isDark` + (variants `dark:` UnoCSS/Tailwind — style GrateWizard) ; + - `setMood(id, { persist: false })` pour une application éphémère + (thème imposé en mode embed, suivi de `prefers-color-scheme`) ; + - `initMood()` ne persiste que si un choix était déjà stocké. ## Adoption (à faire AVEC vérif visuelle mobile + desktop — zéro régression) @@ -40,8 +45,23 @@ export const useLibreMood = () => useMood(moods, { storageKey: 'libredecision_mo ### librodrome (via l'option vars — le CSS `--color-*` reste inchangé) Convertir `stores/palette.ts` pour déléguer l'application DOM à `useMood`, en passant les triplets HSL dans `vars` (`--color-primary`, etc.) et `lightDarkClasses: true`. Le store peut rester pour l'API publique, mais l'application au DOM passe par le mécanisme partagé. Vérifier les 4 saisons + light/dark. -### GrateWizard / SejeteralO -Adopter après les deux premiers, même principe. SejeteralO (pas d'UnoCSS, vue-router 5) en dernier, prudemment. +### GrateWizard (dark/light seulement, contraintes embed) +Deux moods `light`/`dark` avec `darkClass: 'dark'` (variants UnoCSS) et +`syncColorMode: false`. `useDarkMode` reste l'API publique et garde sa logique +de décision : thème imposé par `?theme=` en embed (`setMood(id, { persist: false })` +— ne jamais écraser la préférence standalone, même origine), migration du +stockage legacy `'true'/'false'` → `'dark'/'light'`, suivi de +`prefers-color-scheme` tant qu'aucun choix explicite n'est stocké. +Vérifier : toggle + persistance standalone, embed `?theme=light` non persisté, sceau. + +### SejeteralO +Adopter en dernier (pas d'UnoCSS, vue-router 5), prudemment. + +## ⚠️ Distribution — à régler AVANT tout merge/push des adoptions +Les `extends` relatifs (`../yvv-nuxt-base`) pointent HORS des repos consommateurs : +les builds Docker (`COPY . .`) et la CI échoueront. Options à trancher : +repo distant + dépendance git, `git subtree` vendoré par projet, ou contexte +Docker élargi. En dev local, rien à faire. ## Principe diff --git a/app/composables/useMood.ts b/app/composables/useMood.ts index a65c579..f2a8cb3 100644 --- a/app/composables/useMood.ts +++ b/app/composables/useMood.ts @@ -29,6 +29,17 @@ export interface UseMoodOptions { syncColorMode?: boolean /** bascule aussi les classes .palette-light / .palette-dark (style librodrome) (défaut: false) */ lightDarkClasses?: boolean + /** + * Classe unique à basculer sur selon mood.isDark (ex. 'dark' pour + * les variants dark: d'UnoCSS/Tailwind — style GrateWizard). Défaut: aucune. + */ + darkClass?: string +} + +export interface SetMoodOptions { + /** écrit le choix en localStorage (défaut: true). false = application + * éphémère (ex. thème imposé en mode embed, suivi de prefers-color-scheme) */ + persist?: boolean } const currentMood: Ref = ref('') @@ -51,9 +62,10 @@ export function useMood(moods: MoodDef[], options: UseMoodOptions = {}) { defaultId = moods[0]?.id ?? '', syncColorMode = true, lightDarkClasses = false, + darkClass, } = options - function applyMood(moodId: string) { + function applyMood(moodId: string, { persist = true }: SetMoodOptions = {}) { if (import.meta.server) return const mood = moods.find(m => m.id === moodId) if (!mood) return @@ -71,6 +83,11 @@ export function useMood(moods: MoodDef[], options: UseMoodOptions = {}) { } } + // classe dark unique (compat variants dark: UnoCSS/Tailwind) + if (darkClass) { + html.classList.toggle(darkClass, mood.isDark) + } + // classes palette-light / palette-dark (compat style librodrome) if (lightDarkClasses) { html.classList.toggle('palette-light', !mood.isDark) @@ -89,19 +106,21 @@ export function useMood(moods: MoodDef[], options: UseMoodOptions = {}) { } } - localStorage.setItem(storageKey, moodId) + if (persist) localStorage.setItem(storageKey, moodId) currentMood.value = moodId } - function setMood(moodId: string) { - applyMood(moodId) + function setMood(moodId: string, options: SetMoodOptions = {}) { + applyMood(moodId, options) } function initMood() { if (import.meta.server) return const saved = localStorage.getItem(storageKey) const moodId = saved && moods.some(m => m.id === saved) ? saved : defaultId - applyMood(moodId) + // ne persiste que ce qui était déjà stocké : appliquer le défaut + // ne doit pas verrouiller un choix que l'utilisateur n'a pas fait + applyMood(moodId, { persist: saved != null }) } return {