# Le Kit — l'UI déclarative des apps

> Votre app ne produit jamais d'HTML : elle renvoie un arbre JSON de composants (le Kit) que Capibara rend avec ses propres composants, à sa charte — page d'app, panneau sur une fiche, widget, page du site public, formulaire.
> Catégorie : Référence · mis à jour le 2026-09-04 · plateforme v1

## Le principe

Une app Capibara décrit son interface en **JSON** et la plateforme la rend
avec **ses** composants : même look que le reste de Capibara dans le
back-office, thème du site du tenant sur le public. Vous n'écrivez ni HTML,
ni CSS, ni JavaScript côté client — vous composez des blocs, comme les
composants d'un message Discord ou les blocs d'un message Slack, mais avec un
vrai langage de mise en page (colonnes, volets, onglets, assistants, tableaux
paginés, formulaires typés, sections du site builder).

Ce que ça vous garantit : accessibilité, responsive, mode sombre, sécurité
(aucune injection possible) et une interface que vos utilisateurs
reconnaissent immédiatement. Ce que ça vous interdit : du CSS libre, des
couleurs hors thème, des scripts. C'est voulu.

**Essayez tout de suite** : le [Kit Studio](/dev/kit-studio) rend votre JSON
en direct dans chaque surface, avec des données d'exemple, et exporte le code
TypeScript équivalent. La palette se **glisse-dépose** sur l'aperçu (le
composant s'ajoute au document, comme au clic) et chaque page d'app du portail
a son onglet **Studio**, avec un brouillon propre au projet.

## Le document

```json
{
  "kit": 1,
  "title": "Commandes",
  "ui": { "type": "stack", "gap": "lg", "children": [ … ] },
  "state": "…",
  "components": { "pill": { "props": ["label"], "ui": { "type": "badge", "text": "{{ props.label }}" } } }
}
```

- `kit` : version du contrat (obligatoire). Un composant inconnu d'une
  version future est rendu par son `fallback` déclaré, sinon par une note
  « mise à jour requise » — jamais un écran vide.
- `ui` : l'arbre de composants.
- `state` : un état opaque (≤ 8 Ko), signé par la plateforme, qui voyage
  avec la surface — pagination, assistants multi-étapes, sans base.
- `components` : vos composants réutilisables, instanciés par
  `{ "type": "$ref", "component": "pill", "props": { "label": "A" } }`.

## Les surfaces

| Surface | Où | Contexte reçu |
|---|---|---|
| `page` | page d'app du back-office (`/apps/<clé>/<page>`), entrée de menu | utilisateur, route, état |
| `panel` | fiche **contact ou société** du CRM (`subject.type = 'party'` — le seul sujet ouvert aujourd'hui ; les fiches des autres modules viendront avec leur propre valeur) | utilisateur + `subject { type, id }` |
| `widget` | carte de la grille d'accueil | utilisateur |
| `siteBlock` | bloc posé par l'auteur du site dans une page de son site public (« Vos apps » de l'éditeur) | `visitor` (membre du site connecté ou `null`), champs du bloc dans `params` — jamais de `viewer` |
| `sitePage` | page publique du site de l'organisation, à l'adresse qu'elle choisit (Mon site › Apps) | `visitor`, `params.path` / `params.query` |
| `form` | modal ou page de saisie ouverte par une action | utilisateur + valeurs |
| `settings` | réservée : le protocole sait la rendre mais aucun écran du tenant ne l'ouvre encore — les réglages d'une app passent par `settings.fields` du manifeste (cf. [Données](/dev/docs/donnees)) | config courante |

## Gabarits : dessiner puis ne coder que la donnée

Le Studio produit un **gabarit** ; votre code le remplit avec
`ui.render(template, data)` — le serveur reste la source, le dessin reste le
dessin.

- `{{ chemin }}` dans une chaîne : interpolation. Une chaîne qui n'est QUE
  la liaison garde le **type** de la valeur (`"{{ order.total }}"` → nombre).
- `{ "$bind": "chemin" }` : remplacé par la valeur (tableau, objet…).
- `{ "type": "each", "$each": "orders", "$as": "o", "node": { … } }` : répète
  `node` pour chaque élément (`{{ o.ref }}`, `{{ $index }}`), dans n'importe
  quel tableau (`children`, `rows`, `items`, `options`…).
- `"$if": "chemin"` sur un élément de tableau : retiré si faux (`"!chemin"`
  inverse).

Chemins : `a.b.c`, `a[0].b`, `$index`, `$data` (racine). Pas d'expression,
pas de code : rien ne s'exécute dans un gabarit.

## Actions

Un bouton, un menu, une ligne de tableau, un formulaire portent une
`action` (identifiant) et des `params`. Au clic, la plateforme invoque votre
handler avec l'action, les paramètres, les valeurs du formulaire et l'état ;
vous répondez par une nouvelle UI, un `toast`, une navigation interne, un
`patch`, l'ouverture d'un formulaire ou un simple `ack` suivi d'un
`followup`. Les liens (`href`) acceptent un chemin interne, `https://`,
`mailto:` et `tel:` — jamais autre chose.

## Style borné

`tone` (neutre, plume, gorge, ambre, rouge), `surface` (plein, carte,
muet), `gap`, `size`, `align`, ratios d'image, icônes Lucide par nom.
Aucune couleur hexadécimale, aucune police, aucun CSS : c'est le thème qui
décide, pas l'app.

## Accessibilité (dans le schéma)

`alt` obligatoire sur une image, `label` sur toute saisie, `ariaLabel` sur
un bouton sans libellé. Un document qui l'oublie est refusé à la validation.

## Bornes

| Borne | Valeur |
|---|---|
| Composants par document | 200 (page et page de site : 500) |
| Profondeur | 8 |
| Texte cumulé | 32 Ko |
| Images | 20 |
| Lignes d'un tableau | 100 |
| Éléments d'une liste | 200 |
| Options d'une saisie | 200 |
| État (`state`) | 8 Ko |
| Composants déclarés | 40 (imbrication ≤ 4) |
| Types de composants | 38 |

## Catalogue des composants (généré depuis le schéma)

Le JSON Schema complet est servi sur [`/dev/kit/schema.json`](/dev/kit/schema.json)
(complétion dans le Studio, validation dans votre éditeur, lecture par un
assistant IA).

### Mise en page

Structurer l'écran : piles, grilles, colonnes, volets, onglets, assistants.

#### `stack` — Pile

Empile ses enfants verticalement (ou en ligne) avec un espacement régulier — la brique de base de toute page.

| Propriété | Type | Requis |
|---|---|:-:|
| `direction` | `vertical` · `horizontal` |  |
| `gap` | `none` · `sm` · `md` · `lg` |  |
| `align` | `start` · `center` · `end` · `stretch` |  |
| `justify` | `start` · `center` · `end` · `between` |  |
| `wrap` | booléen |  |
| `children` | liste de nœuds | oui |

#### `grid` — Grille

Répartit ses enfants en colonnes égales (2 à 4), qui se replient sur mobile.

| Propriété | Type | Requis |
|---|---|:-:|
| `columns` | `2` · `3` · `4` |  |
| `gap` | `none` · `sm` · `md` · `lg` |  |
| `children` | liste de nœuds | oui |

#### `columns` — Colonnes

Deux ou trois colonnes à largeurs choisies (1/2, 2/3…), chacune avec son propre contenu.

| Propriété | Type | Requis |
|---|---|:-:|
| `gap` | `none` · `sm` · `md` · `lg` |  |
| `columns` | liste de objets (≤ 4) | oui |

#### `card` — Carte

Un bloc encadré avec titre optionnel, pour isoler un contenu ou un formulaire.

| Propriété | Type | Requis |
|---|---|:-:|
| `title` | texte (≤ 200) |  |
| `subtitle` | texte (≤ 200) |  |
| `tone` | `neutral` · `plume` · `gorge` · `amber` · `red` |  |
| `surface` | `plain` · `card` · `muted` |  |
| `actions` | liste de objets (≤ 4) |  |
| `children` | liste de nœuds | oui |

#### `section` — Section

Un titre de section, une phrase d'explication et le contenu en dessous.

| Propriété | Type | Requis |
|---|---|:-:|
| `title` | texte (≤ 200) | oui |
| `description` | texte (≤ 2000) |  |
| `children` | liste de nœuds | oui |

#### `sidebar` — Volet latéral

Un contenu principal et un volet à gauche ou à droite (filtres, résumé, aide).

| Propriété | Type | Requis |
|---|---|:-:|
| `side` | `left` · `right` |  |
| `width` | `sm` · `md` |  |
| `sidebar` | liste de nœuds | oui |
| `children` | liste de nœuds | oui |

#### `pageHeader` — En-tête de page

Titre, sous-titre et boutons d'action alignés à droite — à poser en tête d'une page.

| Propriété | Type | Requis |
|---|---|:-:|
| `title` | texte (≤ 200) | oui |
| `subtitle` | texte (≤ 2000) |  |
| `breadcrumbs` | liste de objets (≤ 6) |  |
| `actions` | liste de nœuds (≤ 4) |  |
| `tabs` | liste de objets (≤ 12) |  |
| `activeTab` | texte |  |

#### `toolbar` — Barre d'outils

Recherche et filtres en ligne, envoyés à une action à chaque changement (formulaire en direct).

| Propriété | Type | Requis |
|---|---|:-:|
| `search` | objet |  |
| `children` | liste de nœuds |  |

#### `statsRow` — Rangée d'indicateurs

Plusieurs chiffres clés côte à côte (des composants `kpi`).

| Propriété | Type | Requis |
|---|---|:-:|
| `items` | liste de objets (≤ 6) | oui |

#### `split` — Liste + détail

Une liste à gauche (maître) et le détail de l'élément choisi à droite.

| Propriété | Type | Requis |
|---|---|:-:|
| `ratio` | `1/3` · `1/2` · `2/3` |  |
| `master` | liste de nœuds | oui |
| `detail` | liste de nœuds | oui |

#### `stickyBar` — Barre collante

Une barre d'actions qui reste visible en bas de l'écran (Enregistrer, Annuler…).

| Propriété | Type | Requis |
|---|---|:-:|
| `children` | liste de nœuds | oui |

#### `spacer` — Espace

Un espace vertical vide, de xs à xl.

| Propriété | Type | Requis |
|---|---|:-:|
| `size` | `sm` · `md` · `lg` |  |

#### `divider` — Séparateur

Un filet horizontal, avec un libellé optionnel au milieu (« Ou »).

| Propriété | Type | Requis |
|---|---|:-:|
| `label` | texte (≤ 200) |  |

#### `tabs` — Onglets

Plusieurs volets de contenu, un seul visible à la fois.

| Propriété | Type | Requis |
|---|---|:-:|
| `defaultTab` | texte |  |
| `items` | liste de objets (≤ 12) | oui |

#### `accordion` — Accordéon

Des sections repliables (questions-réponses, détails secondaires).

| Propriété | Type | Requis |
|---|---|:-:|
| `items` | liste de objets (≤ 30) | oui |

#### `stepper` — Assistant par étapes

Un parcours en étapes numérotées, l'étape courante ouverte.

| Propriété | Type | Requis |
|---|---|:-:|
| `current` | nombre (≥ 0) | oui |
| `steps` | liste de objets (≤ 10) | oui |
| `actions` | liste de nœuds (≤ 3) |  |

### Texte & indicateurs

Titres, paragraphes, badges, chiffres clés, notes, états vides.

#### `heading` — Titre

Un titre de niveau 1 à 4.

| Propriété | Type | Requis |
|---|---|:-:|
| `text` | nœud ou nombre | oui |
| `level` | `1` · `2` · `3` |  |
| `align` | `start` · `center` · `end` |  |

#### `text` — Texte

Un paragraphe en Markdown léger (gras, italique, liens, listes).

| Propriété | Type | Requis |
|---|---|:-:|
| `markdown` | texte (≤ 20000) | oui |
| `size` | `sm` · `md` · `lg` |  |
| `tone` | `neutral` · `plume` · `gorge` · `amber` · `red` |  |
| `align` | `start` · `center` · `end` |  |

#### `badge` — Badge

Une petite pastille de statut colorée (Nouveau, Payée, En retard).

| Propriété | Type | Requis |
|---|---|:-:|
| `text` | nœud ou nombre | oui |
| `tone` | `neutral` · `plume` · `gorge` · `amber` · `red` |  |

#### `kpi` — Chiffre clé

Un indicateur chiffré avec libellé, variation et icône — seul ou dans une rangée.

| Propriété | Type | Requis |
|---|---|:-:|
| `label` | texte (≤ 200) | oui |
| `value` | nœud ou nombre | oui |
| `delta` | nœud ou nombre |  |
| `deltaTone` | `neutral` · `plume` · `gorge` · `amber` · `red` |  |
| `hint` | texte (≤ 200) |  |
| `icon` | `award` · `bar-chart` · `briefcase` · `calendar` · `check-circle` · `clock` · `code` · `coffee` · `credit-card` · `file-text` · `gift` · `globe` · `heart` · `image` · `layers` · `lightbulb` · `lock` · `mail` · `message-circle` · `monitor` · `package` · `phone` · `rocket` · `settings` · `shield` · `shopping-cart` · `smartphone` · `sparkles` · `star` · `target` · `trending-up` · `trophy` · `truck` · `users` · `video` · `wrench` · `zap` |  |

#### `note` — Note

Un encadré d'information, d'avertissement, de succès ou d'erreur.

| Propriété | Type | Requis |
|---|---|:-:|
| `tone` | `info` · `success` · `warning` · `error` |  |
| `title` | texte (≤ 200) |  |
| `text` | texte (≤ 2000) | oui |

#### `empty` — État vide

Le message affiché quand il n'y a encore rien, avec un bouton pour commencer.

| Propriété | Type | Requis |
|---|---|:-:|
| `title` | texte (≤ 200) | oui |
| `description` | texte (≤ 2000) |  |
| `icon` | `award` · `bar-chart` · `briefcase` · `calendar` · `check-circle` · `clock` · `code` · `coffee` · `credit-card` · `file-text` · `gift` · `globe` · `heart` · `image` · `layers` · `lightbulb` · `lock` · `mail` · `message-circle` · `monitor` · `package` · `phone` · `rocket` · `settings` · `shield` · `shopping-cart` · `smartphone` · `sparkles` · `star` · `target` · `trending-up` · `trophy` · `truck` · `users` · `video` · `wrench` · `zap` |  |
| `action` | objet |  |

### Données

Tableaux paginés, listes, chronologies, graphiques, images.

#### `table` — Tableau

Colonnes typées (texte, montant, date, badge, lien), actions par ligne et pagination « Charger plus ».

| Propriété | Type | Requis |
|---|---|:-:|
| `columns` | liste de objets (≤ 12) | oui |
| `rows` | liste de objets (≤ 100) | oui |
| `emptyText` | texte (≤ 200) |  |
| `pagination` | objet |  |
| `caption` | texte (≤ 200) |  |

#### `list` — Liste

Des lignes titre + sous-titre avec avatar, badge ou action — pour des éléments courts.

| Propriété | Type | Requis |
|---|---|:-:|
| `items` | liste de objets (≤ 200) | oui |
| `emptyText` | texte (≤ 200) |  |

#### `timeline` — Chronologie

Des événements datés les uns sous les autres (historique, journal).

| Propriété | Type | Requis |
|---|---|:-:|
| `items` | liste de objets (≤ 100) | oui |

#### `chart` — Graphique

Courbes dans le temps ou barres comparatives, dessinées par les graphiques de Capibara.

| Propriété | Type | Requis |
|---|---|:-:|
| `kind` | `timeseries` · `bars` | oui |
| `height` | nombre (≥ 120, ≤ 480) |  |
| `series` | liste de objets (≤ 4) |  |
| `points` | liste de objets (≤ 400) |  |
| `bars` | liste de objets (≤ 40) |  |
| `unit` | texte (≤ 20) |  |

#### `progress` — Progression

Une barre de progression (0 à 100 %) avec son libellé.

| Propriété | Type | Requis |
|---|---|:-:|
| `value` | nombre (≥ 0, ≤ 100) | oui |
| `label` | texte (≤ 200) |  |
| `tone` | `neutral` · `plume` · `gorge` · `amber` · `red` |  |

#### `image` — Image

Une image de la médiathèque ou en https, avec ratio et largeur bornés, texte alternatif obligatoire.

| Propriété | Type | Requis |
|---|---|:-:|
| `src` | texte (≤ 2048) | oui |
| `alt` | texte (≤ 300) | oui |
| `ratio` | `auto` · `16/9` · `4/3` · `1/1` · `3/4` |  |
| `width` | `sm` · `md` · `full` |  |
| `rounded` | booléen |  |
| `caption` | texte (≤ 200) |  |

#### `avatar` — Avatar

Le rond d'initiales ou la photo d'une personne.

| Propriété | Type | Requis |
|---|---|:-:|
| `name` | texte (≤ 200) | oui |
| `src` | texte (≤ 2048) |  |
| `size` | `sm` · `md` · `lg` |  |

### Formulaires

Champs typés validés par la plateforme, envoyés à une action de l'app.

#### `field` — Champ

Un champ de saisie typé (texte, nombre, date, choix, sélecteur de contact…) — à placer dans un formulaire.

| Propriété | Type | Requis |
|---|---|:-:|
| `kind` | `text` · `textarea` · `number` · `money` · `select` · `multiselect` · `toggle` · `date` · `datetime` · `file` · `party` · `product` · `user` · `document` · `project` | oui |
| `name` | texte | oui |
| `label` | texte (≤ 200) | oui |
| `placeholder` | texte (≤ 200) |  |
| `hint` | texte (≤ 2000) |  |
| `required` | booléen |  |
| `disabled` | booléen |  |
| `value` | texte (≤ 20000) ou nombre ou booléen ou liste de texte (≤ 200) (≤ 200) ou null |  |
| `options` | liste de objets (≤ 200) |  |
| `min` | nombre |  |
| `max` | nombre |  |
| `pattern` | texte (≤ 200) |  |
| `rows` | nombre (≥ 2, ≤ 20) |  |
| `accept` | texte (≤ 200) |  |

#### `form` — Formulaire

Regroupe des champs, valide les saisies (requis, bornes, motif) et envoie l'action de son bouton.

| Propriété | Type | Requis |
|---|---|:-:|
| `name` | texte | oui |
| `layout` | `stack` · `grid` |  |
| `children` | liste de nœuds | oui |
| `submit` | objet | oui |
| `cancel` | objet |  |

### Actions & navigation

Boutons, liens et menus qui déclenchent vos actions ou naviguent.

#### `button` — Bouton

Déclenche une action de votre app, ouvre un formulaire ou navigue — avec confirmation possible.

| Propriété | Type | Requis |
|---|---|:-:|
| `label` | texte (≤ 200) |  |
| `ariaLabel` | texte (≤ 200) |  |
| `icon` | `award` · `bar-chart` · `briefcase` · `calendar` · `check-circle` · `clock` · `code` · `coffee` · `credit-card` · `file-text` · `gift` · `globe` · `heart` · `image` · `layers` · `lightbulb` · `lock` · `mail` · `message-circle` · `monitor` · `package` · `phone` · `rocket` · `settings` · `shield` · `shopping-cart` · `smartphone` · `sparkles` · `star` · `target` · `trending-up` · `trophy` · `truck` · `users` · `video` · `wrench` · `zap` |  |
| `action` | texte |  |
| `params` | objet |  |
| `href` | texte (≤ 2048) |  |
| `variant` | `primary` · `secondary` · `outline` · `ghost` · `danger` · `link` |  |
| `size` | `sm` · `md` · `lg` |  |
| `confirm` | texte (≤ 2000) |  |
| `disabled` | booléen |  |
| `submits` | texte |  |

#### `link` — Lien

Un lien vers une page de Capibara ou une adresse https.

| Propriété | Type | Requis |
|---|---|:-:|
| `label` | texte (≤ 200) | oui |
| `href` | texte (≤ 2048) | oui |

#### `menu` — Menu

Un bouton qui déroule plusieurs actions (Exporter, Supprimer…).

| Propriété | Type | Requis |
|---|---|:-:|
| `label` | texte (≤ 200) |  |
| `ariaLabel` | texte (≤ 200) |  |
| `icon` | `award` · `bar-chart` · `briefcase` · `calendar` · `check-circle` · `clock` · `code` · `coffee` · `credit-card` · `file-text` · `gift` · `globe` · `heart` · `image` · `layers` · `lightbulb` · `lock` · `mail` · `message-circle` · `monitor` · `package` · `phone` · `rocket` · `settings` · `shield` · `shopping-cart` · `smartphone` · `sparkles` · `star` · `target` · `trending-up` · `trophy` · `truck` · `users` · `video` · `wrench` · `zap` |  |
| `items` | liste de objets (≤ 12) | oui |

### Site public

Les blocs du constructeur de site, rendus au thème du site du tenant.

#### `siteSection` — Section du site

Un des blocs du constructeur de site (héro, tarifs, FAQ…), rendu au thème du site public.

| Propriété | Type | Requis |
|---|---|:-:|
| `block` | texte | oui |
| `props` | objet | oui |

### Gabarits & composants

Réutiliser un composant, répéter un gabarit, prévoir un repli.

#### `$ref` — Composant réutilisable

Instancie un composant déclaré dans `components` avec ses propres props.

| Propriété | Type | Requis |
|---|---|:-:|
| `component` | texte | oui |
| `props` | objet |  |

#### `fallback` — Repli

Le texte affiché à la place d'un composant qu'une version plus ancienne de Capibara ne connaît pas.

| Propriété | Type | Requis |
|---|---|:-:|
| `text` | texte (≤ 2000) | oui |

#### `each` — Répétition

Répète un gabarit pour chaque élément d'une liste de données (`$each`, `$as`).

| Propriété | Type | Requis |
|---|---|:-:|
| `$each` | texte (≤ 200) | oui |
| `$as` | texte |  |
| `$if` | texte (≤ 200) |  |
| `node` | objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou nœud ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou objet ou nœud ou objet ou objet ou objet ou objet ou objet ou objet | oui |
