Prise en main

Installation

التثبيت

Une commande installe DSM dans votre application Next.js : les sources sont copiées dans votre projet, puis évoluent comme le reste de votre code.

Installation en une commande

La CLI dsm-maroc détecte votre projet (dossier src, gestionnaire de paquets, alias @/), copie les sources et installe les dépendances.

terminal
npx dsm-maroc@latest init          # fondations, composants, polices et motifs
npx dsm-maroc@latest add button    # ou un seul composant et ce qu'il importe
npx dsm-maroc@latest list          # tous les composants disponibles

Ce que fait init

Copie src/dsm/, ajoute app/dsm.css importé depuis votre globals.css, dépose app/fonts.ts et public/patterns/, puis installe Base UI, CVA, clsx, tailwind-merge et Lucide. Il ne touche jamais à un fichier existant sans --overwrite. Il reste ensuite à brancher les fournisseurs (étape 3).

Le paquet est publié sur npm : npmjs.com/package/dsm-maroc. Chaque version embarque un instantané des sources ; pour mettre à jour un composant, relancez npx dsm-maroc@latest add button --overwrite et relisez le diff.

Le fichier dsm.css installé conserve la palette Tailwind de votre application. La documentation, elle, la désactive (--color-*: initial) pour n'autoriser que les jetons DSM ; ajoutez cette ligne dans le bloc @theme inline si vous voulez la même discipline.

Prérequis

DSM cible une base technique précise ; vérifiez-la avant de copier les sources.

  • Node.js 20 ou plus

    Requis par Next.js 16 et par la résolution de modules utilisée dans les scripts du projet.

  • Next.js 16 (App Router)

    Les composants sont des Server Components par défaut ; le routeur Pages n'est pas pris en charge.

  • Tailwind CSS v4

    Les jetons DSM s'appuient sur @theme inline et @utility, deux mécanismes propres à la v4.

Installation manuelle — 1. Installer les dépendances

Si vous préférez copier les sources vous-même, les trois étapes suivantes reproduisent ce que fait la CLI.

terminal
pnpm add @base-ui/react class-variance-authority clsx tailwind-merge lucide-react
pnpm add -D tailwindcss @tailwindcss/postcss

2. Copier les fichiers DSM

Quatre emplacements suffisent : les composants, les jetons, les motifs et les polices.

terminal
cp -r dsm/src/dsm ./src/dsm
cp dsm/src/app/globals.css ./src/app/globals.css
cp dsm/src/app/fonts.ts ./src/app/fonts.ts
cp -r dsm/public/patterns ./public/patterns

3. Brancher les fournisseurs

ThemeProvider et LocaleProvider s'installent une seule fois, dans le layout racine. themeInitScript s'exécute avant l'hydratation pour éviter tout flash de thème.

src/app/layout.tsx
import Script from "next/script";
import { fontVariables } from "./fonts";
import { ThemeProvider, themeInitScript } from "@/dsm/components/theme";
import { LocaleProvider } from "@/dsm/i18n/provider";
import "./globals.css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="fr" dir="ltr" className={fontVariables} suppressHydrationWarning>
      <head>
        <Script id="dsm-theme-init" strategy="beforeInteractive">{themeInitScript}</Script>
      </head>
      <body>
        <ThemeProvider>
          <LocaleProvider locale="fr">{children}</LocaleProvider>
        </ThemeProvider>
      </body>
    </html>
  );
}

La direction RTL est déjà prise en charge

LocaleProvider enveloppe ses enfants dans le DirectionProvider de Base UI. N'ajoutez pas votre propre fournisseur de direction : cela créerait deux sources de vérité pour le sens d'écriture.

4. Utiliser les jetons dans Tailwind

globals.css mappe chaque variable --dsm-* sur une couleur Tailwind via @theme inline : vous écrivez bg-canvas, pas var(--dsm-canvas).

// Dans n'importe quel composant de votre application
<div className="bg-surface text-ink border border-line rounded-lg p-6">
  <button className="bg-primary text-primary-fg hover:bg-primary-hover rounded-md px-4 h-11">
    Continuer
  </button>
</div>

5. Ajouter ou modifier une locale

Une locale se déclare à trois endroits : la liste des locales, ses métadonnées, puis son dictionnaire complet.

src/dsm/i18n/index.ts
export const locales: Locale[] = ["fr", "ar", "zgh", "en"];

export const localeMeta: Record<Locale, …> = {
  // …
  en: { code: "en", dir: "ltr", label: "Anglais", nativeLabel: "English", short: "EN", fontClass: "font-sans" },
};

export const ui: Record<Locale, UiStrings> = {
  // Ajoutez la clé manquante dans LES QUATRE dictionnaires, jamais un seul
  fr: { /* … */ },
  ar: { /* … */ },
  zgh: { /* … */ },
  en: { /* … */ },
};

Structure de projet

arborescence
src/
├── app/
│   ├── fonts.ts              # IBM Plex Sans/Arabic, Noto Sans Tifinagh, IBM Plex Mono
│   ├── globals.css           # jetons, thème Tailwind, utilitaires dsm-*
│   └── layout.tsx            # ThemeProvider + LocaleProvider + polices
├── dsm/
│   ├── components/           # button.tsx, card.tsx, header.tsx, theme.tsx…
│   ├── i18n/
│   │   ├── index.ts           # locales, UiStrings, dictionnaires
│   │   └── provider.tsx       # LocaleProvider, useLocale, useT
│   ├── icons/
│   │   ├── index.tsx           # jeu Lucide curaté + glyphes directionnels
│   │   └── social.tsx
│   ├── lib/cn.ts
│   └── index.ts               # ré-export de tout src/dsm
public/
└── patterns/                  # khatam.svg, khatam-fill.svg, band.svg

Dépannage

Les polices ne se chargent pas hors ligne

next/font/google télécharge les fichiers de police à la compilation. En environnement sans accès réseau, hébergez vous-même les fichiers woff2 et remplacez les appels de next/font/google danssrc/app/fonts.ts par next/font/local, en conservant les mêmes noms de variable CSS.

« Functions cannot be passed directly to Client Components »

Un Server Component ne peut pas passer une fonction (un onClick, un résolveur de lien…) en prop à un composant client. Remplacez la fonction par une donnée sérialisable — par exemple une table localeLinks: Record<Locale, string> — et laissez le composant client choisir l'entrée à afficher plutôt que d'exécuter une fonction reçue du serveur.

Avertissement d'hydratation autour de data-theme

L'attribut data-theme est posé côté client par themeInitScript avant l'hydratation : le HTML rendu par le serveur ne le contient pas. Gardez suppressHydrationWarning sur la balise <html> pour que React ignore cette différence attendue, sans le propager plus bas dans l'arbre.