Prise en main
Installation
التثبيت
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.
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 disponiblesCe que fait init
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.
pnpm add @base-ui/react class-variance-authority clsx tailwind-merge lucide-react
pnpm add -D tailwindcss @tailwindcss/postcss2. Copier les fichiers DSM
Quatre emplacements suffisent : les composants, les jetons, les motifs et les polices.
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/patterns3. 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.
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.
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
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.svgDé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.