forked from EHV/sejeteralo
8bfa5f98ef
- extends '../../yvv-nuxt-base' (frontend en sous-dossier)
- app/composables/usePalette.ts : moods avec vars précalculées
(--color-primary-dark dérivé + variables SVG), délégation à useMood
(darkClass 'palette-dark', syncColorMode false, storageKey 'sej-palette')
et synchro de useState('theme-dark') lu par la page graphiques
- DisplaySettings.vue : palette inline (~90 lignes) remplacée par le composable ;
font-size/densité inchangés
- README : note layer partagé
Vérifié Playwright : 6 palettes × light/dark × mobile/desktop (bodyBg suit
--color-bg, .palette-dark ne bascule que sur nuit/ocean) ; build npm vert ;
prod :3099 → 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
132 lines
4.5 KiB
TypeScript
132 lines
4.5 KiB
TypeScript
import type { MoodDef } from '#imports'
|
|
|
|
/**
|
|
* Palettes SejeteralO — identité couleur du projet.
|
|
* L'application DOM (variables CSS + classe .palette-dark), la persistance et
|
|
* le light/dark sont délégués au mécanisme partagé `useMood` (@yvv/nuxt-base).
|
|
* Chaque palette injecte AUSSI ses variables SVG (thème des graphiques) et un
|
|
* `--color-primary-dark` dérivé — d'où le précalcul en `vars`.
|
|
*/
|
|
export interface Palette {
|
|
name: string
|
|
label: string
|
|
dark: boolean
|
|
primary: string
|
|
accent: string
|
|
bg: string
|
|
surface: string
|
|
text: string
|
|
textMuted: string
|
|
border: string
|
|
// SVG-specific (thème des graphiques)
|
|
svgPlotBg: string
|
|
svgGrid: string
|
|
svgLegendBg: string
|
|
svgText: string
|
|
svgTextLight: string
|
|
}
|
|
|
|
export const paletteList: Palette[] = [
|
|
// ── Light palettes ──
|
|
{
|
|
name: 'eau', label: 'Eau', dark: false,
|
|
primary: '#2563eb', accent: '#059669',
|
|
bg: '#f8fafc', surface: '#ffffff', text: '#1e293b', textMuted: '#64748b', border: '#e2e8f0',
|
|
svgPlotBg: '#f8fafc', svgGrid: '#e2e8f0', svgLegendBg: 'white', svgText: '#334155', svgTextLight: '#64748b',
|
|
},
|
|
{
|
|
name: 'terre', label: 'Terre', dark: false,
|
|
primary: '#92400e', accent: '#d97706',
|
|
bg: '#fefbf6', surface: '#ffffff', text: '#1c1917', textMuted: '#78716c', border: '#e7e5e4',
|
|
svgPlotBg: '#faf8f5', svgGrid: '#e7e5e4', svgLegendBg: 'white', svgText: '#44403c', svgTextLight: '#78716c',
|
|
},
|
|
{
|
|
name: 'foret', label: 'Foret', dark: false,
|
|
primary: '#166534', accent: '#65a30d',
|
|
bg: '#f7fdf9', surface: '#ffffff', text: '#14532d', textMuted: '#6b7c6e', border: '#d1e7d6',
|
|
svgPlotBg: '#f5fbf7', svgGrid: '#d1e7d6', svgLegendBg: 'white', svgText: '#2d4a35', svgTextLight: '#6b7c6e',
|
|
},
|
|
{
|
|
name: 'ardoise', label: 'Ardoise', dark: false,
|
|
primary: '#475569', accent: '#64748b',
|
|
bg: '#f8fafc', surface: '#ffffff', text: '#1e293b', textMuted: '#64748b', border: '#e2e8f0',
|
|
svgPlotBg: '#f8fafc', svgGrid: '#e2e8f0', svgLegendBg: 'white', svgText: '#334155', svgTextLight: '#64748b',
|
|
},
|
|
// ── Dark palettes ──
|
|
{
|
|
name: 'nuit', label: 'Nuit', dark: true,
|
|
primary: '#60a5fa', accent: '#34d399',
|
|
bg: '#0f172a', surface: '#1e293b', text: '#f1f5f9', textMuted: '#94a3b8', border: '#334155',
|
|
svgPlotBg: '#1e293b', svgGrid: '#334155', svgLegendBg: '#1e293b', svgText: '#cbd5e1', svgTextLight: '#94a3b8',
|
|
},
|
|
{
|
|
name: 'ocean', label: 'Ocean', dark: true,
|
|
primary: '#38bdf8', accent: '#2dd4bf',
|
|
bg: '#0c1222', surface: '#162032', text: '#e2e8f0', textMuted: '#7dd3fc', border: '#1e3a5f',
|
|
svgPlotBg: '#162032', svgGrid: '#1e3a5f', svgLegendBg: '#162032', svgText: '#bae6fd', svgTextLight: '#7dd3fc',
|
|
},
|
|
]
|
|
|
|
/** Assombrit une couleur hex (facteur 0.82) — variante `--color-primary-dark`. */
|
|
function darken(hex: string): string {
|
|
const r = parseInt(hex.slice(1, 3), 16)
|
|
const g = parseInt(hex.slice(3, 5), 16)
|
|
const b = parseInt(hex.slice(5, 7), 16)
|
|
const f = 0.82
|
|
return `#${Math.round(r * f).toString(16).padStart(2, '0')}${Math.round(g * f).toString(16).padStart(2, '0')}${Math.round(b * f).toString(16).padStart(2, '0')}`
|
|
}
|
|
|
|
const paletteMoods: MoodDef[] = paletteList.map(p => ({
|
|
id: p.name,
|
|
label: p.label,
|
|
isDark: p.dark,
|
|
vars: {
|
|
'--color-primary': p.primary,
|
|
'--color-primary-dark': darken(p.primary),
|
|
'--color-secondary': p.accent,
|
|
'--color-bg': p.bg,
|
|
'--color-surface': p.surface,
|
|
'--color-text': p.text,
|
|
'--color-text-muted': p.textMuted,
|
|
'--color-border': p.border,
|
|
// Thème SVG (graphiques)
|
|
'--svg-plot-bg': p.svgPlotBg,
|
|
'--svg-grid': p.svgGrid,
|
|
'--svg-legend-bg': p.svgLegendBg,
|
|
'--svg-text': p.svgText,
|
|
'--svg-text-light': p.svgTextLight,
|
|
},
|
|
}))
|
|
|
|
export function usePalette() {
|
|
const { currentMood, setMood, initMood } = useMood(paletteMoods, {
|
|
storageKey: 'sej-palette',
|
|
defaultId: 'eau',
|
|
syncColorMode: false, // pas de @nuxtjs/color-mode
|
|
lightDarkClasses: false, // le CSS ne stylise que .palette-dark
|
|
darkClass: 'palette-dark',
|
|
})
|
|
|
|
// État dark partagé, lu par la page graphiques (commune/[slug]) pour le thème SVG.
|
|
const isDark = useState('theme-dark', () => false)
|
|
|
|
function syncDark(id: string) {
|
|
const m = paletteMoods.find(x => x.id === id)
|
|
if (m) isDark.value = m.isDark
|
|
}
|
|
|
|
const currentPalette = computed(() => currentMood.value || 'eau')
|
|
|
|
function setPalette(name: string) {
|
|
setMood(name)
|
|
syncDark(name)
|
|
}
|
|
|
|
function initPalette() {
|
|
initMood()
|
|
syncDark(currentMood.value || 'eau')
|
|
}
|
|
|
|
return { paletteList, currentPalette, setPalette, initPalette, isDark }
|
|
}
|