Tematización
Cómo está montado el tema — componentes propios, una capa de tokens y un modo oscuro que conmuta por clase.
El tema no es una dependencia que configuras. Es un conjunto de archivos que la capa posee, construido sobre shadcn-vue y Tailwind 4, y la razón de esa forma decide lo que te cuesta sobrescribir algo.
Cambiar algo es Sobrescribir el tema. Esta página explica por qué las costuras están donde están.
Los componentes se copian, no se importan
shadcn-vue no es una biblioteca de componentes que instalas. Su CLI escribe el
código fuente de un componente en tu proyecto, y desde ese momento el archivo es
tuyo. La capa guarda sus primitivos en app/components/ui/, y components.json
apunta el CLI a ese alias, así que añadir uno es una sola orden.
Se siguen dos consecuencias, y van en direcciones opuestas:
- Puedes cambiar cualquier cosa, sin bifurcar. Un componente que quieres que se vea distinto es un archivo que editas — no hay un upstream contra el que pelear, ni una API del tema en la que buscar un hueco.
- Nada se actualiza solo. Una corrección de un primitivo te llega cuando vuelves a ejecutar el CLI para él, no cuando una dependencia sube de versión. La capa asume ese coste para sus propios primitivos; un componente que sobrescribes en tu proyecto lo asumes tú.
Ese canje es toda la razón del tercer nivel de la guía de sobrescritura: el cambio más pequeño que resuelve la tarea es el correcto, porque un componente que reemplazas es un componente que ahora mantienes.
Por qué shadcn-vue en lugar de un tema de documentación ya hecho está registrado en ADR 0008.
Una capa de tokens, dos hojas de estilo
La paleta son propiedades personalizadas de CSS en oklch, definidas una vez en
:root y redefinidas en .dark. Cada componente lee un token; ninguno guarda un
color. Eso es lo que hace posible una sobrescritura de una línea — redefines
--primary en tu propia hoja de estilos y todas las superficies que lo usan se
mueven.
Dos archivos lo sostienen, y no son intercambiables:
| Archivo | Contiene |
|---|---|
app/assets/css/duxt.css | La paleta, la entrada de Tailwind, el preajuste de la capa — el archivo que lees |
app/assets/css/typeset.css | El typeset de shadcn, copiado literalmente — el archivo que no editas |
Detrás de typeset.css no hay entrada de registro, así que el CLI no puede
obtenerlo ni actualizarlo. Actualizar significa descargar el archivo de nuevo, y
por eso los cambios locales van en el preajuste de duxt.css: una edición hecha
en typeset.css es una que la siguiente actualización descarta en silencio.
El modo oscuro es una clase, no una media query
@custom-variant dark (&:is(.dark *)) — el tema conmuta con una clase dark, y
@nuxtjs/color-mode pone exactamente esa clase porque la capa lo configura con
classSuffix: ''.
Este es el detalle con el que tropieza quien sobrescribe un color. Un token
definido solo bajo @media (prefers-color-scheme: dark) no se lee nunca, y un
token definido una vez en :root se usa en ambos modos — lo que significa que es
incorrecto en uno de ellos. Redefine en ambos bloques, o acepta que un modo
conserve el valor de la capa.
La paleta se aparta del valor de shadcn, a propósito
--muted-foreground es aquí más oscuro de lo que lo entrega el conjunto neutro de
shadcn. Ese token lleva las descripciones de página, la tabla de contenidos y la
miga de pan, y con el valor de shadcn mide por debajo del 4,5:1 que pide WCAG AA
sobre las superficies en las que realmente aterriza.
Ninguna comprobación basada en navegador lo detecta: una pasada de accesibilidad
sobre las páginas construidas no tiene color calculado que medir. La capa mide por
tanto los tokens mismos, en tests/contrast.test.ts, emparejando cada color de
primer plano con los fondos sobre los que se usa de verdad. Si sobrescribes un
token de primer plano, asumes esa garantía — la prueba lee el archivo de la capa,
no el tuyo.
Hay que decirle a Tailwind dónde está la capa
@source '../../../app' está cerca del principio de duxt.css y no es adorno.
Tailwind busca nombres de clase desde la raíz de compilación, que es el
directorio del proyecto consumidor. La capa vive fuera de él, en node_modules,
así que sin esa línea faltaría en el CSS generado cada utilidad usada por un
componente de la capa: un sitio que se representa sin estilos.
La ruta es relativa a la hoja de estilos, de modo que vale allí donde el paquete esté instalado.
Dónde te deja esto
- Un color, un radio, una tipografía: redefine el token en tu propia hoja de estilos.
- Un diseño que no encaja en tu sitio: ensombrece el componente por su nombre.
- Un primitivo que la capa no trae: añádelo con el CLI.
Cada paso hacia abajo cuesta más mantenimiento que el anterior. La guía de sobrescritura recorre los tres, y Dale identidad a tu sitio cubre la parte que es identidad y no tema — el nombre, la marca y los iconos.