Sistema de temas¶
La fase 58 (v1.41.0) reemplazó el antiguo par claro/oscuro por un
sistema de seis temas clásicos sobre una única dimensión
data-theme, más una opción auto que sigue al sistema
operativo. La fase 63 (v1.63.0) añadió seis presets WCAG AA
recomendados, de modo que el selector incluye en total 12
temas.
Presets recomendados (Fase 63 / v1.63.0)¶
El selector en Ajustes → Apariencia encabeza con una subpestaña Recomendados:
- Claros:
catppuccin-latte,supabase,graphite - Oscuros:
catppuccin-mocha,soft-pop,amethyst-haze
Se generaron a partir de presets de tweakcn mediante
scripts/generate_preset_themes.py como temas completos de 44
tokens, con WCAG AA forzado de forma computacional
(contrast.test.ts sobre los 12 temas). Los seis clásicos
(light, dark, ocean, forest, high-contrast, sepia)
permanecen sin cambios.
Cómo funciona¶
- Los tokens de color canónicos residen en
frontend/src/styles/themes/theme-<id>.css, un bloque por valor dedata-theme(light,dark,ocean,forest,high-contrast,sepia). Cada archivo define el conjunto semántico de tokens completo: no hay fallback al tema claro. - Los tokens independientes del tema (espaciados, radio,
fuentes, la paleta de métodos de marca) y los alias heredados
(
--bg,--surface,--fg,--danger, ...) residen enstyles/global.css :root. Los alias resuelven a través de los tokens canónicos y, así, siguen automáticamente al tema activo. - Los archivos de tema se importan en
main.tsx, el claro primero, para que el tema activo gane el empate de especificidad frente a:root. frontend/src/lib/themes.tses el registro:THEMES, los tiposThemeId/ThemeChoice,resolveTheme(choice, prefersDark)para el mapeoautoy las muestras de color de la vista previa.frontend/src/hooks/useTheme.tsposee el atributodata-themeaplicado y guarda la elección bajoadaptive-learner.theme(migra una sola vez la antigua claveadaptive-learner-theme).index.htmlcontiene un pequeño script en línea que aplica el tema guardado antes del primer paint (sin parpadeo). Refleja la resolución del hook; mantenlos sincronizados.- Los gráficos (Recharts) no pueden leer variables CSS en
atributos SVG, por lo que
lib/chartTheme.ts+useChartThemeleen los valores computados de los tokens y vuelven a leerlos al cambiardata-theme.
Conjunto de tokens (definido por cada tema)¶
Fondos (--bg-primary/secondary/surface/elevated/overlay), texto
(--fg-primary/secondary/muted/inverse), bordes
(--border-primary/subtle/accent), interactivo
(--interactive-bg/hover/active/disabled), acento
(--accent, -hover, -fg, -subtle, -rgb), pares de estado
(--success/-bg, --error/-bg, --warning/-bg, --info/-bg),
feedback de ejercicios
(--exercise-correct/-wrong/-selected/-matched), --star, series
de gráficos (--chart-1..6) y sombras
(--shadow-card/-elevated/-md).
styles/themes/themes.test.ts falla si a un tema le falta uno de
estos tokens o tiene uno de más; styles/contrast.test.ts
comprueba WCAG 2.1 AA sobre los 12 temas. La referencia completa
de tokens está en
Arquitectura de tokens de diseño.
Añadir un tema nuevo¶
- Copia un archivo existente, p. ej.
cp theme-dark.css theme-midnight.css, y cambia el selector a[data-theme="midnight"]. Conserva cada token; cambia solo los valores. Nada de estilos de componentes aquí. - Regístralo en
lib/themes.ts: añade aTHEMESuna entradaThemeMeta(id,labelen inglés,familylight|dark y unswatchpara la vista previa de Ajustes) y agrega la id a la uniónThemeId. - Impórtalo en
main.tsxdespués detheme-light.css(el orden solo cuenta en relación con el claro). - Permítelo en el guard de pre-paint: agrega la id al array
validen el<script>en línea deindex.html. - i18n: añade
ui.themes.midnighten los ocho catálogos bajobackend/config/i18n/*.yamly ejecutamake sync-i18n. - Comprueba:
npx vitest run src/styles/themes src/styles/contrast— los pins de completitud y de contraste deben seguir en verde (ajusta los valores hasta que el contraste cumpla AA en el tema nuevo).
Eso es todo: el ThemePicker, el script de pre-paint, los gráficos y cada componente adoptan el tema nuevo automáticamente, porque todos leen los tokens canónicos.
Reglas¶
- Sin colores codificados de forma fija en los componentes.
styles/no-hardcoded-colors.test.tslo impone para los estilos.tsx(una lista de permitidos documentada cubre los resolutores de gráficos, el confeti decorativo y los colores de datos). - Cada tema define cada token. Sin huecos con herencia del claro: ese fue el error del audit F1 (tokens sin definir que mostraban hex claro en modo oscuro).
- El cambio de tema es instantáneo: un intercambio de
data-theme, nunca una recarga.