
# Système de design

`design` est l'endroit où vit le système de design de votre application. On y trouve les jetons — les mêmes que `theme` a toujours portés — plus tout ce qu'un système de design doit transmettre et que des jetons seuls ne peuvent pas porter : les principes qui les justifient, la marque autour de laquelle il est construit, la voix de l'application, ce à quoi sert chaque couleur, l'échelle sur laquelle sa typographie est composée, le registre dans lequel ses images travaillent, et ce à quoi sert chaque composant.

C'est ce dernier point qui compte. Une palette de valeurs hexadécimales dit tout à un moteur de rendu et rien à un humain. `primary: '#3b5bdb'` ne précise pas s'il s'agit d'un fond de bouton d'action ou d'une couleur de texte courant : celui qui ajoutera une page plus tard — un collègue, ou un agent IA travaillant dans votre dépôt — choisit au jugé, et le design dérive. `design` rend ces règles déclarables, et `sovrium design-system` les transmet sous forme de fichier.

```yaml
design:
  theme:
    colors:
      primary: '#3b5bdb'
      primary-fg: '#ffffff'

  principles:
    - La retenue plutôt que l'ornement
    - La couleur est dépensée sur l'erreur, et nulle part ailleurs

  logo:
    src: /logos/logotype.svg
    srcDark: /logos/logotype-clair.svg
    alt: Acme
    minWidth: 96px
    misuse:
      - Ne jamais recolorer la marque.

  typeScale:
    h1:
      size: '3rem'
      lineHeight: 1.1
      weight: 700
    body:
      size: '1rem'
      lineHeight: 1.6

  imagery:
    iconSet: Lucide
    photography:
      - Aucune image de banque d'images.

  voice:
    personality: [chaleureux, direct, jamais condescendant]
    pronoun: tu
    prefer:
      - "Commence chaque appel à l'action par son verbe : Déployer, Enregistrer, Supprimer."
      - Chaque état vide porte une ligne de guidage nommant la prochaine action.
    avoid:
      - Aucun point d'exclamation dans l'interface produit.
      - Aucun emoji.
    tone:
      empty: Dis ce que c'est, puis la seule prochaine action.
      loading: Dis combien de temps, et qu'on peut quitter la page.
      error: Énonce la contrainte, puis propose deux issues. Jamais d'accusation.
      success: Une ligne, terminée par un point. Pas de feu d'artifice.
      destructive: Nomme ce qui est supprimé, en quelle quantité, et si c'est réversible.

  colorRoles:
    primary:
      usage: Fond de bouton d'action principal uniquement. Jamais de texte courant, jamais une pastille de statut.
      pairsWith: primary-fg

  components:
    section-header:
      usage: Un bandeau titré qui introduit une section de page.
      when: À utiliser au-dessus de toute section comptant plus de trois enfants.
      dont: Ne jamais en imbriquer un dans un autre — les niveaux de titre entrent en collision.
```

## `design.theme` et l'alias `theme`

`design.theme` est l'emplacement canonique des jetons de design. Il accepte exactement ce qu'accepte la clé `theme` de premier niveau, catégorie par catégorie et sans changement : `colors`, `darkColors`, `fonts`, `spacing`, `shadows`, `borderRadius`, `breakpoints`, `animations`, `baseline`, `colorScheme`, `codeBlock`.

**La clé `theme` de premier niveau fonctionne toujours.** C'est un alias pris en charge, et toutes les configurations existantes continuent de fonctionner sans modification.

:::callout
**Déclarer les deux est une erreur, pas une fusion.** Si `theme` et `design.theme` sont tous deux présents, `sovrium validate` refuse la configuration et indique la correction. Une fusion devrait désigner un gagnant, et le côté perdant ne s'appliquerait nulle part sans que rien ne le signale — Sovrium refuse donc plutôt que de deviner.
:::

```yaml
# Canonique
design:
  theme:
    colors:
      primary: '#3b5bdb'

# Toujours pris en charge — un alias déprécié
theme:
  colors:
    primary: '#3b5bdb'
```

`sovrium validate` affiche un avis de dépréciation lorsqu'il rencontre la forme de premier niveau, sur la sortie d'erreur, et se termine tout de même avec le code `0`. L'alias sera retiré à la prochaine version majeure.

## Principes

`design.principles` est une liste ordonnée des convictions qui justifient vos jetons. Elle s'affiche en tête de l'export : celui qui le lit obtient le raisonnement avant les valeurs.

```yaml
design:
  principles:
    - La retenue plutôt que l'ornement
    - Le visiteur est le héros, pas le produit
```

## Logo

`design.logo` porte la marque et les règles pour la placer — la première section de toute charte de marque.

```yaml
design:
  logo:
    src: /logos/logotype.svg
    srcDark: /logos/logotype-clair.svg
    alt: Acme
    clearSpace: Laisser une zone de protection égale à la hauteur de la marque sur les quatre côtés.
    minWidth: 96px
    misuse:
      - Ne jamais recolorer la marque.
      - Ne jamais l'étirer, la faire pivoter ni lui ajouter d'effets.
      - Ne jamais la poser sur une photographie chargée sans aplat de fond.
```

| Champ        | Remarques                                                                                            |
| ------------ | ---------------------------------------------------------------------------------------------------- |
| `src`        | **Obligatoire.** La marque principale, telle qu'elle apparaît sur la surface claire par défaut.      |
| `srcDark`    | La variante affichée quand l'interface est en **mode sombre**. Voir l'avertissement ci-dessous.      |
| `alt`        | **Obligatoire.** Le nom accessible — en général le seul nom de l'application.                        |
| `clearSpace` | La zone de protection, en toutes lettres.                                                            |
| `minWidth`   | La plus petite largeur à laquelle la marque peut être reproduite : un nombre suivi de `px` ou `rem`. |
| `misuse`     | Ce qu'il ne faut jamais faire à la marque.                                                           |

`alt` est obligatoire avec `src` parce qu'une marque sans nom accessible est un défaut, et non une déclaration partielle : elle parvient à un lecteur d'écran comme un pur néant. Écrivez le nom de l'application, pas `logo` : un lecteur d'écran annonce déjà l'élément comme une image.

`clearSpace` est délibérément du texte et non une dimension. Toute charte digne de ce nom exprime la zone de protection **par rapport à la marque** — « la hauteur du S sur les quatre côtés » — parce que c'est la règle qui survit à un changement de taille. `minWidth` est une dimension parce que, contrairement à la zone de protection, il s'agit réellement d'un seul nombre, et c'est le nombre qu'un consommateur peut faire respecter.

`misuse` est la moitié qui change quelque chose. « Utilisez le logotype » n'apprend rien à un designer qui allait le faire de toute façon ; « ne jamais le recolorer, ne jamais le poser sur une photographie chargée » est la phrase qui empêche ce que vous ne vouliez pas.

:::callout
**`srcDark` est nommé d'après le MODE, pas d'après l'encre.** C'est le champ le plus souvent rempli à l'envers, parce que les fichiers de logo sont d'ordinaire nommés d'après leur encre et que les deux conventions sont inverses. Un fichier à encre **foncée** est celui qu'on affiche en mode **clair** : il va donc dans `src`, et le fichier à encre claire va dans `srcDark`. Omettez complètement `srcDark` lorsqu'une seule marque se lit sur les deux surfaces — exiger un second fichier inviterait un doublon qui se périmerait ensuite.
:::

### Où vit le fichier

`src` et `srcDark` portent une **référence**, pas des octets, et la seule chose qu'un consommateur puisse en faire est de la placer dans un attribut `src=`. Deux formes sont acceptées :

- **Chemin relatif à la racine** — `/logos/logotype.svg`. Cette forme couvre les deux emplacements de stockage avec une seule règle : un fichier du répertoire public de l'application est servi à `/nom.ext`, et un objet de bucket à `/api/buckets/{bucket}/files/{chemin}`.
- **URL absolue en `https://`** — `https://cdn.example.com/logotype.svg`, pour une marque servie par un CDN ou détenue par un tiers.

Tout le reste est refusé, chaque fois pour une raison :

| Refusé                                | Pourquoi                                                                                                                                                                                                                                                                                |
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Un chemin relatif nu (`logotype.svg`) | Il se résout par rapport à la **page courante** : la même déclaration charge `/logotype.svg` sur la page d'accueil et `/fr/docs/guide/logotype.svg` dans la zone documentation. Il n'existe aucune façon d'en écrire un correct : n'accepter que des erreurs serait donc le seul effet. |
| `http://`                             | Une application en https qui charge une image en http produit du contenu mixte : le navigateur le bloque en silence et la marque est simplement absente.                                                                                                                                |
| `data:`                               | Insérer un logotype dans le HTML de chaque page se paie à chaque requête et ne se met en cache sur aucune.                                                                                                                                                                              |

**Privilégiez le SVG.** Un logotype est un dessin au trait : indépendant de la résolution, plus léger que n'importe quel encodage matriciel, net à toutes les densités, et sans aucune question de codec. La règle AVIF qui régit les [images versionnées](/fr/docs/ecoconception) concerne les captures d'écran, pas les marques : le logotype d'un client est sa marque déposée, et le réencoder n'appartient pas à Sovrium. Une marque déclarée ici est servie telle quelle et n'entre jamais dans le pipeline de transformation d'images à l'exécution.

## Voix

`design.voice` décrit la façon dont votre application s'exprime, quelle que soit la situation. Tous les champs sont optionnels.

| Champ         | Type       | Contenu                                                                           |
| ------------- | ---------- | --------------------------------------------------------------------------------- |
| `personality` | `string[]` | Les traits valables partout : `['chaleureux', 'direct', 'jamais condescendant']`. |
| `pronoun`     | `string`   | La façon dont l'application s'adresse au lecteur — `tu`, `vous`, `you`, `Sie`.    |
| `prefer`      | `string[]` | Les tournures à privilégier, formulées comme des consignes.                       |
| `avoid`       | `string[]` | Les tournures à refuser.                                                          |
| `tone`        | `object`   | La façon dont le registre change selon la situation — voir ci-dessous.            |

`pronoun` est une chaîne libre, et non une liste fermée, parce que le registre dépend de la langue : une application française tranche entre `tu` et `vous`, une application anglophone n'a pas ce choix à faire. Une liste fermée refuserait la première langue qu'elle n'énumère pas.

`prefer` et `avoid` sont les deux moitiés d'une charte rédactionnelle. Portez l'effort sur `avoid` : ce sont les refus qui arrêtent un texte hors marque mais plausible.

### Ton

`design.voice.tone` couvre les cinq moments où un système prend la parole. Chaque valeur est une consigne qui dit _comment_ écrire ce moment, pas la chaîne littérale : les chaînes dépendent de la locale et vivent dans [`languages`](/fr/docs/languages).

| Clé           | Le moment                                                        |
| ------------- | ---------------------------------------------------------------- |
| `empty`       | Une table sans aucune ligne, une fonctionnalité jamais utilisée. |
| `loading`     | Une tâche longue pendant laquelle le lecteur attend.             |
| `error`       | Tout ce qui arrête le lecteur : validation, réseau, refus.       |
| `success`     | Un enregistrement confirmé, un déploiement terminé.              |
| `destructive` | Une confirmation qui doit nommer sa conséquence.                 |

Déclarez les moments auxquels vous avez réfléchi et omettez les autres. Une clé absente vaut mieux qu'une consigne inventée pour remplir le tableau.

## Rôles de couleur

`design.colorRoles` répond à la question à laquelle `theme.colors` ne peut pas répondre : à quoi sert cette couleur ?

```yaml
design:
  theme:
    colors:
      primary: '#3b5bdb'
      primary-fg: '#ffffff'
  colorRoles:
    primary:
      usage: Fond de bouton d'action principal uniquement. Jamais de texte courant.
      pairsWith: primary-fg
```

`usage` dit à quoi elle sert et — plus utile encore — à quoi elle ne sert pas. `pairsWith` nomme le jeton avec lequel celui-ci est conçu pour se marier, en général son pendant de premier plan : le lecteur n'a alors pas à remesurer le contraste.

**Chaque clé doit nommer une couleur déclarée dans votre palette.** Un rôle documentant un jeton inexistant est un guidage sur lequel personne ne peut agir, et c'est en pratique une faute de frappe — `sovrium validate` le refuse donc et liste les jetons qui existent réellement. `pairsWith` n'est délibérément **pas** vérifié de la même façon : un pendant légitime est souvent un jeton de rôle de la plateforme que votre application n'a jamais redéclaré.

## Échelle typographique

`design.typeScale` est l'échelle ordonnée sur laquelle votre typographie est composée — douze niveaux nommés, de `display` jusqu'à `overline`, chacun avec sa taille, son interlignage, sa graisse et son approche.

```yaml
design:
  typeScale:
    h1:
      size: '3rem'
      lineHeight: 1.1
      weight: 700
      letterSpacing: '-0.02em'
    body:
      size: '1rem'
      lineHeight: 1.6
```

C'est la seule clé de `design` qui émet du **CSS** : chaque niveau déclaré devient une propriété personnalisée `--text-{niveau}` avec ses modificateurs, et un utilitaire Tailwind `text-{niveau}`. C'est ce qui en fait le remplaçant opérationnel des trois champs inertes de `theme.fonts` signalés en fin de page.

Voir [Échelle typographique](/fr/docs/design-type-scale) pour l'échelle complète, les règles d'unités, et ce que l'export en fait.

## Imagerie

`design.imagery` couvre l'apparence de l'application là où les jetons n'atteignent rien. Deux pages peuvent utiliser exactement les mêmes jetons et ressembler malgré tout à deux produits différents, parce que l'une a choisi une photo de banque d'images montrant des gens qui pointent un tableau blanc et l'autre une capture du produit en fonctionnement.

```yaml
design:
  imagery:
    principles:
      - Montrer le produit qui fonctionne, jamais une métaphore du produit.
      - Une personne dans une image fait son travail, elle ne pose pas.
    photography:
      - Aucune image de banque d'images.
      - Lumière naturelle uniquement — aucun étalonnage vers une teinte de marque.
      - Les captures d'écran sont prises en 2x sur un fond neutre.
    iconSet: Lucide
    patterns:
      - Une seule texture de grain de papier à 4 % d'opacité, jamais plus d'une surface par écran.
      - Les illustrations sont au trait d'épaisseur unique dans la couleur signature, jamais en aplat.
```

| Champ         | Type       | Contenu                                                                                                                    |
| ------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------- |
| `principles`  | `string[]` | Le registre dans lequel les images travaillent, sous forme de convictions. Le pendant de `design.principles` pour l'image. |
| `photography` | `string[]` | Les règles concrètes qu'une personne applique en choisissant ou en photographiant.                                         |
| `iconSet`     | `string`   | Le nom de l'**unique** bibliothèque d'icônes dont toutes les icônes proviennent.                                           |
| `patterns`    | `string[]` | Les marques non photographiques : texture, style d'illustration, géométrie de fond.                                        |

`principles` et `photography` sont séparés parce qu'un principe est un étalon de jugement et qu'une règle est une contrainte à respecter. « Montrer le produit qui fonctionne » est un principe ; « aucune image de banque d'images, jamais » est une règle, et les confondre fait passer la règle pour négociable.

`iconSet` est le champ au plus fort effet de levier. La dérive des icônes ne vient pas de quelqu'un qui choisit une _mauvaise_ icône : elle vient de trois auteurs qui choisissent chacun une icône _raisonnable_ dans trois bibliothèques différentes, après quoi aucune discipline sur les jetons ne fera ressembler la barre d'outils à un seul produit. Nommer la bibliothèque une fois supprime la décision. C'est un **nom**, et non une URL ou un identifiant de paquet : ce dont le lecteur a besoin, c'est de savoir dans quelle bibliothèque chercher — une version épinglée répond à une autre question et se périme à chaque montée de version.

**Rien dans `imagery` ne nomme de fichier**, délibérément. Un logo est un artefact précis avec une URL ; l'imagerie est une classe d'artefacts assortie de règles, et les images elles-mêmes se déclarent là où elles servent : dans les pages, dans les enregistrements, dans les buckets. Une liste d'actifs ici aurait l'air de faire quelque chose et serait en réalité une seconde copie, non affichée, qui se périmerait dès que l'un des deux côtés changerait.

## Guidage des composants

`design.components` donne à vos propres composants réutilisables autre chose que leur nom à présenter.

```yaml
components:
  - name: section-header
    type: container
    children:
      - type: text
        element: h2
        content: $title

design:
  components:
    section-header:
      usage: Un bandeau titré qui introduit une section de page.
      when: À utiliser au-dessus de toute section comptant plus de trois enfants.
      dont: Ne jamais en imbriquer un dans un autre — les niveaux de titre entrent en collision.
```

Trois champs, trois questions différentes : `usage` dit ce que c'est, `when` la situation qui le désigne plutôt qu'un voisin, et `dont` le mésusage à refuser. Tous optionnels. **Chaque clé doit nommer un `components[].name` déclaré** — un guidage rattaché à un composant inexistant ne s'affiche nulle part, et la cause habituelle est un renommage appliqué d'un seul côté.

## L'exporter

Tout l'intérêt de déclarer cela est de pouvoir le transmettre. Un seul générateur produit les trois surfaces : elles ne peuvent donc pas diverger sur ce qu'est votre système de design.

### `sovrium design-system`

```bash
# Le brief pour agent, sur la sortie standard
sovrium design-system app.yaml

# Versionné à côté de la configuration, référencé depuis vos consignes d'agent
sovrium design-system app.yaml --output DESIGN.md

# Le document de jetons, pour l'outillage
sovrium design-system app.yaml --format json --output tokens.json
```

Le markdown est le format par défaut, parce que le lecteur par défaut est un modèle. La commande s'exécute hors ligne — sans serveur, sans base de données — et convient donc à un hook de pré-commit ou à une étape d'intégration continue. Elle refuse un `--format` inconnu, et refuse une configuration qui échoue à la validation plutôt que d'exporter un système de design décrivant une application incapable de démarrer.

### Les points d'accès

Les deux sont réservés aux administrateurs, en lecture seule, et renvoient `404` à toute autre personne.

| Point d'accès                       | Format                                                                                       |
| ----------------------------------- | -------------------------------------------------------------------------------------------- |
| `GET /api/admin/design-system.json` | Document [W3C Design Tokens (DTCG) 2025.10](https://www.designtokens.org/tr/drafts/format/). |
| `GET /api/admin/design-system.md`   | Le même brief que celui affiché par la commande.                                             |

La [console du système de design](/fr/docs/design-system-console) restitue le même contenu sous forme de page lisible par une personne, et peut le publier derrière un lien révocable à destination de quelqu'un qui n'a pas de compte.

Dans le document JSON, les jetons apparaissent sous les types DTCG standard — les couleurs comme objets `{colorSpace, components, hex}`, les dimensions comme `{value, unit}` — l'échelle typographique comme jetons composites `typography`, et la couche propre à Sovrium (principes, logo, voix, rôles de couleur, imagerie, guidage des composants) voyage dans `$extensions` sous la clé `com.sovrium.design-system`, ce à quoi DTCG réserve précisément `$extensions`.

### Ce que l'export laisse délibérément hors de l'arbre de jetons

Certaines valeurs n'ont pas de forme DTCG fidèle, et un jeton mal formé est pire qu'un jeton absent — un outil agirait dessus. Celles-ci conservent leur texte brut dans `$extensions` au lieu d'être forcées dans un type standard :

| Valeur                                                 | Raison                                                                                                                                                            |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `theme.shadows.*`                                      | Les ombres DTCG sont décomposées en `color`/`offsetX`/`offsetY`/`blur`/`spread` ; une chaîne CSS `box-shadow` brute ne peut pas être reconvertie de façon fiable. |
| Une valeur d'espacement comme `clamp(1rem, 2vw, 3rem)` | Une dimension DTCG, c'est un nombre et une unité.                                                                                                                 |
| `theme.darkColors`                                     | DTCG n'a pas de notion de mode ou de schéma dans cette version.                                                                                                   |

## Trois champs de `theme.fonts` remplacés

Ceux-ci passent la validation, puis rien ne les lit. Ils sont signalés ici, et nommés dans l'export sous `$extensions`, pour que vous n'entreteniez pas une valeur sans effet :

| Champ                      | Ce qui se passe réellement                                                                                                                                                                                          |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `theme.fonts.*.lineHeight` | **N'atteint rien.** Aucune propriété CSS personnalisée n'est émise pour ce champ.                                                                                                                                   |
| `theme.fonts.*.size`       | N'atteint que l'ancien moteur de rendu de section `hero`, sous forme de taille de police en ligne. Ce champ ne devient jamais une variable CSS.                                                                     |
| `theme.fonts.*.weights`    | Seule la **première** entrée est lue, et uniquement par ce même moteur `hero`. Ce champ n'atteint jamais une règle `@font-face` : `weights: [300, 400, 700]` ne charge donc aucun fichier de police supplémentaire. |

**Déclarez plutôt les tailles, l'interlignage, la graisse et l'approche dans [`design.typeScale`](/fr/docs/design-type-scale)**, où chaque niveau émet une véritable propriété personnalisée CSS et un utilitaire `text-{niveau}` utilisable. Pour charger plusieurs graisses d'une police, déclarez explicitement les variantes nécessaires dans `theme.fonts`.

`sovrium validate` affiche un avis `Superseded:` nommant chaque chemin déclaré, et se termine avec le code `0`. **Remplacés, et non dépréciés** : « déprécié » signifie _ceci fonctionnait et va disparaître_, et ces trois champs n'ont jamais fonctionné. Ils continuent d'être décodés jusqu'à la prochaine version majeure, aux côtés de l'alias `theme` de premier niveau — les refuser maintenant arrêterait une application qui démarre sans rien changer au rendu. Aucune conversion n'est faite pour vous non plus : `typeScale` est par **niveau** là où `theme.fonts` est par **police**, si bien qu'une traduction automatique devrait deviner quelle police correspond à quel barreau.

## Pages associées

- [Aperçu du thème et couleurs](/fr/docs/theme) — les catégories de jetons en détail.
- [Échelle typographique](/fr/docs/design-type-scale) — `design.typeScale` en détail.
- [Typographie](/fr/docs/theme-typography) — `theme.fonts`, les polices dans lesquelles un niveau est composé.
- [Console du système de design](/fr/docs/design-system-console) — la page de console et le lien de partage révocable.
- [Validation et génération de schéma](/fr/docs/cli-validate) — `sovrium design-system` aux côtés de `validate` et `schema`.
- [Tableau de bord d'administration](/fr/docs/admin-dashboard) — l'API de lecture réservée aux administrateurs.
- [Aperçu du schéma d'application](/fr/docs/schema-overview) — toutes les propriétés racine.
