Thématisation
Comment le thème est construit — des composants possédés, une couche de jetons et un mode sombre qui bascule sur une classe.
Le thème n'est pas une dépendance que vous configurez. C'est un ensemble de fichiers que la couche possède, bâti sur shadcn-vue et Tailwind 4 — et la raison de cette forme décide de ce que coûte une surcharge.
Changer quelque chose, c'est Surcharger le thème. Cette page explique pourquoi les coutures sont là où elles sont.
Les composants sont copiés, pas importés
shadcn-vue n'est pas une bibliothèque de composants que l'on installe. Son CLI
écrit le code source d'un composant dans votre projet, et à partir de là le
fichier est le vôtre. La couche garde ses primitives dans app/components/ui/, et
components.json pointe le CLI sur cet alias : en ajouter une tient en une seule
commande.
Deux conséquences en découlent, et elles vont en sens inverse :
- Vous pouvez tout changer, sans fork. Un composant que vous voulez différent est un fichier que vous modifiez — pas d'amont contre lequel lutter, pas d'API de thème dans laquelle chercher une faille.
- Rien ne se met à jour tout seul. Un correctif amont sur une primitive vous parvient quand vous relancez le CLI pour elle, pas quand une dépendance monte de version. La couche assume ce coût pour ses propres primitives ; un composant que vous surchargez dans votre projet, vous l'assumez.
Ce compromis est toute la raison du troisième niveau du guide de surcharge : le plus petit changement qui fait le travail est le bon, car un composant que vous remplacez est un composant que vous maintenez désormais.
Pourquoi shadcn-vue plutôt qu'un thème de documentation tout fait est consigné dans l'ADR 0008.
Une couche de jetons, deux feuilles de style
La palette est faite de propriétés personnalisées CSS en oklch, définies une
fois sur :root et redéfinies sur .dark. Chaque composant lit un jeton ; aucun
ne détient une couleur. C'est ce qui rend possible une surcharge d'une ligne — vous
redéfinissez --primary dans votre propre feuille de style et toutes les surfaces
qui l'utilisent suivent.
Deux fichiers la portent, et ils ne sont pas interchangeables :
| Fichier | Contient |
|---|---|
app/assets/css/duxt.css | La palette, l'entrée Tailwind, le préréglage de la couche — le fichier que vous lisez |
app/assets/css/typeset.css | Le typeset de shadcn, repris tel quel — le fichier que vous ne modifiez pas |
Aucune entrée de registre ne se cache derrière typeset.css : le CLI ne peut donc
ni le récupérer ni le mettre à jour. Mettre à jour signifie retélécharger le
fichier, et c'est pourquoi les changements locaux ont leur place dans le
préréglage de duxt.css — une modification faite dans typeset.css est une
modification que la prochaine mise à jour écarte en silence.
Le mode sombre est une classe, pas une media query
@custom-variant dark (&:is(.dark *)) — le thème bascule sur une classe dark, et
@nuxtjs/color-mode pose exactement cette classe parce que la couche le configure
avec classSuffix: ''.
C'est le détail sur lequel on trébuche en surchargeant une couleur. Un jeton
défini uniquement sous @media (prefers-color-scheme: dark) n'est jamais lu, et un
jeton défini une seule fois sur :root sert dans les deux modes — donc il est faux
dans l'un des deux. Redéfinissez dans les deux blocs, ou acceptez qu'un mode garde
la valeur de la couche.
La palette s'écarte du défaut de shadcn, délibérément
--muted-foreground est ici plus sombre que ne le livre le jeu neutre de shadcn.
Ce jeton porte les descriptions de page, le sommaire et le fil d'Ariane, et à la
valeur de shadcn il mesure en dessous du 4,5:1 exigé par WCAG AA sur les surfaces
où il atterrit réellement.
Aucune vérification passant par un navigateur ne l'attrape : un passage
d'accessibilité sur les pages construites n'a pas de couleur calculée à mesurer. La
couche mesure donc les jetons eux-mêmes, dans tests/contrast.test.ts, en
appariant chaque couleur de premier plan avec les fonds sur lesquels elle est
vraiment utilisée. Surchargez un jeton de premier plan et vous reprenez cette
garantie — le test lit le fichier de la couche, pas le vôtre.
Il faut dire à Tailwind où se trouve la couche
@source '../../../app' figure en haut de duxt.css et n'est pas décoratif.
Tailwind cherche les noms de classe depuis la racine de compilation, c'est-à-dire
le dossier du projet consommateur. La couche vit en dehors, dans node_modules :
sans cette ligne, chaque utilitaire employé par un composant de la couche
manquerait au CSS généré — un site rendu sans aucun style.
Le chemin est relatif à la feuille de style, il tient donc partout où le paquet est installé.
Ce que cela donne
- Une couleur, un rayon, une police : redéfinissez le jeton dans votre feuille de style.
- Une mise en page qui ne convient pas à votre site : occultez le composant par son nom.
- Une primitive que la couche n'apporte pas : ajoutez-la avec le CLI.
Chaque marche vers le bas coûte plus d'entretien que la précédente. Le guide de surcharge parcourt les trois, et Donner son identité à votre site traite la part qui relève de l'identité et non du thème — le nom, la marque et les icônes.