theme
El tema claro, oscuro o propio de la aplicación sin parpadeo, con la elección en una cookie, el tema en html desde el primer byte y useTheme en los componentes.
theme activa el tema claro/oscuro (y temas propios) de Meeko. La elección del usuario se guarda en una cookie; el servidor escribe el tema en <html> desde el primer byte, y cuando no puede saberlo (una página prerenderizada, el adaptador static, el modo "system" sin client hint) un script mínimo y bloqueante de <head> lo aplica antes del primer pintado. Los componentes leen y cambian el tema con useTheme() y <MeekoThemeToggle>.
Sin theme, nada cambia: ni script, ni cookie, ni cabeceras, ni POST /_meeko/theme.
Uso#
// meeko.config.ts
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({
theme: true, // light y dark, "system" por defecto, data-theme y la cookie meeko-theme
});/* app/styles/main.css */
:root { color-scheme: light; --bg: #fff; --ink: #111; }
:root[data-theme="dark"] { color-scheme: dark; --bg: #111; --ink: #eee; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) { color-scheme: dark; --bg: #111; --ink: #eee; }
}Tipo#
readonly theme?: boolean | ThemeConfiginterface ThemeConfig {
readonly themes?: readonly string[]
readonly default?: string
readonly system?: boolean | { readonly light: string; readonly dark: string }
readonly darkThemes?: readonly string[]
readonly attribute?: 'class' | `data-${string}`
readonly colorScheme?: boolean
readonly cookie?: string
readonly storageKey?: string | false
readonly clientHints?: boolean
}Opciones#
theme acepta true (los valores por defecto) o un objeto:
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
themes |
readonly string[] |
["light", "dark"] |
Los temas: letras, dígitos, - o _, y no "system". |
default |
string |
"system", o el primer tema con system: false |
La elección de quien todavía no eligió. |
system |
boolean | { light, dark } |
true |
El modo "system", que sigue a prefers-color-scheme. true usa light y dark; un objeto elige otros temas; false lo desactiva. |
darkThemes |
readonly string[] |
el oscuro de system, o dark |
Temas que reciben color-scheme: dark. |
attribute |
"class" | "data-…" |
"data-theme" |
Dónde va el tema en <html>: un atributo data-* o el nombre como clase. |
colorScheme |
boolean |
true |
Aplica color-scheme a <html> por CSSOM. |
cookie |
string |
"meeko-theme" |
La cookie de la elección. |
storageKey |
string | false |
el valor de cookie |
La clave de localStorage, que avisa a las otras pestañas; false usa sólo la cookie. |
clientHints |
boolean |
true |
Pide y lee Sec-CH-Prefers-Color-Scheme para resolver "system" en el servidor. |
Resultado#
| Caso | Quién pone el tema en <html> |
|---|---|
| SSR con la cookie | El servidor, desde el primer byte. |
SSR en "system" con el client hint (Chromium) |
El servidor. |
SSR en "system" sin el hint (Firefox, Safari) |
El script de <head>, antes del primer pintado. |
Página prerenderizada o adaptador static |
El script de <head>, con la cookie o localStorage. |
Las respuestas SSR llevan Accept-CH: Sec-CH-Prefers-Color-Scheme (y Critical-CH en "system") y Vary: Cookie, Sec-CH-Prefers-Color-Scheme.
Ejemplos#
Temas propios#
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({
theme: { themes: ["day", "night", "sepia"], system: { light: "day", dark: "night" }, darkThemes: ["night"], attribute: "class" },
});Un selector de tema#
<!-- app/components/ThemePicker.meeko -->
<script lang="ts">
const { theme } = useTheme();
</script>
<template>
<select model="theme">
<option value="system">Sistema</option>
<option value="light">Claro</option>
<option value="dark">Oscuro</option>
</select>
</template>O, sin escribir nada, <MeekoThemeToggle />, que también funciona sin JavaScript con un formulario.
Una cookie por aplicación en desarrollo#
import { defineMeekoConfig } from "@meekojs/meeko/framework";
// Las cookies son por host, no por puerto: dos apps en localhost compartirían meeko-theme.
export default defineMeekoConfig({ theme: { cookie: "tienda-theme" } });Errores comunes#
Temas propios con system activo#
export default defineMeekoConfig({ theme: { themes: ["sepia", "dark"] } });meeko dev, meeko build, meeko check y meeko routes se detienen con …/meeko.config.ts: theme: system necesita los temas "light" y "dark"; asigne otros nombres con system: { light, dark } o desactívelo con system: false.
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({ theme: { themes: ["sepia", "dark"], system: false } });Un default que no está en themes#
export default defineMeekoConfig({ theme: { default: "sepia" } });…/meeko.config.ts: theme: default debe ser uno de los temas de themes (light, dark).
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({ theme: { themes: ["light", "dark", "sepia"], default: "sepia" } });El tema en document#
export default defineMeekoConfig({ document: { theme: { themes: ["light", "dark"] } } as never });…/meeko.config.ts: document.theme no es una opción de meeko.config.ts; use theme en el nivel superior.
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({ theme: true });<MeekoThemeToggle> sin theme#
<!-- sin theme en meeko.config.ts -->
<template><MeekoThemeToggle /></template>meeko check da MEEK111 y, en ejecución, el componente lanza MEEK111: <MeekoThemeToggle> necesita theme en meeko.config.ts (theme: true) y una aplicación de Meeko en marcha.
import { defineMeekoConfig } from "@meekojs/meeko/framework";
export default defineMeekoConfig({ theme: true });Notas#
- CSP. En SSR, el script y el
<style>de los iconos llevan el nonce (el deDocumentOptions.nonceo, desde1.7, el que genera Meeko conserver.contentSecurityPolicy); en las páginas prerenderizadas ystatic, sus hashes entran en la CSP.color-schemese aplica por CSSOM, que una CSP sin'unsafe-inline'enstyle-srcpermite. - Seguridad. El servidor sólo acepta en la cookie un tema de
themes(o"system"); cualquier otro valor se ignora y nunca se escribe en el HTML. La cookie no esHttpOnlyy no contiene nada privado. - Caché. Las respuestas SSR varían por la cookie: un CDN que las cachee debe incluir la cookie del tema en la clave, o servirá a un usuario el tema de otro. Las páginas prerenderizadas no varían por el tema.
- Hidratación. El navegador hidrata con los valores del servidor y aplica los suyos al terminar, antes de
onMounted: nunca hay un desajuste. - Antes de
1.4. Sithemeeran las opciones de un módulo conconfigKey: "theme", el error lo indica: dele otraconfigKeyal módulo (véasedocs/migration-1.4.md).