Changer le thème

shadcn/ui : guide complet d'installation et de personnalisation du thème (variables CSS)

Easton editorial illustration: design-system assembly tray

La première fois que j’ai utilisé shadcn/ui, j’ai été déconcerté par le fait que ce n’est « pas un paquet npm ». Copier du code dans le projet ? Ça paraissait rudimentaire.

Après quelques essais, j’ai compris que c’est précisément sa force : vous possédez tout le source des composants, vous modifiez comme vous voulez, sans conflits de versions ni limites imposées par une lib.

Aujourd’hui, on parle installation, configuration et personnalisation du thème shadcn/ui, en mettant l’accent sur les variables CSS pour un design system aligné sur votre marque. À la fin de l’article, vous devriez tenir la config de base en 5 minutes, puis consacrer une heure ou deux à peaufiner le thème.


1. Installation rapide : deux approches

Méthode 1 : initialisation CLI (recommandée)

Pour un nouveau projet, lancez :

npx shadcn@latest init

La CLI pose plusieurs questions : TypeScript ou JavaScript ? Quel style ? Quel thème par défaut ? Suivez les invites.

Après installation, vous aurez notamment :

  • components.json — fichier de configuration
  • lib/utils.ts — utilitaires
  • components/ui/ — répertoire des composants

Pour ajouter un composant, par exemple un bouton :

npx shadcn@latest add button

Le code est copié dans components/ui/button.tsx ; importez-le et utilisez-le.

Piège à connaître : si le projet tourne déjà depuis un moment, tailwind.config.js et globals.css contiennent peut-être déjà beaucoup de réglages. La commande init les écrase — installez shadcn le plus tôt possible.

Un bon conseil : traitez shadcn/ui comme une des « premières dépendances » du projet, pas un ajout tardif. J’ai appris à mes dépens.

Méthode 2 : installation manuelle (projets existants)

Si le projet est déjà structuré et que le risque d’écrasement par la CLI est trop élevé, installez à la main.

Étape 1 : Tailwind CSS

Les composants shadcn s’appuient sur Tailwind. Si ce n’est pas installé, suivez le guide officiel — on ne détaille pas ici.

Étape 2 : dépendances

npm install class-variance-authority clsx tailwind-merge
npm install lucide-react

class-variance-authority (CVA) servira pour les variantes de composants.

Étape 3 : alias de chemins

Dans tsconfig.json :

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Vous pourrez importer avec @/components/ui/button au lieu de ../../../.

Étape 4 : créer components.json

À la racine du projet :

{
  "style": "new-york",
  "rsc": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

cssVariables: true indique que le thème repose sur des variables CSS, pas uniquement sur des classes utility Tailwind.

Étape 5 : styles de base

Ajoutez dans globals.css les styles de base shadcn — détaillés dans la section thème ci-dessous.


2. Comprendre le système de thème : variables CSS

Le thème shadcn/ui repose sur une convention simple : chaque couleur a une paire background et foreground.

Exemple :

:root {
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
}

--primary est le fond du bouton, --primary-foreground le texte dessus. Modifier une variable met à jour tous les composants qui l’utilisent.

Liste des variables CSS

Variables par défaut shadcn/ui :

VariableUsage
--backgroundFond de page
--foregroundTexte de page
--cardFond de carte
--card-foregroundTexte de carte
--popoverFond de popover
--popover-foregroundTexte de popover
--primaryCouleur principale (boutons, liens)
--primary-foregroundTexte sur la couleur principale
--secondaryCouleur secondaire
--secondary-foregroundTexte sur la couleur secondaire
--mutedFond atténué
--muted-foregroundTexte atténué
--accentCouleur d’accent
--accent-foregroundTexte sur l’accent
--destructiveActions destructives (suppression)
--destructive-foregroundTexte sur destructive
--borderBordures
--inputChamps de saisie
--ringAnneau de focus

Une fois le couple background / foreground assimilé, le reste suit logiquement.

Le secret du format HSL

Les valeurs ne sont pas au format HSL classique :

/* ❌ HSL standard */
--primary: hsl(222.2, 47.4%, 11.2%);

/* ✅ format shadcn */
--primary: 222.2 47.4% 11.2%;

Pourquoi ce format « nu » ?

Tailwind gère les modificateurs d’opacité, par ex. bg-primary/50 pour 50 % d’opacité. Avec un hsl() complet dans la variable, cela ne fonctionne pas.

Au format nu, Tailwind ajoute hsl() et l’opacité automatiquement — astucieux.


3. Personnaliser le thème de votre marque

Méthode 1 : modifier directement les variables CSS

Ouvrez globals.css, section :root, changez les valeurs.

Passer du bleu par défaut au violet :

:root {
  --primary: 270 60% 60%;
  --primary-foreground: 0 0% 100%;
}

.dark {
  --primary: 270 60% 70%;
  --primary-foreground: 0 0% 0%;
}

Enregistrez : tous les boutons et liens en bg-primary deviennent violets.

Méthode 2 : espace colorimétrique OKLCH (Tailwind v4)

Avec Tailwind v4, OKLCH donne une perception plus uniforme qu’HSL et des palettes plus cohérentes.

:root {
  --primary: oklch(0.6 0.2 270);
  --primary-foreground: oklch(0.98 0 0);
}

Pour oklch(0.6 0.2 270) :

  • 0.6 — luminosité (0-1)
  • 0.2 — chroma (environ 0-0,4)
  • 270 — teinte en degrés (0-360)

Méthode 3 : générateur en ligne

Pour éviter de tout régler à la main : Shadcn Theme Generator

Choisissez une couleur principale ; l’outil produit l’ensemble des variables, clair et sombre. Copiez-collez dans globals.css.


4. Configuration du mode sombre

Bascule de thème avec next-themes

shadcn/ui n’inclut pas de bascule de thème ; next-themes s’en charge.

Installation :

npm install next-themes

Dans layout.tsx :

import { ThemeProvider } from "next-themes"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="fr" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

Points clés :

  • suppressHydrationWarning évite les avertissements d’hydratation
  • attribute="class" bascule via une classe
  • defaultTheme="system" suit le système par défaut
  • enableSystem active la détection du thème système

Bouton de bascule

Avec le hook useTheme :

import { useTheme } from "next-themes"
import { Moon, Sun } from "lucide-react"

export function ThemeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <button
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      className="p-2 rounded-md hover:bg-accent"
    >
      {theme === "dark" ? <Sun size={20} /> : <Moon size={20} />}
    </button>
  )
}

Mode sombre par défaut

Option 1 : classe dark en dur

<html lang="fr" className="dark">

Thème fixe sombre, sans bascule.

Option 2 : thème par défaut

<ThemeProvider
  attribute="class"
  defaultTheme="dark"  // sombre par défaut
  enableSystem={false} // pas de détection système
>

L’utilisateur peut basculer ; l’état initial est sombre.


5. Personnalisation avancée : variantes de composants

Variantes custom avec CVA

Pour des styles supplémentaires (danger, succès, dégradé), CVA simplifie la définition :

import { cva, type VariantProps } from "class-variance-authority"

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

Utilisation :

<button className={buttonVariants({ variant: "destructive", size: "lg" })}>
  Supprimer
</button>

Ne pas modifier le source shadcn d’origine

Bonne pratique : le code est dans votre repo, mais évitez de toucher les fichiers générés par shadcn. Les mises à jour imposent sinon des merges manuels.

Préférez un wrapper :

// components/brand-button.tsx
import { Button } from "@/components/ui/button"
import { cva } from "class-variance-authority"

const brandButtonVariants = cva("...", {
  variants: {
    brand: {
      primary: "bg-brand-primary text-white",
      secondary: "bg-brand-secondary text-black",
    },
  },
})

export function BrandButton({ brand, ...props }) {
  return <Button className={brandButtonVariants({ brand })} {...props} />
}

Le Button d’origine reste intact ; vos mises à jour shadcn ne cassent pas BrandButton.


6. Problèmes fréquents

Les styles ne s’appliquent pas après installation

Vérifiez :

  1. globals.css est-il importé dans layout.tsx ?
  2. La config content de Tailwind inclut-elle components/**/* ?
  3. Les chemins dans components.json sont-ils corrects ?

Clignotement au changement de thème

Souvent un problème d’hydratation :

  1. suppressHydrationWarning sur <html>
  2. ThemeProvider enveloppe toute l’application
  3. Ne lisez pas theme côté serveur (undefined en SSR)

Variables CSS ignorées

Causes possibles :

  1. Nom incorrect (--primary-foreground, pas --primaryForeground)
  2. Pas de bloc .dark correspondant
  3. Format invalide (HSL ou OKLCH « nu » requis)

Conflits de styles

Si un autre design system coexiste :

  1. Namespace sur les composants shadcn (ex. shadcn-button)
  2. Ajuster la priorité des layers Tailwind
  3. Variantes CVA propres, sans dépendre des styles par défaut

7. Synthèse

L’installation shadcn/ui est simple une fois comprise l’idée « copier le code plutôt qu’installer un paquet » : contrôle total, maintenance locale des composants.

Pour le thème, le système de variables CSS est élégant : quelques valeurs modifiées, toute l’app suit. Avec next-themes, clair/sombre tient en quelques lignes.

En bref :

  1. Nouveaux projets : privilégiez npx shadcn@latest init
  2. Variables sémantiques : primary, secondary, pas des noms de couleur bruts
  3. Contraste en clair et en sombre
  4. Wrappers plutôt que modification du source shadcn

La prochaine fois que vous montez une UI thématisée rapidement, essayez shadcn/ui — le plaisir du copier-coller contrôlé, il faut le vivre.



Références

Installation et personnalisation du thème shadcn/ui

Installer shadcn/ui depuis zéro, configurer le système de thème et obtenir un design de marque

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Initialisation rapide via CLI

    Dans un nouveau projet, lancez :

    • npx shadcn@latest init
    • Choisissez TypeScript / style New York / thème par défaut
    • Attendez la fin de la configuration par la CLI
  2. 2

    Step 2: Modifier la couleur principale de marque

    Éditez les variables CSS dans globals.css :

    • Ouvrez app/globals.css
    • Repérez --primary sous :root
    • Remplacez par votre couleur de marque (format HSL ou OKLCH)
    • Ajustez aussi --primary-foreground pour le contraste
  3. 3

    Step 3: Configurer le mode sombre

    Installez et configurez next-themes :

    • npm install next-themes
    • Ajoutez ThemeProvider dans layout.tsx
    • Définissez suppressHydrationWarning pour éviter les avertissements d'hydratation
    • Créez un composant de bascule de thème
  4. 4

    Step 4: Créer des variantes de composants

    Définissez des styles custom avec CVA :

    • Installez class-variance-authority
    • Définissez variants et defaultVariants
    • Appliquez buttonVariants() dans le composant
    • Gardez les composants shadcn d'origine intacts

FAQ

Quelle différence entre shadcn/ui et une bibliothèque UI classique ?
shadcn/ui n'est pas un paquet npm : vous copiez le source des composants dans votre projet. Avantage : contrôle total et personnalisation, sans conflits de versions. Inconvénient : vous maintenez le code des composants dans chaque projet.
Pourquoi installer shadcn/ui dès l'initialisation d'un nouveau projet ?
La commande init de shadcn écrase tailwind.config.js et globals.css. Si le projet est déjà avancé, vos réglages existants seront perdus — mieux vaut installer tôt.
Pourquoi les variables CSS sont-elles au format « nu » plutôt qu'en HSL standard ?
Le format nu (ex. 222.2 47.4% 11.2%) permet les modificateurs d'opacité Tailwind, comme bg-primary/50 pour 50 % d'opacité. Avec un hsl() complet, cette fonctionnalité ne fonctionne pas.
Comment modifier la couleur principale de marque ?
Ouvrez globals.css, modifiez --primary et --primary-foreground sous :root avec vos valeurs. Tous les composants utilisant bg-primary se mettront à jour automatiquement.
Pourquoi le mode sombre clignote-t-il au chargement ?
Souvent à cause d'un décalage d'hydratation. Ajoutez suppressHydrationWarning sur la balise html, enveloppez toute l'app avec ThemeProvider, et ne lisez pas theme côté serveur lors du rendu SSR.
Faut-il modifier directement le source des composants shadcn ?
Déconseillé. shadcn met souvent à jour ses composants ; si vous modifiez les fichiers d'origine, les mises à jour imposent des fusions manuelles. Préférez des composants wrapper en gardant le source intact.

7 min de lecture · Publié le: 26 mars 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog