diff --git a/.gitignore b/.gitignore index 7a08b4b..aa07775 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ node_modules # Generated Tokens (npm run tokens:build) projects/ui-kit/styles/generated +# Copie servie par Storybook pour le téléchargement (regénérée par docs:config) +storybook/public/component-vars.scss + # Compodoc (generated for Storybook) documentation.json diff --git a/AGENTS.md b/AGENTS.md index 2a299ea..bb3852c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -269,6 +269,62 @@ $card-radius: var(--radius-md); /// Rayon des coins. override, otherwise the doc shows the wrong default value. - Every config variable must have its `///`: `npm run docs:config` counts the missing ones. +#### Every structural value is read through a `--ui-*` hook + +In **package mode** the kit's SCSS is already compiled, so `_ui-config.scss` and a component's +local variables are out of reach. Each structural value is therefore read *through* a custom +property whose fallback is the shipped default — the hook goes on the **config variable**, never +on the usage sites: + +```scss +// --- Config --- +$radius: var(--ui-button-radius, var(--radius-sm)); /// Rayon des coins. +$stroke-width: var(--ui-button-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de la bordure. + +.ui-button { border-radius: $radius; border: $stroke-width solid transparent; } +``` + +- **Naming**: `--ui-{family}[-{part}]-{property}[-{modifier}]`, modifier **last** (same rule as + the tokens: `actions-high-surface-hover`). `{family}` is the entry-point folder minus `ui-`, so + a sub-component uses its family (`ui-tab-list.scss` → `--ui-tabs-*`). The property vocabulary + and the modifier list live in `scripts/component-vars.build.mjs`; `npm run docs:config` **fails** + on a hook that doesn't parse, so extend the vocabulary there rather than inventing a name. +- **Inside a map**, every value carries its own hook (`height: var(--ui-button-height-small, …)`). +- **What gets one**: dimensions, spacings, gaps, radii, stroke/focus-ring widths, font sizes, + durations, offsets, z-index — anything structural, including raw `px`/`rem` literals. +- **What doesn't**: SCSS lists/maps that generate classes (`$levels`, `$variants` — build-time, + no custom property can carry them), and **colours**, which stay on the semantic tokens (theme × + 3 brands × WCAG). Exception: a colour whose per-instance repaint is a real use case (mask + backdrop, loading marker, skeleton shine, rating fill). +- **A component must never declare a name it also exposes**: a custom property cannot reference + itself, and a declaration on the element beats an inherited override. When a value must live on + a variable (read by a sub-component, re-read by the consumer), declare a **private mirror** and + read the public hook in its fallback: + ```scss + :host { --_width: var(--ui-sidebar-width, #{$width-default}); } /// Largeur du panneau déployé. + ``` + The `///` stays on the mirror: that is what publishes the public hook's role. +- Never a value hardcoded inline in a rule when it is structural: promote it to a config variable + with its hook and its `///`. +- `npm run docs:config` regenerates `projects/ui-kit/styles/component-vars.scss` (the consumer's copy-me + theme) and `figma/component-vars.json` from these hooks. Both are committed; + `docs:config:check` fails when they are stale, when a hook doesn't parse, or when a hook has + no `///`. Values in both files are read from the **compiled CSS**, not from the SCSS text — so + `rem-calc(44px - 2 * $gutter)` lands as `2.25rem`, not as an unevaluated expression. +- A hook whose default **differs per variant** (`--ui-button-radius` is `--radius-sm`, or + `--radius-full` when `_rounded`) is excluded from the theme file: declaring one value there + would flatten the others. So is a hook the component declares itself. The generator classifies + this on its own — nothing to annotate. +- **`_ui-config.scss` carries hooks too**, named `--ui-` (`$form-control-size` → + `--ui-form-control-size`). They sit between the token and the component hook, and reach every + consumer for free since components *interpolate* them: + `var(--ui-checkbox-box-size, var(--ui-form-control-size, var(--size-components-2xs)))`. Use + this layer when a value is shared by a category and the token it points at is used elsewhere + too (`--size-components-2xs` also feeds `ui-tag`, so retuning the token would overshoot). + ⚠️ A constant that **references another constant** (`$form-focus-ring-width: $focus-ring-width`) + takes **no hook of its own**: it inherits the referenced one, and hooking both makes + `docs.config.mjs`'s binding resolver recurse forever. + ### Shared structural constants — `ui-config` Structural values common to components (focus ring width, form control size, diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1c771..1882f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,30 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr Désactivé par défaut : sans Storybook, story et MDX sont deux fichiers qui importent des packages absents. Le choix est retenu dans `ui-kit.json` et `update` s'y tient. - **Les README du kit (EN + FR) présentent les deux modes de consommation** (FSHSP-116) : section « Deux modes de consommation » avant l'installation — tableau comparatif *dépendance* / *starter* (ce qui arrive dans le dépôt, imports, styles, documentation, personnalisation, mise à jour), qui renvoie au package compagnon pour la voie starter. FSHSP-109 avait livré le mode starter sans qu'il apparaisse nulle part sur la page npm, où le README est la seule documentation. +- **Les composants sont personnalisables en mode package, via des custom properties `--ui-*` exposées.** Jusqu'ici le seul levier de personnalisation était le rebinding SCSS (`_ui-config.scss`, variables locales) : inaccessible quand le kit est consommé en dépendance, puisque son SCSS est déjà compilé. Un projet ne pouvait retoucher que les design tokens — donc rien de propre à un composant, ni à une seule de ses tailles. Chaque valeur **structurelle** des 54 composants est maintenant lue à travers un hook dont le défaut est le réglage livré : + + ```scss + // avant : valeur compilée, hors d'atteinte + height: var(--size-components-sm); + // après : surchargeable, même défaut + height: var(--ui-button-height-small, var(--size-components-sm)); + ``` + + **579 hooks** exposés, nommés `--ui-{famille}[-{partie}]-{propriété}[-{modifieur}]` (modifieur en dernier, comme les tokens). Le composant ne déclare jamais ces noms : les poser sur `:root` (tout le projet), sur un sélecteur (une zone) ou sur l'élément suffit à gagner. + + ```scss + :root { --ui-button-height-small: 24px; } + .toolbar ui-button { --ui-button-height-small: 24px; } + ``` + + Les **couleurs** restent volontairement sur les tokens sémantiques (clair/sombre × 3 marques × contraste WCAG) : seuls quelques hooks de couleur existent, là où repeindre **un exemplaire** est un cas d'usage réel (voile de masque, marqueur de chargement, reflet de squelette, remplissage de notation). + + Aucun défaut ne change : la migration a été vérifiée fichier par fichier en comparant le CSS compilé avant/après, hook réduit à son défaut. +- **`@4sh/ui-kit/styles/component-vars.scss`** — livré avec le package : un fichier prêt à copier où les **581 hooks** réglables sont déclarés à la valeur livrée par le starter, groupés par catégorie → composant → type de propriété (dimensions, espacements, bordures, typographie, couleurs), valeurs alignées sur une seule colonne. On le copie dans le projet, on le charge après `styles.css`, on change les valeurs voulues ; tel quel il ne change rien. Également téléchargeable depuis Storybook (*Spécifications → Thème & Système de Tokens*). + + Généré par `npm run docs:config`, **valeurs lues dans le CSS réellement compilé par Sass** : une expression comme `rem-calc(44px - 2 * $gutter)` y figure résolue (`2.25rem`), jamais sous sa forme SCSS. Les 17 hooks pour lesquels un réglage global n'a pas de sens (valeur dépendante de la variante rendue, propriété que le composant se pose lui-même) en sont volontairement absents — la colonne « Hook exposé » de chaque composant les donne. + + Le mot **« thème »** reste réservé au couple marque + clair/sombre (`ThemeService`, `BrandService`) : cette couche-ci s'appelle **hooks**, du nom des custom properties qu'elle règle, pour éviter trois sens différents du même mot. - **`@4sh/ui-kit-schematics` embarque son `LICENSE` et ses README** (EN + FR). Le package déclarait `Apache-2.0` sans en fournir le texte, alors que la licence (§4a) demande qu'une redistribution en joigne une copie — d'autant plus ici que ce package existe pour que son contenu soit recopié ailleurs. Sa page npmjs, jusque-là vide, redirige maintenant vers `@4sh/ui-kit` : le compagnon ne s'installe jamais directement. ### Fixed @@ -62,6 +86,21 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr Les bases partagées atterrissaient dans `components/ui/{catégorie}/_shared/`, c'est-à-dire des services (`theme.service`) et des utilitaires (`mask-engine`) rangés sous `components/`. Elles vivent maintenant dans `ui-core/`, en conservant leur regroupement par domaine. Les fichiers propres à un composant (`date-utils`, `ui-alert.types`, `ui-toast.service`…) restent à côté de lui : ils ne servent qu'à lui. **Mise à jour non triviale** pour un projet déjà installé en `0.2.0` : les chemins des fichiers copiés changent tous, donc `update` ne reconnaîtra pas les anciens. Déplacer les fichiers existants avant de lancer la commande, ou repartir d'un `add` propre. +- **Les constantes partagées de `_ui-config.scss` sont retouchables sans recompiler.** Ma migration n'avait hookifié que les variables *locales* de chaque composant : il n'existait aucun levier de **catégorie** en mode package. Changer la taille de tous les contrôles de formulaire demandait soit de poser un hook par composant, soit de retoucher `--size-components-2xs`, qui déborde (il alimente aussi `ui-tag`, `ui-select`, `ui-autocomplete`, `ui-datepicker`). + + **16 constantes** exposées, nommées d'après leur variable SCSS (`$form-control-size` → `--ui-form-control-size`). Les composants les *interpolant*, la cascade se construit d'elle-même : + + ```css + var(--ui-checkbox-box-size, var(--ui-form-control-size, var(--size-components-2xs))) + /* ↑ composant ↑ catégorie ↑ token */ + ``` + + Une constante qui **référence une autre constante** (`$form-focus-ring-width: $focus-ring-width`) ne prend pas de nom propre : elle hérite de celui qu'elle vise, si bien que `--ui-focus-ring-width` couvre à lui seul les anneaux de focus du kit. Les quatre tailles d'avatar restent des nombres bruts (elles passent par `rem-calc()`), déjà atteignables par `--ui-avatar-size*`. +- **`ui-icon` : l'échelle et la surcharge d'exemplaire sont deux niveaux distincts.** `--ui-icon-size` était répété dans les cinq entrées de l'échelle ; il est désormais lu une seule fois, au-dessus d'une variable interne que chaque taille alimente. Comportement inchangé : `--ui-icon-size-{sm,md,default,lg,xl}` retouche un **pas de l'échelle** (donc toutes les icônes de cette taille), `--ui-icon-size` force **un exemplaire** quel que soit son `size` — c'est par là qu'un composant dimensionne l'icône qu'il projette. +- **`ui-checkbox` et `ui-select` exposent la taille de leur marqueur** (`--ui-checkbox-icon-size`, `--ui-select-checkbox-icon-size`). Rétrécir la case (`--ui-checkbox-box-size`) laissait la coche à la taille de l'échelle d'icônes, donc débordante. Le défaut de ces deux hooks pointe sur le pas d'échelle utilisé, si bien que retoucher l'échelle continue de traverser. +- **`--ui-card-padding` ne se relit plus sous ce nom : le miroir relisible est `--ui-card-gutter`.** Une custom property ne pouvant pas se référencer elle-même, le nom déclaré par la carte et le nom surchargeable devaient être distincts. `--ui-card-padding` devient le **point de surcharge** (gouttière de la carte), `--ui-card-gutter` la valeur à **relire** pour regouttiérer un contenu `contentFlush` : `padding-inline: var(--ui-card-gutter)`. +- **`--ui-sidebar-width`, `--ui-sidebar-rail-width`, `--ui-empty-state-media-size`, `--ui-empty-state-media-color`, `--ui-input-group-radius`, `--ui-input-group-overlap` et `--ui-skeleton-shine` sont désormais réellement surchargeables.** Ces sept custom properties étaient documentées comme des points de surcharge, mais le composant les **déclarait** sur son propre élément : une valeur posée par le consommateur sur `:root` était systématiquement écrasée (et, pour `--ui-skeleton-shine`, écrasée en mode sombre uniquement). Les déclarations passent sur un miroir privé, le nom public reste lu en amont. Mêmes valeurs par défaut. +- **`ui-tab-list` : `--ui-tabs-active-bar-color` / `--ui-tabs-active-bar-size` deviennent `--ui-tabs-indicator-color` / `--ui-tabs-indicator-thickness`** (le mot `active` se lisait comme un état alors que la convention met le modifieur en dernier). Les anciens noms restent honorés une version, en repli. - **L'en-tête de traçabilité des fichiers copiés par `ng generate @4sh/ui-kit:add` porte la mention de licence** (`Apache-2.0 — Copyright 2026 4SH.`) en plus de son origine et de sa version. Une fois copié, le fichier vit dans le dépôt du consommateur, où plus rien n'indiquait sous quels termes il est fourni. Conséquence attendue : le premier `ng generate @4sh/ui-kit:update` suivant cette version affiche un diff d'une ligne en tête de chaque fichier installé. ## [0.2.0] - 2026-08-14 diff --git a/docs/PUBLISHING.md b/docs/PUBLISHING.md index bfd2f8b..19718d4 100644 --- a/docs/PUBLISHING.md +++ b/docs/PUBLISHING.md @@ -332,6 +332,11 @@ Contenu et résolution des imports strictement identiques à une vraie publication. Pour du développement en parallèle : `npm install ../starter-angular/dist/ui-kit`. +Côté branchement dans le projet consommateur (feuille à charger, `data-theme` / +`data-brand`, surcharge des tokens et des variables de composant), tout est dans +[`projects/ui-kit/README.md`](../projects/ui-kit/README.md#theme-brand-and-overrides) — +c'est ce fichier que voit l'utilisateur sur npmjs. + Pour éprouver le **starter**, le compagnon suffit — il porte les sources *et* les schematics, et le parcours n'installe pas le kit : @@ -340,13 +345,16 @@ npm run schematics:pack # → 4sh-ui-kit-schematics-.tgz # dans le projet consommateur npm install -D /chemin/vers/4sh-ui-kit-schematics-.tgz -npx ng generate @4sh/ui-kit-schematics:ng-add --components ui-button ui-checkbox +npx ng generate @4sh/ui-kit-schematics:ng-add --all ``` On passe par `ng generate …:ng-add` plutôt que `ng add` : la commande publiée `ng add @4sh/ui-kit-schematics` irait chercher le package sur le registre, pas le -tarball local. La règle exécutée est exactement la même. Et `--components` évite -le prompt interactif, ce qui rend l'essai scriptable — l'omettre le rétablit. +tarball local. La règle exécutée est exactement la même. Et `--all` évite le +prompt interactif, ce qui rend l'essai scriptable — l'omettre le rétablit, et +c'est là qu'on coche à la barre d'espace. Pour n'essayer que quelques +composants sans prompt : `ng generate @4sh/ui-kit-schematics:add --components +ui-button --components ui-checkbox`, l'option ne vivant plus que sur `add`. Vérifications qui valent la peine, une fois la commande passée : diff --git a/figma/README.md b/figma/README.md new file mode 100644 index 0000000..6152dab --- /dev/null +++ b/figma/README.md @@ -0,0 +1,59 @@ +# `component-vars.json` + +The components' `--ui-*` variables, in the same DTCG shape as +`src/design-tokens/*.json` — so the plugin that imports your token files imports this +one too. Generated by `npm run docs:config`; `docs:config:check` fails if it is stale. + +**These are not design tokens.** A token belongs to the system (`--units-sm`); a +component variable belongs to one component, and 439 of the 560 entries merely *alias* a +token. Hence a **dedicated collection** (`$extensions.com.4sh.ui-kit.figmaCollection`) +rather than adding them to `semantics` or `metrics`. + +```json +"button": { + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de `ui-button`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": ["WIDTH_HEIGHT"], + "codeSyntax": { "WEB": "var(--ui-button-height)" } + }, + "com.4sh.ui-kit": { "cssVar": "--ui-button-height", "component": "ui-button" } + } + } +} +``` + +## Importing + +- Write to the file that **owns** the collections — `targetFileKey`. In the UI Kit the + variables are `remote`, hence read-only (see `CLAUDE.md`). +- Keep the aliases as aliases: that is what preserves brand, light/dark and responsive. +- Literal values are in `px` (converted from `rem`, base 16), like the token files. +- `$extensions.com.4sh.ui-kit.skipped` lists 38 entries with nothing to create: `calc()` + expressions, multi-value CSS shorthands, relative units (`em`, `ch`, `%`), duration / + easing / cursor / z-index, and internal plumbing. + +## Two flags worth reading + +- `perInstance: true` — the variable has no single value (the component picks a different + default per rendered variant). Creating it is fine, but it does not represent *the* + value of the component. +- `descriptionSource: "derived"` — the description was composed from the variable name, + because the value lives in a SCSS map whose `///` comment covers all its entries. Worth + a read before publishing. + +## `global/*` aliases + +Three aliases point at the `global/*` family: `global.text.default`, `global.text.muted`, +`global.background.muted`. Their names are the ones in `src/design-tokens/` — **nothing +to change there**. + +But an alias only resolves if the target variable carries exactly that name. If your +Figma file still uses the pre-rename names (`global/high/content/default`), those three +links resolve to nothing. Search for `global/text/default` in your Figma variables: if it +exists, ignore this; if not, run `docs/figma-migration-global.md` first — its step order +is constrained and step 3 is irreversible. diff --git a/figma/component-vars.json b/figma/component-vars.json new file mode 100644 index 0000000..d62c780 --- /dev/null +++ b/figma/component-vars.json @@ -0,0 +1,13344 @@ +{ + "$description": "component-vars", + "$extensions": { + "com.4sh.ui-kit": { + "generatedBy": "scripts/component-vars.build.mjs", + "what": "Les hooks `--ui-*` des composants, au format DTCG, prêts à être importés dans UNE collection Figma dédiée. Ce ne sont pas des design tokens : ils appartiennent à un composant, et la plupart ne font que POINTER vers un token (alias `{collection.chemin}`).", + "figmaCollection": "component-vars", + "targetFileKey": "lH4jhyZFkIeJ1Ob1tlY7Wm", + "modes": [ + "Mode 1" + ], + "counts": { + "tokens": 575, + "alias": 455, + "literal": 120, + "skipped": 42, + "derivedDescriptions": 197 + }, + "aliasTargets": [ + "metrics", + "primitives", + "responsive", + "semantics", + "typography" + ], + "skipped": [ + { + "cssVar": "--ui-autocomplete-panel-max-width", + "component": "ui-autocomplete", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-avatar-badge-offset-x", + "component": "ui-avatar", + "reason": "unité relative `%` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-avatar-badge-offset-y", + "component": "ui-avatar", + "reason": "unité relative `%` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-avatar-badge-z-index", + "component": "ui-avatar", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-control-transition-duration", + "component": "ui-config", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-control-transition-easing", + "component": "ui-config", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-datepicker-panel-max-width", + "component": "ui-datepicker", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-datepicker-panel-shadow", + "component": "ui-datepicker", + "reason": "`--shadow-default-md` n'est pas un token du pipeline : rien à aliaser." + }, + { + "cssVar": "--ui-drawer-z-index", + "component": "ui-drawer", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-empty-state-max-width", + "component": "ui-empty-state", + "reason": "unité relative `ch` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-field-box-cursor", + "component": "ui-field", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-field-focus-ring-opacity", + "component": "ui-field", + "reason": "unité relative `%` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-icon-aspect-ratio", + "component": "ui-icon", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-icon-min-size", + "component": "ui-icon", + "reason": "unité relative `em` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-icon-size", + "component": "ui-icon", + "reason": "`--_scale` n'est pas un token du pipeline : rien à aliaser." + }, + { + "cssVar": "--ui-image-object-fit", + "component": "ui-image", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-input-group-item-radius", + "component": "ui-input-group-addon", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-input-tags-panel-max-width", + "component": "ui-input-tags", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-link-underline-offset", + "component": "ui-link", + "reason": "unité relative `em` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter." + }, + { + "cssVar": "--ui-modal-z-index", + "component": "ui-modal", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-motion-delay", + "component": "système de motion", + "reason": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + }, + { + "cssVar": "--ui-motion-duration", + "component": "système de motion", + "reason": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + }, + { + "cssVar": "--ui-motion-easing", + "component": "système de motion", + "reason": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + }, + { + "cssVar": "--ui-overlay-panel-shadow", + "component": "ui-config", + "reason": "`--shadow-default-md` n'est pas un token du pipeline : rien à aliaser." + }, + { + "cssVar": "--ui-overlay-shadow", + "component": "panneaux flottants", + "reason": "`--shadow-default-md` n'est pas un token du pipeline : rien à aliaser." + }, + { + "cssVar": "--ui-popover-max-height", + "component": "ui-popover", + "reason": "hook sans défaut : point de surcharge pur, le composant ne pose rien." + }, + { + "cssVar": "--ui-progress-bar-color", + "component": "ui-progress-bar", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-progress-bar-indeterminate-duration", + "component": "ui-progress-bar", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-rating-fill", + "component": "ui-rating", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-read-only-label-width", + "component": "ui-read-only", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-select-panel-max-width", + "component": "ui-select", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-sidebar-menu-item-size", + "component": "ui-sidebar", + "reason": "hook sans défaut : point de surcharge pur, le composant ne pose rien." + }, + { + "cssVar": "--ui-sidebar-z-index", + "component": "ui-sidebar", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + }, + { + "cssVar": "--ui-spinner-duration", + "component": "ui-spinner", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-table-cell-padding-large", + "component": "ui-table", + "reason": "raccourci CSS à plusieurs valeurs : une variable Figma n’en porte qu’une." + }, + { + "cssVar": "--ui-table-cell-padding-small", + "component": "ui-table", + "reason": "raccourci CSS à plusieurs valeurs : une variable Figma n’en porte qu’une." + }, + { + "cssVar": "--ui-table-frozen-top", + "component": "ui-table", + "reason": "pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas." + }, + { + "cssVar": "--ui-table-head-padding", + "component": "ui-table", + "reason": "raccourci CSS à plusieurs valeurs : une variable Figma n’en porte qu’une." + }, + { + "cssVar": "--ui-table-head-padding-small", + "component": "ui-table", + "reason": "raccourci CSS à plusieurs valeurs : une variable Figma n’en porte qu’une." + }, + { + "cssVar": "--ui-textarea-padding-block", + "component": "ui-textarea", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-textarea-padding-block-small", + "component": "ui-textarea", + "reason": "expression calc() : une variable Figma ne porte pas de calcul." + }, + { + "cssVar": "--ui-toast-z-index", + "component": "ui-toast-container", + "reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)." + } + ] + } + }, + "accordion": { + "control": { + "size": { + "$value": "40px", + "$type": "dimension", + "$description": "Diamètre du chevron circulaire.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-accordion-control-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-accordion-control-size", + "component": "ui-accordion-panel", + "entryPoint": "@4sh/ui-kit/informative/ui-accordion", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus de l'en-tête (constante partagée `ui-config`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-accordion-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-accordion-focus-ring-width", + "component": "ui-accordion-panel", + "entryPoint": "@4sh/ui-kit/informative/ui-accordion", + "descriptionSource": "scss" + } + } + } + }, + "alert": { + "close": { + "opacity": { + "$value": "0.7", + "$type": "number", + "$description": "Opacité au repos du bouton de fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "OPACITY" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-close-opacity)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-close-opacity", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.xs}", + "$type": "dimension", + "$description": "Rayon des coins du bouton de fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-close-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-close-radius", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + } + }, + "content": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement titre ↔ message.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-content-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-content-gap", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "padding-right": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Réserve à droite du contenu, avant la fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-content-padding-right)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-content-padding-right", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring": { + "offset": { + "$value": "2px", + "$type": "dimension", + "$description": "Décalage de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-focus-ring-offset)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-focus-ring-offset", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-focus-ring-width", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ contenu ↔ fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-gap", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "icon": { + "padding-top": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Décalage vertical de l'icône, pour l'aligner sur la première ligne de texte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-icon-padding-top)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-icon-padding-top", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + } + }, + "min-height": { + "$value": "56px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-alert`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-min-height", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + }, + "min-height-large": { + "$value": "66px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-alert` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-min-height-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-min-height-large", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-padding-x", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset vertical.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-padding-y", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-radius", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-stroke-width", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "scss" + } + } + }, + "text": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-alert` — text.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-text-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-text-font-size", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + }, + "font-size-large": { + "$value": "{responsive.size.typography.text.lg}", + "$type": "dimension", + "$description": "Taille de police de `ui-alert` — text (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-text-font-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-text-font-size-large", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + } + }, + "title": { + "font-size": { + "$value": "{responsive.size.typography.title.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-alert` — title.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-title-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-title-font-size", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + }, + "font-size-large": { + "$value": "{responsive.size.typography.title.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-alert` — title (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-alert-title-font-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-alert-title-font-size-large", + "component": "ui-alert", + "entryPoint": "@4sh/ui-kit/informative/ui-alert", + "descriptionSource": "derived" + } + } + } + } + }, + "autocomplete": { + "clear": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon du bouton d'effacement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-clear-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-clear-radius", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Taille du bouton d'effacement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-clear-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-clear-size", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus (valeur sélectionnée, bouton d'effacement).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-focus-ring-width", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-autocomplete`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-font-size", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-autocomplete` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-font-size-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "group": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-autocomplete` — group.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-group-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-group-font-size", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-autocomplete` — group (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-group-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-group-font-size-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "height": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-autocomplete` — group.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-group-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-group-height", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-autocomplete` — group (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-group-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-group-height-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + } + }, + "header": { + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal des zones d'en-tête et de pied projetées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-header-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-header-padding-x", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset vertical des zones d'en-tête et de pied projetées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-header-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-header-padding-y", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + }, + "input": { + "min-width": { + "$value": "80px", + "$type": "dimension", + "$description": "Largeur minimale de la zone de saisie restante (mode `multiple`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-input-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-input-min-width", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + }, + "option": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre l'icône et le libellé d'une option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-gap", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur de `ui-autocomplete` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-height", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-autocomplete` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-height-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-autocomplete` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-padding-x", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-autocomplete` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-padding-x-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'une option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-option-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-option-radius", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + }, + "panel": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les groupes du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-panel-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-panel-gap", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset du panneau de suggestions.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-panel-padding", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + }, + "scroller": { + "max-height": { + "$value": "312px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-autocomplete` — scroller.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-scroller-max-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-scroller-max-height", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + }, + "max-height-small": { + "$value": "256px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-autocomplete` — scroller (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-scroller-max-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-scroller-max-height-small", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "derived" + } + } + } + }, + "tag": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les valeurs sélectionnées (mode `multiple`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-tag-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-tag-gap", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon d'une valeur sélectionnée (mode `multiple`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-autocomplete-tag-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-autocomplete-tag-radius", + "component": "ui-autocomplete", + "entryPoint": "@4sh/ui-kit/forms/ui-autocomplete", + "descriptionSource": "scss" + } + } + } + } + }, + "avatar": { + "font-size": { + "$value": "{responsive.size.typography.title.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-avatar`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-font-size", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "font-size-large": { + "$value": "{responsive.size.typography.title.xl}", + "$type": "dimension", + "$description": "Taille de police de `ui-avatar` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-font-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-font-size-large", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.title.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-avatar` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-font-size-small", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "font-size-tiny": { + "$value": "{responsive.size.typography.title.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-avatar` (variante `tiny`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-font-size-tiny)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-font-size-tiny", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins (forme ronde par défaut).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-radius", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "scss" + } + } + }, + "radius-square": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon des coins en présentation `square`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-radius-square)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-radius-square", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "56px", + "$type": "dimension", + "$description": "Taille de `ui-avatar`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-size", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "size-large": { + "$value": "64px", + "$type": "dimension", + "$description": "Taille de `ui-avatar` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-size-large", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "42px", + "$type": "dimension", + "$description": "Taille de `ui-avatar` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-size-small", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "size-tiny": { + "$value": "34px", + "$type": "dimension", + "$description": "Taille de `ui-avatar` (variante `tiny`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-size-tiny)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-size-tiny", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "derived" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur du liseré.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-stroke-width", + "component": "ui-avatar", + "entryPoint": "@4sh/ui-kit/informative/ui-avatar", + "descriptionSource": "scss" + } + } + } + }, + "avatar-group": { + "overlap": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Chevauchement des avatars d'un groupe.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-group-overlap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-group-overlap", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss", + "perInstance": true, + "note": "2 défauts selon la variante rendue : en déclarer un seul écraserait les autres." + } + } + } + }, + "avatar-square": { + "radius": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon des coins d'un avatar carré.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-avatar-square-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-avatar-square-radius", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "badge": { + "dot": { + "size": { + "$value": "10px", + "$type": "dimension", + "$description": "Taille de `ui-badge` — dot.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-dot-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-dot-size", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "size-large": { + "$value": "12px", + "$type": "dimension", + "$description": "Taille de `ui-badge` — dot (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-dot-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-dot-size-large", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "8px", + "$type": "dimension", + "$description": "Taille de `ui-badge` — dot (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-dot-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-dot-size-small", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-badge`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-font-size", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "font-size-large": { + "$value": "{responsive.size.typography.text.lg}", + "$type": "dimension", + "$description": "Taille de police de `ui-badge` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-font-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-font-size-large", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-badge` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-font-size-small", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement icône ↔ libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-gap", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "28px", + "$type": "dimension", + "$description": "Hauteur de `ui-badge`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-height", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "height-large": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-badge` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-height-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-height-large", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "22px", + "$type": "dimension", + "$description": "Hauteur de `ui-badge` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-height-small", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-badge`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-padding-x", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "padding-x-large": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-badge` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-padding-x-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-padding-x-large", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-badge` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-padding-x-small", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-radius", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-badge-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-badge-stroke-width", + "component": "ui-badge", + "entryPoint": "@4sh/ui-kit/informative/ui-badge", + "descriptionSource": "scss" + } + } + } + }, + "breadcrumb": { + "content": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre l'icône et le libellé d'un élément", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-content-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-content-gap", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus des éléments interactifs", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-focus-ring-width", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-breadcrumb`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-font-size", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-breadcrumb` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-font-size-small", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "derived" + } + } + }, + "item": { + "gap": { + "$value": "{responsive.spacing.default}", + "$type": "dimension", + "$description": "Espacement entre éléments et séparateurs", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-item-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-item-gap", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + } + }, + "line-height": { + "$value": "1.4", + "$type": "number", + "$description": "Interlignage des éléments", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "LINE_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-line-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-line-height", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.2xs}", + "$type": "dimension", + "$description": "Rayon des éléments interactifs (anneau de focus)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-radius", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + }, + "weight": { + "$value": "{typography.weight.bold}", + "$type": "string", + "$description": "Graisse des éléments (liens et page courante)", + "$extensions": { + "com.figma": { + "resolvedType": "STRING", + "scopes": [ + "FONT_WEIGHT", + "FONT_STYLE" + ], + "codeSyntax": { + "WEB": "var(--ui-breadcrumb-weight)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-breadcrumb-weight", + "component": "ui-breadcrumb", + "entryPoint": "@4sh/ui-kit/navigation/ui-breadcrumb", + "descriptionSource": "scss" + } + } + } + }, + "button": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-button-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-focus-ring-width", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-button`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-button-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-font-size", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-button` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-button-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-font-size-small", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-gap", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de `ui-button`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-button-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-height", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de `ui-button` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-button-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-height-small", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "icon-stacked": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement icône ↔ libellé quand l'icône est empilée (`iconPos` top / bottom).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-icon-stacked-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-icon-stacked-gap", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal en icône empilée.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-icon-stacked-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-icon-stacked-padding-x", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset vertical en icône empilée (la hauteur passe en `auto`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-icon-stacked-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-icon-stacked-padding-y", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-button`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-padding-x", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-button` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-button-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-padding-x-small", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins, reformable par un hôte (`ui-button-split`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-button-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-radius", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss", + "perInstance": true, + "note": "2 défauts selon la variante rendue : en déclarer un seul écraserait les autres." + } + } + }, + "radius-rounded": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins en présentation `rounded`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-button-radius-rounded)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-radius-rounded", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-button-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-stroke-width", + "component": "ui-button", + "entryPoint": "@4sh/ui-kit/actions/ui-button", + "descriptionSource": "scss" + } + } + } + }, + "button-split": { + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Chevauchement des deux boutons (= largeur de bordure, évite le doublement du trait partagé)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-button-split-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-button-split-stroke-width", + "component": "ui-button-split", + "entryPoint": "@4sh/ui-kit/actions/ui-button-split", + "descriptionSource": "scss" + } + } + } + }, + "card": { + "footer": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre éléments du pied.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-card-footer-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-footer-gap", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + } + }, + "gap": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Espacement entre en-tête / contenu / pied.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-card-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-gap", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + }, + "header": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement titre ↔ sous-titre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-card-header-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-header-gap", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + } + }, + "padding": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Inset du corps.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-card-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-padding", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon des coins.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-card-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-radius", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-card-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-card-stroke-width", + "component": "ui-card", + "entryPoint": "@4sh/ui-kit/layout/ui-card", + "descriptionSource": "scss" + } + } + } + }, + "checkbox": { + "box": { + "radius": { + "$value": "{metrics.radius.xs}", + "$type": "dimension", + "$description": "Rayon des coins de la boîte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-box-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-box-radius", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Taille de la boîte à cocher", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-box-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-box-size", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-focus-ring-width", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police du label.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-font-size", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre la boîte et le label", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-gap", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + }, + "icon": { + "size": { + "$value": "{responsive.size.typography.icon.md}", + "$type": "dimension", + "$description": "Taille du marqueur (coche / indéterminé).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-icon-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-icon-size", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + } + }, + "label": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement libellé ↔ marqueur requis.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-label-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-label-gap", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure de la boîte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-checkbox-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-checkbox-stroke-width", + "component": "ui-checkbox", + "entryPoint": "@4sh/ui-kit/forms/ui-checkbox", + "descriptionSource": "scss" + } + } + } + }, + "chip": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-focus-ring-width", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-chip`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-font-size", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-chip` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-font-size-small", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement de `ui-chip`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-gap", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "gap-small": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement de `ui-chip` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-gap-small", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "height": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-chip`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-height", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-chip` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-height-small", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "image": { + "offset": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Débord du visuel dans l'inset de début.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-image-offset)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-image-offset", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon du visuel.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-image-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-image-radius", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "radius-square": { + "$value": "{metrics.radius.xs}", + "$type": "dimension", + "$description": "Rayon du visuel en présentation `square`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-image-radius-square)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-image-radius-square", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "24px", + "$type": "dimension", + "$description": "Taille de `ui-chip` — image.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-image-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-image-size", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "18px", + "$type": "dimension", + "$description": "Taille de `ui-chip` — image (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-image-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-image-size-small", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + } + }, + "padding-inline-end-removable": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset de fin resserré quand le chip porte une action de suppression.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-padding-inline-end-removable)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-padding-inline-end-removable", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-chip`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-padding-x", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-chip` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-padding-x-small", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-radius", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "radius-square": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins en présentation `square`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-radius-square)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-radius-square", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "remove": { + "padding": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset du bouton de suppression.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-remove-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-remove-padding", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins du bouton de suppression.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-remove-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-remove-radius", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-chip-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-chip-stroke-width", + "component": "ui-chip", + "entryPoint": "@4sh/ui-kit/informative/ui-chip", + "descriptionSource": "scss" + } + } + } + }, + "control": { + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Bordure de la boîte de champ (suit `--ui-control-stroke-width`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-control-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-control-stroke-width", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "datepicker": { + "buttonbar": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement des actions de la barre de boutons.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-buttonbar-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-buttonbar-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-top": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Retrait au-dessus de la barre de boutons, sous son filet.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-buttonbar-padding-top)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-buttonbar-padding-top", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "cell": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du libellé d'une cellule mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-cell-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-cell-font-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "min-height": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale d'une cellule mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-cell-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-cell-min-height", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal d'une cellule mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-cell-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-cell-padding-x", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset vertical d'une cellule mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-cell-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-cell-padding-y", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'une cellule mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-cell-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-cell-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "day": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du chiffre d'une cellule jour.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-day-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-day-font-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "opacity-disabled": { + "$value": "0.45", + "$type": "number", + "$description": "Opacité d'un jour désactivé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "OPACITY" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-day-opacity-disabled)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-day-opacity-disabled", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "opacity-outside": { + "$value": "0.6", + "$type": "number", + "$description": "Opacité d'un jour appartenant au mois adjacent.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "OPACITY" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-day-opacity-outside)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-day-opacity-outside", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des cellules jour (cercle)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-day-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-day-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Diamètre d'une cellule jour", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-day-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-day-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Anneau de focus (jours, navigation)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-focus-ring-width", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "grid": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les semaines de la grille.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-grid-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-grid-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "header": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement de l'en-tête de navigation.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-header-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-header-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "month-divider": { + "padding-left": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Retrait après le filet séparant deux mois.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-month-divider-padding-left)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-month-divider-padding-left", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "month": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les sections d'une colonne de mois.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-month-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-month-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "months": { + "gap": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Espacement entre les mois affichés côte à côte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-months-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-months-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "nav": { + "size": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Taille des boutons de navigation mensuelle", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-nav-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-nav-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "panel": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre sections (header / grille / heure)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-panel-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-panel-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Padding interne du panneau", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-panel-padding", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon de bordure du panneau", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-panel-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-panel-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "width": { + "$value": "360px", + "$type": "dimension", + "$description": "Largeur du panneau calendrier", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-panel-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-panel-width", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "picker": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement des grilles mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-picker-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-picker-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur des bordures (panneau, cellule « today »)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-stroke-width", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "subheader": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement de l'en-tête propre à un mois (multi-mois).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-subheader-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-subheader-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "time-btn": { + "height": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Hauteur d'une flèche d'incrément.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-btn-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-btn-height", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'une flèche d'incrément.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-btn-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-btn-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "width": { + "$value": "{responsive.size.components.xs}", + "$type": "dimension", + "$description": "Largeur d'une flèche d'incrément.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-btn-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-btn-width", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "time-field": { + "width": { + "$value": "48px", + "$type": "dimension", + "$description": "Largeur des champs heures / minutes / AM-PM", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-field-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-field-width", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "time": { + "font-size": { + "$value": "{responsive.size.typography.title.md}", + "$type": "dimension", + "$description": "Taille des chiffres et du séparateur de l'heure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-font-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les unités de la zone heure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-top": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Retrait au-dessus de la zone heure, sous son filet.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-padding-top)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-padding-top", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "time-unit": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre les flèches et la valeur d'une unité.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-unit-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-unit-gap", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "time-value": { + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal d'un champ heure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-value-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-value-padding-x", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset vertical d'un champ heure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-value-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-value-padding-y", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'un champ heure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-time-value-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-time-value-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "title": { + "font-size": { + "$value": "{responsive.size.typography.title.md}", + "$type": "dimension", + "$description": "Taille du titre mois / année.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-title-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-title-font-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal du titre cliquable.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-title-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-title-padding-x", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset vertical du titre cliquable.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-title-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-title-padding-y", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon du titre cliquable.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-title-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-title-radius", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + }, + "weekday": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du libellé des jours de semaine.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-weekday-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-weekday-font-size", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset vertical de l'en-tête des jours de semaine.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-datepicker-weekday-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-datepicker-weekday-padding-y", + "component": "ui-datepicker", + "entryPoint": "@4sh/ui-kit/forms/ui-datepicker", + "descriptionSource": "scss" + } + } + } + } + }, + "drawer": { + "action": { + "size": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille de la cible tactile du bouton de fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-action-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-action-size", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus du bouton de fermeture", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-focus-ring-width", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + }, + "header": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les éléments de l'en-tête.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-header-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-header-gap", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + } + }, + "mask": { + "background": { + "$value": "{primitives.black.50}", + "$type": "color", + "$description": "Assombrissement du masque.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "FRAME_FILL", + "SHAPE_FILL" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-mask-background)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-mask-background", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + } + }, + "panel": { + "padding": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Inset du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-panel-padding", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + } + }, + "size-edge": { + "$value": "160px", + "$type": "dimension", + "$description": "Hauteur d'un tiroir haut / bas.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-size-edge)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-size-edge", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + }, + "size-side": { + "$value": "320px", + "$type": "dimension", + "$description": "Largeur d'un tiroir gauche / droite.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-size-side)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-size-side", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-drawer-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-drawer-stroke-width", + "component": "ui-drawer", + "entryPoint": "@4sh/ui-kit/layout/ui-drawer", + "descriptionSource": "scss" + } + } + } + }, + "empty-state": { + "actions": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les actions.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-actions-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-actions-gap", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "scss" + } + } + } + }, + "desc": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-empty-state` — desc.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-desc-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-desc-font-size", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-empty-state` — desc (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-desc-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-desc-font-size-small", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + } + }, + "gap": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Espacement de `ui-empty-state`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-gap", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + }, + "gap-small": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Espacement de `ui-empty-state` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-gap-small", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + }, + "media": { + "color": { + "$value": "{semantics.global.text.muted}", + "$type": "color", + "$description": "Couleur de la zone visuelle (l’icône utilise `currentColor`).", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-media-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-media-color", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{metrics.units.4xl}", + "$type": "dimension", + "$description": "Taille de `ui-empty-state` — media.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-media-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-media-size", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "{metrics.units.2xl}", + "$type": "dimension", + "$description": "Taille de `ui-empty-state` — media (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-media-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-media-size-small", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + } + }, + "text": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement titre ↔ description.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-text-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-text-gap", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "scss" + } + } + } + }, + "title": { + "font-size": { + "$value": "{responsive.size.typography.title.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-empty-state` — title.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-title-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-title-font-size", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.title.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-empty-state` — title (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-empty-state-title-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-empty-state-title-font-size-small", + "component": "ui-empty-state", + "entryPoint": "@4sh/ui-kit/informative/ui-empty-state", + "descriptionSource": "derived" + } + } + } + } + }, + "field": { + "box": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre le contrôle et ses affixes.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-field-box-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-box-gap", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-focus-ring-width", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "footer": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre le message et le compteur, dans le pied.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-field-footer-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-footer-gap", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Gouttière verticale entre le libellé, la boîte et le pied.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-field-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-gap", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "gap-small": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Gouttière verticale, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-field-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-gap-small", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de la boîte de champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-height", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de la boîte de champ, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-height-small", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "min-height-multiline": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale de la boîte multiligne (`ui-textarea`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-min-height-multiline)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-min-height-multiline", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "min-height-multiline-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale de la boîte multiligne, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-min-height-multiline-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-min-height-multiline-small", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "padding-y-auto": { + "height": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset vertical de la boîte à hauteur automatique (valeurs enveloppées de `ui-select`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-padding-y-auto-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-padding-y-auto-height", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon exposé pour reformer les coins (utilisé par `ui-input-group`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-field-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-radius", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure de la boîte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-field-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-field-stroke-width", + "component": "ui-field", + "entryPoint": "@4sh/ui-kit/forms/ui-field", + "descriptionSource": "scss" + } + } + } + }, + "file-upload": { + "browse": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du libellé du bouton de sélection.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-browse-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-browse-font-size", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du libellé du bouton de sélection, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-browse-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-browse-font-size-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal du bouton de sélection.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-browse-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-browse-padding-x", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal du bouton de sélection, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-browse-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-browse-padding-x-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "content": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les lignes de fichier.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-content-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-content-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "field": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du libellé en présentation compacte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-font-size", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du libellé en présentation compacte, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-font-size-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement libellé ↔ bouton de sélection.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de `ui-file-upload` — field.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-height", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de `ui-file-upload` — field (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-height-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal du libellé en présentation compacte.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-field-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-field-padding-x", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-focus-ring-width", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Espacement entre la zone, la barre d'outils et la liste.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "item": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus du bouton de retrait.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-focus-ring-width", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Espacement de `ui-file-upload-list` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-gap", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "gap-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement de `ui-file-upload-list` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-gap-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-file-upload-list` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-padding-x", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-file-upload-list` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-padding-x-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding-y": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset vertical de `ui-file-upload-list` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-padding-y", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding-y-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset vertical de `ui-file-upload-list` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-padding-y-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-padding-y-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.lg}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-file-upload-list` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-radius", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "radius-small": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-file-upload-list` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-radius-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-radius-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure d'une ligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-stroke-width", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "item-infos": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement nom ↔ métadonnées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-infos-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-infos-gap", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "item-media": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement vignette ↔ informations.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-media-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-media-gap", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "40px", + "$type": "dimension", + "$description": "Taille de `ui-file-upload-list` — item-media.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-media-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-media-size", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille de `ui-file-upload-list` — item-media (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-media-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-media-size-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + } + }, + "item-meta": { + "font-size": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-file-upload-list` — item-meta.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-meta-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-meta-font-size", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-file-upload-list` — item-meta (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-meta-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-meta-font-size-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + } + }, + "item-name": { + "font-size": { + "$value": "{responsive.size.typography.title.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-file-upload-list` — item-name.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-name-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-name-font-size", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-file-upload-list` — item-name (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-name-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-name-font-size-small", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + } + }, + "item-progress": { + "color": { + "$value": "{semantics.global.text.default}", + "$type": "color", + "$description": "Couleur de la portion remplie de la barre de progression.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-progress-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-progress-color", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la barre de progression.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-progress-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-progress-height", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "item-remove": { + "padding": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset du bouton de retrait.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-remove-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-remove-padding", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon du bouton de retrait.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-remove-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-remove-radius", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "item-thumb": { + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon de la vignette d'aperçu.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-item-thumb-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-item-thumb-radius", + "component": "ui-file-upload-list", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "message": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille d'un message.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-message-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-message-font-size", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "messages": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre les messages.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-messages-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-messages-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "placeholder": { + "opacity": { + "$value": "0.75", + "$type": "number", + "$description": "Opacité du texte indicatif tant qu'aucun fichier n'est choisi.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "OPACITY" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-placeholder-opacity)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-placeholder-opacity", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon de la zone de dépôt.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-radius", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure de la zone de dépôt.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-stroke-width", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "toolbar": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les actions de la barre d'outils.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-toolbar-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-toolbar-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "zone-content": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement titre ↔ indication.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-content-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-content-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "zone": { + "gap": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Espacement icône ↔ contenu de la zone de dépôt.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "gap-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ contenu de la zone de dépôt, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-gap-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "min-height": { + "$value": "112px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-file-upload` — zone.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-min-height", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "min-height-small": { + "$value": "94px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-file-upload` — zone (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-min-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-min-height-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "derived" + } + } + }, + "padding": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset de la zone de dépôt.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-padding", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "zone-hint": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de l'indication sous le titre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-hint-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-hint-font-size", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de l'indication sous le titre, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-hint-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-hint-font-size-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + }, + "zone-title": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du titre de la zone de dépôt.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-title-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-title-font-size", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du titre de la zone de dépôt, taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-title-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-title-font-size-small", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre les fragments du titre de la zone.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-file-upload-zone-title-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-file-upload-zone-title-gap", + "component": "ui-file-upload", + "entryPoint": "@4sh/ui-kit/forms/ui-file-upload", + "descriptionSource": "scss" + } + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Anneau de focus des actions (suit `--ui-focus-ring-width`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-focus-ring-width", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "form-control": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre un contrôle et son libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-form-control-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-control-gap", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Côté d'un contrôle carré (case à cocher, radio).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-form-control-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-control-size", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "form-field": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre le contrôle et ses affixes.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-form-field-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-field-gap", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de la boîte de champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-form-field-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-field-height", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de la boîte de champ, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-form-field-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-field-height-small", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de la boîte de champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-form-field-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-field-padding-x", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins de la boîte de champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-form-field-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-field-radius", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "form-textarea": { + "min-height": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale d'une boîte multiligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-form-textarea-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-textarea-min-height", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "min-height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale d'une boîte multiligne, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-form-textarea-min-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-form-textarea-min-height-small", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "helper": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-helper`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-helper-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-helper-font-size", + "component": "ui-helper", + "entryPoint": "@4sh/ui-kit/informative/ui-helper", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-helper` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-helper-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-helper-font-size-small", + "component": "ui-helper", + "entryPoint": "@4sh/ui-kit/informative/ui-helper", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement icône ↔ message.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-helper-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-helper-gap", + "component": "ui-helper", + "entryPoint": "@4sh/ui-kit/informative/ui-helper", + "descriptionSource": "scss" + } + } + } + }, + "icon": { + "size-default": { + "$value": "{responsive.size.typography.icon.default}", + "$type": "dimension", + "$description": "Taille de `ui-icon` (variante `default`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-icon-size-default)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-icon-size-default", + "component": "ui-icon", + "entryPoint": "@4sh/ui-kit/base/ui-icon", + "descriptionSource": "derived" + } + } + }, + "size-lg": { + "$value": "{responsive.size.typography.icon.lg}", + "$type": "dimension", + "$description": "Taille de `ui-icon` (variante `lg`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-icon-size-lg)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-icon-size-lg", + "component": "ui-icon", + "entryPoint": "@4sh/ui-kit/base/ui-icon", + "descriptionSource": "derived" + } + } + }, + "size-md": { + "$value": "{responsive.size.typography.icon.md}", + "$type": "dimension", + "$description": "Taille de `ui-icon` (variante `md`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-icon-size-md)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-icon-size-md", + "component": "ui-icon", + "entryPoint": "@4sh/ui-kit/base/ui-icon", + "descriptionSource": "derived" + } + } + }, + "size-sm": { + "$value": "{responsive.size.typography.icon.sm}", + "$type": "dimension", + "$description": "Taille de `ui-icon` (variante `sm`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-icon-size-sm)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-icon-size-sm", + "component": "ui-icon", + "entryPoint": "@4sh/ui-kit/base/ui-icon", + "descriptionSource": "derived" + } + } + }, + "size-xl": { + "$value": "{responsive.size.typography.icon.xl}", + "$type": "dimension", + "$description": "Taille de `ui-icon` (variante `xl`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-icon-size-xl)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-icon-size-xl", + "component": "ui-icon", + "entryPoint": "@4sh/ui-kit/base/ui-icon", + "descriptionSource": "derived" + } + } + } + }, + "image": { + "placeholder": { + "min-height": { + "$value": "{metrics.units.xl}", + "$type": "dimension", + "$description": "Hauteur minimale du placeholder.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-image-placeholder-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-image-placeholder-min-height", + "component": "ui-image", + "entryPoint": "@4sh/ui-kit/base/ui-image", + "descriptionSource": "scss" + } + } + }, + "min-width": { + "$value": "{metrics.units.xl}", + "$type": "dimension", + "$description": "Largeur minimale du placeholder (visible même sans dimension imposée).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-image-placeholder-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-image-placeholder-min-width", + "component": "ui-image", + "entryPoint": "@4sh/ui-kit/base/ui-image", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins du placeholder.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-image-placeholder-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-image-placeholder-radius", + "component": "ui-image", + "entryPoint": "@4sh/ui-kit/base/ui-image", + "descriptionSource": "scss" + } + } + } + } + }, + "input": { + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du contrôle et de l'unité, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-font-size-small", + "component": "ui-input", + "entryPoint": "@4sh/ui-kit/forms/ui-input", + "descriptionSource": "scss" + } + } + }, + "unit": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de l'unité affichée dans le champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-unit-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-unit-font-size", + "component": "ui-input", + "entryPoint": "@4sh/ui-kit/forms/ui-input", + "descriptionSource": "scss" + } + } + } + } + }, + "input-group": { + "addon": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du texte de la cellule.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-font-size", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du texte, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-font-size-small", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre deux contenus de la cellule.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-gap", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de la cellule, alignée sur la boîte de champ.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-height", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de la cellule, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-height-small", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal du contenu.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-padding-x", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-addon-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-addon-stroke-width", + "component": "ui-input-group-addon", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + } + }, + "overlap": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Recouvrement de deux items voisins (= largeur de bordure, évite le doublement du trait partagé).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-overlap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-overlap", + "component": "ui-input-group", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins extérieurs du groupe.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-input-group-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-group-radius", + "component": "ui-input-group", + "entryPoint": "@4sh/ui-kit/forms/ui-input-group", + "descriptionSource": "scss" + } + } + } + }, + "input-mask": { + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du contrôle, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-mask-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-mask-font-size-small", + "component": "ui-input-mask", + "entryPoint": "@4sh/ui-kit/forms/ui-input-mask", + "descriptionSource": "scss" + } + } + } + }, + "input-number": { + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du texte (saisie et unité) en variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-number-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-number-font-size-small", + "component": "ui-input-number", + "entryPoint": "@4sh/ui-kit/forms/ui-input-number", + "descriptionSource": "scss" + } + } + }, + "spinner": { + "width": { + "$value": "30px", + "$type": "dimension", + "$description": "Largeur de la colonne du spinner (mixin partagé `utils.field-spinner($btn, $width)`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-number-spinner-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-number-spinner-width", + "component": "ui-input-number", + "entryPoint": "@4sh/ui-kit/forms/ui-input-number", + "descriptionSource": "scss" + } + } + } + }, + "unit": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du texte de l'unité.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-number-unit-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-number-unit-font-size", + "component": "ui-input-number", + "entryPoint": "@4sh/ui-kit/forms/ui-input-number", + "descriptionSource": "scss" + } + } + } + } + }, + "input-otp": { + "cell": { + "size": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Taille de `ui-input-otp` — cell.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-cell-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-cell-size", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + }, + "size-large": { + "$value": "{responsive.size.components.xl}", + "$type": "dimension", + "$description": "Taille de `ui-input-otp` — cell (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-cell-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-cell-size-large", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Taille de `ui-input-otp` — cell (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-cell-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-cell-size-small", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Largeur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-focus-ring-width", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-otp`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-font-size", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + }, + "font-size-large": { + "$value": "{responsive.size.typography.text.lg}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-otp` (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-font-size-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-font-size-large", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-otp` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-font-size-small", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre les cellules (`--units-sm`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-gap", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des cellules (aligné sur `ui-field`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-radius", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-otp-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-otp-stroke-width", + "component": "ui-input-otp", + "entryPoint": "@4sh/ui-kit/forms/ui-input-otp", + "descriptionSource": "scss" + } + } + } + }, + "input-tags": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus d'un tag.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-focus-ring-width", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-tags`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-font-size", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-tags` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-font-size-small", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "group": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du texte d'un en-tête de groupe.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-group-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-group-font-size", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur d'un en-tête de groupe du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-group-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-group-height", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + } + }, + "input": { + "min-width": { + "$value": "80px", + "$type": "dimension", + "$description": "Largeur minimale de la zone de saisie restante.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-input-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-input-min-width", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + } + }, + "option": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre la coche et le libellé d'une option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-gap", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur de `ui-input-tags` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-height", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-input-tags` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-height-small", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-input-tags` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-padding-x", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-input-tags` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-padding-x-small", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'une option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-option-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-option-radius", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + } + }, + "panel": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les groupes du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-panel-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-panel-gap", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset du panneau de suggestions.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-panel-padding", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + } + }, + "scroller": { + "max-height": { + "$value": "200px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-input-tags` — scroller.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-scroller-max-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-scroller-max-height", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "max-height-small": { + "$value": "180px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-input-tags` — scroller (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-scroller-max-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-scroller-max-height-small", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + } + }, + "tag": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-input-tags` — tag.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-tag-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-tag-font-size", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les tags saisis.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-tag-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-tag-gap", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "gap-custom": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement interne d'un tag rendu via un template `#item`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-tag-gap-custom)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-tag-gap-custom", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-input-tags` — tag.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-tag-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-tag-height", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon d'un tag.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-input-tags-tag-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-input-tags-tag-radius", + "component": "ui-input-tags", + "entryPoint": "@4sh/ui-kit/forms/ui-input-tags", + "descriptionSource": "scss" + } + } + } + } + }, + "label": { + "color": { + "$value": "{semantics.form.high.content.default}", + "$type": "color", + "$description": "Couleur du texte, pilotable par un composant de formulaire parent (hover, disabled…).", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-label-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-label-color", + "component": "ui-label", + "entryPoint": "@4sh/ui-kit/forms/ui-label", + "descriptionSource": "scss", + "perInstance": true, + "note": "2 défauts selon la variante rendue : en déclarer un seul écraserait les autres." + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-label-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-label-font-size", + "component": "ui-label", + "entryPoint": "@4sh/ui-kit/forms/ui-label", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police du libellé, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-label-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-label-font-size-small", + "component": "ui-label", + "entryPoint": "@4sh/ui-kit/forms/ui-label", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espace entre le texte et son marqueur.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-label-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-label-gap", + "component": "ui-label", + "entryPoint": "@4sh/ui-kit/forms/ui-label", + "descriptionSource": "scss" + } + } + } + }, + "link": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-link-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-focus-ring-width", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-link`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-link-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-font-size", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-link` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-link-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-font-size-small", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-link-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-gap", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.2xs}", + "$type": "dimension", + "$description": "Rayon de la zone de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-link-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-radius", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "scss" + } + } + }, + "underline": { + "thickness": { + "$value": "from-font", + "$type": "string", + "$description": "Épaisseur du soulignement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-link-underline-thickness)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-link-underline-thickness", + "component": "ui-link", + "entryPoint": "@4sh/ui-kit/actions/ui-link", + "descriptionSource": "scss" + } + } + } + } + }, + "menu": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-focus-ring-width", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-menu`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-font-size", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-menu` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-font-size-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "group": { + "indent": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Indentation des groupes imbriqués.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-group-indent)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-group-indent", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "scss" + } + } + } + }, + "header": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-menu` — header.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-header-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-header-font-size", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-menu` — header (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-header-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-header-font-size-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "height": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-menu` — header.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-header-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-header-height", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-menu` — header (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-header-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-header-height-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + } + }, + "item": { + "height": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur de `ui-menu` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-item-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-item-height", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-menu` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-item-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-item-height-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-menu` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-item-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-item-padding-x", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-menu` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-item-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-item-padding-x-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des menuitems.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-item-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-item-radius", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "scss" + } + } + } + }, + "min-width": { + "$value": "210px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-menu`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-min-width", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "min-width-small": { + "$value": "160px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-menu` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-min-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-min-width-small", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "derived" + } + } + }, + "panel": { + "padding": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Gouttière interne du panneau (**partagée** avec les autres panneaux flottants).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-menu-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-menu-panel-padding", + "component": "ui-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-menu", + "descriptionSource": "scss" + } + } + } + } + }, + "modal": { + "action": { + "size": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille de la cible tactile du bouton de fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-action-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-action-size", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "dialog": { + "padding": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Inset du dialogue.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-dialog-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-dialog-padding", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon du dialogue.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-dialog-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-dialog-radius", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus des boutons d'en-tête", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-focus-ring-width", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + }, + "header": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les éléments de l'en-tête.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-header-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-header-gap", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "mask": { + "background": { + "$value": "{primitives.black.50}", + "$type": "color", + "$description": "Assombrissement du masque.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "FRAME_FILL", + "SHAPE_FILL" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-mask-background)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-mask-background", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Inset entre le masque et le dialogue.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-mask-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-mask-padding", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "resize-handle": { + "size": { + "$value": "16px", + "$type": "dimension", + "$description": "Taille de la poignée de redimensionnement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-resize-handle-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-resize-handle-size", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure du dialogue.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-modal-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-modal-stroke-width", + "component": "ui-modal", + "entryPoint": "@4sh/ui-kit/layout/ui-modal", + "descriptionSource": "scss" + } + } + } + }, + "nudger": { + "gap": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Espacement entre les boutons et l'affichage de la valeur.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-nudger-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-nudger-gap", + "component": "ui-nudger", + "entryPoint": "@4sh/ui-kit/forms/ui-nudger", + "descriptionSource": "scss" + } + } + }, + "value": { + "font-size": { + "$value": "{responsive.size.typography.title.xl}", + "$type": "dimension", + "$description": "Taille de police de `ui-nudger` — value.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-nudger-value-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-nudger-value-font-size", + "component": "ui-nudger", + "entryPoint": "@4sh/ui-kit/forms/ui-nudger", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.title.lg}", + "$type": "dimension", + "$description": "Taille de police de `ui-nudger` — value (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-nudger-value-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-nudger-value-font-size-small", + "component": "ui-nudger", + "entryPoint": "@4sh/ui-kit/forms/ui-nudger", + "descriptionSource": "derived" + } + } + }, + "min-width": { + "$value": "40px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-nudger` — value.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-nudger-value-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-nudger-value-min-width", + "component": "ui-nudger", + "entryPoint": "@4sh/ui-kit/forms/ui-nudger", + "descriptionSource": "derived" + } + } + }, + "min-width-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-nudger` — value (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-nudger-value-min-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-nudger-value-min-width-small", + "component": "ui-nudger", + "entryPoint": "@4sh/ui-kit/forms/ui-nudger", + "descriptionSource": "derived" + } + } + } + } + }, + "overlay-panel": { + "padding": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Gouttière interne des panneaux flottants.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-overlay-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-overlay-panel-padding", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins des panneaux flottants.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-overlay-panel-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-overlay-panel-radius", + "component": "ui-config", + "entryPoint": "@4sh/ui-kit/styles", + "descriptionSource": "scss" + } + } + } + }, + "overlay": { + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins du panneau flottant, pour un exemplaire.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-overlay-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-overlay-radius", + "component": "panneaux flottants", + "entryPoint": "@4sh/ui-kit/overlay", + "descriptionSource": "scss", + "perInstance": true, + "note": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + } + } + }, + "stroke": { + "$value": "{semantics.form.high.stroke.default}", + "$type": "color", + "$description": "Couleur de bordure du panneau flottant, pour un exemplaire.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "STROKE_COLOR" + ], + "codeSyntax": { + "WEB": "var(--ui-overlay-stroke)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-overlay-stroke", + "component": "panneaux flottants", + "entryPoint": "@4sh/ui-kit/overlay", + "descriptionSource": "scss", + "perInstance": true, + "note": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + } + } + }, + "surface": { + "$value": "{semantics.form.high.surface.default}", + "$type": "color", + "$description": "Fond du panneau flottant, pour un exemplaire.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "FRAME_FILL", + "SHAPE_FILL" + ], + "codeSyntax": { + "WEB": "var(--ui-overlay-surface)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-overlay-surface", + "component": "panneaux flottants", + "entryPoint": "@4sh/ui-kit/overlay", + "descriptionSource": "scss", + "perInstance": true, + "note": "poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte." + } + } + } + }, + "paginator": { + "bar": { + "gap": { + "$value": "{metrics.units.2xl}", + "$type": "dimension", + "$description": "Espacement entre les blocs de la barre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-bar-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-bar-gap", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + } + }, + "blocks": { + "gap": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Espacement interne d'un bloc.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-blocks-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-blocks-gap", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + } + }, + "button": { + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des boutons (carré arrondi).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-button-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-button-radius", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille des contrôles / min-width des numéros.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-button-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-button-size", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Anneau de focus des boutons.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-focus-ring-width", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + }, + "numbers": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les numéros de page.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-paginator-numbers-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-paginator-numbers-gap", + "component": "ui-paginator", + "entryPoint": "@4sh/ui-kit/table/ui-paginator", + "descriptionSource": "scss" + } + } + } + } + }, + "popover": { + "arrow": { + "size": { + "$value": "8px", + "$type": "dimension", + "$description": "Diamètre de la flèche — synchronisé avec `ARROW_GAP` dans le `.ts`", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-popover-arrow-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-popover-arrow-size", + "component": "ui-popover", + "entryPoint": "@4sh/ui-kit/layout/ui-popover", + "descriptionSource": "scss" + } + } + } + }, + "max-width": { + "$value": "320px", + "$type": "dimension", + "$description": "Largeur maximale avant retour à la ligne (le CDK garde le panneau dans le viewport)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-popover-max-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-popover-max-width", + "component": "ui-popover", + "entryPoint": "@4sh/ui-kit/layout/ui-popover", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Espacement interne du contenu projeté", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-popover-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-popover-padding", + "component": "ui-popover", + "entryPoint": "@4sh/ui-kit/layout/ui-popover", + "descriptionSource": "scss" + } + } + } + }, + "progress-bar": { + "track": { + "height": { + "$value": "8px", + "$type": "dimension", + "$description": "Hauteur de `ui-progress-bar` — track.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-progress-bar-track-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-progress-bar-track-height", + "component": "ui-progress-bar", + "entryPoint": "@4sh/ui-kit/informative/ui-progress-bar", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "4px", + "$type": "dimension", + "$description": "Hauteur de `ui-progress-bar` — track (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-progress-bar-track-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-progress-bar-track-height-small", + "component": "ui-progress-bar", + "entryPoint": "@4sh/ui-kit/informative/ui-progress-bar", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.xs}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-progress-bar` — track.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-progress-bar-track-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-progress-bar-track-radius", + "component": "ui-progress-bar", + "entryPoint": "@4sh/ui-kit/informative/ui-progress-bar", + "descriptionSource": "derived" + } + } + }, + "radius-small": { + "$value": "{metrics.radius.2xs}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-progress-bar` — track (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-progress-bar-track-radius-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-progress-bar-track-radius-small", + "component": "ui-progress-bar", + "entryPoint": "@4sh/ui-kit/informative/ui-progress-bar", + "descriptionSource": "derived" + } + } + } + } + }, + "radio": { + "box": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins du cercle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-box-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-box-radius", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Diamètre du cercle radio", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-box-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-box-size", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + } + }, + "dot": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins du point sélectionné.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-dot-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-dot-radius", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Diamètre du point sélectionné (spécifique radio)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-dot-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-dot-size", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-focus-ring-width", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police du label.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-font-size", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre le cercle et le label", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-gap", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + }, + "label": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement libellé ↔ marqueur requis.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-label-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-label-gap", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure du cercle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-radio-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-radio-stroke-width", + "component": "ui-radio", + "entryPoint": "@4sh/ui-kit/forms/ui-radio", + "descriptionSource": "scss" + } + } + } + }, + "rating": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre étoiles.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-gap", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "gap-lg": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre étoiles, taille `lg`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-gap-lg)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-gap-lg", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "gap-md": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre étoiles, taille `md`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-gap-md)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-gap-md", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "gap-sm": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre étoiles, taille `sm`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-gap-sm)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-gap-sm", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "gap-xl": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement entre étoiles, taille `xl`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-gap-xl)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-gap-xl", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "icon": { + "color": { + "$value": "{semantics.form.low.content.default}", + "$type": "color", + "$description": "Couleur d'une étoile vide.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-icon-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-icon-color", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Inset de la zone de clic d'une étoile.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-icon-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-icon-padding", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon de la zone de clic d'une étoile.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-icon-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-icon-radius", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + } + }, + "icon-fill": { + "color": { + "$value": "{semantics.form.high.content.checked}", + "$type": "color", + "$description": "Couleur de la portion remplie d'une étoile.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-rating-icon-fill-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-rating-icon-fill-color", + "component": "ui-rating", + "entryPoint": "@4sh/ui-kit/forms/ui-rating", + "descriptionSource": "scss" + } + } + } + } + }, + "read-only": { + "field": { + "height": { + "$value": "{responsive.size.components.default}", + "$type": "dimension", + "$description": "Hauteur de la zone de valeur en `matchField` (taille `default`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-read-only-field-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-read-only-field-height", + "component": "ui-read-only", + "entryPoint": "@4sh/ui-kit/informative/ui-read-only", + "descriptionSource": "scss" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Idem pour la taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-read-only-field-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-read-only-field-height-small", + "component": "ui-read-only", + "entryPoint": "@4sh/ui-kit/informative/ui-read-only", + "descriptionSource": "scss" + } + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-read-only`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-read-only-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-read-only-font-size", + "component": "ui-read-only", + "entryPoint": "@4sh/ui-kit/informative/ui-read-only", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-read-only` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-read-only-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-read-only-font-size-small", + "component": "ui-read-only", + "entryPoint": "@4sh/ui-kit/informative/ui-read-only", + "descriptionSource": "derived" + } + } + }, + "label": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Écart label→valeur en mode `matchField` (sinon compact).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-read-only-label-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-read-only-label-gap", + "component": "ui-read-only", + "entryPoint": "@4sh/ui-kit/informative/ui-read-only", + "descriptionSource": "scss" + } + } + } + } + }, + "segment-control": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus des segments", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-focus-ring-width", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-segment-control`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-font-size", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-segment-control` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-font-size-small", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "0", + "$type": "number", + "$description": "Espacement entre les segments.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-gap", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "min-height": { + "$value": "44px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-segment-control`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-min-height", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "min-height-small": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-segment-control` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-min-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-min-height-small", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "option": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ libellé dans un segment.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-gap", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "min-height": { + "$value": "36px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-segment-control` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-min-height", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "min-height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur minimale de `ui-segment-control` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-min-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-min-height-small", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "min-width": { + "$value": "36px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-segment-control` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-min-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-min-width", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "min-width-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Largeur minimale de `ui-segment-control` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-min-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-min-width-small", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-segment-control` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-padding-x", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-segment-control` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-padding-x-small", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins d'un segment.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-option-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-option-radius", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + } + }, + "padding": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Gouttière interne autour des segments (coins de l'indicateur concentriques au track)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-padding", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.md}", + "$type": "dimension", + "$description": "Rayon des coins de la piste.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-radius", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la bordure de la piste", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-stroke-width", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + }, + "thumb": { + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins de l'indicateur glissant.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-segment-control-thumb-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-segment-control-thumb-radius", + "component": "ui-segment-control", + "entryPoint": "@4sh/ui-kit/forms/ui-segment-control", + "descriptionSource": "scss" + } + } + } + } + }, + "select": { + "checkbox-icon": { + "size": { + "$value": "{responsive.size.typography.icon.sm}", + "$type": "dimension", + "$description": "Taille de la coche dans la case (mode `checkbox`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-checkbox-icon-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-checkbox-icon-size", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "checkbox": { + "radius": { + "$value": "{metrics.radius.xs}", + "$type": "dimension", + "$description": "Rayon de la case visuelle (mode `checkbox`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-select-checkbox-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-checkbox-radius", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "18px", + "$type": "dimension", + "$description": "Taille de la case visuelle (mode `checkbox`)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-checkbox-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-checkbox-size", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "clear": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon du bouton d'effacement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-select-clear-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-clear-radius", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Taille du bouton d'effacement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-clear-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-clear-size", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "filter": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement icône ↔ saisie dans le filtre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-filter-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-filter-gap", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — filter.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-filter-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-filter-height", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "{responsive.size.components.xs}", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — filter (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-filter-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-filter-height-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal du contenu du filtre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-filter-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-filter-padding-x", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la bordure du filtre.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-filter-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-filter-stroke-width", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus (bouton d'effacement).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-focus-ring-width", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-select`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-select-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-font-size", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-select` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-select-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-font-size-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "group": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-select` — group.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-select-group-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-group-font-size", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-select` — group (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-select-group-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-group-font-size-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "height": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — group.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-group-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-group-height", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — group (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-group-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-group-height-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + } + }, + "header": { + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal des zones d'en-tête et de pied projetées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-header-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-header-padding-x", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset vertical des zones d'en-tête et de pied projetées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-header-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-header-padding-y", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "option": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre case/contenu/coche dans une ligne", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-gap", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "height": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-height", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-select` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-height-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-select` — option.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-padding-x", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-select` — option (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-padding-x-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'une ligne d'option", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-select-option-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-option-radius", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "panel": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les groupes du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-panel-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-panel-gap", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "padding": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Inset du panneau d'options.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-panel-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-panel-padding", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + }, + "scroller": { + "max-height": { + "$value": "312px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-select` — scroller.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-scroller-max-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-scroller-max-height", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + }, + "max-height-small": { + "$value": "256px", + "$type": "dimension", + "$description": "Hauteur maximale de `ui-select` — scroller (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-scroller-max-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-scroller-max-height-small", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "derived" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure (case visuelle)", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-select-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-stroke-width", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + }, + "values": { + "gap": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement entre les valeurs affichées dans le déclencheur (mode `multiple`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-select-values-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-select-values-gap", + "component": "ui-select", + "entryPoint": "@4sh/ui-kit/forms/ui-select", + "descriptionSource": "scss" + } + } + } + } + }, + "separator": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille du libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-separator-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-separator-font-size", + "component": "ui-separator", + "entryPoint": "@4sh/ui-kit/informative/ui-separator", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille du libellé en taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-separator-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-separator-font-size-small", + "component": "ui-separator", + "entryPoint": "@4sh/ui-kit/informative/ui-separator", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement filet ↔ libellé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-separator-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-separator-gap", + "component": "ui-separator", + "entryPoint": "@4sh/ui-kit/informative/ui-separator", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur du filet.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-separator-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-separator-stroke-width", + "component": "ui-separator", + "entryPoint": "@4sh/ui-kit/informative/ui-separator", + "descriptionSource": "scss" + } + } + }, + "stroke-width-small": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur du filet en taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-separator-stroke-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-separator-stroke-width-small", + "component": "ui-separator", + "entryPoint": "@4sh/ui-kit/informative/ui-separator", + "descriptionSource": "scss" + } + } + } + }, + "sidebar": { + "backdrop": { + "background": { + "$value": "{primitives.black.50}", + "$type": "color", + "$description": "Fond du voile en présentation superposée.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "FRAME_FILL", + "SHAPE_FILL" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-backdrop-background)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-backdrop-background", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "close": { + "size": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille de la cible tactile du bouton de fermeture.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-close-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-close-size", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-focus-ring-width", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-sidebar-menu`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-font-size", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-sidebar-menu` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-font-size-small", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "group": { + "indent": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Retrait des items d'un sous-groupe.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-group-indent)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-group-indent", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "header": { + "font-size": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-sidebar-menu` — header.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-header-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-header-font-size", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-sidebar-menu` — header (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-header-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-header-font-size-small", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + } + }, + "icon-slot": { + "size": { + "$value": "20px", + "$type": "dimension", + "$description": "Largeur réservée à l'icône d'un item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-icon-slot-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-icon-slot-size", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "item": { + "height": { + "$value": "40px", + "$type": "dimension", + "$description": "Hauteur de `ui-sidebar-menu` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-item-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-item-height", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "32px", + "$type": "dimension", + "$description": "Hauteur de `ui-sidebar-menu` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-item-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-item-height-small", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-sidebar-menu` — item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-item-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-item-padding-x", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "padding-x-small": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal de `ui-sidebar-menu` — item (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-item-padding-x-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-item-padding-x-small", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon d'un item.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-item-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-item-radius", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "rail": { + "width": { + "$value": "72px", + "$type": "dimension", + "$description": "Largeur du rail d’icônes replié.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-rail-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-rail-width", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "region": { + "padding": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset des régions (en-tête, corps, pied).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-region-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-region-padding", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "separator": { + "width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur d'un séparateur.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-separator-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-separator-width", + "component": "ui-sidebar-menu", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure du panneau.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-stroke-width", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + }, + "width": { + "$value": "280px", + "$type": "dimension", + "$description": "Largeur du panneau déployé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-sidebar-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-sidebar-width", + "component": "ui-sidebar", + "entryPoint": "@4sh/ui-kit/navigation/ui-sidebar", + "descriptionSource": "scss" + } + } + } + }, + "skeleton": { + "background": { + "$value": "{semantics.global.background.muted}", + "$type": "color", + "$description": "Couleur de fond de la boîte du placeholder.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "FRAME_FILL", + "SHAPE_FILL" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-background)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-background", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "scss" + } + } + }, + "circle": { + "height": { + "$value": "100px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — circle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-circle-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-circle-height", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "64px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — circle (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-circle-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-circle-height-small", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-skeleton` — circle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-circle-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-circle-radius", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "width": { + "$value": "100px", + "$type": "dimension", + "$description": "Largeur de `ui-skeleton` — circle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-circle-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-circle-width", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "width-small": { + "$value": "64px", + "$type": "dimension", + "$description": "Largeur de `ui-skeleton` — circle (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-circle-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-circle-width-small", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + } + }, + "rectangle": { + "height": { + "$value": "100px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — rectangle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-rectangle-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-rectangle-height", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "64px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — rectangle (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-rectangle-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-rectangle-height-small", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-skeleton` — rectangle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-rectangle-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-rectangle-radius", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "width": { + "$value": "100px", + "$type": "dimension", + "$description": "Largeur de `ui-skeleton` — rectangle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-rectangle-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-rectangle-width", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "width-small": { + "$value": "64px", + "$type": "dimension", + "$description": "Largeur de `ui-skeleton` — rectangle (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-rectangle-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-rectangle-width-small", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + } + }, + "shine": { + "$value": "var(--_shine, rgba(255, 255, 255, 0.6))", + "$type": "color", + "$description": "Teinte du reflet glissant de l’animation `wave`.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-shine)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-shine", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "scss", + "perInstance": true, + "note": "surcharge d’un exemplaire : son défaut passe par une variable interne au composant, qui ne résout rien depuis l’extérieur." + } + } + }, + "text": { + "height": { + "$value": "20px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — text.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-text-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-text-height", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "12px", + "$type": "dimension", + "$description": "Hauteur de `ui-skeleton` — text (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-text-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-text-height-small", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.xl}", + "$type": "dimension", + "$description": "Rayon des coins de `ui-skeleton` — text.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-text-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-text-radius", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + }, + "width": { + "$value": "224px", + "$type": "dimension", + "$description": "Largeur de `ui-skeleton` — text.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-skeleton-text-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-skeleton-text-width", + "component": "ui-skeleton", + "entryPoint": "@4sh/ui-kit/informative/ui-skeleton", + "descriptionSource": "derived" + } + } + } + } + }, + "slider": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus des poignées", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-focus-ring-width", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "handle": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des poignées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-handle-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-handle-radius", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "22px", + "$type": "dimension", + "$description": "Diamètre des poignées", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-handle-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-handle-size", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de bordure des poignées.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-handle-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-handle-stroke-width", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + } + }, + "length": { + "$value": "192px", + "$type": "dimension", + "$description": "Longueur de la piste en orientation verticale", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-length)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-length", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "mark": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des repères.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-mark-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-mark-radius", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "6px", + "$type": "dimension", + "$description": "Taille des repères (agrandie pour la lisibilité).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-mark-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-mark-size", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + } + }, + "thickness": { + "$value": "8px", + "$type": "dimension", + "$description": "Épaisseur de la piste", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-thickness)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-thickness", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + }, + "track": { + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon de la piste et de la portion remplie.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-slider-track-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-slider-track-radius", + "component": "ui-slider", + "entryPoint": "@4sh/ui-kit/forms/ui-slider", + "descriptionSource": "scss" + } + } + } + } + }, + "spinner": { + "color": { + "$value": "{semantics.informative.highlightLow.content.default}", + "$type": "color", + "$description": "Couleur du marqueur (le SVG et l’icône utilisent `currentColor`).", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-spinner-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-spinner-color", + "component": "ui-spinner", + "entryPoint": "@4sh/ui-kit/informative/ui-spinner", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-spinner`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-spinner-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-spinner-font-size", + "component": "ui-spinner", + "entryPoint": "@4sh/ui-kit/informative/ui-spinner", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police de `ui-spinner` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-spinner-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-spinner-font-size-small", + "component": "ui-spinner", + "entryPoint": "@4sh/ui-kit/informative/ui-spinner", + "descriptionSource": "derived" + } + } + }, + "size": { + "$value": "32px", + "$type": "dimension", + "$description": "Taille de `ui-spinner`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-spinner-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-spinner-size", + "component": "ui-spinner", + "entryPoint": "@4sh/ui-kit/informative/ui-spinner", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "20px", + "$type": "dimension", + "$description": "Taille de `ui-spinner` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-spinner-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-spinner-size-small", + "component": "ui-spinner", + "entryPoint": "@4sh/ui-kit/informative/ui-spinner", + "descriptionSource": "derived" + } + } + } + }, + "stepper": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-stepper-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-stepper-focus-ring-width", + "component": "ui-step", + "entryPoint": "@4sh/ui-kit/navigation/ui-stepper", + "descriptionSource": "scss" + } + } + }, + "marker": { + "size": { + "$value": "44px", + "$type": "dimension", + "$description": "Diamètre du marqueur d'étape.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-stepper-marker-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-stepper-marker-size", + "component": "ui-step-item", + "entryPoint": "@4sh/ui-kit/navigation/ui-stepper", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure du marqueur.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-stepper-marker-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-stepper-marker-stroke-width", + "component": "ui-step", + "entryPoint": "@4sh/ui-kit/navigation/ui-stepper", + "descriptionSource": "scss" + } + } + } + }, + "rail": { + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur du rail reliant les étapes.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-stepper-rail-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-stepper-rail-stroke-width", + "component": "ui-step-item", + "entryPoint": "@4sh/ui-kit/navigation/ui-stepper", + "descriptionSource": "scss" + } + } + } + }, + "separator": { + "stroke-width": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur du séparateur entre étapes.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-stepper-separator-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-stepper-separator-stroke-width", + "component": "ui-step", + "entryPoint": "@4sh/ui-kit/navigation/ui-stepper", + "descriptionSource": "scss" + } + } + } + } + }, + "table": { + "cell": { + "padding": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset de `ui-table` — cell.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-table-cell-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-cell-padding", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "derived" + } + } + } + }, + "drop-indicator": { + "width": { + "$value": "{metrics.stroke.lg}", + "$type": "dimension", + "$description": "Épaisseur de l'indicateur de dépose (row reorder).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-table-drop-indicator-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-drop-indicator-width", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Anneau de focus (lignes, en-têtes).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-table-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-focus-ring-width", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "scss" + } + } + }, + "head": { + "padding-large": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset de `ui-table` — head (variante `large`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-table-head-padding-large)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-head-padding-large", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "derived" + } + } + } + }, + "loading": { + "padding-y": { + "$value": "{metrics.units.3xl}", + "$type": "dimension", + "$description": "Hauteur d'air de la ligne d'attente (`loading`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-table-loading-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-loading-padding-y", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "scss" + } + } + } + }, + "resizer-hit": { + "width": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Largeur de la zone de saisie du redimensionneur.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-table-resizer-hit-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-resizer-hit-width", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "scss" + } + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur des traits de séparation / gridlines.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-table-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-table-stroke-width", + "component": "ui-table", + "entryPoint": "@4sh/ui-kit/table/ui-table", + "descriptionSource": "scss" + } + } + } + }, + "tabs": { + "active-bar": { + "color": { + "$value": "{semantics.navigation.high.stroke.active}", + "$type": "color", + "$description": "Couleur de la barre d'onglet actif.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-active-bar-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-active-bar-color", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la barre d'onglet actif.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-active-bar-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-active-bar-size", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-focus-ring-width", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + }, + "indicator": { + "color": { + "$value": "{semantics.navigation.high.stroke.active}", + "$type": "color", + "$description": "Couleur de la barre d'onglet actif.", + "$extensions": { + "com.figma": { + "resolvedType": "COLOR", + "scopes": [ + "ALL_FILLS" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-indicator-color)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-indicator-color", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + }, + "thickness": { + "$value": "{metrics.stroke.default}", + "$type": "dimension", + "$description": "Épaisseur de la barre d'onglet actif.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-indicator-thickness)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-indicator-thickness", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + } + }, + "min-height": { + "$value": "44px", + "$type": "dimension", + "$description": "Hauteur minimale d'un onglet (cible tactile).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-min-height", + "component": "ui-tab", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + }, + "nav": { + "size": { + "$value": "40px", + "$type": "dimension", + "$description": "Taille des boutons de défilement.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-nav-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-nav-size", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + } + }, + "track": { + "thickness": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la piste sous les onglets.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tabs-track-thickness)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tabs-track-thickness", + "component": "ui-tab-list", + "entryPoint": "@4sh/ui-kit/navigation/ui-tabs", + "descriptionSource": "scss" + } + } + } + } + }, + "tag": { + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police de `ui-tag`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-font-size", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police de `ui-tag` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-font-size-small", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement de `ui-tag`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-gap", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "gap-small": { + "$value": "{metrics.units.xs}", + "$type": "dimension", + "$description": "Espacement de `ui-tag` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-gap-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-gap-small", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "height": { + "$value": "{responsive.size.components.2xs}", + "$type": "dimension", + "$description": "Hauteur de `ui-tag`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-height", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "22px", + "$type": "dimension", + "$description": "Hauteur de `ui-tag` (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-height-small", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "derived" + } + } + }, + "padding-x": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset horizontal.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-padding-x", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-radius", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "scss" + } + } + }, + "radius-square": { + "$value": "{metrics.radius.sm}", + "$type": "dimension", + "$description": "Rayon des coins en présentation `square`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-radius-square)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-radius-square", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la bordure.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-tag-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tag-stroke-width", + "component": "ui-tag", + "entryPoint": "@4sh/ui-kit/informative/ui-tag", + "descriptionSource": "scss" + } + } + } + }, + "textarea": { + "count": { + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du compteur de caractères.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-count-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-count-font-size", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.sm}", + "$type": "dimension", + "$description": "Taille de police du compteur de caractères, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-count-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-count-font-size-small", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du contrôle, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-font-size-small", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + }, + "line-height": { + "$value": "1.5", + "$type": "number", + "$description": "Interlignage du contrôle multiligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "LINE_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-line-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-line-height", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + }, + "min-height": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale du contrôle multiligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-min-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-min-height", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + }, + "min-height-small": { + "$value": "{responsive.size.components.sm}", + "$type": "dimension", + "$description": "Hauteur minimale du contrôle multiligne, variante `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-min-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-min-height-small", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.md}", + "$type": "dimension", + "$description": "Inset horizontal du contrôle multiligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-textarea-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-textarea-padding-x", + "component": "ui-textarea", + "entryPoint": "@4sh/ui-kit/forms/ui-textarea", + "descriptionSource": "scss" + } + } + } + }, + "toast": { + "edge": { + "gap": { + "$value": "{metrics.units.lg}", + "$type": "dimension", + "$description": "Marge par rapport aux bords du viewport.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toast-edge-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toast-edge-gap", + "component": "ui-toast-container", + "entryPoint": "@4sh/ui-kit/informative/ui-toast", + "descriptionSource": "scss" + } + } + } + }, + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-toast-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toast-focus-ring-width", + "component": "ui-toast", + "entryPoint": "@4sh/ui-kit/informative/ui-toast", + "descriptionSource": "scss" + } + } + }, + "stack": { + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espacement entre les toasts empilés.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toast-stack-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toast-stack-gap", + "component": "ui-toast-container", + "entryPoint": "@4sh/ui-kit/informative/ui-toast", + "descriptionSource": "scss" + } + } + } + }, + "width": { + "$value": "360px", + "$type": "dimension", + "$description": "Largeur d'un toast empilé.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toast-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toast-width", + "component": "ui-toast-container", + "entryPoint": "@4sh/ui-kit/informative/ui-toast", + "descriptionSource": "scss" + } + } + }, + "width-expanded": { + "$value": "480px", + "$type": "dimension", + "$description": "Largeur en mode bannière.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toast-width-expanded)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toast-width-expanded", + "component": "ui-toast-container", + "entryPoint": "@4sh/ui-kit/informative/ui-toast", + "descriptionSource": "scss" + } + } + } + }, + "toggle": { + "focus-ring-width": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Épaisseur de l'anneau de focus", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-focus-ring-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-focus-ring-width", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.default}", + "$type": "dimension", + "$description": "Taille de police du label.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-font-size", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "font-size-small": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du label en taille `small`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-font-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-font-size-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "gap": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Espace entre l'interrupteur et le label", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-gap", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "label": { + "gap": { + "$value": "{metrics.units.2xs}", + "$type": "dimension", + "$description": "Espacement libellé ↔ marqueur requis.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-label-gap)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-label-gap", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + } + }, + "thumb": { + "offset": { + "$value": "20px", + "$type": "dimension", + "$description": "Décalage de `ui-toggle` — thumb.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-thumb-offset)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-thumb-offset", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "offset-small": { + "$value": "16px", + "$type": "dimension", + "$description": "Décalage de `ui-toggle` — thumb (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-thumb-offset-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-thumb-offset-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins de la pastille.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-thumb-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-thumb-radius", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "size": { + "$value": "20px", + "$type": "dimension", + "$description": "Taille de `ui-toggle` — thumb.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-thumb-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-thumb-size", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "size-small": { + "$value": "16px", + "$type": "dimension", + "$description": "Taille de `ui-toggle` — thumb (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-thumb-size-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-thumb-size-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + } + }, + "track": { + "height": { + "$value": "24px", + "$type": "dimension", + "$description": "Hauteur de `ui-toggle` — track.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-height)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-height", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "height-small": { + "$value": "20px", + "$type": "dimension", + "$description": "Hauteur de `ui-toggle` — track (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-height-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-height-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "padding": { + "$value": "1px", + "$type": "dimension", + "$description": "Inset de `ui-toggle` — track.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-padding)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-padding", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "padding-small": { + "$value": "1px", + "$type": "dimension", + "$description": "Inset de `ui-toggle` — track (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-padding-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-padding-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "radius": { + "$value": "{metrics.radius.full}", + "$type": "dimension", + "$description": "Rayon des coins de la piste.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-radius", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de la bordure de la piste.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-stroke-width", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "scss" + } + } + }, + "width": { + "$value": "44px", + "$type": "dimension", + "$description": "Largeur de `ui-toggle` — track.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-width", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + }, + "width-small": { + "$value": "36px", + "$type": "dimension", + "$description": "Largeur de `ui-toggle` — track (variante `small`).", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-toggle-track-width-small)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-toggle-track-width-small", + "component": "ui-toggle", + "entryPoint": "@4sh/ui-kit/forms/ui-toggle", + "descriptionSource": "derived" + } + } + } + } + }, + "tooltip": { + "arrow": { + "size": { + "$value": "8px", + "$type": "dimension", + "$description": "Taille de la flèche, tenue en phase avec la constante `ARROW_GAP` du `.ts`.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-arrow-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-arrow-size", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + } + }, + "font-size": { + "$value": "{responsive.size.typography.text.md}", + "$type": "dimension", + "$description": "Taille de police du contenu.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "FONT_SIZE" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-font-size)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-font-size", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + }, + "max-width": { + "$value": "256px", + "$type": "dimension", + "$description": "Largeur max avant retour à la ligne.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "WIDTH_HEIGHT" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-max-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-max-width", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + }, + "padding-x": { + "$value": "{metrics.units.default}", + "$type": "dimension", + "$description": "Inset horizontal de la bulle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-padding-x)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-padding-x", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + }, + "padding-y": { + "$value": "{metrics.units.sm}", + "$type": "dimension", + "$description": "Inset vertical de la bulle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "GAP" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-padding-y)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-padding-y", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + }, + "radius": { + "$value": "{metrics.radius.default}", + "$type": "dimension", + "$description": "Rayon des coins de la bulle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "CORNER_RADIUS" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-radius)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-radius", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + }, + "stroke-width": { + "$value": "{metrics.stroke.sm}", + "$type": "dimension", + "$description": "Épaisseur de bordure de la bulle.", + "$extensions": { + "com.figma": { + "resolvedType": "FLOAT", + "scopes": [ + "STROKE_FLOAT" + ], + "codeSyntax": { + "WEB": "var(--ui-tooltip-stroke-width)" + } + }, + "com.4sh.ui-kit": { + "cssVar": "--ui-tooltip-stroke-width", + "component": "ui-tooltip", + "entryPoint": "@4sh/ui-kit/informative/ui-tooltip", + "descriptionSource": "scss" + } + } + } + } +} diff --git a/package.json b/package.json index ca92df6..d0436f2 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "ui-kit:build": "ng build ui-kit && npm run ui-kit:styles", "tokens:build": "node scripts/tokens.build.mjs", "build-info": "node scripts/build-info.mjs", - "docs:config": "node scripts/docs.config.mjs", - "docs:config:check": "node scripts/docs.config.mjs --check && npm run components:check", + "docs:config": "node scripts/docs.config.mjs && node scripts/component-vars.build.mjs", + "docs:config:check": "node scripts/docs.config.mjs --check && node scripts/component-vars.build.mjs --check && npm run components:check", "components:check": "node scripts/components.check.mjs", "ui-kit:styles": "sass projects/ui-kit/styles/index.scss dist/ui-kit/styles.css --load-path=projects/ui-kit/styles --no-source-map --style=compressed", "ui-kit:pack": "npm run ui-kit:build && npm pack ./dist/ui-kit", diff --git a/projects/ui-kit/README.fr.md b/projects/ui-kit/README.fr.md index 8100cc7..dd6d239 100644 --- a/projects/ui-kit/README.fr.md +++ b/projects/ui-kit/README.fr.md @@ -199,6 +199,50 @@ les mêmes fonctions, mixins et constantes partagées. > Sass distincte, tout CSS qui y serait exposé serait dupliqué dans chaque > composant. Les classes utilitaires globales sont dans `styles.css`. +### Thème, marque et surcharges + +Le clair/sombre et la marque sont des **attributs sur ``** — rien à importer, +`styles.css` porte déjà tous les modes : + +| Attribut | Valeurs | Absent signifie | +|---|---|---| +| `data-theme` | `dark` | clair | +| `data-brand` | `brand2`, `brand3` | marque 1 | + +```html + +``` + +À poser comme vous voulez (un service, du SSR, un flag de build) : le kit ne fait que +les lire. + +Pour changer une valeur, trois niveaux, du plus large au plus étroit : + +```scss +// src/styles/main.scss — chargé après styles.css +@use 'presets/component-vars'; // 2. les valeurs d'un composant + +:root { --units-lg: 20px; } // 1. un token : tout le kit suit +:root[data-theme='dark'] { --global-background-default: #101014; } + +.toolbar ui-button { --ui-button-height-small: 24px; } // 3. une zone de l'écran +``` + +1. **Un token** (`--units-*`, `--radius-*`, `--actions-*`…) — doit venir **après** + `styles.css`, qui les déclare. Pour une valeur propre à un mode, viser le même + sélecteur (`:root[data-theme='dark']`). +2. **Les valeurs d'un composant** — copier `@4sh/ui-kit/styles/component-vars.scss` + dans votre `styles/presets/` : toutes les variables `--ui-*` à leur valeur livrée, + prêtes à retoucher. L'ordre de chargement n'importe pas ici, le kit ne fait que + *lire* ces noms. + Les valeurs partagées par toute une famille sont dans le même fichier, en + `--ui-form-*` / `--ui-control-*` / `--ui-overlay-panel-*` : une seule déclaration + déplace tous les consommateurs, et une variable de composant reste prioritaire. +3. **Une zone** — la même variable `--ui-*` sur n'importe quel sélecteur ou élément. + +Référence complète (chaque variable, son rôle, sa valeur mesurée) : Storybook → +*Spécifications → Thème & Système de Tokens*. + --- ## `@4sh/ui-kit/forms` — socle des champs diff --git a/projects/ui-kit/README.md b/projects/ui-kit/README.md index c7a21e2..7e86ac0 100644 --- a/projects/ui-kit/README.md +++ b/projects/ui-kit/README.md @@ -200,6 +200,48 @@ shared functions, mixins and constants. > CSS exposed there would be duplicated into every component. The global utility > classes live in `styles.css`. +### Theme, brand and overrides + +Light/dark and brand are **attributes on ``** — nothing to import, `styles.css` +already carries every mode: + +| Attribute | Values | Absent means | +|---|---|---| +| `data-theme` | `dark` | light | +| `data-brand` | `brand2`, `brand3` | brand 1 | + +```html + +``` + +Set them however suits you (a service, SSR, a build flag): the kit only reads them. + +To change a value, three levels, from the broadest to the narrowest: + +```scss +// src/styles/main.scss — loaded after styles.css +@use 'presets/component-vars'; // 2. one component's values + +:root { --units-lg: 20px; } // 1. a token: the whole kit follows +:root[data-theme='dark'] { --global-background-default: #101014; } + +.toolbar ui-button { --ui-button-height-small: 24px; } // 3. one area of the screen +``` + +1. **A token** (`--units-*`, `--radius-*`, `--actions-*`…) — must come **after** + `styles.css`, which declares them. For a mode-specific value, match the same + selector (`:root[data-theme='dark']`). +2. **A component's values** — copy `@4sh/ui-kit/styles/component-vars.scss` into your + `styles/presets/`: every `--ui-*` variable at its shipped value, ready to retune. + Load order is irrelevant here, the kit only *reads* these names. + Values shared by a whole family live in the same file under `--ui-form-*` / + `--ui-control-*` / `--ui-overlay-panel-*`: one declaration moves every consumer, + and a component-level variable still wins over it. +3. **One area** — the same `--ui-*` on any selector or element. + +Full reference (every variable, its role and its measured value): Storybook → +*Spécifications → Thème & Système de Tokens*. + --- ## `@4sh/ui-kit/forms` — form field foundation diff --git a/projects/ui-kit/actions/ui-button-split/src/lib/ui-button-split.scss b/projects/ui-kit/actions/ui-button-split/src/lib/ui-button-split.scss index 6deb8d6..b6a8b68 100644 --- a/projects/ui-kit/actions/ui-button-split/src/lib/ui-button-split.scss +++ b/projects/ui-kit/actions/ui-button-split/src/lib/ui-button-split.scss @@ -8,7 +8,7 @@ // ===================================================================== // Shared default (ui-config) — the overlap must equal the button border width. -$stroke-width: utils.$control-stroke-width; /// Chevauchement des deux boutons (= largeur de bordure, évite le doublement du trait partagé) +$stroke-width: var(--ui-button-split-stroke-width, #{utils.$control-stroke-width}); /// Chevauchement des deux boutons (= largeur de bordure, évite le doublement du trait partagé) .ui-button-split { display: inline-flex; diff --git a/projects/ui-kit/actions/ui-button/src/lib/ui-button.scss b/projects/ui-kit/actions/ui-button/src/lib/ui-button.scss index bed90c0..9ab26f6 100644 --- a/projects/ui-kit/actions/ui-button/src/lib/ui-button.scss +++ b/projects/ui-kit/actions/ui-button/src/lib/ui-button.scss @@ -25,22 +25,31 @@ $levels: high, low, success, warning, error; /// Familles de couleur générées $variants: outlined, ghost; /// Variantes composées avec `$levels` (`filled` = jeu nu, sans classe) $polarities: dark, light; /// Luminosités de fond gérées par `onColor` (jeux épinglés, insensibles au thème) +$radius: var(--ui-button-radius, var(--radius-sm)); /// Rayon des coins, reformable par un hôte (`ui-button-split`). +$radius-rounded: var(--ui-button-radius, var(--ui-button-radius-rounded, var(--radius-full))); /// Rayon des coins en présentation `rounded`. +$gap: var(--ui-button-gap, var(--units-sm)); /// Espacement icône ↔ libellé. +$stroke-width: var(--ui-button-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de la bordure. +$focus-ring-width: var(--ui-button-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus. + +$icon-stacked-gap: var(--ui-button-icon-stacked-gap, var(--units-xs)); /// Espacement icône ↔ libellé quand l'icône est empilée (`iconPos` top / bottom). +$icon-stacked-padding-y: var(--ui-button-icon-stacked-padding-y, var(--units-md)); /// Inset vertical en icône empilée (la hauteur passe en `auto`). +$icon-stacked-padding-x: var(--ui-button-icon-stacked-padding-x, var(--units-default)); /// Inset horizontal en icône empilée. + // `default` = base styles; other keys become `._` modifiers. /// Dimensions par taille (spécifique bouton) $sizes: ( 'default': ( - height: var(--size-components-default), - padding-x: var(--units-default), - font-size: var(--size-typography-text-default), + height: var(--ui-button-height, var(--size-components-default)), + padding-x: var(--ui-button-padding-x, var(--units-default)), + font-size: var(--ui-button-font-size, var(--size-typography-text-default)), ), 'small': ( - height: var(--size-components-sm), - padding-x: var(--units-md), - font-size: var(--size-typography-text-md), + height: var(--ui-button-height-small, var(--size-components-sm)), + padding-x: var(--ui-button-padding-x-small, var(--units-md)), + font-size: var(--ui-button-font-size-small, var(--size-typography-text-md)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus // A $sizes height is either a token reference (`var(...)`, passed through as-is) // or a raw px number, which goes through rem-calc. @@ -100,11 +109,9 @@ $focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de display: inline-flex; align-items: center; justify-content: center; - gap: var(--units-sm); - border: utils.$control-stroke-width solid transparent; - // Radius hook: a host can reshape the corners (e.g. glued buttons in - // ui-button-split) by setting `--ui-button-radius` on the host. - border-radius: var(--ui-button-radius, var(--radius-sm)); /// Rayon exposé pour reformer les coins (utilisé par `ui-button-split`). + gap: $gap; + border: $stroke-width solid transparent; + border-radius: $radius; font-family: var(--fontfamily-base); font-weight: var(--weight-bold); line-height: 1.2; @@ -138,15 +145,15 @@ $focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de // Pill shape. Keeps the `--ui-button-radius` hook so a host (ui-button-split) // can still reshape glued corners. - &._rounded { border-radius: var(--ui-button-radius, var(--radius-full)); } + &._rounded { border-radius: $radius-rounded; } // iconPos top | bottom &._icon-top, &._icon-bottom { flex-direction: column; - gap: var(--units-xs); + gap: $icon-stacked-gap; height: auto; - padding: var(--units-md) var(--units-default); + padding: $icon-stacked-padding-y $icon-stacked-padding-x; } // Icon-only: square (width = size height), no horizontal padding. diff --git a/projects/ui-kit/actions/ui-link/src/lib/ui-link.scss b/projects/ui-kit/actions/ui-link/src/lib/ui-link.scss index 9fe6834..0da6404 100644 --- a/projects/ui-kit/actions/ui-link/src/lib/ui-link.scss +++ b/projects/ui-kit/actions/ui-link/src/lib/ui-link.scss @@ -12,29 +12,34 @@ /// Dimensions par taille (spécifique lien) $sizes: ( 'default': ( - font-size: var(--size-typography-text-default), + font-size: var(--ui-link-font-size, var(--size-typography-text-default)), ), 'small': ( - font-size: var(--size-typography-text-md), + font-size: var(--ui-link-font-size-small, var(--size-typography-text-md)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$focus-ring-width: var(--ui-link-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus + +$gap: var(--ui-link-gap, var(--units-sm)); /// Espacement icône ↔ libellé. +$radius: var(--ui-link-radius, var(--radius-2xs)); /// Rayon de la zone de focus. +$underline-thickness: var(--ui-link-underline-thickness, from-font); /// Épaisseur du soulignement. +$underline-offset: var(--ui-link-underline-offset, 0.15em); /// Décalage du soulignement. .ui-link { // --- Base ---------------------------------------------------------- display: inline-flex; align-items: center; - gap: var(--units-sm); + gap: $gap; font-family: var(--fontfamily-base); font-weight: var(--weight-bold); font-size: map.get($size-default, font-size); line-height: 1.4; color: var(--actions-low-content-default); text-decoration: none; - text-decoration-thickness: from-font; - text-underline-offset: 0.15em; - border-radius: var(--radius-2xs); + text-decoration-thickness: $underline-thickness; + text-underline-offset: $underline-offset; + border-radius: $radius; cursor: pointer; @include utils.control-transition(color, text-decoration-color, box-shadow); diff --git a/projects/ui-kit/base/ui-icon/src/lib/ui-icon.scss b/projects/ui-kit/base/ui-icon/src/lib/ui-icon.scss index 4bc91cf..d14a441 100644 --- a/projects/ui-kit/base/ui-icon/src/lib/ui-icon.scss +++ b/projects/ui-kit/base/ui-icon/src/lib/ui-icon.scss @@ -1,27 +1,37 @@ +// Font-size per `size`, keyed on the dedicated icon scale (not the text one). +/// Taille du glyphe pour chaque valeur de `size`. +$icon-sizes: ( + 'sm': var(--ui-icon-size-sm, var(--size-typography-icon-sm)), + 'md': var(--ui-icon-size-md, var(--size-typography-icon-md)), + 'default': var(--ui-icon-size-default, var(--size-typography-icon-default)), + 'lg': var(--ui-icon-size-lg, var(--size-typography-icon-lg)), + 'xl': var(--ui-icon-size-xl, var(--size-typography-icon-xl)), +); + +$min-size: var(--ui-icon-min-size, 1em); /// Plancher de la boîte du glyphe (largeur et hauteur). +$aspect-ratio: var(--ui-icon-aspect-ratio, 1); /// Ratio de la boîte du glyphe. + :host { display: inline-flex; line-height: 0; } -// Font-size per `size`, keyed on the dedicated icon scale (not the text one). -$icon-sizes: ( - 'sm': var(--size-typography-icon-sm), - 'md': var(--size-typography-icon-md), - 'default': var(--size-typography-icon-default), - 'lg': var(--size-typography-icon-lg), - 'xl': var(--size-typography-icon-xl), -); - .ui-icon { display: inline-flex; align-items: center; justify-content: center; - min-width: 1em; - min-height: 1em; - aspect-ratio: 1; + min-width: $min-size; + min-height: $min-size; + aspect-ratio: $aspect-ratio; color: inherit; + // Two levels, and the reason `--ui-icon-size` is read once instead of inside each + // size: the `size` input picks a step of the scale (`--_scale`), and a parent can + // force ONE icon past that scale (`--ui-icon-size`) — that's how a component sizes + // the icon it projects. + font-size: var(--ui-icon-size, var(--_scale)); /// Taille imposée à un exemplaire, quel que soit son `size`. + @each $name, $value in $icon-sizes { - &._#{$name} { font-size: var(--ui-icon-size, #{$value}); } /// Taille de police, surchargeable par instance. + &._#{$name} { --_scale: #{$value}; } } } diff --git a/projects/ui-kit/base/ui-image/src/lib/ui-image.scss b/projects/ui-kit/base/ui-image/src/lib/ui-image.scss index 4449571..101e9b8 100644 --- a/projects/ui-kit/base/ui-image/src/lib/ui-image.scss +++ b/projects/ui-kit/base/ui-image/src/lib/ui-image.scss @@ -1,3 +1,9 @@ +$object-fit: var(--ui-image-object-fit, contain); /// Ajustement de l'image dans sa boîte. + +$placeholder-min-width: var(--ui-image-placeholder-min-width, var(--units-xl)); /// Largeur minimale du placeholder (visible même sans dimension imposée). +$placeholder-min-height: var(--ui-image-placeholder-min-height, var(--units-xl)); /// Hauteur minimale du placeholder. +$placeholder-radius: var(--ui-image-placeholder-radius, var(--radius-default)); /// Rayon des coins du placeholder. + :host { display: flex; align-items: center; @@ -5,7 +11,7 @@ } .ui-image { - object-fit: contain; + object-fit: $object-fit; } .ui-image-svg { @@ -22,9 +28,9 @@ display: flex; /* Layout */ align-items: center; justify-content: center; - min-width: var(--units-xl); /* Metrics — visible even when unsized */ - min-height: var(--units-xl); - border-radius: var(--radius-default); + min-width: $placeholder-min-width; /* Metrics — visible even when unsized */ + min-height: $placeholder-min-height; + border-radius: $placeholder-radius; background: var(--global-background-muted); /* Colors — adapts light/dark via tokens */ color: var(--global-text-muted); } diff --git a/projects/ui-kit/forms/ui-autocomplete/src/lib/ui-autocomplete.scss b/projects/ui-kit/forms/ui-autocomplete/src/lib/ui-autocomplete.scss index fd55ce5..726b945 100644 --- a/projects/ui-kit/forms/ui-autocomplete/src/lib/ui-autocomplete.scss +++ b/projects/ui-kit/forms/ui-autocomplete/src/lib/ui-autocomplete.scss @@ -9,31 +9,41 @@ /// Géométrie et typo des options, des en-têtes de groupe et hauteur de défilement, par taille. $sizes: ( 'default': ( - option-height: utils.rem-calc(40), - option-padding-x: var(--units-default), - font-size: var(--size-typography-text-default), - group-height: utils.rem-calc(32), - group-font-size: var(--size-typography-text-md), - scroll-height: utils.rem-calc(312), + option-height: var(--ui-autocomplete-option-height, #{utils.rem-calc(40)}), + option-padding-x: var(--ui-autocomplete-option-padding-x, var(--units-default)), + font-size: var(--ui-autocomplete-font-size, var(--size-typography-text-default)), + group-height: var(--ui-autocomplete-group-height, #{utils.rem-calc(32)}), + group-font-size: var(--ui-autocomplete-group-font-size, var(--size-typography-text-md)), + scroll-height: var(--ui-autocomplete-scroller-max-height, #{utils.rem-calc(312)}), ), 'small': ( - option-height: utils.rem-calc(32), - option-padding-x: var(--units-md), - font-size: var(--size-typography-text-md), - group-height: utils.rem-calc(24), - group-font-size: var(--size-typography-text-sm), - scroll-height: utils.rem-calc(256), + option-height: var(--ui-autocomplete-option-height-small, #{utils.rem-calc(32)}), + option-padding-x: var(--ui-autocomplete-option-padding-x-small, var(--units-md)), + font-size: var(--ui-autocomplete-font-size-small, var(--size-typography-text-md)), + group-height: var(--ui-autocomplete-group-height-small, #{utils.rem-calc(24)}), + group-font-size: var(--ui-autocomplete-group-font-size-small, var(--size-typography-text-sm)), + scroll-height: var(--ui-autocomplete-scroller-max-height-small, #{utils.rem-calc(256)}), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. $size-small: map.get($sizes, 'small'); /// Raccourci vers l'entrée `small` de `$sizes`. -$panel-padding: var(--units-xs); /// Inset du panneau de suggestions. -$panel-gap: var(--units-xs); /// Espacement entre les groupes du panneau. -$option-radius: var(--radius-sm); /// Rayon d'une option. -$option-gap: var(--units-sm); /// Espacement entre l'icône et le libellé d'une option. -$tag-gap: var(--units-xs); /// Espacement entre les valeurs sélectionnées (mode `multiple`). -$input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie restante (mode `multiple`). +$panel-padding: var(--ui-autocomplete-panel-padding, var(--units-xs)); /// Inset du panneau de suggestions. +$panel-gap: var(--ui-autocomplete-panel-gap, var(--units-xs)); /// Espacement entre les groupes du panneau. +$panel-max-width: var(--ui-autocomplete-panel-max-width, calc(100vw - var(--units-lg))); /// Largeur maximale du panneau, gouttière laissée au viewport. +$header-padding-y: var(--ui-autocomplete-header-padding-y, var(--units-xs)); /// Inset vertical des zones d'en-tête et de pied projetées. +$header-padding-x: var(--ui-autocomplete-header-padding-x, var(--units-sm)); /// Inset horizontal des zones d'en-tête et de pied projetées. + +$option-radius: var(--ui-autocomplete-option-radius, var(--radius-sm)); /// Rayon d'une option. +$option-gap: var(--ui-autocomplete-option-gap, var(--units-sm)); /// Espacement entre l'icône et le libellé d'une option. + +$tag-gap: var(--ui-autocomplete-tag-gap, var(--units-xs)); /// Espacement entre les valeurs sélectionnées (mode `multiple`). +$tag-radius: var(--ui-autocomplete-tag-radius, var(--radius-full)); /// Rayon d'une valeur sélectionnée (mode `multiple`). +$input-min-width: var(--ui-autocomplete-input-min-width, #{utils.rem-calc(80)}); /// Largeur minimale de la zone de saisie restante (mode `multiple`). + +$clear-size: var(--ui-autocomplete-clear-size, var(--size-components-2xs)); /// Taille du bouton d'effacement. +$clear-radius: var(--ui-autocomplete-clear-radius, var(--radius-full)); /// Rayon du bouton d'effacement. +$focus-ring-width: var(--ui-autocomplete-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus (valeur sélectionnée, bouton d'effacement). .ui-autocomplete { display: block; @@ -58,12 +68,12 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie display: inline-flex; align-items: center; max-width: 100%; - border-radius: var(--radius-full); + border-radius: $tag-radius; @include utils.control-transition(box-shadow); &:focus-visible { outline: none; - box-shadow: 0 0 0 utils.$form-focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); + box-shadow: 0 0 0 $focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); } } @@ -83,13 +93,13 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie align-items: center; justify-content: center; box-sizing: border-box; - width: var(--size-components-2xs); - height: var(--size-components-2xs); + width: $clear-size; + height: $clear-size; flex-shrink: 0; margin: 0; padding: 0; border: none; - border-radius: var(--radius-full); + border-radius: $clear-radius; background: none; color: var(--form-low-content-default); cursor: pointer; @@ -100,7 +110,7 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie &:focus-visible { outline: none; color: var(--form-high-content-focused); - box-shadow: 0 0 0 utils.$form-focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); + box-shadow: 0 0 0 $focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); } &:disabled { cursor: not-allowed; color: var(--form-high-content-disabled); } } @@ -133,13 +143,13 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie flex-direction: column; gap: $panel-gap; box-sizing: border-box; - max-width: calc(100vw - var(--units-lg)); + max-width: $panel-max-width; padding: $panel-padding; // --- Header / footer (projected templates) -------------------------- .ui-autocomplete-header, .ui-autocomplete-footer { - padding: var(--units-xs) var(--units-sm); + padding: $header-padding-y $header-padding-x; font-family: var(--fontfamily-base); font-size: map.get($size-default, group-font-size); color: var(--form-high-content-default); diff --git a/projects/ui-kit/forms/ui-checkbox/src/lib/ui-checkbox.scss b/projects/ui-kit/forms/ui-checkbox/src/lib/ui-checkbox.scss index 88080e6..35c0af1 100644 --- a/projects/ui-kit/forms/ui-checkbox/src/lib/ui-checkbox.scss +++ b/projects/ui-kit/forms/ui-checkbox/src/lib/ui-checkbox.scss @@ -8,9 +8,16 @@ // ===================================================================== // Shared defaults (ui-config) — replace a value here to detune ONLY this component. -$box-size: utils.$form-control-size; /// Taille de la boîte à cocher -$focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus -$gap: utils.$form-control-gap; /// Espace entre la boîte et le label +$box-size: var(--ui-checkbox-box-size, #{utils.$form-control-size}); /// Taille de la boîte à cocher +$focus-ring-width: var(--ui-checkbox-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus +$gap: var(--ui-checkbox-gap, #{utils.$form-control-gap}); /// Espace entre la boîte et le label + +$box-radius: var(--ui-checkbox-box-radius, var(--radius-xs)); /// Rayon des coins de la boîte. +$stroke-width: var(--ui-checkbox-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de la bordure de la boîte. +$icon-size: var(--ui-checkbox-icon-size, var(--ui-icon-size-md, var(--size-typography-icon-md))); /// Taille du marqueur (coche / indéterminé). + +$font-size: var(--ui-checkbox-font-size, var(--size-typography-text-default)); /// Taille de police du label. +$label-gap: var(--ui-checkbox-label-gap, var(--units-2xs)); /// Espacement libellé ↔ marqueur requis. .ui-checkbox { display: inline-flex; @@ -53,8 +60,8 @@ $gap: utils.$form-control-gap; /// Espace entre la boîte et l width: 100%; height: 100%; background-color: var(--_surface); - border: utils.$control-stroke-width solid var(--_stroke); - border-radius: var(--radius-xs); + border: $stroke-width solid var(--_stroke); + border-radius: $box-radius; @include utils.control-transition(background-color, border-color, box-shadow); } @@ -65,6 +72,7 @@ $gap: utils.$form-control-gap; /// Espace entre la boîte et l // --- Check / indeterminate mark ------------------------------------- &-icon { + --ui-icon-size: #{$icon-size}; color: var(--_content); opacity: 0; transform: scale(0.6); @@ -81,9 +89,9 @@ $gap: utils.$form-control-gap; /// Espace entre la boîte et l &-label { display: inline-flex; align-items: center; - gap: var(--units-2xs); + gap: $label-gap; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $font-size; color: var(--ui-label-color, var(--form-high-content-default)); cursor: inherit; user-select: none; diff --git a/projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss b/projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss index b128e74..4967793 100644 --- a/projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss +++ b/projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss @@ -3,23 +3,63 @@ // ===================================================================== // ui-datepicker : the trigger box + helper live in `ui-input` (shell // `ui-field`). Here we style the calendar panel (overlay or inline). -// 100% design tokens; structural constants are local vars below — retarget -// them (or pass `panelStyleClass`) to customise without forking the SCSS. +// 100% design tokens; structural constants are the local vars below (or +// pass `panelStyleClass` for a per-instance skin). // ===================================================================== // --- Local structural config ------------ -$panel-width: utils.rem-calc(360); /// Largeur du panneau calendrier -$panel-radius: utils.$overlay-panel-radius; /// Rayon de bordure du panneau -$panel-padding: utils.$overlay-panel-padding; /// Padding interne du panneau -$panel-gap: var(--units-sm); /// Espace entre sections (header / grille / heure) -$panel-shadow: utils.$overlay-panel-shadow; /// Ombre du panneau en overlay (retirée en inline) - -$day-size: var(--size-components-default); /// Diamètre d'une cellule jour -$day-radius: var(--radius-full); /// Rayon des cellules jour (cercle) -$nav-size: var(--size-components-default); /// Taille des boutons de navigation mensuelle -$time-field-width: utils.rem-calc(48); /// Largeur des champs heures / minutes / AM-PM -$focus-ring-width: utils.$focus-ring-width; /// Anneau de focus (jours, navigation) -$stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau, cellule « today ») +$panel-width: var(--ui-datepicker-panel-width, #{utils.rem-calc(360)}); /// Largeur du panneau calendrier +$panel-radius: var(--ui-datepicker-panel-radius, #{utils.$overlay-panel-radius}); /// Rayon de bordure du panneau +$panel-padding: var(--ui-datepicker-panel-padding, #{utils.$overlay-panel-padding}); /// Padding interne du panneau +$panel-gap: var(--ui-datepicker-panel-gap, var(--units-sm)); /// Espace entre sections (header / grille / heure) +$panel-shadow: var(--ui-datepicker-panel-shadow, #{utils.$overlay-panel-shadow}); /// Ombre du panneau en overlay (retirée en inline) +$panel-max-width: var(--ui-datepicker-panel-max-width, calc(100vw - var(--units-lg))); /// Largeur maximale du panneau, gouttière laissée au viewport. + +$day-size: var(--ui-datepicker-day-size, var(--size-components-default)); /// Diamètre d'une cellule jour +$day-radius: var(--ui-datepicker-day-radius, var(--radius-full)); /// Rayon des cellules jour (cercle) +$day-font-size: var(--ui-datepicker-day-font-size, var(--size-typography-text-default)); /// Taille du chiffre d'une cellule jour. +$day-opacity-other-month: var(--ui-datepicker-day-opacity-outside, 0.6); /// Opacité d'un jour appartenant au mois adjacent. +$day-opacity-disabled: var(--ui-datepicker-day-opacity-disabled, 0.45); /// Opacité d'un jour désactivé. +$nav-size: var(--ui-datepicker-nav-size, var(--size-components-default)); /// Taille des boutons de navigation mensuelle +$time-field-width: var(--ui-datepicker-time-field-width, #{utils.rem-calc(48)}); /// Largeur des champs heures / minutes / AM-PM +$focus-ring-width: var(--ui-datepicker-focus-ring-width, #{utils.$focus-ring-width}); /// Anneau de focus (jours, navigation) +$stroke-width: var(--ui-datepicker-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur des bordures (panneau, cellule « today ») + +$header-gap: var(--ui-datepicker-header-gap, var(--units-sm)); /// Espacement de l'en-tête de navigation. +$subheader-gap: var(--ui-datepicker-subheader-gap, var(--units-sm)); /// Espacement de l'en-tête propre à un mois (multi-mois). +$title-font-size: var(--ui-datepicker-title-font-size, var(--size-typography-title-md)); /// Taille du titre mois / année. +$title-padding-y: var(--ui-datepicker-title-padding-y, var(--units-2xs)); /// Inset vertical du titre cliquable. +$title-padding-x: var(--ui-datepicker-title-padding-x, var(--units-sm)); /// Inset horizontal du titre cliquable. +$title-radius: var(--ui-datepicker-title-radius, var(--radius-sm)); /// Rayon du titre cliquable. + +$months-gap: var(--ui-datepicker-months-gap, var(--units-lg)); /// Espacement entre les mois affichés côte à côte. +$month-gap: var(--ui-datepicker-month-gap, var(--units-sm)); /// Espacement entre les sections d'une colonne de mois. +$month-divider-padding-left: var(--ui-datepicker-month-divider-padding-left, var(--units-lg)); /// Retrait après le filet séparant deux mois. + +$picker-gap: var(--ui-datepicker-picker-gap, var(--units-xs)); /// Espacement des grilles mois / année. +$cell-min-height: var(--ui-datepicker-cell-min-height, var(--size-components-sm)); /// Hauteur minimale d'une cellule mois / année. +$cell-padding-y: var(--ui-datepicker-cell-padding-y, var(--units-sm)); /// Inset vertical d'une cellule mois / année. +$cell-padding-x: var(--ui-datepicker-cell-padding-x, var(--units-md)); /// Inset horizontal d'une cellule mois / année. +$cell-radius: var(--ui-datepicker-cell-radius, var(--radius-sm)); /// Rayon d'une cellule mois / année. +$cell-font-size: var(--ui-datepicker-cell-font-size, var(--size-typography-text-default)); /// Taille du libellé d'une cellule mois / année. + +$weekday-padding-y: var(--ui-datepicker-weekday-padding-y, var(--units-xs)); /// Inset vertical de l'en-tête des jours de semaine. +$weekday-font-size: var(--ui-datepicker-weekday-font-size, var(--size-typography-text-md)); /// Taille du libellé des jours de semaine. +$grid-gap: var(--ui-datepicker-grid-gap, var(--units-xs)); /// Espacement entre les semaines de la grille. + +$time-gap: var(--ui-datepicker-time-gap, var(--units-sm)); /// Espacement entre les unités de la zone heure. +$time-padding-top: var(--ui-datepicker-time-padding-top, var(--units-sm)); /// Retrait au-dessus de la zone heure, sous son filet. +$time-unit-gap: var(--ui-datepicker-time-unit-gap, var(--units-2xs)); /// Espacement entre les flèches et la valeur d'une unité. +$time-font-size: var(--ui-datepicker-time-font-size, var(--size-typography-title-md)); /// Taille des chiffres et du séparateur de l'heure. +$time-value-padding-y: var(--ui-datepicker-time-value-padding-y, var(--units-2xs)); /// Inset vertical d'un champ heure. +$time-value-padding-x: var(--ui-datepicker-time-value-padding-x, var(--units-sm)); /// Inset horizontal d'un champ heure. +$time-value-radius: var(--ui-datepicker-time-value-radius, var(--radius-sm)); /// Rayon d'un champ heure. +$time-btn-width: var(--ui-datepicker-time-btn-width, var(--size-components-xs)); /// Largeur d'une flèche d'incrément. +$time-btn-height: var(--ui-datepicker-time-btn-height, var(--size-components-2xs)); /// Hauteur d'une flèche d'incrément. +$time-btn-radius: var(--ui-datepicker-time-btn-radius, var(--radius-sm)); /// Rayon d'une flèche d'incrément. + +$buttonbar-gap: var(--ui-datepicker-buttonbar-gap, var(--units-sm)); /// Espacement des actions de la barre de boutons. +$buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-sm)); /// Retrait au-dessus de la barre de boutons, sous son filet. .ui-datepicker { &-trigger { display: block; cursor: pointer; } @@ -31,7 +71,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau gap: $panel-gap; box-sizing: border-box; width: max-content; - max-width: calc(100vw - var(--units-lg)); + max-width: $panel-max-width; padding: $panel-padding; border: $stroke-width solid var(--form-high-stroke-default); border-radius: $panel-radius; @@ -47,7 +87,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau display: flex; align-items: center; justify-content: space-between; - gap: var(--units-sm); + gap: $header-gap; } &-nav { @@ -78,15 +118,15 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau &-title { flex: 1 1 auto; text-align: center; - font-size: var(--size-typography-title-md); + font-size: $title-font-size; font-weight: var(--weight-bold); color: var(--form-high-content-default); // Clickable variant (drill-up: date → month → year). &._button { - padding: var(--units-2xs) var(--units-sm); + padding: $title-padding-y $title-padding-x; border: none; - border-radius: var(--radius-sm); + border-radius: $title-radius; background: none; font-family: var(--fontfamily-base); cursor: pointer; @@ -102,17 +142,17 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau } // --- Multi-month layout -------------------------------------------- - &-months { display: flex; gap: var(--units-lg); } + &-months { display: flex; gap: $months-gap; } &-month { display: flex; flex-direction: column; - gap: var(--units-sm); + gap: $month-gap; flex: 0 0 auto; width: $panel-width; &:not(:first-child) { border-left: $stroke-width solid var(--form-high-stroke-default); - padding-left: var(--units-lg); + padding-left: $month-divider-padding-left; } } @@ -121,14 +161,14 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau &-subheader { display: flex; align-items: center; - gap: var(--units-sm); + gap: $subheader-gap; } &-nav-spacer { flex-shrink: 0; width: $nav-size; height: $nav-size; } // --- Month / Year picker grids ------------------------------------- &-picker { display: grid; - gap: var(--units-xs); + gap: $picker-gap; width: $panel-width; &._month { grid-template-columns: repeat(3, 1fr); } @@ -140,13 +180,13 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau align-items: center; justify-content: center; box-sizing: border-box; - min-height: var(--size-components-sm); - padding: var(--units-sm) var(--units-md); + min-height: $cell-min-height; + padding: $cell-padding-y $cell-padding-x; border: $stroke-width solid transparent; - border-radius: var(--radius-sm); + border-radius: $cell-radius; background-color: var(--actions-low-surface-default); font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $cell-font-size; color: var(--form-high-content-default); cursor: pointer; @include utils.control-transition(background-color, border-color, color); @@ -176,14 +216,14 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau } &-weekday { - padding: var(--units-xs) 0; - font-size: var(--size-typography-text-md); + padding: $weekday-padding-y 0; + font-size: $weekday-font-size; font-weight: var(--weight-bold); color: var(--form-low-content-default); user-select: none; } - &-grid { display: flex; flex-direction: column; gap: var(--units-xs); } + &-grid { display: flex; flex-direction: column; gap: $grid-gap; } // --- Day cell ------------------------------------------------------ $range-bg: var(--informative-highlightlow-surface-hover); /// Fond continu de la plage sélectionnée (`range`). @@ -201,7 +241,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau border: none; background: transparent; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $day-font-size; color: var(--form-high-content-default); cursor: pointer; @include utils.control-transition(color); @@ -229,7 +269,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau } // Previous/next month + disabled - &._other-month { color: var(--form-low-content-default); opacity: 0.6; } + &._other-month { color: var(--form-low-content-default); opacity: $day-opacity-other-month; } // Today (outlined chip) — only when not selected. &._today::before { border-color: var(--actions-high-stroke-default); } @@ -265,7 +305,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau &:disabled { color: var(--form-high-content-disabled); - opacity: 0.45; + opacity: $day-opacity-disabled; cursor: not-allowed; } } @@ -275,8 +315,8 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau display: flex; align-items: center; justify-content: center; - gap: var(--units-sm); - padding-top: var(--units-sm); + gap: $time-gap; + padding-top: $time-padding-top; border-top: $stroke-width solid var(--form-high-stroke-default); &:first-child { padding-top: 0; border-top: none; } @@ -286,19 +326,19 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau display: flex; flex-direction: column; align-items: center; - gap: var(--units-2xs); + gap: $time-unit-gap; } &-time-value { box-sizing: border-box; display: inline-block; width: $time-field-width; - padding: var(--units-2xs) var(--units-sm); + padding: $time-value-padding-y $time-value-padding-x; border: none; - border-radius: var(--radius-sm); + border-radius: $time-value-radius; background: none; font-family: var(--fontfamily-base); - font-size: var(--size-typography-title-md); + font-size: $time-font-size; font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; color: var(--form-high-content-default); @@ -323,11 +363,11 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau display: flex; align-items: center; justify-content: center; - width: var(--size-components-xs); - height: var(--size-components-2xs); + width: $time-btn-width; + height: $time-btn-height; padding: 0; border: none; - border-radius: var(--radius-sm); + border-radius: $time-btn-radius; background: none; color: var(--form-low-content-default); cursor: pointer; @@ -341,7 +381,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau } &-time-colon { - font-size: var(--size-typography-title-md); + font-size: $time-font-size; font-weight: var(--weight-bold); color: var(--form-high-content-default); } @@ -351,8 +391,8 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur des bordures (panneau display: flex; align-items: center; justify-content: space-between; - gap: var(--units-sm); - padding-top: var(--units-sm); + gap: $buttonbar-gap; + padding-top: $buttonbar-padding-top; border-top: $stroke-width solid var(--form-high-stroke-default); } } diff --git a/projects/ui-kit/forms/ui-field/src/lib/ui-field.scss b/projects/ui-kit/forms/ui-field/src/lib/ui-field.scss index a13832f..f818d1c 100644 --- a/projects/ui-kit/forms/ui-field/src/lib/ui-field.scss +++ b/projects/ui-kit/forms/ui-field/src/lib/ui-field.scss @@ -14,22 +14,29 @@ // Do not re-add `padding` here: it would shrink every control back inside. // ===================================================================== -$height: utils.$form-field-height; /// Hauteur de la boîte de champ. -$height-small: utils.$form-field-height-small; /// Hauteur de la boîte de champ, variante `small`. -$radius: utils.$form-field-radius; /// Rayon de bordure de la boîte. -$padding-x: utils.$form-field-padding-x; /// Inset horizontal du champ, porté par le contrôle projeté. -$gap: utils.$form-field-gap; /// Espace entre le contrôle et ses affixes. -$stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure de la boîte. -$focus-ring-width: utils.$form-field-focus-ring-width; /// Épaisseur de l'anneau de focus. -$textarea-min-height: utils.$form-textarea-min-height; /// Hauteur minimale de la boîte multiligne (`ui-textarea`). -$textarea-min-height-small: utils.$form-textarea-min-height-small; /// Hauteur minimale de la boîte multiligne, variante `small`. +$gap: var(--ui-field-gap, var(--units-xs)); /// Gouttière verticale entre le libellé, la boîte et le pied. +$gap-small: var(--ui-field-gap-small, var(--units-xs)); /// Gouttière verticale, variante `small`. +$footer-gap: var(--ui-field-footer-gap, var(--units-sm)); /// Espace entre le message et le compteur, dans le pied. + +$height: var(--ui-field-height, #{utils.$form-field-height}); /// Hauteur de la boîte de champ. +$height-small: var(--ui-field-height-small, #{utils.$form-field-height-small}); /// Hauteur de la boîte de champ, variante `small`. +$radius: utils.$form-field-radius; /// Rayon de bordure de la boîte. +$box-gap: var(--ui-field-box-gap, #{utils.$form-field-gap}); /// Espace entre le contrôle et ses affixes. +$stroke-width: var(--ui-field-stroke-width, #{utils.$form-field-stroke-width}); /// Épaisseur de bordure de la boîte. + +$focus-ring-width: var(--ui-field-focus-ring-width, #{utils.$form-field-focus-ring-width}); /// Épaisseur de l'anneau de focus. +$focus-ring-opacity: var(--ui-field-focus-ring-opacity, 50%); /// Opacité de l'anneau de focus. + +$padding-y-auto-height: var(--ui-field-padding-y-auto-height, var(--units-xs)); /// Inset vertical de la boîte à hauteur automatique (valeurs enveloppées de `ui-select`). +$min-height-multiline: var(--ui-field-min-height-multiline, #{utils.$form-textarea-min-height}); /// Hauteur minimale de la boîte multiligne (`ui-textarea`). +$min-height-multiline-small: var(--ui-field-min-height-multiline-small, #{utils.$form-textarea-min-height-small}); /// Hauteur minimale de la boîte multiligne, variante `small`. .ui-field { display: flex; flex-direction: column; - gap: var(--units-xs); + gap: $gap; - &._small { gap: var(--units-xs); } + &._small { gap: $gap-small; } // --- Box ----------------------------------------------------------- // Border colour routed through local vars, overridden per level below. @@ -40,7 +47,7 @@ $textarea-min-height-small: utils.$form-textarea-min-height-small; /// Hauteur m display: flex; align-items: center; - gap: $gap; + gap: $box-gap; box-sizing: border-box; height: $height; padding: 0; @@ -57,7 +64,7 @@ $textarea-min-height-small: utils.$form-textarea-min-height-small; /// Hauteur m &-box:hover:not(:focus-within) { border-color: var(--_stroke-hover); } &-box:focus-within { border-color: var(--_stroke-focus); - box-shadow: 0 0 0 $focus-ring-width rgb(from var(--_stroke-focus) r g b / 50%); + box-shadow: 0 0 0 $focus-ring-width rgb(from var(--_stroke-focus) r g b / #{$focus-ring-opacity}); } // --- Levels (border palette) --------------------------------------- @@ -84,7 +91,7 @@ $textarea-min-height-small: utils.$form-textarea-min-height-small; /// Hauteur m &._auto-height &-box { height: auto; min-height: $height; - padding-block: var(--units-xs); + padding-block: $padding-y-auto-height; } &._auto-height._small &-box { min-height: $height-small; } @@ -92,15 +99,15 @@ $textarea-min-height-small: utils.$form-textarea-min-height-small; /// Hauteur m &._multiline &-box { align-items: stretch; height: auto; - min-height: $textarea-min-height; + min-height: $min-height-multiline; } - &._multiline._small &-box { min-height: $textarea-min-height-small; } + &._multiline._small &-box { min-height: $min-height-multiline-small; } // --- Footer (message + optional counter) --------------------------- &-footer { display: flex; align-items: baseline; justify-content: space-between; - gap: var(--units-sm); + gap: $footer-gap; } } diff --git a/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload-list.scss b/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload-list.scss index 6422c81..8466d1d 100644 --- a/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload-list.scss +++ b/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload-list.scss @@ -12,26 +12,37 @@ /// Géométrie, typo et vignette d'une ligne de fichier, par taille. $sizes: ( 'default': ( - radius: var(--radius-lg), - padding-y: var(--units-md), - padding-x: var(--units-default), - gap: var(--units-md), - media: 40px, - name-size: var(--size-typography-title-sm), - meta-size: var(--size-typography-text-sm), + radius: var(--ui-file-upload-item-radius, var(--radius-lg)), + padding-y: var(--ui-file-upload-item-padding-y, var(--units-md)), + padding-x: var(--ui-file-upload-item-padding-x, var(--units-default)), + gap: var(--ui-file-upload-item-gap, var(--units-md)), + media: var(--ui-file-upload-item-media-size, #{utils.rem-calc(40px)}), + name-size: var(--ui-file-upload-item-name-font-size, var(--size-typography-title-sm)), + meta-size: var(--ui-file-upload-item-meta-font-size, var(--size-typography-text-sm)), ), 'small': ( - radius: var(--radius-md), - padding-y: var(--units-sm), - padding-x: var(--units-md), - gap: var(--units-sm), - media: 32px, - name-size: var(--size-typography-text-md), - meta-size: var(--size-typography-text-sm), + radius: var(--ui-file-upload-item-radius-small, var(--radius-md)), + padding-y: var(--ui-file-upload-item-padding-y-small, var(--units-sm)), + padding-x: var(--ui-file-upload-item-padding-x-small, var(--units-md)), + gap: var(--ui-file-upload-item-gap-small, var(--units-sm)), + media: var(--ui-file-upload-item-media-size-small, #{utils.rem-calc(32px)}), + name-size: var(--ui-file-upload-item-name-font-size-small, var(--size-typography-text-md)), + meta-size: var(--ui-file-upload-item-meta-font-size-small, var(--size-typography-text-sm)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une ligne. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. + +$stroke-width: var(--ui-file-upload-item-stroke-width, #{utils.$form-field-stroke-width}); /// Épaisseur de bordure d'une ligne. +$media-gap: var(--ui-file-upload-item-media-gap, var(--units-sm)); /// Espacement vignette ↔ informations. +$infos-gap: var(--ui-file-upload-item-infos-gap, var(--units-2xs)); /// Espacement nom ↔ métadonnées. +$thumb-radius: var(--ui-file-upload-item-thumb-radius, var(--radius-sm)); /// Rayon de la vignette d'aperçu. + +$remove-padding: var(--ui-file-upload-item-remove-padding, var(--units-2xs)); /// Inset du bouton de retrait. +$remove-radius: var(--ui-file-upload-item-remove-radius, var(--radius-sm)); /// Rayon du bouton de retrait. +$focus-ring-width: var(--ui-file-upload-item-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus du bouton de retrait. + +$progress-height: var(--ui-file-upload-item-progress-height, var(--stroke-default)); /// Épaisseur de la barre de progression. +$progress-color: var(--ui-file-upload-item-progress-color, var(--global-text-default)); /// Couleur de la portion remplie de la barre de progression. @mixin row-size($s) { border-radius: map.get($s, radius); @@ -39,8 +50,8 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l gap: map.get($s, gap); .ui-file-upload-list-media { - width: utils.rem-calc(map.get($s, media)); - height: utils.rem-calc(map.get($s, media)); + width: map.get($s, media); + height: map.get($s, media); } .ui-file-upload-list-name { font-size: map.get($s, name-size); } .ui-file-upload-list-meta { font-size: map.get($s, meta-size); } @@ -50,7 +61,7 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l position: relative; display: flex; align-items: center; - gap: var(--units-md); + gap: map.get($size-default, gap); box-sizing: border-box; width: 100%; overflow: hidden; @@ -70,7 +81,7 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l &-file { display: flex; align-items: center; - gap: var(--units-sm); + gap: $media-gap; min-width: 0; // allow the name to truncate flex: 1 1 auto; } @@ -88,13 +99,13 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l width: 100%; height: 100%; object-fit: cover; - border-radius: var(--radius-sm); + border-radius: $thumb-radius; } &-infos { display: flex; flex-direction: column; - gap: var(--units-2xs); + gap: $infos-gap; min-width: 0; } @@ -121,10 +132,10 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l align-items: center; justify-content: center; flex: none; - padding: var(--units-2xs); + padding: $remove-padding; background: transparent; border: none; - border-radius: var(--radius-sm); + border-radius: $remove-radius; color: var(--form-high-content-default); cursor: pointer; line-height: 0; @@ -133,7 +144,7 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l &:hover { color: var(--global-text-default); } &:focus-visible { outline: none; - box-shadow: 0 0 0 utils.$form-focus-ring-width var(--form-high-stroke-focused); + box-shadow: 0 0 0 $focus-ring-width var(--form-high-stroke-focused); } } @@ -143,13 +154,13 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure d'une l left: 0; right: 0; bottom: 0; - height: var(--stroke-default); + height: $progress-height; background-color: var(--form-high-surface-hover); } &-progress-bar { display: block; height: 100%; - background-color: var(--global-text-default); + background-color: $progress-color; @include utils.control-transition(width); } diff --git a/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload.scss b/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload.scss index eee3305..753b5bb 100644 --- a/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload.scss +++ b/projects/ui-kit/forms/ui-file-upload/src/lib/ui-file-upload.scss @@ -5,29 +5,60 @@ // ui-file-upload : co-located styles. All values come from design tokens. // // Extend: -// • Size → add entries to $field-height / $zone-min (+ the size type). +// • Size → add entries to $field-height / $zone-min and the matching +// `-small` config values (+ the size type). // ===================================================================== // --- Config ---------------------------------------------------------- -$stroke: utils.$form-field-stroke-width; /// Épaisseur de bordure de la zone de dépôt. -$radius: utils.$form-field-radius; /// Rayon de la zone de dépôt. -$focus-ring: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus. +$stroke-width: var(--ui-file-upload-stroke-width, #{utils.$form-field-stroke-width}); /// Épaisseur de bordure de la zone de dépôt. +$radius: var(--ui-file-upload-radius, #{utils.$form-field-radius}); /// Rayon de la zone de dépôt. +$focus-ring-width: var(--ui-file-upload-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus. +$gap: var(--ui-file-upload-gap, var(--units-default)); /// Espacement entre la zone, la barre d'outils et la liste. /// Hauteur de la zone en présentation compacte, par taille. $field-height: ( - 'default': utils.$form-field-height, - 'small': utils.$form-field-height-small, + 'default': var(--ui-file-upload-field-height, #{utils.$form-field-height}), + 'small': var(--ui-file-upload-field-height-small, #{utils.$form-field-height-small}), ); + +$field-gap: var(--ui-file-upload-field-gap, var(--units-sm)); /// Espacement libellé ↔ bouton de sélection. +$field-padding-x: var(--ui-file-upload-field-padding-x, var(--units-md)); /// Inset horizontal du libellé en présentation compacte. +$placeholder-opacity: var(--ui-file-upload-placeholder-opacity, 0.75); /// Opacité du texte indicatif tant qu'aucun fichier n'est choisi. + +$field-font-size: var(--ui-file-upload-field-font-size, var(--size-typography-text-default)); /// Taille du libellé en présentation compacte. +$field-font-size-small: var(--ui-file-upload-field-font-size-small, var(--size-typography-text-md)); /// Taille du libellé en présentation compacte, taille `small`. + +$browse-padding-x: var(--ui-file-upload-browse-padding-x, var(--units-default)); /// Inset horizontal du bouton de sélection. +$browse-padding-x-small: var(--ui-file-upload-browse-padding-x-small, var(--units-md)); /// Inset horizontal du bouton de sélection, taille `small`. +$browse-font-size: var(--ui-file-upload-browse-font-size, var(--size-typography-text-default)); /// Taille du libellé du bouton de sélection. +$browse-font-size-small: var(--ui-file-upload-browse-font-size-small, var(--size-typography-text-md)); /// Taille du libellé du bouton de sélection, taille `small`. + /// Hauteur minimale de la zone de dépôt, par taille. $zone-min: ( - 'default': 112px, - 'small': 94px, + 'default': var(--ui-file-upload-zone-min-height, 112px), + 'small': var(--ui-file-upload-zone-min-height-small, 94px), ); +$zone-padding: var(--ui-file-upload-zone-padding, var(--units-default)); /// Inset de la zone de dépôt. +$zone-gap: var(--ui-file-upload-zone-gap, var(--units-lg)); /// Espacement icône ↔ contenu de la zone de dépôt. +$zone-gap-small: var(--ui-file-upload-zone-gap-small, var(--units-sm)); /// Espacement icône ↔ contenu de la zone de dépôt, taille `small`. +$zone-content-gap: var(--ui-file-upload-zone-content-gap, var(--units-xs)); /// Espacement titre ↔ indication. +$zone-title-gap: var(--ui-file-upload-zone-title-gap, var(--units-2xs)); /// Espacement entre les fragments du titre de la zone. + +$zone-title-font-size: var(--ui-file-upload-zone-title-font-size, var(--size-typography-text-default)); /// Taille du titre de la zone de dépôt. +$zone-title-font-size-small: var(--ui-file-upload-zone-title-font-size-small, var(--size-typography-text-md)); /// Taille du titre de la zone de dépôt, taille `small`. +$zone-hint-font-size: var(--ui-file-upload-zone-hint-font-size, var(--size-typography-text-md)); /// Taille de l'indication sous le titre. +$zone-hint-font-size-small: var(--ui-file-upload-zone-hint-font-size-small, var(--size-typography-text-sm)); /// Taille de l'indication sous le titre, taille `small`. + +$toolbar-gap: var(--ui-file-upload-toolbar-gap, var(--units-sm)); /// Espacement entre les actions de la barre d'outils. +$content-gap: var(--ui-file-upload-content-gap, var(--units-sm)); /// Espacement entre les lignes de fichier. +$messages-gap: var(--ui-file-upload-messages-gap, var(--units-2xs)); /// Espacement entre les messages. +$message-font-size: var(--ui-file-upload-message-font-size, var(--size-typography-text-md)); /// Taille d'un message. + .ui-file-upload { display: flex; flex-direction: column; - gap: var(--units-default); + gap: $gap; width: 100%; font-family: var(--fontfamily-base); @@ -51,12 +82,12 @@ $zone-min: ( display: flex; align-items: center; justify-content: space-between; - gap: var(--units-sm); + gap: $field-gap; height: map.get($field-height, 'default'); - padding-left: var(--units-md); + padding-left: $field-padding-x; overflow: hidden; background-color: var(--form-high-surface-default); - border: $stroke solid var(--form-high-stroke-default); + border: $stroke-width solid var(--form-high-stroke-default); border-radius: $radius; color: var(--form-high-content-default); cursor: pointer; @@ -76,12 +107,12 @@ $zone-min: ( overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-size: var(--size-typography-text-default); + font-size: $field-font-size; font-weight: var(--weight-regular); - &._placeholder { color: var(--form-high-content-default); opacity: 0.75; } + &._placeholder { color: var(--form-high-content-default); opacity: $placeholder-opacity; } } - &._small &-field-text { font-size: var(--size-typography-text-md); } + &._small &-field-text { font-size: $field-font-size-small; } // Browse "button" (styled span — the label owns the click) ---------- &-browse { @@ -89,13 +120,13 @@ $zone-min: ( align-items: center; align-self: stretch; flex: none; - padding: 0 var(--units-default); + padding: 0 $browse-padding-x; background-color: var(--actions-high-surface-default); color: var(--actions-high-content-default); - font-size: var(--size-typography-text-default); + font-size: $browse-font-size; font-weight: var(--weight-bold); } - &._small &-browse { padding: 0 var(--units-md); font-size: var(--size-typography-text-md); } + &._small &-browse { padding: 0 $browse-padding-x-small; font-size: $browse-font-size-small; } &-field:hover &-browse { background-color: var(--actions-high-surface-hover); } // ================================================================= @@ -106,11 +137,11 @@ $zone-min: ( flex-direction: column; align-items: center; justify-content: center; - gap: var(--units-lg); + gap: $zone-gap; min-height: map.get($zone-min, 'default'); - padding: var(--units-default); + padding: $zone-padding; background-color: var(--form-high-surface-default); - border: $stroke dashed var(--form-high-stroke-default); + border: $stroke-width dashed var(--form-high-stroke-default); border-radius: $radius; color: var(--form-high-content-default); text-align: center; @@ -122,7 +153,7 @@ $zone-min: ( border-color: var(--form-high-stroke-hover); } } - &._small &-zone { gap: var(--units-sm); min-height: map.get($zone-min, 'small'); } + &._small &-zone { gap: $zone-gap-small; min-height: map.get($zone-min, 'small'); } // Active drag-over highlight (solid border + focused surface). &._dragging &-zone { @@ -137,7 +168,7 @@ $zone-min: ( display: flex; flex-direction: column; align-items: center; - gap: var(--units-xs); + gap: $zone-content-gap; } &-zone-title { @@ -145,11 +176,11 @@ $zone-min: ( flex-wrap: wrap; align-items: center; justify-content: center; - gap: var(--units-2xs); - font-size: var(--size-typography-text-default); + gap: $zone-title-gap; + font-size: $zone-title-font-size; font-weight: var(--weight-regular); } - &._small &-zone-title { font-size: var(--size-typography-text-md); } + &._small &-zone-title { font-size: $zone-title-font-size-small; } // "Click to upload" styled as a link (the whole zone is the trigger). &-zone-link { @@ -161,9 +192,9 @@ $zone-min: ( &-zone-hint { color: var(--global-text-muted); - font-size: var(--size-typography-text-md); + font-size: $zone-hint-font-size; } - &._small &-zone-hint { font-size: var(--size-typography-text-sm); } + &._small &-zone-hint { font-size: $zone-hint-font-size-small; } // ================================================================= // Focus ring (native input drives it) — sibling of the label @@ -172,7 +203,7 @@ $zone-min: ( &-input:focus-visible ~ &-zone { outline: none; border-color: var(--form-high-stroke-focused); - box-shadow: 0 0 0 $focus-ring var(--form-high-stroke-focused); + box-shadow: 0 0 0 $focus-ring-width var(--form-high-stroke-focused); } // ================================================================= @@ -181,13 +212,13 @@ $zone-min: ( &-toolbar { display: flex; flex-wrap: wrap; - gap: var(--units-sm); + gap: $toolbar-gap; } &-content { display: flex; flex-direction: column; - gap: var(--units-sm); + gap: $content-gap; width: 100%; margin: 0; padding: 0; @@ -196,11 +227,11 @@ $zone-min: ( li { display: block; } } - &-messages { display: flex; flex-direction: column; gap: var(--units-2xs); } + &-messages { display: flex; flex-direction: column; gap: $messages-gap; } &-message { margin: 0; color: var(--informative-errorhigh-content-default); - font-size: var(--size-typography-text-md); + font-size: $message-font-size; } // ================================================================= diff --git a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group-addon.scss b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group-addon.scss index 51d649a..0e2f841 100644 --- a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group-addon.scss +++ b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group-addon.scss @@ -7,12 +7,14 @@ // `--ui-input-group-item-radius` (inherits across the component boundary). // ===================================================================== -$height: utils.$form-field-height; /// Hauteur de la cellule, alignée sur la boîte de champ. -$height-small: utils.$form-field-height-small; /// Hauteur de la cellule, variante `small`. -$radius: utils.$form-field-radius; /// Rayon des coins quand la cellule est utilisée hors d'un groupe. -$padding-x: utils.$form-field-padding-x; /// Inset horizontal du contenu. -$gap: utils.$form-field-gap; /// Espace entre deux contenus de la cellule. -$stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure. +$height: var(--ui-input-group-addon-height, #{utils.$form-field-height}); /// Hauteur de la cellule, alignée sur la boîte de champ. +$height-small: var(--ui-input-group-addon-height-small, #{utils.$form-field-height-small}); /// Hauteur de la cellule, variante `small`. +$radius: utils.$form-field-radius; /// Rayon des coins quand la cellule est utilisée hors d'un groupe. +$padding-x: var(--ui-input-group-addon-padding-x, #{utils.$form-field-padding-x}); /// Inset horizontal du contenu. +$gap: var(--ui-input-group-addon-gap, #{utils.$form-field-gap}); /// Espace entre deux contenus de la cellule. +$stroke-width: var(--ui-input-group-addon-stroke-width, #{utils.$form-field-stroke-width}); /// Épaisseur de bordure. +$font-size: var(--ui-input-group-addon-font-size, var(--size-typography-text-default)); /// Taille du texte de la cellule. +$font-size-small: var(--ui-input-group-addon-font-size-small, var(--size-typography-text-md)); /// Taille du texte, variante `small`. :host { display: flex; } @@ -30,7 +32,7 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure. background-color: var(--global-background-muted); color: var(--global-text-muted); font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $font-size; line-height: 1.2; white-space: nowrap; user-select: none; @@ -38,6 +40,6 @@ $stroke-width: utils.$form-field-stroke-width; /// Épaisseur de bordure. &._small { min-width: $height-small; min-height: $height-small; - font-size: var(--size-typography-text-md); + font-size: $font-size-small; } } diff --git a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.scss b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.scss index e41846c..f9443d5 100644 --- a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.scss +++ b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.scss @@ -8,14 +8,14 @@ // them. // ===================================================================== -$radius: utils.$form-field-radius; /// Rayon des coins extérieurs du groupe. -$overlap: utils.$form-field-stroke-width; /// Recouvrement de deux items voisins (= largeur de bordure, évite le doublement du trait partagé). +$radius: var(--ui-input-group-radius, #{utils.$form-field-radius}); /// Rayon des coins extérieurs du groupe. +$overlap: var(--ui-input-group-overlap, #{utils.$form-field-stroke-width}); /// Recouvrement de deux items voisins (= largeur de bordure, évite le doublement du trait partagé). :host { display: block; } .ui-input-group { - --ui-input-group-radius: #{$radius}; /// Rayon exposé pour reformer les coins extérieurs. - --ui-input-group-overlap: calc(-1 * #{$overlap}); + --_radius: #{$radius}; // miroir privé : le nom public reste surchargeable depuis l’extérieur + --_overlap: calc(-1 * #{$overlap}); display: flex; align-items: stretch; diff --git a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.ts b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.ts index bfac62c..b83ebf0 100644 --- a/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.ts +++ b/projects/ui-kit/forms/ui-input-group/src/lib/ui-input-group.ts @@ -59,8 +59,8 @@ export class UiInputGroup { const items = this.items(); const last = items.length - 1; items.forEach((el, index) => { - const start = index === 0 ? 'var(--ui-input-group-radius)' : '0'; - const end = index === last ? 'var(--ui-input-group-radius)' : '0'; + const start = index === 0 ? 'var(--_radius)' : '0'; + const end = index === last ? 'var(--_radius)' : '0'; const radius = `${start} ${end} ${end} ${start}`; // Radius hooks exposed by the children (custom properties inherit // across the component boundary — no ::ng-deep). @@ -68,7 +68,7 @@ export class UiInputGroup { el.style.setProperty('--ui-button-radius', radius); el.style.setProperty('--ui-input-group-item-radius', radius); el.style.position = 'relative'; // lets the focused item paint above its neighbours - el.style.marginInlineStart = index === 0 ? '' : 'var(--ui-input-group-overlap)'; + el.style.marginInlineStart = index === 0 ? '' : 'var(--_overlap)'; // Add-ons and buttons keep their natural width; the controls take the rest. const fixed = el.matches('ui-input-group-addon, ui-button'); el.style.flex = fixed ? '0 0 auto' : '1 1 0'; diff --git a/projects/ui-kit/forms/ui-input-mask/src/lib/ui-input-mask.scss b/projects/ui-kit/forms/ui-input-mask/src/lib/ui-input-mask.scss index 5c9eb26..b7f593b 100644 --- a/projects/ui-kit/forms/ui-input-mask/src/lib/ui-input-mask.scss +++ b/projects/ui-kit/forms/ui-input-mask/src/lib/ui-input-mask.scss @@ -5,10 +5,12 @@ // Box/label/helper/states come from `ui-field`. // ===================================================================== +$font-size-small: var(--ui-input-mask-font-size-small, var(--size-typography-text-md)); /// Taille de police du contrôle, variante `small`. + .ui-input-mask { &-native { @include utils.field-native-input; - &._small { font-size: var(--size-typography-text-md); } + &._small { font-size: $font-size-small; } } &-icon { @include utils.field-affix; } diff --git a/projects/ui-kit/forms/ui-input-number/src/lib/ui-input-number.scss b/projects/ui-kit/forms/ui-input-number/src/lib/ui-input-number.scss index 00a26d8..1c45f7e 100644 --- a/projects/ui-kit/forms/ui-input-number/src/lib/ui-input-number.scss +++ b/projects/ui-kit/forms/ui-input-number/src/lib/ui-input-number.scss @@ -5,22 +5,24 @@ // Box/label/helper/states come from `ui-field`. // ===================================================================== -$spinner-width: utils.rem-calc(30); /// Largeur de la colonne du spinner (mixin partagé `utils.field-spinner($btn, $width)`). +$spinner-width: var(--ui-input-number-spinner-width, #{utils.rem-calc(30)}); /// Largeur de la colonne du spinner (mixin partagé `utils.field-spinner($btn, $width)`). +$unit-font-size: var(--ui-input-number-unit-font-size, var(--size-typography-text-default)); /// Taille du texte de l'unité. +$font-size-small: var(--ui-input-number-font-size-small, var(--size-typography-text-md)); /// Taille du texte (saisie et unité) en variante `small`. .ui-input-number { &-native { @include utils.field-native-input; - &._small { font-size: var(--size-typography-text-md); } + &._small { font-size: $font-size-small; } } &-unit { @include utils.field-affix; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $unit-font-size; white-space: nowrap; user-select: none; - &._small { font-size: var(--size-typography-text-md); } + &._small { font-size: $font-size-small; } } &-spinner { @include utils.field-spinner('button', $spinner-width); } diff --git a/projects/ui-kit/forms/ui-input-otp/src/lib/ui-input-otp.scss b/projects/ui-kit/forms/ui-input-otp/src/lib/ui-input-otp.scss index c893904..f9ce6f3 100644 --- a/projects/ui-kit/forms/ui-input-otp/src/lib/ui-input-otp.scss +++ b/projects/ui-kit/forms/ui-input-otp/src/lib/ui-input-otp.scss @@ -13,23 +13,23 @@ /// Map avec clés `size` (dimension) et `font-size` (typographie) pour chaque variante (`small`/`default`/`large`). $sizes: ( 'default': ( - size: var(--size-components-default), - font-size: var(--size-typography-text-default), + size: var(--ui-input-otp-cell-size, var(--size-components-default)), + font-size: var(--ui-input-otp-font-size, var(--size-typography-text-default)), ), 'small': ( - size: var(--size-components-sm), - font-size: var(--size-typography-text-md), + size: var(--ui-input-otp-cell-size-small, var(--size-components-sm)), + font-size: var(--ui-input-otp-font-size-small, var(--size-typography-text-md)), ), 'large': ( - size: var(--size-components-xl), - font-size: var(--size-typography-text-lg), + size: var(--ui-input-otp-cell-size-large, var(--size-components-xl)), + font-size: var(--ui-input-otp-font-size-large, var(--size-typography-text-lg)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$radius: utils.$form-field-radius; /// Rayon des cellules (aligné sur `ui-field`). -$stroke-width: utils.$form-field-stroke-width; /// Épaisseur de la bordure. -$focus-ring-width: utils.$form-field-focus-ring-width; /// Largeur de l'anneau de focus. -$gap: var(--units-sm); /// Espace entre les cellules (`--units-sm`). +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$radius: var(--ui-input-otp-radius, #{utils.$form-field-radius}); /// Rayon des cellules (aligné sur `ui-field`). +$stroke-width: var(--ui-input-otp-stroke-width, #{utils.$form-field-stroke-width}); /// Épaisseur de la bordure. +$focus-ring-width: var(--ui-input-otp-focus-ring-width, #{utils.$form-field-focus-ring-width}); /// Largeur de l'anneau de focus. +$gap: var(--ui-input-otp-gap, var(--units-sm)); /// Espace entre les cellules (`--units-sm`). @mixin otp-size($size) { .ui-input-otp-cell { diff --git a/projects/ui-kit/forms/ui-input-tags/src/lib/ui-input-tags.scss b/projects/ui-kit/forms/ui-input-tags/src/lib/ui-input-tags.scss index 1e51be4..b6c2a04 100644 --- a/projects/ui-kit/forms/ui-input-tags/src/lib/ui-input-tags.scss +++ b/projects/ui-kit/forms/ui-input-tags/src/lib/ui-input-tags.scss @@ -9,30 +9,41 @@ /// Géométrie et typo des tags, des options et du panneau, par taille. $sizes: ( 'default': ( - tag-height: utils.rem-calc(24), - option-height: utils.rem-calc(40), - option-padding-x: var(--units-default), - font-size: var(--size-typography-text-default), - tag-font-size: var(--size-typography-text-md), - scroll-height: utils.rem-calc(200), + tag-height: var(--ui-input-tags-tag-height, #{utils.rem-calc(24)}), + option-height: var(--ui-input-tags-option-height, #{utils.rem-calc(40)}), + option-padding-x: var(--ui-input-tags-option-padding-x, var(--units-default)), + font-size: var(--ui-input-tags-font-size, var(--size-typography-text-default)), + tag-font-size: var(--ui-input-tags-tag-font-size, var(--size-typography-text-md)), + scroll-height: var(--ui-input-tags-scroller-max-height, #{utils.rem-calc(200)}), ), 'small': ( - tag-height: utils.rem-calc(20), - option-height: utils.rem-calc(32), - option-padding-x: var(--units-md), - font-size: var(--size-typography-text-md), - tag-font-size: var(--size-typography-text-sm), - scroll-height: utils.rem-calc(180), + tag-height: var(--ui-input-tags-tag-height-small, #{utils.rem-calc(20)}), + option-height: var(--ui-input-tags-option-height-small, #{utils.rem-calc(32)}), + option-padding-x: var(--ui-input-tags-option-padding-x-small, var(--units-md)), + font-size: var(--ui-input-tags-font-size-small, var(--size-typography-text-md)), + tag-font-size: var(--ui-input-tags-tag-font-size-small, var(--size-typography-text-sm)), + scroll-height: var(--ui-input-tags-scroller-max-height-small, #{utils.rem-calc(180)}), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. $size-small: map.get($sizes, 'small'); /// Raccourci vers l'entrée `small` de `$sizes`. -$panel-padding: var(--units-xs); /// Inset du panneau de suggestions. -$panel-gap: var(--units-xs); /// Espacement entre les groupes du panneau. -$option-radius: var(--radius-sm); /// Rayon d'une option. -$tag-gap: var(--units-xs); /// Espacement entre les tags saisis. -$input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie restante. +$panel-padding: var(--ui-input-tags-panel-padding, var(--units-xs)); /// Inset du panneau de suggestions. +$panel-gap: var(--ui-input-tags-panel-gap, var(--units-xs)); /// Espacement entre les groupes du panneau. +$panel-max-width: var(--ui-input-tags-panel-max-width, calc(100vw - var(--units-lg))); /// Largeur maximale du panneau de suggestions. + +$option-radius: var(--ui-input-tags-option-radius, var(--radius-sm)); /// Rayon d'une option. +$option-gap: var(--ui-input-tags-option-gap, var(--units-sm)); /// Espacement entre la coche et le libellé d'une option. + +$group-height: var(--ui-input-tags-group-height, #{utils.rem-calc(32)}); /// Hauteur d'un en-tête de groupe du panneau. +$group-font-size: var(--ui-input-tags-group-font-size, var(--size-typography-text-md)); /// Taille du texte d'un en-tête de groupe. + +$tag-gap: var(--ui-input-tags-tag-gap, var(--units-xs)); /// Espacement entre les tags saisis. +$tag-radius: var(--ui-input-tags-tag-radius, var(--radius-full)); /// Rayon d'un tag. +$tag-gap-custom: var(--ui-input-tags-tag-gap-custom, var(--units-2xs)); /// Espacement interne d'un tag rendu via un template `#item`. +$focus-ring-width: var(--ui-input-tags-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus d'un tag. + +$input-min-width: var(--ui-input-tags-input-min-width, #{utils.rem-calc(80)}); /// Largeur minimale de la zone de saisie restante. .ui-input-tags { display: block; @@ -65,17 +76,17 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie display: inline-flex; align-items: center; max-width: 100%; - border-radius: var(--radius-full); + border-radius: $tag-radius; @include utils.control-transition(box-shadow); &:focus-visible { outline: none; - box-shadow: 0 0 0 utils.$form-focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); + box-shadow: 0 0 0 $focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); } // Custom-template wrapper baseline (matches the default chip height). &._custom { - gap: var(--units-2xs); + gap: $tag-gap-custom; min-height: map.get($size-default, tag-height); color: var(--form-high-content-default); font-family: var(--fontfamily-base); @@ -100,7 +111,7 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie display: flex; flex-direction: column; box-sizing: border-box; - max-width: calc(100vw - var(--units-lg)); + max-width: $panel-max-width; padding: $panel-padding; .ui-input-tags-scroller { @@ -122,7 +133,7 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie .ui-input-tags-option { display: flex; align-items: center; - gap: var(--units-sm); + gap: $option-gap; box-sizing: border-box; height: map.get($size-default, option-height); flex-shrink: 0; @@ -171,11 +182,11 @@ $input-min-width: utils.rem-calc(80); /// Largeur minimale de la zone de saisie display: flex; align-items: center; box-sizing: border-box; - height: utils.rem-calc(32); + height: $group-height; flex-shrink: 0; padding: 0 map.get($size-default, option-padding-x); font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-md); + font-size: $group-font-size; font-weight: var(--weight-bold); color: var(--form-low-content-default); diff --git a/projects/ui-kit/forms/ui-input/src/lib/ui-input.scss b/projects/ui-kit/forms/ui-input/src/lib/ui-input.scss index 7de819a..9ba0455 100644 --- a/projects/ui-kit/forms/ui-input/src/lib/ui-input.scss +++ b/projects/ui-kit/forms/ui-input/src/lib/ui-input.scss @@ -5,10 +5,13 @@ // `ui-field`; here we style the native + affixes via shared mixins. // ===================================================================== +$font-size-small: var(--ui-input-font-size-small, var(--size-typography-text-md)); /// Taille de police du contrôle et de l'unité, variante `small`. +$unit-font-size: var(--ui-input-unit-font-size, var(--size-typography-text-default)); /// Taille de police de l'unité affichée dans le champ. + .ui-input { &-native { @include utils.field-native-input; - &._small { font-size: var(--size-typography-text-md); } + &._small { font-size: $font-size-small; } } &-icon { @include utils.field-affix; } @@ -16,11 +19,11 @@ &-unit { @include utils.field-affix; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $unit-font-size; white-space: nowrap; user-select: none; - &._small { font-size: var(--size-typography-text-md); } + &._small { font-size: $font-size-small; } } &-action { @include utils.field-action; } diff --git a/projects/ui-kit/forms/ui-label/src/lib/ui-label.scss b/projects/ui-kit/forms/ui-label/src/lib/ui-label.scss index ada35a0..926928a 100644 --- a/projects/ui-kit/forms/ui-label/src/lib/ui-label.scss +++ b/projects/ui-kit/forms/ui-label/src/lib/ui-label.scss @@ -5,13 +5,17 @@ // component can drive the label state without piercing encapsulation. // ===================================================================== +$gap: var(--ui-label-gap, var(--units-xs)); /// Espace entre le texte et son marqueur. +$font-size: var(--ui-label-font-size, var(--size-typography-text-md)); /// Taille de police du libellé. +$font-size-small: var(--ui-label-font-size-small, var(--size-typography-text-sm)); /// Taille de police du libellé, variante `small`. + .ui-label { display: inline-flex; align-items: center; - gap: var(--units-xs); + gap: $gap; font-family: var(--fontfamily-base); font-weight: var(--weight-regular); - font-size: var(--size-typography-text-md); + font-size: $font-size; color: var(--ui-label-color, var(--form-high-content-default)); /// Couleur du texte, pilotable par un composant de formulaire parent (hover, disabled…). cursor: inherit; @@ -20,7 +24,7 @@ } &._small { - font-size: var(--size-typography-text-sm); + font-size: $font-size-small; } &._disabled { diff --git a/projects/ui-kit/forms/ui-nudger/src/lib/ui-nudger.scss b/projects/ui-kit/forms/ui-nudger/src/lib/ui-nudger.scss index 51a6135..18e2f54 100644 --- a/projects/ui-kit/forms/ui-nudger/src/lib/ui-nudger.scss +++ b/projects/ui-kit/forms/ui-nudger/src/lib/ui-nudger.scss @@ -15,24 +15,25 @@ /// Typo de la valeur et largeur minimale stabilisant le rendu, par taille. $sizes: ( 'default': ( - font-size: var(--size-typography-title-xl), - min-width: 40px, + font-size: var(--ui-nudger-value-font-size, var(--size-typography-title-xl)), + min-width: var(--ui-nudger-value-min-width, #{utils.rem-calc(40px)}), ), 'small': ( - font-size: var(--size-typography-title-lg), - min-width: 32px, + font-size: var(--ui-nudger-value-font-size-small, var(--size-typography-title-lg)), + min-width: var(--ui-nudger-value-min-width-small, #{utils.rem-calc(32px)}), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$gap: var(--ui-nudger-gap, var(--units-default)); /// Espacement entre les boutons et l'affichage de la valeur. .ui-nudger { display: inline-flex; align-items: center; - gap: var(--units-default); + gap: $gap; // --- Value display ------------------------------------------------- &-value { - min-width: utils.rem-calc(map.get($size-default, min-width)); + min-width: map.get($size-default, min-width); font-family: var(--fontfamily-title); font-weight: var(--weight-bold); font-size: map.get($size-default, font-size); @@ -48,7 +49,7 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default @each $name, $size in $sizes { @if $name != 'default' { &._#{$name} .ui-nudger-value { - min-width: utils.rem-calc(map.get($size, min-width)); + min-width: map.get($size, min-width); font-size: map.get($size, font-size); } } diff --git a/projects/ui-kit/forms/ui-radio/src/lib/ui-radio.scss b/projects/ui-kit/forms/ui-radio/src/lib/ui-radio.scss index da27519..97edf5b 100644 --- a/projects/ui-kit/forms/ui-radio/src/lib/ui-radio.scss +++ b/projects/ui-kit/forms/ui-radio/src/lib/ui-radio.scss @@ -8,12 +8,17 @@ // ===================================================================== // Shared defaults (ui-config) — replace a value here to detune ONLY this component. -$box-size: utils.$form-control-size; /// Diamètre du cercle radio -$focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus -$gap: utils.$form-control-gap; /// Espace entre le cercle et le label +$box-size: var(--ui-radio-box-size, #{utils.$form-control-size}); /// Diamètre du cercle radio +$focus-ring-width: var(--ui-radio-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus +$gap: var(--ui-radio-gap, #{utils.$form-control-gap}); /// Espace entre le cercle et le label // Component-specific (radio only). -$dot-size: var(--units-md); /// Diamètre du point sélectionné (spécifique radio) +$dot-size: var(--ui-radio-dot-size, var(--units-md)); /// Diamètre du point sélectionné (spécifique radio) +$box-radius: var(--ui-radio-box-radius, var(--radius-full)); /// Rayon des coins du cercle. +$dot-radius: var(--ui-radio-dot-radius, var(--radius-full)); /// Rayon des coins du point sélectionné. +$stroke-width: var(--ui-radio-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de la bordure du cercle. +$font-size: var(--ui-radio-font-size, var(--size-typography-text-default)); /// Taille de police du label. +$label-gap: var(--ui-radio-label-gap, var(--units-2xs)); /// Espacement libellé ↔ marqueur requis. .ui-radio { display: inline-flex; @@ -56,8 +61,8 @@ $dot-size: var(--units-md); /// Diamètre du point sélectionné (spécifique ra width: 100%; height: 100%; background-color: var(--_surface); - border: utils.$control-stroke-width solid var(--_stroke); - border-radius: var(--radius-full); + border: $stroke-width solid var(--_stroke); + border-radius: $box-radius; @include utils.control-transition(background-color, border-color, box-shadow); } @@ -71,7 +76,7 @@ $dot-size: var(--units-md); /// Diamètre du point sélectionné (spécifique ra width: $dot-size; height: $dot-size; background-color: var(--_content); - border-radius: var(--radius-full); + border-radius: $dot-radius; transform: scale(0); @include utils.control-transition(transform); } @@ -84,9 +89,9 @@ $dot-size: var(--units-md); /// Diamètre du point sélectionné (spécifique ra &-label { display: inline-flex; align-items: center; - gap: var(--units-2xs); + gap: $label-gap; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $font-size; color: var(--ui-label-color, var(--form-high-content-default)); cursor: inherit; user-select: none; diff --git a/projects/ui-kit/forms/ui-rating/src/lib/ui-rating.scss b/projects/ui-kit/forms/ui-rating/src/lib/ui-rating.scss index 01955f2..6b41121 100644 --- a/projects/ui-kit/forms/ui-rating/src/lib/ui-rating.scss +++ b/projects/ui-kit/forms/ui-rating/src/lib/ui-rating.scss @@ -4,7 +4,19 @@ // ui-rating : co-located styles. All values come from design tokens. // ===================================================================== -$focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus. +$focus-ring-width: var(--ui-rating-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus. + +$gap: var(--ui-rating-gap, var(--units-2xs)); /// Espacement entre étoiles. +$gap-sm: var(--ui-rating-gap-sm, var(--units-2xs)); /// Espacement entre étoiles, taille `sm`. +$gap-md: var(--ui-rating-gap-md, var(--units-2xs)); /// Espacement entre étoiles, taille `md`. +$gap-lg: var(--ui-rating-gap-lg, var(--units-2xs)); /// Espacement entre étoiles, taille `lg`. +$gap-xl: var(--ui-rating-gap-xl, var(--units-2xs)); /// Espacement entre étoiles, taille `xl`. + +$icon-padding: var(--ui-rating-icon-padding, var(--units-2xs)); /// Inset de la zone de clic d'une étoile. +$icon-radius: var(--ui-rating-icon-radius, var(--radius-full)); /// Rayon de la zone de clic d'une étoile. + +$icon-color: var(--ui-rating-icon-color, var(--form-low-content-default)); /// Couleur d'une étoile vide. +$icon-fill-color: var(--ui-rating-icon-fill-color, var(--form-high-content-checked)); /// Couleur de la portion remplie d'une étoile. .ui-rating { position: relative; @@ -25,14 +37,14 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f &-stars { display: flex; - gap: var(--units-2xs); // base gap + gap: $gap; } - &._size-sm &-stars { gap: var(--units-2xs); } - &._size-md &-stars { gap: var(--units-2xs); } - &._size-default &-stars { gap: var(--units-2xs); } - &._size-lg &-stars { gap: var(--units-2xs); } - &._size-xl &-stars { gap: var(--units-2xs); } + &._size-sm &-stars { gap: $gap-sm; } + &._size-md &-stars { gap: $gap-md; } + &._size-default &-stars { gap: $gap; } + &._size-lg &-stars { gap: $gap-lg; } + &._size-xl &-stars { gap: $gap-xl; } &._vertical &-stars { flex-direction: column; @@ -42,16 +54,16 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f display: flex; align-items: center; justify-content: center; - padding: var(--units-2xs); // hit area + padding: $icon-padding; cursor: pointer; - border-radius: var(--radius-full); + border-radius: $icon-radius; pointer-events: auto; &-stack { position: relative; display: flex; line-height: 1; - color: var(--form-low-content-default); + color: $icon-color; @include utils.control-transition(color); } @@ -66,7 +78,7 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f &-fill { position: absolute; inset: 0; - color: var(--form-high-content-checked); + color: $icon-fill-color; clip-path: inset(0 calc((1 - var(--ui-rating-fill, 1)) * 100%) 0 0); @include utils.control-transition(color); } diff --git a/projects/ui-kit/forms/ui-segment-control/src/lib/ui-segment-control.scss b/projects/ui-kit/forms/ui-segment-control/src/lib/ui-segment-control.scss index 5d72064..4976295 100644 --- a/projects/ui-kit/forms/ui-segment-control/src/lib/ui-segment-control.scss +++ b/projects/ui-kit/forms/ui-segment-control/src/lib/ui-segment-control.scss @@ -9,33 +9,44 @@ // ===================================================================== // --- Config (extension points) --------------------------------------- +$gutter: var(--ui-segment-control-padding, var(--units-xs)); /// Gouttière interne autour des segments (coins de l'indicateur concentriques au track) +$gutter-size: 4px; /// Miroir numérique de la gouttière, pour le calcul des tailles. + /// Hauteur / padding / taille de police par taille $sizes: ( 'default': ( - height: 44px, - padding-x: var(--units-default), - font-size: var(--size-typography-text-default), - min: 44px, + height: var(--ui-segment-control-min-height, #{utils.rem-calc(44px)}), + option-h: var(--ui-segment-control-option-min-height, #{utils.rem-calc(44px - 2 * $gutter-size)}), + option-w: var(--ui-segment-control-option-min-width, #{utils.rem-calc(44px - 2 * $gutter-size)}), + padding-x: var(--ui-segment-control-option-padding-x, var(--units-default)), + font-size: var(--ui-segment-control-font-size, var(--size-typography-text-default)), ), 'small': ( - height: 40px, - padding-x: var(--units-md), - font-size: var(--size-typography-text-md), - min: 40px, + height: var(--ui-segment-control-min-height-small, #{utils.rem-calc(40px)}), + option-h: var(--ui-segment-control-option-min-height-small, #{utils.rem-calc(40px - 2 * $gutter-size)}), + option-w: var(--ui-segment-control-option-min-width-small, #{utils.rem-calc(40px - 2 * $gutter-size)}), + padding-x: var(--ui-segment-control-option-padding-x-small, var(--units-md)), + font-size: var(--ui-segment-control-font-size-small, var(--size-typography-text-md)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$stroke-width: utils.$control-stroke-width; /// Épaisseur de la bordure de la piste -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus des segments -$gutter: var(--units-xs); /// Gouttière interne autour des segments (coins de l'indicateur concentriques au track) -$gutter-size: 4px; /// Miroir numérique de la gouttière, pour le calcul des tailles. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. + +$stroke-width: var(--ui-segment-control-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de la bordure de la piste +$focus-ring-width: var(--ui-segment-control-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus des segments + +$radius: var(--ui-segment-control-radius, var(--radius-md)); /// Rayon des coins de la piste. +$gap: var(--ui-segment-control-gap, 0); /// Espacement entre les segments. + +$thumb-radius: var(--ui-segment-control-thumb-radius, var(--radius-sm)); /// Rayon des coins de l'indicateur glissant. +$option-radius: var(--ui-segment-control-option-radius, var(--radius-sm)); /// Rayon des coins d'un segment. +$option-gap: var(--ui-segment-control-option-gap, var(--units-sm)); /// Espacement icône ↔ libellé dans un segment. @mixin segment-size($size) { - min-height: utils.rem-calc(map.get($size, height)); + min-height: map.get($size, height); .ui-segment-control-option { - min-height: utils.rem-calc(map.get($size, height) - 2 * $gutter-size); - min-width: utils.rem-calc(map.get($size, min) - 2 * $gutter-size); + min-height: map.get($size, option-h); + min-width: map.get($size, option-w); padding: 0 map.get($size, padding-x); font-size: map.get($size, font-size); } @@ -48,8 +59,8 @@ $gutter-size: 4px; /// Miroir numérique de la g align-items: stretch; box-sizing: border-box; padding: $gutter; - gap: 0; - border-radius: var(--radius-md); + gap: $gap; + border-radius: $radius; // Border as an inset shadow → no layout box, so the thumb offset math // (offsetLeft/offsetTop) stays exact regardless of border width. box-shadow: inset 0 0 0 $stroke-width var(--global-border-default); @@ -75,7 +86,7 @@ $gutter-size: 4px; /// Miroir numérique de la g top: 0; left: 0; z-index: 0; - border-radius: var(--radius-sm); + border-radius: $thumb-radius; background-color: var(--actions-high-surface-default); pointer-events: none; @include utils.motion-transition(transform, width, height); @@ -120,10 +131,10 @@ $gutter-size: 4px; /// Miroir numérique de la g display: inline-flex; align-items: center; justify-content: center; - gap: var(--units-sm); + gap: $option-gap; box-sizing: border-box; border: 0; - border-radius: var(--radius-sm); + border-radius: $option-radius; background-color: transparent; color: var(--actions-low-content-default); font-family: inherit; @@ -146,7 +157,7 @@ $gutter-size: 4px; /// Miroir numérique de la g outline: none; box-shadow: 0 0 0 $focus-ring-width var(--actions-low-stroke-focused); } - + &._selected { color: var(--actions-high-content-default); font-weight: var(--weight-bold); diff --git a/projects/ui-kit/forms/ui-select/src/lib/ui-select.scss b/projects/ui-kit/forms/ui-select/src/lib/ui-select.scss index d37f5da..2b71922 100644 --- a/projects/ui-kit/forms/ui-select/src/lib/ui-select.scss +++ b/projects/ui-kit/forms/ui-select/src/lib/ui-select.scss @@ -9,33 +9,50 @@ /// Géométrie et typo des options, des en-têtes de groupe, du filtre et hauteur de défilement, par taille. $sizes: ( 'default': ( - option-height: utils.rem-calc(40), - option-padding-x: var(--units-default), - font-size: var(--size-typography-text-default), - group-height: utils.rem-calc(32), - group-font-size: var(--size-typography-text-md), - filter-height: var(--size-components-sm), - scroll-height: utils.rem-calc(312), + option-height: var(--ui-select-option-height, #{utils.rem-calc(40)}), + option-padding-x: var(--ui-select-option-padding-x, var(--units-default)), + font-size: var(--ui-select-font-size, var(--size-typography-text-default)), + group-height: var(--ui-select-group-height, #{utils.rem-calc(32)}), + group-font-size: var(--ui-select-group-font-size, var(--size-typography-text-md)), + filter-height: var(--ui-select-filter-height, var(--size-components-sm)), + scroll-height: var(--ui-select-scroller-max-height, #{utils.rem-calc(312)}), ), 'small': ( - option-height: utils.rem-calc(32), - option-padding-x: var(--units-md), - font-size: var(--size-typography-text-md), - group-height: utils.rem-calc(24), - group-font-size: var(--size-typography-text-sm), - filter-height: var(--size-components-xs), - scroll-height: utils.rem-calc(256), + option-height: var(--ui-select-option-height-small, #{utils.rem-calc(32)}), + option-padding-x: var(--ui-select-option-padding-x-small, var(--units-md)), + font-size: var(--ui-select-font-size-small, var(--size-typography-text-md)), + group-height: var(--ui-select-group-height-small, #{utils.rem-calc(24)}), + group-font-size: var(--ui-select-group-font-size-small, var(--size-typography-text-sm)), + filter-height: var(--ui-select-filter-height-small, var(--size-components-xs)), + scroll-height: var(--ui-select-scroller-max-height-small, #{utils.rem-calc(256)}), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. $size-small: map.get($sizes, 'small'); /// Raccourci vers l'entrée `small` de `$sizes`. -$panel-padding: var(--units-xs); /// Inset du panneau d'options. -$panel-gap: var(--units-xs); /// Espacement entre les groupes du panneau. -$option-radius: var(--radius-sm); /// Rayon d'une ligne d'option -$option-gap: var(--units-sm); /// Espace entre case/contenu/coche dans une ligne -$checkbox-size: utils.rem-calc(18); /// Taille de la case visuelle (mode `checkbox`) -$stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visuelle) +$panel-padding: var(--ui-select-panel-padding, var(--units-xs)); /// Inset du panneau d'options. +$panel-gap: var(--ui-select-panel-gap, var(--units-xs)); /// Espacement entre les groupes du panneau. +$panel-max-width: var(--ui-select-panel-max-width, calc(100vw - var(--units-lg))); /// Largeur maximale du panneau, gouttière laissée au viewport. +$header-padding-y: var(--ui-select-header-padding-y, var(--units-xs)); /// Inset vertical des zones d'en-tête et de pied projetées. +$header-padding-x: var(--ui-select-header-padding-x, var(--units-sm)); /// Inset horizontal des zones d'en-tête et de pied projetées. + +$option-radius: var(--ui-select-option-radius, var(--radius-sm)); /// Rayon d'une ligne d'option +$option-gap: var(--ui-select-option-gap, var(--units-sm)); /// Espace entre case/contenu/coche dans une ligne +$values-gap: var(--ui-select-values-gap, var(--units-xs)); /// Espacement entre les valeurs affichées dans le déclencheur (mode `multiple`). +$checkbox-size: var(--ui-select-checkbox-size, #{utils.rem-calc(18)}); /// Taille de la case visuelle (mode `checkbox`) +// The mark has to follow the box, same reason as ui-checkbox: falls back to the `sm` +// step of the icon scale, which is what it is rendered at. +$checkbox-icon-size: var(--ui-select-checkbox-icon-size, var(--ui-icon-size-sm, var(--size-typography-icon-sm))); /// Taille de la coche dans la case (mode `checkbox`). +$checkbox-radius: var(--ui-select-checkbox-radius, var(--radius-xs)); /// Rayon de la case visuelle (mode `checkbox`). +$stroke-width: var(--ui-select-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de bordure (case visuelle) + +$filter-gap: var(--ui-select-filter-gap, var(--units-sm)); /// Espacement icône ↔ saisie dans le filtre. +$filter-padding-x: var(--ui-select-filter-padding-x, var(--units-sm)); /// Inset horizontal du contenu du filtre. +$filter-stroke-width: var(--ui-select-filter-stroke-width, var(--stroke-sm)); /// Épaisseur de la bordure du filtre. + +$clear-size: var(--ui-select-clear-size, var(--size-components-2xs)); /// Taille du bouton d'effacement. +$clear-radius: var(--ui-select-clear-radius, var(--radius-full)); /// Rayon du bouton d'effacement. +$focus-ring-width: var(--ui-select-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus (bouton d'effacement). .ui-select { display: block; @@ -89,7 +106,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu display: flex; align-items: center; flex-wrap: wrap; - gap: var(--units-xs); + gap: $values-gap; min-width: 0; } @@ -128,13 +145,13 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu align-items: center; justify-content: center; box-sizing: border-box; - width: var(--size-components-2xs); - height: var(--size-components-2xs); + width: $clear-size; + height: $clear-size; flex-shrink: 0; margin: 0; padding: 0; border: none; - border-radius: var(--radius-full); + border-radius: $clear-radius; background: none; color: var(--form-low-content-default); cursor: pointer; @@ -145,7 +162,7 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu &:focus-visible { outline: none; color: var(--form-high-content-focused); - box-shadow: 0 0 0 utils.$form-focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); + box-shadow: 0 0 0 $focus-ring-width rgb(from var(--form-high-stroke-focused) r g b / 50%); } &:disabled { cursor: not-allowed; color: var(--form-high-content-disabled); } } @@ -171,13 +188,13 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu flex-direction: column; gap: $panel-gap; box-sizing: border-box; - max-width: calc(100vw - var(--units-lg)); + max-width: $panel-max-width; padding: $panel-padding; // --- Header / footer (projected templates) -------------------------- .ui-select-header, .ui-select-footer { - padding: var(--units-xs) var(--units-sm); + padding: $header-padding-y $header-padding-x; font-family: var(--fontfamily-base); font-size: map.get($size-default, group-font-size); color: var(--form-high-content-default); @@ -187,12 +204,12 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu .ui-select-filter { display: flex; align-items: center; - gap: var(--units-sm); + gap: $filter-gap; box-sizing: border-box; height: map.get($size-default, filter-height); flex-shrink: 0; padding: 0; - border: var(--stroke-sm) solid var(--form-high-stroke-default); + border: $filter-stroke-width solid var(--form-high-stroke-default); border-radius: $option-radius; background-color: var(--form-high-surface-default); @include utils.control-transition(border-color); @@ -205,11 +222,11 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu .ui-select-filter-icon { flex-shrink: 0; color: var(--form-low-content-default); - @include utils.field-inset-edges('margin', var(--units-sm)); + @include utils.field-inset-edges('margin', $filter-padding-x); } .ui-select-filter-input { - @include utils.field-native-input(var(--units-sm)); + @include utils.field-native-input($filter-padding-x); font-size: map.get($size-default, group-font-size); } @@ -298,11 +315,12 @@ $stroke-width: utils.$control-stroke-width; /// Épaisseur de bordure (case visu height: $checkbox-size; flex-shrink: 0; border: $stroke-width solid var(--form-high-stroke-default); - border-radius: var(--radius-xs); + border-radius: $checkbox-radius; background-color: var(--form-high-surface-default); @include utils.control-transition(background-color, border-color); .ui-select-checkbox-icon { + --ui-icon-size: #{$checkbox-icon-size}; color: var(--form-high-content-checked); opacity: 0; transform: scale(0.6); diff --git a/projects/ui-kit/forms/ui-slider/src/lib/ui-slider.scss b/projects/ui-kit/forms/ui-slider/src/lib/ui-slider.scss index ec049d2..893a0c1 100644 --- a/projects/ui-kit/forms/ui-slider/src/lib/ui-slider.scss +++ b/projects/ui-kit/forms/ui-slider/src/lib/ui-slider.scss @@ -5,11 +5,16 @@ // ===================================================================== // --- Config --------------------------------------- -$track-thickness: utils.rem-calc(8); /// Épaisseur de la piste -$handle-size: utils.rem-calc(22); /// Diamètre des poignées -$mark-size: utils.rem-calc(6); /// Taille des repères (agrandie pour la lisibilité). -$vertical-length: 12rem; /// Longueur de la piste en orientation verticale -$focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus des poignées +$track-thickness: var(--ui-slider-thickness, #{utils.rem-calc(8)}); /// Épaisseur de la piste +$handle-size: var(--ui-slider-handle-size, #{utils.rem-calc(22)}); /// Diamètre des poignées +$mark-size: var(--ui-slider-mark-size, #{utils.rem-calc(6)}); /// Taille des repères (agrandie pour la lisibilité). +$vertical-length: var(--ui-slider-length, 12rem); /// Longueur de la piste en orientation verticale +$focus-ring-width: var(--ui-slider-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus des poignées + +$track-radius: var(--ui-slider-track-radius, var(--radius-full)); /// Rayon de la piste et de la portion remplie. +$handle-radius: var(--ui-slider-handle-radius, var(--radius-full)); /// Rayon des poignées. +$handle-stroke-width: var(--ui-slider-handle-stroke-width, #{utils.$control-stroke-width}); /// Épaisseur de bordure des poignées. +$mark-radius: var(--ui-slider-mark-radius, var(--radius-full)); /// Rayon des repères. .ui-slider { position: relative; @@ -25,8 +30,8 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f &-track { position: relative; width: 100%; - height: var(--ui-slider-thickness, #{$track-thickness}); - border-radius: var(--radius-full); + height: $track-thickness; + border-radius: $track-radius; background-color: var(--global-background-muted); } @@ -35,7 +40,7 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f position: absolute; top: 0; height: 100%; - border-radius: var(--radius-full); + border-radius: $track-radius; background-color: var(--actions-high-surface-default); @include utils.control-transition(background-color); } @@ -44,12 +49,12 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f &-handle { position: absolute; top: 50%; - width: var(--ui-slider-handle-size, #{$handle-size}); - height: var(--ui-slider-handle-size, #{$handle-size}); + width: $handle-size; + height: $handle-size; transform: translate(-50%, -50%); box-sizing: border-box; - border: utils.$control-stroke-width solid var(--actions-high-stroke-default); - border-radius: var(--radius-full); + border: $handle-stroke-width solid var(--actions-high-stroke-default); + border-radius: $handle-radius; background-color: var(--actions-high-surface-default); cursor: grab; @include utils.control-transition(background-color, border-color, box-shadow); @@ -83,7 +88,7 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f width: $mark-size; height: $mark-size; transform: translate(-50%, -50%); - border-radius: var(--radius-full); + border-radius: $mark-radius; background-color: var(--informative-defaulthigh-surface-default); &._active { @@ -96,11 +101,11 @@ $focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de f flex-direction: column; width: auto; min-width: $handle-size; - height: var(--ui-slider-length, #{$vertical-length}); + height: $vertical-length; min-height: 0; .ui-slider-track { - width: var(--ui-slider-thickness, #{$track-thickness}); + width: $track-thickness; height: 100%; } diff --git a/projects/ui-kit/forms/ui-textarea/src/lib/ui-textarea.scss b/projects/ui-kit/forms/ui-textarea/src/lib/ui-textarea.scss index 8949459..e4b1647 100644 --- a/projects/ui-kit/forms/ui-textarea/src/lib/ui-textarea.scss +++ b/projects/ui-kit/forms/ui-textarea/src/lib/ui-textarea.scss @@ -4,11 +4,17 @@ // ui-textarea : projected parts only. The box/label/helper/states live in // ===================================================================== -$pad-x: utils.$form-field-padding-x; /// Inset horizontal du contrôle multiligne. -$pad-y: utils.$form-textarea-padding-y; /// Padding vertical du contrôle multiligne. -$pad-y-small: utils.$form-textarea-padding-y-small; /// Padding vertical du contrôle multiligne, variante `small`. -$min-height: utils.$form-textarea-min-height; /// Hauteur minimale du contrôle multiligne. -$min-height-small: utils.$form-textarea-min-height-small; /// Hauteur minimale du contrôle multiligne, variante `small`. +$pad-x: var(--ui-textarea-padding-x, #{utils.$form-field-padding-x}); /// Inset horizontal du contrôle multiligne. + +$min-height: var(--ui-textarea-min-height, #{utils.$form-textarea-min-height}); /// Hauteur minimale du contrôle multiligne. +$min-height-small: var(--ui-textarea-min-height-small, #{utils.$form-textarea-min-height-small}); /// Hauteur minimale du contrôle multiligne, variante `small`. +$line-height: var(--ui-textarea-line-height, 1.5); /// Interlignage du contrôle multiligne. +$padding-block: var(--ui-textarea-padding-block, calc((#{$min-height} - 1.5em) / 2)); /// Inset vertical centrant une ligne dans la hauteur minimale. +$padding-block-small: var(--ui-textarea-padding-block-small, calc((#{$min-height-small} - 1.5em) / 2)); /// Inset vertical centrant une ligne, variante `small`. +$font-size-small: var(--ui-textarea-font-size-small, var(--size-typography-text-md)); /// Taille de police du contrôle, variante `small`. + +$count-font-size: var(--ui-textarea-count-font-size, var(--size-typography-text-md)); /// Taille de police du compteur de caractères. +$count-font-size-small: var(--ui-textarea-count-font-size-small, var(--size-typography-text-sm)); /// Taille de police du compteur de caractères, variante `small`. .ui-textarea { &-native { @@ -16,14 +22,14 @@ $min-height-small: utils.$form-textarea-min-height-small; /// Hauteur minimale d box-sizing: border-box; width: 100%; min-height: $min-height; - line-height: 1.5; - padding-block: calc((#{$min-height} - 1.5em) / 2); + line-height: $line-height; + padding-block: $padding-block; max-width: 100%; &._small { min-height: $min-height-small; - padding-block: calc((#{$min-height-small} - 1.5em) / 2); - font-size: var(--size-typography-text-md); + padding-block: $padding-block-small; + font-size: $font-size-small; } &._auto-resize { overflow: hidden; } @@ -34,11 +40,11 @@ $min-height-small: utils.$form-textarea-min-height-small; /// Hauteur minimale d margin-left: auto; flex-shrink: 0; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-md); + font-size: $count-font-size; color: var(--form-low-content-default); user-select: none; - &._small { font-size: var(--size-typography-text-sm); } + &._small { font-size: $count-font-size-small; } &._over { color: var(--form-error-content-default); } &._disabled { color: var(--form-high-content-disabled); } } diff --git a/projects/ui-kit/forms/ui-toggle/src/lib/ui-toggle.scss b/projects/ui-kit/forms/ui-toggle/src/lib/ui-toggle.scss index c12e053..d7c4829 100644 --- a/projects/ui-kit/forms/ui-toggle/src/lib/ui-toggle.scss +++ b/projects/ui-kit/forms/ui-toggle/src/lib/ui-toggle.scss @@ -4,35 +4,51 @@ // ===================================================================== // ui-toggle : co-located styles. All values come from design tokens. // Extend: -// • Size → add an entry to $sizes (+ the `ToggleSize` type). `travel` (thumb -// slide) is derived: track-w − 2·pad − thumb. +// • Size → add an entry to $sizes (+ the `ToggleSize` type). `offset` (thumb +// slide) = track-w − 4·pad − thumb (2·pad + the 2 track borders). // ===================================================================== -$focus-ring-width: utils.$form-focus-ring-width; /// Épaisseur de l'anneau de focus -$gap: utils.$form-control-gap; /// Espace entre l'interrupteur et le label +$focus-ring-width: var(--ui-toggle-focus-ring-width, #{utils.$form-focus-ring-width}); /// Épaisseur de l'anneau de focus +$gap: var(--ui-toggle-gap, #{utils.$form-control-gap}); /// Espace entre l'interrupteur et le label + +$track-radius: var(--ui-toggle-track-radius, var(--radius-full)); /// Rayon des coins de la piste. +$track-stroke-width: var(--ui-toggle-track-stroke-width, var(--stroke-sm)); /// Épaisseur de la bordure de la piste. +$thumb-radius: var(--ui-toggle-thumb-radius, var(--radius-full)); /// Rayon des coins de la pastille. + +$font-size: var(--ui-toggle-font-size, var(--size-typography-text-default)); /// Taille de police du label. +$font-size-small: var(--ui-toggle-font-size-small, var(--size-typography-text-md)); /// Taille de police du label en taille `small`. +$label-gap: var(--ui-toggle-label-gap, var(--units-2xs)); /// Espacement libellé ↔ marqueur requis. /// Dimensions de la piste et de la pastille par taille (spécifique toggle) $sizes: ( - 'default': (track-w: 44px, track-h: 24px, thumb: 20px, pad: 1px), - 'small': (track-w: 36px, track-h: 20px, thumb: 16px, pad: 1px), + 'default': ( + track-w: var(--ui-toggle-track-width, #{utils.rem-calc(44px)}), + track-h: var(--ui-toggle-track-height, #{utils.rem-calc(24px)}), + thumb: var(--ui-toggle-thumb-size, #{utils.rem-calc(20px)}), + pad: var(--ui-toggle-track-padding, #{utils.rem-calc(1px)}), + offset: var(--ui-toggle-thumb-offset, #{utils.rem-calc(44px - 4 * 1px - 20px)}), + ), + 'small': ( + track-w: var(--ui-toggle-track-width-small, #{utils.rem-calc(36px)}), + track-h: var(--ui-toggle-track-height-small, #{utils.rem-calc(20px)}), + thumb: var(--ui-toggle-thumb-size-small, #{utils.rem-calc(16px)}), + pad: var(--ui-toggle-track-padding-small, #{utils.rem-calc(1px)}), + offset: var(--ui-toggle-thumb-offset-small, #{utils.rem-calc(36px - 4 * 1px - 16px)}), + ), ); @mixin toggle-size($size) { - $track-w: map.get($size, track-w); - $pad: map.get($size, pad); - $thumb: map.get($size, thumb); - .ui-toggle-track { - width: utils.rem-calc($track-w); - height: utils.rem-calc(map.get($size, track-h)); - padding: utils.rem-calc($pad); + width: map.get($size, track-w); + height: map.get($size, track-h); + padding: map.get($size, pad); } .ui-toggle-thumb { - width: utils.rem-calc($thumb); - height: utils.rem-calc($thumb); + width: map.get($size, thumb); + height: map.get($size, thumb); } &._checked .ui-toggle-thumb { - transform: translateX(utils.rem-calc($track-w - 4 * $pad - $thumb)); + transform: translateX(map.get($size, offset)); } } @@ -65,8 +81,8 @@ $sizes: ( display: flex; align-items: center; box-sizing: border-box; - border: var(--stroke-sm) solid transparent; - border-radius: var(--radius-full); + border: $track-stroke-width solid transparent; + border-radius: $track-radius; background-color: var(--form-low-content-default); @include utils.control-transition(background-color, border-color, box-shadow); } @@ -77,7 +93,7 @@ $sizes: ( justify-content: center; flex-shrink: 0; box-sizing: border-box; - border-radius: var(--radius-full); + border-radius: $thumb-radius; background-color: var(--form-high-surface-default); color: var(--form-low-content-default); line-height: 0; @@ -119,15 +135,15 @@ $sizes: ( &-label { display: inline-flex; align-items: center; - gap: var(--units-2xs); + gap: $label-gap; font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $font-size; color: var(--ui-label-color, var(--form-high-content-default)); cursor: inherit; user-select: none; @include utils.control-transition(color); } - &._small &-label { font-size: var(--size-typography-text-md); } + &._small &-label { font-size: $font-size-small; } &-required { color: var(--informative-errorlow-content-default); diff --git a/projects/ui-kit/informative/ui-accordion/src/lib/ui-accordion-panel.scss b/projects/ui-kit/informative/ui-accordion/src/lib/ui-accordion-panel.scss index fd0badd..be762dd 100644 --- a/projects/ui-kit/informative/ui-accordion/src/lib/ui-accordion-panel.scss +++ b/projects/ui-kit/informative/ui-accordion/src/lib/ui-accordion-panel.scss @@ -10,8 +10,8 @@ // ===================================================================== // --- Config (local) --------------------------------------------------- -$control-size: utils.rem-calc(40); /// Diamètre du chevron circulaire. -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus de l'en-tête (constante partagée `ui-config`). +$control-size: var(--ui-accordion-control-size, #{utils.rem-calc(40)}); /// Diamètre du chevron circulaire. +$focus-ring-width: var(--ui-accordion-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus de l'en-tête (constante partagée `ui-config`). :host { display: block; diff --git a/projects/ui-kit/informative/ui-alert/src/lib/ui-alert.scss b/projects/ui-kit/informative/ui-alert/src/lib/ui-alert.scss index 3257982..a152762 100644 --- a/projects/ui-kit/informative/ui-alert/src/lib/ui-alert.scss +++ b/projects/ui-kit/informative/ui-alert/src/lib/ui-alert.scss @@ -15,26 +15,40 @@ $levels: default, highlight, success, warning, error; /// Niveaux sémantiques générant les modifiers `._`. $sublevels: high, low; /// Intensités générant les modifiers `._`. +$gap: var(--ui-alert-gap, var(--units-sm)); /// Espacement icône ↔ contenu ↔ fermeture. +$padding-y: var(--ui-alert-padding-y, var(--units-sm)); /// Inset vertical. +$padding-x: var(--ui-alert-padding-x, var(--units-md)); /// Inset horizontal. +$radius: var(--ui-alert-radius, var(--radius-default)); /// Rayon des coins. +$stroke-width: var(--ui-alert-stroke-width, var(--stroke-default)); /// Épaisseur de la bordure. + +$icon-padding-top: var(--ui-alert-icon-padding-top, var(--units-2xs)); /// Décalage vertical de l'icône, pour l'aligner sur la première ligne de texte. +$content-gap: var(--ui-alert-content-gap, var(--units-2xs)); /// Espacement titre ↔ message. +$content-padding-right: var(--ui-alert-content-padding-right, var(--units-sm)); /// Réserve à droite du contenu, avant la fermeture. + +$close-radius: var(--ui-alert-close-radius, var(--radius-xs)); /// Rayon des coins du bouton de fermeture. +$close-opacity: var(--ui-alert-close-opacity, 0.7); /// Opacité au repos du bouton de fermeture. + /// Hauteur minimale et typo, par taille. $sizes: ( 'default': ( - min-height: 56px, - title-size: var(--size-typography-title-sm), - text-size: var(--size-typography-text-md), + min-height: var(--ui-alert-min-height, #{utils.rem-calc(56)}), + title-size: var(--ui-alert-title-font-size, var(--size-typography-title-sm)), + text-size: var(--ui-alert-text-font-size, var(--size-typography-text-md)), ), 'large': ( - min-height: 66px, - title-size: var(--size-typography-title-md), - text-size: var(--size-typography-text-lg), + min-height: var(--ui-alert-min-height-large, #{utils.rem-calc(66)}), + title-size: var(--ui-alert-title-font-size-large, var(--size-typography-title-md)), + text-size: var(--ui-alert-text-font-size-large, var(--size-typography-text-lg)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -// Reused structural constant (kit-wide focus ring). Local alias, never copied. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. +// Reused structural constant (kit-wide focus ring) as the default, never copied. +$focus-ring-width: var(--ui-alert-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. +$focus-ring-offset: var(--ui-alert-focus-ring-offset, #{utils.rem-calc(2)}); /// Décalage de l'anneau de focus. @mixin alert-size($size) { - min-height: utils.rem-calc(map.get($size, min-height)); + min-height: map.get($size, min-height); .ui-alert-title { font-size: map.get($size, title-size); @@ -49,12 +63,12 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. // --- Base ---------------------------------------------------------- display: flex; align-items: flex-start; - gap: var(--units-sm); + gap: $gap; box-sizing: border-box; width: 100%; - padding: var(--units-sm) var(--units-md); - border: var(--stroke-default) solid transparent; - border-radius: var(--radius-default); + padding: $padding-y $padding-x; + border: $stroke-width solid transparent; + border-radius: $radius; background-color: var(--informative-defaulthigh-surface-default); color: var(--informative-defaulthigh-content-default); font-family: var(--fontfamily-base); @@ -66,7 +80,7 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. flex: 0 0 auto; display: flex; align-items: center; - padding-top: var(--units-2xs); + padding-top: $icon-padding-top; line-height: 0; } @@ -76,8 +90,8 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. display: flex; flex-direction: column; justify-content: center; - gap: var(--units-2xs); - padding-right: var(--units-sm); + gap: $content-gap; + padding-right: $content-padding-right; } &-title { @@ -101,12 +115,12 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. justify-content: center; padding: 0; border: 0; - border-radius: var(--radius-xs); + border-radius: $close-radius; background: transparent; color: inherit; line-height: 0; cursor: pointer; - opacity: 0.7; + opacity: $close-opacity; @include utils.motion-transition(opacity); &:hover { @@ -115,7 +129,7 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. &:focus-visible { outline: $focus-ring-width solid currentColor; - outline-offset: utils.rem-calc(2); + outline-offset: $focus-ring-offset; opacity: 1; } diff --git a/projects/ui-kit/informative/ui-avatar-group/src/lib/ui-avatar-group.scss b/projects/ui-kit/informative/ui-avatar-group/src/lib/ui-avatar-group.scss index 7e1b66f..0f7d5f2 100644 --- a/projects/ui-kit/informative/ui-avatar-group/src/lib/ui-avatar-group.scss +++ b/projects/ui-kit/informative/ui-avatar-group/src/lib/ui-avatar-group.scss @@ -6,8 +6,10 @@ // the shared ui-config constant so it can be tuned kit-wide. // ===================================================================== +$overlap: var(--ui-avatar-group-overlap, #{utils.$avatar-group-overlap}); /// Chevauchement horizontal entre avatars empilés. + .ui-avatar-group { - --_overlap: #{utils.$avatar-group-overlap}; /// Chevauchement horizontal entre avatars empilés. + --_overlap: #{$overlap}; display: inline-flex; align-items: center; diff --git a/projects/ui-kit/informative/ui-avatar/src/lib/ui-avatar.scss b/projects/ui-kit/informative/ui-avatar/src/lib/ui-avatar.scss index 2bc40bc..497d1f0 100644 --- a/projects/ui-kit/informative/ui-avatar/src/lib/ui-avatar.scss +++ b/projects/ui-kit/informative/ui-avatar/src/lib/ui-avatar.scss @@ -15,33 +15,41 @@ /// Diamètre et typo des initiales, par taille. $sizes: ( 'tiny': ( - diameter: utils.$avatar-size-tiny, - font: var(--size-typography-title-sm), + diameter: var(--ui-avatar-size-tiny, #{utils.rem-calc(utils.$avatar-size-tiny)}), + font: var(--ui-avatar-font-size-tiny, var(--size-typography-title-sm)), ), 'small': ( - diameter: utils.$avatar-size-small, - font: var(--size-typography-title-md), + diameter: var(--ui-avatar-size-small, #{utils.rem-calc(utils.$avatar-size-small)}), + font: var(--ui-avatar-font-size-small, var(--size-typography-title-md)), ), 'default': ( - diameter: utils.$avatar-size-default, - font: var(--size-typography-title-default), + diameter: var(--ui-avatar-size, #{utils.rem-calc(utils.$avatar-size-default)}), + font: var(--ui-avatar-font-size, var(--size-typography-title-default)), ), 'large': ( - diameter: utils.$avatar-size-large, - font: var(--size-typography-title-xl), + diameter: var(--ui-avatar-size-large, #{utils.rem-calc(utils.$avatar-size-large)}), + font: var(--ui-avatar-font-size-large, var(--size-typography-title-xl)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$radius: var(--ui-avatar-radius, var(--radius-full)); /// Rayon des coins (forme ronde par défaut). +$radius-square: var(--ui-avatar-radius-square, #{utils.$avatar-square-radius}); /// Rayon des coins en présentation `square`. +$stroke-width: var(--ui-avatar-stroke-width, var(--stroke-default)); /// Épaisseur du liseré. + +$badge-offset-x: var(--ui-avatar-badge-offset-x, 25%); /// Débord horizontal du badge de statut. +$badge-offset-y: var(--ui-avatar-badge-offset-y, -25%); /// Débord vertical du badge de statut. +$badge-z-index: var(--ui-avatar-badge-z-index, 1); /// Plan d'empilement du badge de statut. + @mixin avatar-size($size) { - width: utils.rem-calc(map.get($size, diameter)); - height: utils.rem-calc(map.get($size, diameter)); + width: map.get($size, diameter); + height: map.get($size, diameter); font-size: map.get($size, font); } .ui-avatar { // --- Base ---------------------------------------------------------- - --_radius: var(--radius-full); + --_radius: #{$radius}; position: relative; display: inline-flex; @@ -49,7 +57,7 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default justify-content: center; flex-shrink: 0; box-sizing: border-box; - border: var(--stroke-default) solid var(--global-border-default); + border: $stroke-width solid var(--global-border-default); border-radius: var(--_radius); background-color: var(--informative-defaultlow-surface-default); color: var(--informative-defaultlow-content-default); @@ -61,7 +69,7 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default @include avatar-size($size-default); // --- Shape ----------------------------------------- - &._square { --_radius: #{utils.$avatar-square-radius}; } + &._square { --_radius: #{$radius-square}; } // --- Sub-elements -------------------------------------------------- &-image { @@ -81,8 +89,8 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default top: 0; right: 0; display: inline-flex; - transform: translate(25%, -25%); - z-index: 1; + transform: translate($badge-offset-x, $badge-offset-y); + z-index: $badge-z-index; } // --- Sizes ----------------------------------------- diff --git a/projects/ui-kit/informative/ui-badge/src/lib/ui-badge.scss b/projects/ui-kit/informative/ui-badge/src/lib/ui-badge.scss index 61ba8f0..cb36232 100644 --- a/projects/ui-kit/informative/ui-badge/src/lib/ui-badge.scss +++ b/projects/ui-kit/informative/ui-badge/src/lib/ui-badge.scss @@ -15,32 +15,36 @@ $levels: default, highlight, success, warning, error; /// Niveaux sémantiques générant les modifiers `._`. $sublevels: high, low; /// Intensités générant les modifiers `._`. +$gap: var(--ui-badge-gap, var(--units-xs)); /// Espacement icône ↔ libellé. +$radius: var(--ui-badge-radius, var(--radius-full)); /// Rayon des coins. +$stroke-width: var(--ui-badge-stroke-width, var(--stroke-sm)); /// Épaisseur de la bordure. + /// Hauteur, padding, typo et diamètre du point, par taille. $sizes: ( 'default': ( - height: 28px, - padding-x: var(--units-md), - font-size: var(--size-typography-text-default), - dot: 10px, + height: var(--ui-badge-height, #{utils.rem-calc(28)}), + padding-x: var(--ui-badge-padding-x, var(--units-md)), + font-size: var(--ui-badge-font-size, var(--size-typography-text-default)), + dot: var(--ui-badge-dot-size, #{utils.rem-calc(10)}), ), 'small': ( - height: 22px, - padding-x: var(--units-md), - font-size: var(--size-typography-text-md), - dot: 8px, + height: var(--ui-badge-height-small, #{utils.rem-calc(22)}), + padding-x: var(--ui-badge-padding-x-small, var(--units-md)), + font-size: var(--ui-badge-font-size-small, var(--size-typography-text-md)), + dot: var(--ui-badge-dot-size-small, #{utils.rem-calc(8)}), ), 'large': ( - height: 32px, - padding-x: var(--units-lg), - font-size: var(--size-typography-text-lg), - dot: 12px, + height: var(--ui-badge-height-large, #{utils.rem-calc(32)}), + padding-x: var(--ui-badge-padding-x-large, var(--units-lg)), + font-size: var(--ui-badge-font-size-large, var(--size-typography-text-lg)), + dot: var(--ui-badge-dot-size-large, #{utils.rem-calc(12)}), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. @mixin badge-size($size) { - height: utils.rem-calc(map.get($size, height)); - min-width: utils.rem-calc(map.get($size, height)); + height: map.get($size, height); + min-width: map.get($size, height); padding: 0 map.get($size, padding-x); font-size: map.get($size, font-size); } @@ -50,10 +54,10 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default display: inline-flex; align-items: center; justify-content: center; - gap: var(--units-xs); + gap: $gap; box-sizing: border-box; - border: var(--stroke-sm) solid transparent; - border-radius: var(--radius-full); + border: $stroke-width solid transparent; + border-radius: $radius; font-family: var(--fontfamily-base); font-weight: var(--weight-bold); line-height: 1; @@ -84,8 +88,8 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default } #{$sel} { - width: utils.rem-calc(map.get($size, dot)); - height: utils.rem-calc(map.get($size, dot)); + width: map.get($size, dot); + height: map.get($size, dot); min-width: 0; padding: 0; } diff --git a/projects/ui-kit/informative/ui-chip/src/lib/ui-chip.scss b/projects/ui-kit/informative/ui-chip/src/lib/ui-chip.scss index 48722d1..38f6e3f 100644 --- a/projects/ui-kit/informative/ui-chip/src/lib/ui-chip.scss +++ b/projects/ui-kit/informative/ui-chip/src/lib/ui-chip.scss @@ -15,36 +15,48 @@ $levels: default, highlight, success, warning, error; /// Niveaux sémantiques générant les modifiers `._`. $sublevels: high, low; /// Intensités générant les modifiers `._`. +$radius: var(--ui-chip-radius, var(--radius-full)); /// Rayon des coins. +$radius-square: var(--ui-chip-radius-square, var(--radius-sm)); /// Rayon des coins en présentation `square`. +$stroke-width: var(--ui-chip-stroke-width, var(--stroke-sm)); /// Épaisseur de la bordure. +$padding-inline-end-removable: var(--ui-chip-padding-inline-end-removable, var(--units-2xs)); /// Inset de fin resserré quand le chip porte une action de suppression. + +$image-radius: var(--ui-chip-image-radius, var(--radius-full)); /// Rayon du visuel. +$image-radius-square: var(--ui-chip-image-radius-square, var(--radius-xs)); /// Rayon du visuel en présentation `square`. +$image-offset: var(--ui-chip-image-offset, var(--units-2xs)); /// Débord du visuel dans l'inset de début. + +$remove-padding: var(--ui-chip-remove-padding, var(--units-2xs)); /// Inset du bouton de suppression. +$remove-radius: var(--ui-chip-remove-radius, var(--radius-full)); /// Rayon des coins du bouton de suppression. + /// Hauteur, gouttières, typo et taille du visuel, par taille. $sizes: ( 'default': ( - height: 32px, - gap: var(--units-xs), - padding-x: var(--units-sm), - font-size: var(--size-typography-text-default), - visual: 24px, + height: var(--ui-chip-height, #{utils.rem-calc(32)}), + gap: var(--ui-chip-gap, var(--units-xs)), + padding-x: var(--ui-chip-padding-x, var(--units-sm)), + font-size: var(--ui-chip-font-size, var(--size-typography-text-default)), + visual: var(--ui-chip-image-size, #{utils.rem-calc(24)}), ), 'small': ( - height: 24px, - gap: var(--units-2xs), - padding-x: var(--units-sm), - font-size: var(--size-typography-text-md), - visual: 18px, + height: var(--ui-chip-height-small, #{utils.rem-calc(24)}), + gap: var(--ui-chip-gap-small, var(--units-2xs)), + padding-x: var(--ui-chip-padding-x-small, var(--units-sm)), + font-size: var(--ui-chip-font-size-small, var(--size-typography-text-md)), + visual: var(--ui-chip-image-size-small, #{utils.rem-calc(18)}), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$focus-ring-width: var(--ui-chip-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. @mixin chip-size($size) { - height: utils.rem-calc(map.get($size, height)); - min-width: utils.rem-calc(map.get($size, height)); + height: map.get($size, height); + min-width: map.get($size, height); gap: map.get($size, gap); padding: 0 map.get($size, padding-x); font-size: map.get($size, font-size); .ui-chip-image { - width: utils.rem-calc(map.get($size, visual)); - height: utils.rem-calc(map.get($size, visual)); + width: map.get($size, visual); + height: map.get($size, visual); } } @@ -55,8 +67,8 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. justify-content: center; box-sizing: border-box; max-width: 100%; - border: var(--stroke-sm) solid transparent; - border-radius: var(--radius-full); + border: $stroke-width solid transparent; + border-radius: $radius; font-family: var(--fontfamily-base); font-weight: var(--weight-regular); line-height: 1; @@ -69,8 +81,8 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. &-image { flex: none; - margin-inline-start: calc(-1 * var(--units-2xs)); - border-radius: var(--radius-full); + margin-inline-start: calc(-1 * #{$image-offset}); + border-radius: $image-radius; object-fit: cover; } @@ -89,9 +101,9 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. align-items: center; justify-content: center; margin: 0; - padding: var(--units-2xs); + padding: $remove-padding; border: 0; - border-radius: var(--radius-full); + border-radius: $remove-radius; background: transparent; color: inherit; line-height: 0; @@ -103,7 +115,7 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. // --- Layout tweaks --------------------------------- &._removable { - padding-inline-end: var(--units-2xs) !important; + padding-inline-end: $padding-inline-end-removable !important; } // --- Selectable (choice / filter chip) ------------- @@ -118,8 +130,8 @@ $focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. // --- Shape ----------------------------------------- &._square { - border-radius: var(--radius-sm); - .ui-chip-image { border-radius: var(--radius-xs); } + border-radius: $radius-square; + .ui-chip-image { border-radius: $image-radius-square; } } &._disabled { cursor: not-allowed; } diff --git a/projects/ui-kit/informative/ui-empty-state/src/lib/ui-empty-state.scss b/projects/ui-kit/informative/ui-empty-state/src/lib/ui-empty-state.scss index f9980f2..7ffc335 100644 --- a/projects/ui-kit/informative/ui-empty-state/src/lib/ui-empty-state.scss +++ b/projects/ui-kit/informative/ui-empty-state/src/lib/ui-empty-state.scss @@ -13,25 +13,25 @@ /// Gouttières, taille du média et typo, par taille. $sizes: ( 'default': ( - gap: var(--units-lg), - media-size: var(--units-4xl), - title-font: var(--size-typography-title-default), - desc-font: var(--size-typography-text-default), + gap: var(--ui-empty-state-gap, var(--units-lg)), + media-size: var(--ui-empty-state-media-size, var(--units-4xl)), + title-font: var(--ui-empty-state-title-font-size, var(--size-typography-title-default)), + desc-font: var(--ui-empty-state-desc-font-size, var(--size-typography-text-default)), ), 'small': ( - gap: var(--units-default), - media-size: var(--units-2xl), // (40px) - title-font: var(--size-typography-title-md), - desc-font: var(--size-typography-text-md), + gap: var(--ui-empty-state-gap-small, var(--units-default)), + media-size: var(--ui-empty-state-media-size-small, var(--units-2xl)), + title-font: var(--ui-empty-state-title-font-size-small, var(--size-typography-title-md)), + desc-font: var(--ui-empty-state-desc-font-size-small, var(--size-typography-text-md)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$text-gap: var(--units-xs); /// Espacement titre ↔ description. -$actions-gap: var(--units-sm); /// Espacement entre les actions. -$max-width: 40ch; /// Largeur maximale du bloc de texte. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$text-gap: var(--ui-empty-state-text-gap, var(--units-xs)); /// Espacement titre ↔ description. +$actions-gap: var(--ui-empty-state-actions-gap, var(--units-sm)); /// Espacement entre les actions. +$max-width: var(--ui-empty-state-max-width, 40ch); /// Largeur maximale du bloc de texte. @mixin empty-state-size($size) { - --ui-empty-state-media-size: #{map.get($size, media-size)}; /// Côté de l’icône raccourci (le visuel hero). + --_media-size: #{map.get($size, media-size)}; /// Côté de l’icône raccourci (le visuel hero). gap: map.get($size, gap); .ui-empty-state-title { @@ -48,7 +48,7 @@ $max-width: 40ch; /// Largeur maximale du bloc de texte .ui-empty-state { // --- Base ---------------------------------------------------------- - --ui-empty-state-media-color: var(--global-text-muted); /// Couleur de la zone visuelle (l’icône utilise `currentColor`). + --_media-color: var(--ui-empty-state-media-color, var(--global-text-muted)); /// Couleur de la zone visuelle (l’icône utilise `currentColor`). display: flex; flex-direction: column; align-items: center; @@ -66,8 +66,8 @@ $max-width: 40ch; /// Largeur maximale du bloc de texte // `color` cascades into the icon (ui-icon uses `currentColor`); the icon size // is driven through ui-icon's `--ui-icon-size` hook (a custom property, so it // inherits across the component boundary — no ::ng-deep needed to reach it). - color: var(--ui-empty-state-media-color); - --ui-icon-size: var(--ui-empty-state-media-size); + color: var(--_media-color); + --ui-icon-size: var(--_media-size); } &-text { diff --git a/projects/ui-kit/informative/ui-helper/src/lib/ui-helper.scss b/projects/ui-kit/informative/ui-helper/src/lib/ui-helper.scss index f1f8ab5..f2ce946 100644 --- a/projects/ui-kit/informative/ui-helper/src/lib/ui-helper.scss +++ b/projects/ui-kit/informative/ui-helper/src/lib/ui-helper.scss @@ -20,17 +20,19 @@ $levels: ( 'error': var(--informative-errorlow-content-default), ); +$gap: var(--ui-helper-gap, var(--units-xs)); /// Espacement icône ↔ message. + /// Taille de texte, par taille. $sizes: ( - 'default': var(--size-typography-text-default), - 'small': var(--size-typography-text-md), + 'default': var(--ui-helper-font-size, var(--size-typography-text-default)), + 'small': var(--ui-helper-font-size-small, var(--size-typography-text-md)), ); .ui-helper { // --- Base ---------------------------------------------------------- display: inline-flex; align-items: center; - gap: var(--units-xs); + gap: $gap; font-family: var(--fontfamily-base); font-weight: var(--weight-regular); line-height: 1.3; diff --git a/projects/ui-kit/informative/ui-progress-bar/src/lib/ui-progress-bar.scss b/projects/ui-kit/informative/ui-progress-bar/src/lib/ui-progress-bar.scss index 1a9fbab..55dc649 100644 --- a/projects/ui-kit/informative/ui-progress-bar/src/lib/ui-progress-bar.scss +++ b/projects/ui-kit/informative/ui-progress-bar/src/lib/ui-progress-bar.scss @@ -13,18 +13,20 @@ /// Épaisseur et rayon de la piste, par taille. $sizes: ( 'default': ( - track-height: 8px, - radius: var(--radius-xs), + track-height: var(--ui-progress-bar-track-height, #{utils.rem-calc(8)}), + radius: var(--ui-progress-bar-track-radius, var(--radius-xs)), ), 'small': ( - track-height: 4px, - radius: var(--radius-2xs), + track-height: var(--ui-progress-bar-track-height-small, #{utils.rem-calc(4)}), + radius: var(--ui-progress-bar-track-radius-small, var(--radius-2xs)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$fill-color: var(--ui-progress-bar-color, var(--informative-highlighthigh-surface-default)); /// Couleur de la barre de progression. + // Component-specific (local): one indeterminate cycle duration. -$indeterminate-duration: 1.6s; /// Durée d'un cycle en mode indéterminé. +$indeterminate-duration: var(--ui-progress-bar-indeterminate-duration, 1.6s); /// Durée d'un cycle en mode indéterminé. // --- Host: block, fills its container -------------------------------- :host { @@ -44,7 +46,7 @@ $indeterminate-duration: 1.6s; /// Durée d'un cycle en mode indéterminé. display: flex; flex: 1 1 auto; overflow: hidden; - height: utils.rem-calc(map.get($size-default, track-height)); + height: map.get($size-default, track-height); border-radius: map.get($size-default, radius); background-color: var(--global-border-default); } @@ -53,7 +55,7 @@ $indeterminate-duration: 1.6s; /// Durée d'un cycle en mode indéterminé. &-value { height: 100%; border-radius: inherit; - background-color: var(--ui-progress-bar-color, var(--informative-highlighthigh-surface-default)); + background-color: $fill-color; @include utils.motion-transition(width); } @@ -65,7 +67,7 @@ $indeterminate-duration: 1.6s; /// Durée d'un cycle en mode indéterminé. background-color: var(--global-border-default); &._active { - background-color: var(--ui-progress-bar-color, var(--informative-highlighthigh-surface-default)); + background-color: $fill-color; } } @@ -124,7 +126,7 @@ $indeterminate-duration: 1.6s; /// Durée d'un cycle en mode indéterminé. @each $name, $size in $sizes { @if $name != 'default' { &._#{$name} .ui-progress-bar-track { - height: utils.rem-calc(map.get($size, track-height)); + height: map.get($size, track-height); border-radius: map.get($size, radius); } } diff --git a/projects/ui-kit/informative/ui-read-only/src/lib/ui-read-only.scss b/projects/ui-kit/informative/ui-read-only/src/lib/ui-read-only.scss index 68ed7ad..c2e3694 100644 --- a/projects/ui-kit/informative/ui-read-only/src/lib/ui-read-only.scss +++ b/projects/ui-kit/informative/ui-read-only/src/lib/ui-read-only.scss @@ -19,13 +19,13 @@ // --- Config --------------------------------------- /// Taille de texte, par taille. $sizes: ( - 'default': var(--size-typography-text-default), - 'small': var(--size-typography-text-md), + 'default': var(--ui-read-only-font-size, var(--size-typography-text-default)), + 'small': var(--ui-read-only-font-size-small, var(--size-typography-text-md)), ); -$label-gap: utils.$form-field-gap; /// Écart label→valeur en mode `matchField` (sinon compact). -$field-height: utils.$form-field-height; /// Hauteur de la zone de valeur en `matchField` (taille `default`). -$field-height-small: utils.$form-field-height-small; /// Idem pour la taille `small`. +$label-gap: var(--ui-read-only-label-gap, #{utils.$form-field-gap}); /// Écart label→valeur en mode `matchField` (sinon compact). +$field-height: var(--ui-read-only-field-height, #{utils.$form-field-height}); /// Hauteur de la zone de valeur en `matchField` (taille `default`). +$field-height-small: var(--ui-read-only-field-height-small, #{utils.$form-field-height-small}); /// Idem pour la taille `small`. .ui-read-only { margin: 0; // reset native
margin diff --git a/projects/ui-kit/informative/ui-separator/src/lib/ui-separator.scss b/projects/ui-kit/informative/ui-separator/src/lib/ui-separator.scss index 8cec949..e8341fd 100644 --- a/projects/ui-kit/informative/ui-separator/src/lib/ui-separator.scss +++ b/projects/ui-kit/informative/ui-separator/src/lib/ui-separator.scss @@ -4,19 +4,27 @@ // ui-separator : co-located styles. All values come from design tokens. // ===================================================================== +// --- Config ----------------------------------------------------------- +$gap: var(--ui-separator-gap, var(--units-sm)); /// Espacement filet ↔ libellé. +$stroke-width: var(--ui-separator-stroke-width, var(--stroke-default)); /// Épaisseur du filet. +$stroke-width-small: var(--ui-separator-stroke-width-small, var(--stroke-sm)); /// Épaisseur du filet en taille `small`. + +$font-size: var(--ui-separator-font-size, var(--size-typography-text-default)); /// Taille du libellé. +$font-size-small: var(--ui-separator-font-size-small, var(--size-typography-text-md)); /// Taille du libellé en taille `small`. + .ui-separator { // --- Base ---------------------------------------------------------- - --_thickness: var(--stroke-default); + --_thickness: #{$stroke-width}; --_style: solid; display: flex; align-items: center; - gap: var(--units-sm); + gap: $gap; box-sizing: border-box; // --- Variants (line style / thickness) ------------- &._dashed { --_style: dashed; } - &._small { --_thickness: var(--stroke-sm); } + &._small { --_thickness: #{$stroke-width-small}; } // --- Sub-elements -------------------------------------------------- &-line { @@ -28,12 +36,12 @@ flex: 0 0 auto; color: var(--global-text-default); font-family: var(--fontfamily-base); - font-size: var(--size-typography-text-default); + font-size: $font-size; line-height: 1; white-space: nowrap; } - &._small &-label { font-size: var(--size-typography-text-md); } + &._small &-label { font-size: $font-size-small; } // --- Horizontal (default) -------------------------- // Fills the available width; the rule is a top border. diff --git a/projects/ui-kit/informative/ui-skeleton/src/lib/ui-skeleton.scss b/projects/ui-kit/informative/ui-skeleton/src/lib/ui-skeleton.scss index 5c76e80..fe734c7 100644 --- a/projects/ui-kit/informative/ui-skeleton/src/lib/ui-skeleton.scss +++ b/projects/ui-kit/informative/ui-skeleton/src/lib/ui-skeleton.scss @@ -14,19 +14,19 @@ /// Dimensions et rayon de base, par forme. $shapes: ( 'text': ( - width: 224px, - height: 20px, - radius: var(--radius-xl), + width: var(--ui-skeleton-text-width, #{utils.rem-calc(224)}), + height: var(--ui-skeleton-text-height, #{utils.rem-calc(20)}), + radius: var(--ui-skeleton-text-radius, var(--radius-xl)), ), 'circle': ( - width: 100px, - height: 100px, - radius: var(--radius-full), + width: var(--ui-skeleton-circle-width, #{utils.rem-calc(100)}), + height: var(--ui-skeleton-circle-height, #{utils.rem-calc(100)}), + radius: var(--ui-skeleton-circle-radius, var(--radius-full)), ), 'rectangle': ( - width: 100px, - height: 100px, - radius: var(--radius-default), + width: var(--ui-skeleton-rectangle-width, #{utils.rem-calc(100)}), + height: var(--ui-skeleton-rectangle-height, #{utils.rem-calc(100)}), + radius: var(--ui-skeleton-rectangle-radius, var(--radius-default)), ), ); @@ -34,18 +34,22 @@ $shapes: ( /// Surcharges de la variante `small`, par forme. $small: ( 'text': ( - height: 12px, + height: var(--ui-skeleton-text-height-small, #{utils.rem-calc(12)}), ), 'circle': ( - width: 64px, - height: 64px, + width: var(--ui-skeleton-circle-width-small, #{utils.rem-calc(64)}), + height: var(--ui-skeleton-circle-height-small, #{utils.rem-calc(64)}), ), 'rectangle': ( - width: 64px, - height: 64px, + width: var(--ui-skeleton-rectangle-width-small, #{utils.rem-calc(64)}), + height: var(--ui-skeleton-rectangle-height-small, #{utils.rem-calc(64)}), ), ); +// The mode-dependent default travels through a private variable, so the public +// hook keeps winning in dark mode instead of being shadowed by the override. +$shine: var(--ui-skeleton-shine, var(--_shine, rgba(255, 255, 255, 0.6))); /// Teinte du reflet glissant de l’animation `wave`. + :host { display: contents; } @@ -66,7 +70,7 @@ $small: ( background: linear-gradient( 90deg, transparent 0%, - var(--ui-skeleton-shine, rgba(255, 255, 255, 0.6)) 50%, /// Teinte du reflet glissant de l’animation `wave`. + $shine 50%, transparent 100% ); will-change: transform; @@ -76,8 +80,8 @@ $small: ( // --- Shapes (base = `default` size) -------------------------------- @each $name, $s in $shapes { &._#{$name} { - width: utils.rem-calc(map.get($s, width)); - height: utils.rem-calc(map.get($s, height)); + width: map.get($s, width); + height: map.get($s, height); border-radius: map.get($s, radius); } } @@ -91,9 +95,9 @@ $small: ( @each $name, $s in $small { &._#{$name}._small { @if map.has-key($s, width) { - width: utils.rem-calc(map.get($s, width)); + width: map.get($s, width); } - height: utils.rem-calc(map.get($s, height)); + height: map.get($s, height); } } @@ -114,7 +118,7 @@ $small: ( :host-context([data-theme='dark']), :host-context(.dark) { - --ui-skeleton-shine: rgba(255, 255, 255, 0.09); + --_shine: rgba(255, 255, 255, 0.09); } // --- Keyframes ------------------------------------------------------- diff --git a/projects/ui-kit/informative/ui-spinner/src/lib/ui-spinner.scss b/projects/ui-kit/informative/ui-spinner/src/lib/ui-spinner.scss index 851f6f5..0b1c61f 100644 --- a/projects/ui-kit/informative/ui-spinner/src/lib/ui-spinner.scss +++ b/projects/ui-kit/informative/ui-spinner/src/lib/ui-spinner.scss @@ -10,18 +10,18 @@ /// Diamètre et typo du libellé, par taille. $sizes: ( 'default': ( - box: 32px, - font-size: var(--size-typography-text-md), + box: var(--ui-spinner-size, #{utils.rem-calc(32)}), + font-size: var(--ui-spinner-font-size, var(--size-typography-text-md)), ), 'small': ( - box: 20px, - font-size: var(--size-typography-text-sm), + box: var(--ui-spinner-size-small, #{utils.rem-calc(20)}), + font-size: var(--ui-spinner-font-size-small, var(--size-typography-text-sm)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. @function box($size) { - @return var(--ui-spinner-size, #{utils.rem-calc(map.get($size, box))}); /// Taille brute de la boîte du marqueur. + @return map.get($size, box); } .ui-spinner { diff --git a/projects/ui-kit/informative/ui-tag/src/lib/ui-tag.scss b/projects/ui-kit/informative/ui-tag/src/lib/ui-tag.scss index 31a155e..c3b3f69 100644 --- a/projects/ui-kit/informative/ui-tag/src/lib/ui-tag.scss +++ b/projects/ui-kit/informative/ui-tag/src/lib/ui-tag.scss @@ -15,17 +15,22 @@ $levels: default, highlight, success, warning, error; /// Niveaux sémantiques générant les modifiers `._`. $sublevels: high, low; /// Intensités générant les modifiers `._`. +$padding-x: var(--ui-tag-padding-x, var(--units-sm)); /// Inset horizontal. +$radius: var(--ui-tag-radius, var(--radius-full)); /// Rayon des coins. +$radius-square: var(--ui-tag-radius-square, var(--radius-sm)); /// Rayon des coins en présentation `square`. +$stroke-width: var(--ui-tag-stroke-width, var(--stroke-sm)); /// Épaisseur de la bordure. + /// Hauteur, gouttière et typo, par taille. $sizes: ( 'default': ( - height: var(--size-components-2xs), - gap: var(--units-sm), - font-size: var(--size-typography-text-default), + height: var(--ui-tag-height, var(--size-components-2xs)), + gap: var(--ui-tag-gap, var(--units-sm)), + font-size: var(--ui-tag-font-size, var(--size-typography-text-default)), ), 'small': ( - height: 22px, - gap: var(--units-xs), - font-size: var(--size-typography-text-md), + height: var(--ui-tag-height-small, 22px), + gap: var(--ui-tag-gap-small, var(--units-xs)), + font-size: var(--ui-tag-font-size-small, var(--size-typography-text-md)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. @@ -43,9 +48,9 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default align-items: center; justify-content: center; box-sizing: border-box; - padding: 0 var(--units-sm); - border: var(--stroke-sm) solid transparent; - border-radius: var(--radius-full); + padding: 0 $padding-x; + border: $stroke-width solid transparent; + border-radius: $radius; font-family: var(--fontfamily-base); font-weight: var(--weight-regular); line-height: 1; @@ -58,7 +63,7 @@ $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default &-label { display: inline-block; } // --- Shape ----------------------------------------- - &._square { border-radius: var(--radius-sm); } + &._square { border-radius: $radius-square; } // --- Sizes ------------------------------------- @each $name, $size in $sizes { diff --git a/projects/ui-kit/informative/ui-toast/src/lib/ui-toast-container.scss b/projects/ui-kit/informative/ui-toast/src/lib/ui-toast-container.scss index f8a1d3c..8e1e168 100644 --- a/projects/ui-kit/informative/ui-toast/src/lib/ui-toast-container.scss +++ b/projects/ui-kit/informative/ui-toast/src/lib/ui-toast-container.scss @@ -8,11 +8,11 @@ // ===================================================================== // --- Config (local) --------------------------------------------------- -$toast-z-index: 1100; /// Plan d'empilement, au-dessus des overlays CDK. -$toast-width: utils.rem-calc(360); /// Largeur d'un toast empilé. -$toast-width-expanded: utils.rem-calc(480); /// Largeur en mode bannière. -$toast-edge-gap: var(--units-lg); /// Marge par rapport aux bords du viewport. -$toast-stack-gap: var(--units-sm); /// Espacement entre les toasts empilés. +$toast-z-index: var(--ui-toast-z-index, 1100); /// Plan d'empilement, au-dessus des overlays CDK. +$toast-width: var(--ui-toast-width, #{utils.rem-calc(360)}); /// Largeur d'un toast empilé. +$toast-width-expanded: var(--ui-toast-width-expanded, #{utils.rem-calc(480)}); /// Largeur en mode bannière. +$toast-edge-gap: var(--ui-toast-edge-gap, var(--units-lg)); /// Marge par rapport aux bords du viewport. +$toast-stack-gap: var(--ui-toast-stack-gap, var(--units-sm)); /// Espacement entre les toasts empilés. .ui-toast-region { position: fixed; diff --git a/projects/ui-kit/informative/ui-toast/src/lib/ui-toast.scss b/projects/ui-kit/informative/ui-toast/src/lib/ui-toast.scss index cf21362..c0c0fe6 100644 --- a/projects/ui-kit/informative/ui-toast/src/lib/ui-toast.scss +++ b/projects/ui-kit/informative/ui-toast/src/lib/ui-toast.scss @@ -16,7 +16,7 @@ $levels: default, highlight, success, warning, error; /// Niveaux sémantiques g $sublevels: high, low; /// Intensités générant les modifiers `._`. // Reused structural constant (kit-wide focus ring). Local alias, never copied. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. +$focus-ring-width: var(--ui-toast-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. .ui-toast { // --- Base ---------------------------------------------------------- diff --git a/projects/ui-kit/informative/ui-tooltip/src/lib/ui-tooltip.scss b/projects/ui-kit/informative/ui-tooltip/src/lib/ui-tooltip.scss index 71af8a3..6a4f896 100644 --- a/projects/ui-kit/informative/ui-tooltip/src/lib/ui-tooltip.scss +++ b/projects/ui-kit/informative/ui-tooltip/src/lib/ui-tooltip.scss @@ -8,25 +8,30 @@ // --- Config (local, component-scoped) --------------------------------- // Max content width before wrapping. -$tooltip-max-width: utils.rem-calc(256); /// Largeur max avant retour à la ligne. +$tooltip-max-width: var(--ui-tooltip-max-width, #{utils.rem-calc(256)}); /// Largeur max avant retour à la ligne. // Arrow diameter — kept in sync with `ARROW_GAP` in ui-tooltip.ts. -$tooltip-arrow-size: utils.rem-calc(8); /// Taille de la flèche, tenue en phase avec la constante `ARROW_GAP` du `.ts`. -$tooltip-arrow-half: utils.rem-calc(4); /// Demi-largeur de la flèche, pour son positionnement. +$tooltip-arrow-size: var(--ui-tooltip-arrow-size, #{utils.rem-calc(8)}); /// Taille de la flèche, tenue en phase avec la constante `ARROW_GAP` du `.ts`. +$tooltip-arrow-half: calc(#{$tooltip-arrow-size} / 2); /// Demi-largeur de la flèche, dérivée de sa taille. +$tooltip-padding-y: var(--ui-tooltip-padding-y, var(--units-sm)); /// Inset vertical de la bulle. +$tooltip-padding-x: var(--ui-tooltip-padding-x, var(--units-default)); /// Inset horizontal de la bulle. +$tooltip-radius: var(--ui-tooltip-radius, var(--radius-default)); /// Rayon des coins de la bulle. +$tooltip-stroke-width: var(--ui-tooltip-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure de la bulle. +$tooltip-font-size: var(--ui-tooltip-font-size, var(--size-typography-text-md)); /// Taille de police du contenu. .ui-tooltip { position: relative; display: inline-block; box-sizing: border-box; max-width: $tooltip-max-width; - padding: var(--units-sm) var(--units-default); - border: var(--stroke-sm) solid var(--informative-defaulthigh-stroke-default); - border-radius: var(--radius-default); + padding: $tooltip-padding-y $tooltip-padding-x; + border: $tooltip-stroke-width solid var(--informative-defaulthigh-stroke-default); + border-radius: $tooltip-radius; background-color: var(--informative-defaulthigh-surface-default); color: var(--informative-defaulthigh-content-default); box-shadow: var(--shadow-default-md); font-family: var(--fontfamily-base); font-weight: var(--weight-regular); - font-size: var(--size-typography-text-md); + font-size: $tooltip-font-size; line-height: 1.4; overflow-wrap: break-word; @@ -71,39 +76,39 @@ $tooltip-arrow-half: utils.rem-calc(4); /// Demi-largeur de la flèche, pour so width: $tooltip-arrow-size; height: $tooltip-arrow-size; background-color: var(--informative-defaulthigh-surface-default); - border: var(--stroke-sm) solid var(--informative-defaulthigh-stroke-default); + border: $tooltip-stroke-width solid var(--informative-defaulthigh-stroke-default); rotate: 45deg; } // Panel above trigger : arrow on the bottom edge, pointing down. &._top &-arrow { - bottom: -$tooltip-arrow-half; + bottom: calc(-1 * #{$tooltip-arrow-half}); left: 50%; - margin-left: -$tooltip-arrow-half; + margin-left: calc(-1 * #{$tooltip-arrow-half}); border-top: 0; border-left: 0; } // Panel below : arrow on top, pointing up. &._bottom &-arrow { - top: -$tooltip-arrow-half; + top: calc(-1 * #{$tooltip-arrow-half}); left: 50%; - margin-left: -$tooltip-arrow-half; + margin-left: calc(-1 * #{$tooltip-arrow-half}); border-right: 0; border-bottom: 0; } // Panel on the left : arrow on the right edge, pointing right. &._left &-arrow { - right: -$tooltip-arrow-half; + right: calc(-1 * #{$tooltip-arrow-half}); top: 50%; - margin-top: -$tooltip-arrow-half; + margin-top: calc(-1 * #{$tooltip-arrow-half}); border-bottom: 0; border-left: 0; } // Panel on the right : arrow on the left edge, pointing left. &._right &-arrow { - left: -$tooltip-arrow-half; + left: calc(-1 * #{$tooltip-arrow-half}); top: 50%; - margin-top: -$tooltip-arrow-half; + margin-top: calc(-1 * #{$tooltip-arrow-half}); border-top: 0; border-right: 0; } diff --git a/projects/ui-kit/layout/ui-card/src/lib/ui-card.scss b/projects/ui-kit/layout/ui-card/src/lib/ui-card.scss index 60658af..e871259 100644 --- a/projects/ui-kit/layout/ui-card/src/lib/ui-card.scss +++ b/projects/ui-kit/layout/ui-card/src/lib/ui-card.scss @@ -12,12 +12,12 @@ // --- Config (extension points) --------------------------------------- // Local structural constants — retune here to detune ONLY this component. -$card-padding: var(--units-lg); /// Inset du corps. -$card-gap: var(--units-md); /// Espacement entre en-tête / contenu / pied. -$card-header-gap: var(--units-2xs); /// Espacement titre ↔ sous-titre. -$card-footer-gap: var(--units-sm); /// Espacement entre éléments du pied. -$card-radius: var(--radius-md); /// Rayon des coins. -$card-stroke-width: var(--stroke-sm); /// Épaisseur de bordure. +$card-padding: var(--ui-card-padding, var(--units-lg)); /// Inset du corps. +$card-gap: var(--ui-card-gap, var(--units-md)); /// Espacement entre en-tête / contenu / pied. +$card-header-gap: var(--ui-card-header-gap, var(--units-2xs)); /// Espacement titre ↔ sous-titre. +$card-footer-gap: var(--ui-card-footer-gap, var(--units-sm)); /// Espacement entre éléments du pied. +$card-radius: var(--ui-card-radius, var(--radius-md)); /// Rayon des coins. +$card-stroke-width: var(--ui-card-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure. // Block host so consumer width/height (and layout) apply to the card box. :host { @@ -26,9 +26,10 @@ $card-stroke-width: var(--stroke-sm); /// Épaisseur de bordure. .ui-card { // --- Base ---------------------------------------------------------- - // Gutter exposed as a custom property so consumers can re-apply it inside a - // flush content region (e.g. `padding-inline: var(--ui-card-padding)`). - --ui-card-padding: #{$card-padding}; /// Gouttière exposée pour regouttiérer un contenu `contentFlush`. + // Read-only mirror of the gutter: a consumer re-applies it inside a flush + // content region. Distinct from the `--ui-card-padding` override it resolves, + // since a custom property cannot reference itself. + --ui-card-gutter: #{$card-padding}; /// Gouttière relisible pour regouttiérer un contenu `contentFlush` (surcharge : `--ui-card-padding`). display: flex; flex-direction: column; diff --git a/projects/ui-kit/layout/ui-card/src/lib/ui-card.ts b/projects/ui-kit/layout/ui-card/src/lib/ui-card.ts index 5e5de43..9571a80 100644 --- a/projects/ui-kit/layout/ui-card/src/lib/ui-card.ts +++ b/projects/ui-kit/layout/ui-card/src/lib/ui-card.ts @@ -70,7 +70,7 @@ export class UiCard { * Removes the horizontal gutter of the default content region so it bleeds to * the card edges — lets the consumer own the inner layout (e.g. a full-bleed * media/color band + a padded paragraph). Re-apply the gutter on inner blocks - * with `padding-inline: var(--ui-card-padding)`. + * with `padding-inline: var(--ui-card-gutter)`. */ contentFlush = input(false, { transform: booleanAttribute }); /** Accessible name for the card region (use when the card conveys a landmark). */ diff --git a/projects/ui-kit/layout/ui-card/ui-card.mdx b/projects/ui-kit/layout/ui-card/ui-card.mdx index e2b7b7e..a3cb8ba 100644 --- a/projects/ui-kit/layout/ui-card/ui-card.mdx +++ b/projects/ui-kit/layout/ui-card/ui-card.mdx @@ -98,13 +98,13 @@ importer dans le composant hôte. Par défaut le contenu porte la gouttière de la carte. `contentFlush` la **retire** pour laisser le consommateur maîtriser sa mise en page : bande média / couleur edge-to-edge, tableau pleine largeur… La gouttière reste disponible via la variable -CSS `--ui-card-padding` (à ré-appliquer sur les blocs qui doivent rester en retrait). +CSS `--ui-card-gutter` (à ré-appliquer sur les blocs qui doivent rester en retrait). ```html
-

Paragraphe regouttiéré…

+

Paragraphe regouttiéré…

``` diff --git a/projects/ui-kit/layout/ui-card/ui-card.stories.ts b/projects/ui-kit/layout/ui-card/ui-card.stories.ts index 09d010b..6f72eb5 100644 --- a/projects/ui-kit/layout/ui-card/ui-card.stories.ts +++ b/projects/ui-kit/layout/ui-card/ui-card.stories.ts @@ -51,7 +51,7 @@ const meta: Meta = { contentFlush: { control: { type: 'boolean' }, description: - 'Retire la gouttière horizontale du contenu (pleine largeur) : le consommateur gère la mise en page interne. Regouttiérer avec `padding-inline: var(--ui-card-padding)`.', + 'Retire la gouttière horizontale du contenu (pleine largeur) : le consommateur gère la mise en page interne. Regouttiérer avec `padding-inline: var(--ui-card-gutter)`.', table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } }, }, ariaLabel: { @@ -179,7 +179,7 @@ export const RichTitle: Story = { // Contenu « full » : le slot par défaut est mis en pleine largeur via `contentFlush`, // le consommateur gère alors ses propres gouttières. Ici : une bande de couleur -// edge-to-edge suivie d'un paragraphe regouttiéré via `--ui-card-padding`. +// edge-to-edge suivie d'un paragraphe regouttiéré via `--ui-card-gutter`. export const ContentFlush: Story = { render: (args) => ({ props: { ...args, lorem: LOREM }, @@ -187,7 +187,7 @@ export const ContentFlush: Story = {
-

{{ lorem }}

+

{{ lorem }}

`, }), diff --git a/projects/ui-kit/layout/ui-drawer/src/lib/ui-drawer.scss b/projects/ui-kit/layout/ui-drawer/src/lib/ui-drawer.scss index 0433066..5723c70 100644 --- a/projects/ui-kit/layout/ui-drawer/src/lib/ui-drawer.scss +++ b/projects/ui-kit/layout/ui-drawer/src/lib/ui-drawer.scss @@ -13,21 +13,21 @@ // ===================================================================== // --- Config (local) --------------------------------------------------- -$drawer-z-index: 1100; /// Palier d'empilement de base (au-dessus des overlays CDK à 1000) -$stroke-width: var(--stroke-sm); /// Épaisseur de bordure du panneau. -$panel-padding: var(--units-lg); /// Inset du panneau. -$header-gap: var(--units-sm); /// Espacement entre les éléments de l'en-tête. -$action-size: 32px; /// Taille de la cible tactile du bouton de fermeture. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus du bouton de fermeture -$mask-background: var(--primitives-black-50); /// Assombrissement du masque — exposé via `--ui-drawer-mask-background`. -$size-side: utils.rem-calc(320); /// Largeur d'un tiroir gauche / droite. -$size-edge: utils.rem-calc(160); /// Hauteur d'un tiroir haut / bas. +$drawer-z-index: var(--ui-drawer-z-index, 1100); /// Palier d'empilement de base (au-dessus des overlays CDK à 1000) +$stroke-width: var(--ui-drawer-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure du panneau. +$panel-padding: var(--ui-drawer-panel-padding, var(--units-lg)); /// Inset du panneau. +$header-gap: var(--ui-drawer-header-gap, var(--units-sm)); /// Espacement entre les éléments de l'en-tête. +$action-size: var(--ui-drawer-action-size, #{utils.rem-calc(32)}); /// Taille de la cible tactile du bouton de fermeture. +$focus-ring-width: var(--ui-drawer-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus du bouton de fermeture +$mask-background: var(--ui-drawer-mask-background, var(--primitives-black-50)); /// Assombrissement du masque. +$size-side: var(--ui-drawer-size-side, #{utils.rem-calc(320)}); /// Largeur d'un tiroir gauche / droite. +$size-edge: var(--ui-drawer-size-edge, #{utils.rem-calc(160)}); /// Hauteur d'un tiroir haut / bas. .ui-drawer-scrim { position: fixed; inset: 0; z-index: $drawer-z-index; - background-color: var(--ui-drawer-mask-background, #{$mask-background}); + background-color: $mask-background; &._contained { position: absolute; @@ -137,8 +137,8 @@ $size-edge: utils.rem-calc(160); /// Hauteur d'un tiroir haut / bas display: inline-flex; align-items: center; justify-content: center; - width: utils.rem-calc($action-size); - height: utils.rem-calc($action-size); + width: $action-size; + height: $action-size; padding: 0; border: none; border-radius: var(--radius-xs); diff --git a/projects/ui-kit/layout/ui-modal/src/lib/ui-modal.scss b/projects/ui-kit/layout/ui-modal/src/lib/ui-modal.scss index 57a9e4c..3a56180 100644 --- a/projects/ui-kit/layout/ui-modal/src/lib/ui-modal.scss +++ b/projects/ui-kit/layout/ui-modal/src/lib/ui-modal.scss @@ -8,23 +8,23 @@ // ===================================================================== // --- Config (local) --------------------------------------------------- -$modal-z-index: 1100; /// Palier d'empilement de base (au-dessus des overlays CDK à 1000) -$mask-padding: var(--units-lg); /// Inset entre le masque et le dialogue. -$dialog-radius: var(--radius-md); /// Rayon du dialogue. -$dialog-padding: var(--units-lg); /// Inset du dialogue. -$header-gap: var(--units-sm); /// Espacement entre les éléments de l'en-tête. -$stroke-width: var(--stroke-sm); /// Épaisseur de bordure du dialogue. -$action-size: 32px; /// Taille de la cible tactile du bouton de fermeture. -$resize-handle-size: 16px; /// Taille de la poignée de redimensionnement. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus des boutons d'en-tête -$mask-background: var(--primitives-black-50); /// Assombrissement du masque — exposé via `--ui-modal-mask-background`. +$modal-z-index: var(--ui-modal-z-index, 1100); /// Palier d'empilement de base (au-dessus des overlays CDK à 1000) +$mask-padding: var(--ui-modal-mask-padding, var(--units-lg)); /// Inset entre le masque et le dialogue. +$dialog-radius: var(--ui-modal-dialog-radius, var(--radius-md)); /// Rayon du dialogue. +$dialog-padding: var(--ui-modal-dialog-padding, var(--units-lg)); /// Inset du dialogue. +$header-gap: var(--ui-modal-header-gap, var(--units-sm)); /// Espacement entre les éléments de l'en-tête. +$stroke-width: var(--ui-modal-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure du dialogue. +$action-size: var(--ui-modal-action-size, #{utils.rem-calc(32)}); /// Taille de la cible tactile du bouton de fermeture. +$resize-handle-size: var(--ui-modal-resize-handle-size, 16px); /// Taille de la poignée de redimensionnement. +$focus-ring-width: var(--ui-modal-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus des boutons d'en-tête +$mask-background: var(--ui-modal-mask-background, var(--primitives-black-50)); /// Assombrissement du masque. .ui-modal-scrim { position: fixed; inset: 0; z-index: $modal-z-index; pointer-events: none; - background-color: var(--ui-modal-mask-background, #{$mask-background}); + background-color: $mask-background; &._contained { position: absolute; @@ -126,8 +126,8 @@ $mask-background: var(--primitives-black-50); /// Assombrissement du masque — display: inline-flex; align-items: center; justify-content: center; - width: utils.rem-calc($action-size); - height: utils.rem-calc($action-size); + width: $action-size; + height: $action-size; padding: 0; border: none; border-radius: var(--radius-xs); diff --git a/projects/ui-kit/layout/ui-popover/src/lib/ui-popover.scss b/projects/ui-kit/layout/ui-popover/src/lib/ui-popover.scss index 5e75714..8d9392b 100644 --- a/projects/ui-kit/layout/ui-popover/src/lib/ui-popover.scss +++ b/projects/ui-kit/layout/ui-popover/src/lib/ui-popover.scss @@ -5,10 +5,10 @@ // ===================================================================== // --- Config (local, component-scoped) --------------------------------- -$popover-arrow-size: utils.rem-calc(8); /// Diamètre de la flèche — synchronisé avec `ARROW_GAP` dans le `.ts` -$popover-arrow-half: utils.rem-calc(4); /// Demi-largeur de la flèche, pour son positionnement. -$popover-padding: var(--units-md); /// Espacement interne du contenu projeté -$popover-max-width: utils.rem-calc(320); /// Largeur maximale avant retour à la ligne (le CDK garde le panneau dans le viewport) +$popover-arrow-size: var(--ui-popover-arrow-size, #{utils.rem-calc(8)}); /// Diamètre de la flèche — synchronisé avec `ARROW_GAP` dans le `.ts` +$popover-arrow-half: calc(#{$popover-arrow-size} / 2); /// Demi-largeur de la flèche, dérivée de sa taille. +$popover-padding: var(--ui-popover-padding, var(--units-md)); /// Espacement interne du contenu projeté +$popover-max-width: var(--ui-popover-max-width, #{utils.rem-calc(320)}); /// Largeur maximale avant retour à la ligne (le CDK garde le panneau dans le viewport) $popover-surface: var(--global-background-default); /// Fond du panneau. $popover-stroke: var(--global-border-default); /// Couleur de bordure du panneau. @@ -52,33 +52,33 @@ $popover-stroke: var(--global-border-default); /// Couleur de bordure du pa // Panel above the trigger : arrow on the bottom edge, pointing down. &._top &-arrow { - bottom: -$popover-arrow-half; + bottom: calc(-1 * #{$popover-arrow-half}); left: 50%; - margin-left: -$popover-arrow-half; + margin-left: calc(-1 * #{$popover-arrow-half}); border-top: 0; border-left: 0; } // Panel below : arrow on top, pointing up. &._bottom &-arrow { - top: -$popover-arrow-half; + top: calc(-1 * #{$popover-arrow-half}); left: 50%; - margin-left: -$popover-arrow-half; + margin-left: calc(-1 * #{$popover-arrow-half}); border-right: 0; border-bottom: 0; } // Panel on the left of the trigger : arrow on the right edge, pointing right. &._left &-arrow { - right: -$popover-arrow-half; + right: calc(-1 * #{$popover-arrow-half}); top: 50%; - margin-top: -$popover-arrow-half; + margin-top: calc(-1 * #{$popover-arrow-half}); border-bottom: 0; border-left: 0; } // Panel on the right : arrow on the left edge, pointing left. &._right &-arrow { - left: -$popover-arrow-half; + left: calc(-1 * #{$popover-arrow-half}); top: 50%; - margin-top: -$popover-arrow-half; + margin-top: calc(-1 * #{$popover-arrow-half}); border-top: 0; border-right: 0; } diff --git a/projects/ui-kit/navigation/ui-breadcrumb/src/lib/ui-breadcrumb.scss b/projects/ui-kit/navigation/ui-breadcrumb/src/lib/ui-breadcrumb.scss index d84bc5e..f3f5384 100644 --- a/projects/ui-kit/navigation/ui-breadcrumb/src/lib/ui-breadcrumb.scss +++ b/projects/ui-kit/navigation/ui-breadcrumb/src/lib/ui-breadcrumb.scss @@ -12,19 +12,19 @@ /// Dimensions par densité (héritées par les éléments : liens, texte et séparateur) $sizes: ( 'default': ( - font-size: var(--size-typography-text-default), + font-size: var(--ui-breadcrumb-font-size, var(--size-typography-text-default)), ), 'small': ( - font-size: var(--size-typography-text-md), + font-size: var(--ui-breadcrumb-font-size-small, var(--size-typography-text-md)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$item-gap: var(--spacing-default); /// Espacement entre éléments et séparateurs -$content-gap: var(--units-sm); /// Espacement entre l'icône et le libellé d'un élément -$weight: var(--weight-bold); /// Graisse des éléments (liens et page courante) -$line-height: 1.4; /// Interlignage des éléments -$radius: var(--radius-2xs); /// Rayon des éléments interactifs (anneau de focus) -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus des éléments interactifs +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$item-gap: var(--ui-breadcrumb-item-gap, var(--spacing-default)); /// Espacement entre éléments et séparateurs +$content-gap: var(--ui-breadcrumb-content-gap, var(--units-sm)); /// Espacement entre l'icône et le libellé d'un élément +$weight: var(--ui-breadcrumb-weight, var(--weight-bold)); /// Graisse des éléments (liens et page courante) +$line-height: var(--ui-breadcrumb-line-height, 1.4); /// Interlignage des éléments +$radius: var(--ui-breadcrumb-radius, var(--radius-2xs)); /// Rayon des éléments interactifs (anneau de focus) +$focus-ring-width: var(--ui-breadcrumb-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus des éléments interactifs .ui-breadcrumb { // --- Base (nav landmark) -------------------------------------------- diff --git a/projects/ui-kit/navigation/ui-menu/src/lib/ui-menu.scss b/projects/ui-kit/navigation/ui-menu/src/lib/ui-menu.scss index 62f4acf..c4dd9c0 100644 --- a/projects/ui-kit/navigation/ui-menu/src/lib/ui-menu.scss +++ b/projects/ui-kit/navigation/ui-menu/src/lib/ui-menu.scss @@ -11,30 +11,30 @@ $levels: high, low; /// Niveaux générant les modifiers `._`. /// Largeur minimale du panneau, géométrie et typo des items, par taille. $sizes: ( 'default': ( - min-width: utils.rem-calc(210), - item-height: utils.rem-calc(40), - item-padding-x: var(--units-md), - font-size: var(--size-typography-text-default), - header-height: utils.rem-calc(32), - header-font-size: var(--size-typography-text-md), + min-width: var(--ui-menu-min-width, #{utils.rem-calc(210)}), + item-height: var(--ui-menu-item-height, #{utils.rem-calc(40)}), + item-padding-x: var(--ui-menu-item-padding-x, var(--units-md)), + font-size: var(--ui-menu-font-size, var(--size-typography-text-default)), + header-height: var(--ui-menu-header-height, #{utils.rem-calc(32)}), + header-font-size: var(--ui-menu-header-font-size, var(--size-typography-text-md)), ), 'small': ( - min-width: utils.rem-calc(160), - item-height: utils.rem-calc(32), - item-padding-x: var(--units-sm), - font-size: var(--size-typography-text-md), - header-height: utils.rem-calc(24), - header-font-size: var(--size-typography-text-sm), + min-width: var(--ui-menu-min-width-small, #{utils.rem-calc(160)}), + item-height: var(--ui-menu-item-height-small, #{utils.rem-calc(32)}), + item-padding-x: var(--ui-menu-item-padding-x-small, var(--units-sm)), + font-size: var(--ui-menu-font-size-small, var(--size-typography-text-md)), + header-height: var(--ui-menu-header-height-small, #{utils.rem-calc(24)}), + header-font-size: var(--ui-menu-header-font-size-small, var(--size-typography-text-sm)), ), ); $size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$panel-surface: var(--global-background-default); /// Fond opaque du panneau (le token surface `navigation` est transparent par design). -$item-radius: var(--radius-sm); /// Rayon des menuitems. -$group-indent: var(--units-default); /// Indentation des groupes imbriqués. -$panel-padding: utils.$overlay-panel-padding; /// Gouttière interne du panneau (**partagée** avec les autres panneaux flottants). -$separator-width: var(--stroke-sm); /// Épaisseur des séparateurs. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. +$panel-surface: var(--global-background-default); /// Fond opaque du panneau (le token surface `navigation` est transparent par design). +$item-radius: var(--ui-menu-item-radius, var(--radius-sm)); /// Rayon des menuitems. +$group-indent: var(--ui-menu-group-indent, var(--units-default)); /// Indentation des groupes imbriqués. +$panel-padding: var(--ui-menu-panel-padding, #{utils.$overlay-panel-padding}); /// Gouttière interne du panneau (**partagée** avec les autres panneaux flottants). +$separator-width: var(--ui-menu-separator-width, var(--stroke-sm)); /// Épaisseur des séparateurs. +$focus-ring-width: var(--ui-menu-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. @mixin menu-size($size) { min-width: map.get($size, min-width); diff --git a/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar-menu.scss b/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar-menu.scss index f4dc9ea..961eb7d 100644 --- a/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar-menu.scss +++ b/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar-menu.scss @@ -15,24 +15,24 @@ $levels: high, low; /// Niveaux générant les modifiers `._`. /// Hauteur, padding et typo d'un item, par taille. $sizes: ( 'default': ( - item-height: utils.rem-calc(40), - item-padding-x: var(--units-md), - font-size: var(--size-typography-text-default), - header-font-size: var(--size-typography-text-sm), + item-height: var(--ui-sidebar-item-height, #{utils.rem-calc(40)}), + item-padding-x: var(--ui-sidebar-item-padding-x, var(--units-md)), + font-size: var(--ui-sidebar-font-size, var(--size-typography-text-default)), + header-font-size: var(--ui-sidebar-header-font-size, var(--size-typography-text-sm)), ), 'small': ( - item-height: utils.rem-calc(32), - item-padding-x: var(--units-sm), - font-size: var(--size-typography-text-md), - header-font-size: var(--size-typography-text-sm), + item-height: var(--ui-sidebar-item-height-small, #{utils.rem-calc(32)}), + item-padding-x: var(--ui-sidebar-item-padding-x-small, var(--units-sm)), + font-size: var(--ui-sidebar-font-size-small, var(--size-typography-text-md)), + header-font-size: var(--ui-sidebar-header-font-size-small, var(--size-typography-text-sm)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$item-radius: var(--radius-sm); /// Rayon d'un item. -$group-indent: var(--units-lg); /// Retrait des items d'un sous-groupe. -$separator-width: var(--stroke-sm); /// Épaisseur d'un séparateur. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. -$icon-slot: utils.rem-calc(20); /// Largeur réservée à l'icône d'un item. +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$item-radius: var(--ui-sidebar-item-radius, var(--radius-sm)); /// Rayon d'un item. +$group-indent: var(--ui-sidebar-group-indent, var(--units-lg)); /// Retrait des items d'un sous-groupe. +$separator-width: var(--ui-sidebar-separator-width, var(--stroke-sm)); /// Épaisseur d'un séparateur. +$focus-ring-width: var(--ui-sidebar-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. +$icon-slot: var(--ui-sidebar-icon-slot-size, #{utils.rem-calc(20)}); /// Largeur réservée à l'icône d'un item. @mixin menu-size($size) { // Exposed so the collapsed rail can size square icon buttons from the same value. diff --git a/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar.scss b/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar.scss index ffaa2a7..5e66915 100644 --- a/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar.scss +++ b/projects/ui-kit/navigation/ui-sidebar/src/lib/ui-sidebar.scss @@ -14,18 +14,21 @@ // ===================================================================== // --- Config (local) --------------------------------------------------- -$z-index: 1000; /// Plan d'empilement du panneau. -$stroke-width: var(--stroke-sm); /// Épaisseur de bordure du panneau. -$region-padding: var(--units-md); /// Inset des régions (en-tête, corps, pied). -$close-size: 32px; /// Taille de la cible tactile du bouton de fermeture. -$focus-ring-width: utils.$focus-ring-width; /// Épaisseur de l'anneau de focus. -$backdrop-background: var(--primitives-black-50); /// Fond du voile en mode superposé. -$width-default: utils.rem-calc(280); /// Largeur du panneau déployé. -$rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes replié. - +$z-index: var(--ui-sidebar-z-index, 1000); /// Plan d'empilement du panneau. +$stroke-width: var(--ui-sidebar-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure du panneau. +$region-padding: var(--ui-sidebar-region-padding, var(--units-md)); /// Inset des régions (en-tête, corps, pied). +$close-size: var(--ui-sidebar-close-size, #{utils.rem-calc(32)}); /// Taille de la cible tactile du bouton de fermeture. +$focus-ring-width: var(--ui-sidebar-focus-ring-width, #{utils.$focus-ring-width}); /// Épaisseur de l'anneau de focus. +$backdrop-background: var(--ui-sidebar-backdrop-background, var(--primitives-black-50)); /// Fond du voile en présentation superposée. +$width-default: utils.rem-calc(280); /// Largeur livrée du panneau déployé (défaut du hook `--ui-sidebar-width`). +$rail-width-default: utils.rem-calc(72); /// Largeur livrée du rail replié (défaut du hook `--ui-sidebar-rail-width`). + +// The two widths are read by the panel AND its positioner, so they must live on +// a variable. Declaring the public name here would shadow a consumer override, +// hence the private mirror: the hook stays the winning value. :host { - --ui-sidebar-width: #{$width-default}; /// Largeur du panneau déployé. - --ui-sidebar-rail-width: #{$rail-width-default}; /// Largeur du rail d’icônes replié. + --_width: var(--ui-sidebar-width, #{$width-default}); /// Largeur du panneau déployé. + --_rail-width: var(--ui-sidebar-rail-width, #{$rail-width-default}); /// Largeur du rail d’icônes replié. display: contents; } @@ -34,7 +37,7 @@ $rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes position: fixed; inset: 0; z-index: $z-index; - background-color: var(--ui-sidebar-backdrop-background, #{$backdrop-background}); /// Fond du voile en présentation superposée. + background-color: $backdrop-background; &._contained { position: absolute; @@ -64,12 +67,12 @@ $rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes .ui-sidebar-rail { position: relative; flex: 0 0 auto; - inline-size: var(--ui-sidebar-width); + inline-size: var(--_width); align-self: stretch; @include utils.motion-transition(inline-size); &._collapsed { - inline-size: var(--ui-sidebar-rail-width); + inline-size: var(--_rail-width); } @include utils.motion-reduce { @@ -83,7 +86,7 @@ $rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes display: flex; flex-direction: column; min-height: 0; - inline-size: var(--ui-sidebar-width); + inline-size: var(--_width); overflow: hidden; // collapsed rail clips over-wide header/footer content background-color: var(--global-background-default); color: var(--global-text-default); @@ -92,7 +95,7 @@ $rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes @include utils.motion-transition(inline-size, box-shadow); &._collapsed { - inline-size: var(--ui-sidebar-rail-width); + inline-size: var(--_rail-width); } // Border faces the content area (opposite the docked edge). @@ -142,8 +145,8 @@ $rail-width-default: utils.rem-calc(72); /// Largeur du rail d'icônes display: inline-flex; align-items: center; justify-content: center; - inline-size: utils.rem-calc($close-size); - block-size: utils.rem-calc($close-size); + inline-size: $close-size; + block-size: $close-size; padding: 0; border: none; border-radius: var(--radius-default); diff --git a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-item.scss b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-item.scss index 4e67105..9dae43d 100644 --- a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-item.scss +++ b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-item.scss @@ -5,8 +5,8 @@ // ===================================================================== // --- Config --------------------------------------------------- -$marker-size: utils.rem-calc(44); /// Diamètre du marqueur d'étape. -$rail-stroke: var(--stroke-default); /// Épaisseur du rail reliant les étapes. +$marker-size: var(--ui-stepper-marker-size, #{utils.rem-calc(44)}); /// Diamètre du marqueur d'étape. +$rail-stroke: var(--ui-stepper-rail-stroke-width, var(--stroke-default)); /// Épaisseur du rail reliant les étapes. :host { display: block; diff --git a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-panel.scss b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-panel.scss index cdf2a18..4c8ca2a 100644 --- a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-panel.scss +++ b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step-panel.scss @@ -5,7 +5,7 @@ // ===================================================================== // --- Config --------------------------------------------------- -$marker-size: utils.rem-calc(44); /// Diamètre du marqueur d'étape, pour aligner le panneau. +$marker-size: var(--ui-stepper-marker-size, #{utils.rem-calc(44)}); /// Diamètre du marqueur d'étape, pour aligner le panneau. :host { display: block; diff --git a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step.scss b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step.scss index ccb3f84..1ccf00a 100644 --- a/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step.scss +++ b/projects/ui-kit/navigation/ui-stepper/src/lib/ui-step.scss @@ -5,10 +5,10 @@ // ===================================================================== // --- Config --------------------------------------------------- -$marker-size: utils.rem-calc(44); /// Diamètre du marqueur d'étape. -$marker-stroke: var(--stroke-sm); /// Épaisseur de bordure du marqueur. -$separator-stroke: var(--stroke-default); /// Épaisseur du séparateur entre étapes. -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus. +$marker-size: var(--ui-stepper-marker-size, #{utils.rem-calc(44)}); /// Diamètre du marqueur d'étape. +$marker-stroke: var(--ui-stepper-marker-stroke-width, var(--stroke-sm)); /// Épaisseur de bordure du marqueur. +$separator-stroke: var(--ui-stepper-separator-stroke-width, var(--stroke-default)); /// Épaisseur du séparateur entre étapes. +$focus-ring-width: var(--ui-stepper-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus. :host { display: flex; diff --git a/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab-list.scss b/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab-list.scss index 2a5c448..9bd9a5a 100644 --- a/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab-list.scss +++ b/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab-list.scss @@ -7,18 +7,17 @@ // and prev/next navigators. The active indicator is an absolutely-placed // bar that glides under (horizontal) or beside (vertical) the active tab, // timed by the motion system. Colour/size are themable via the exposed -// `--ui-tabs-active-bar-*` custom properties (Custom Indicator). +// `--ui-tabs-indicator-*` custom properties (Custom Indicator). // ===================================================================== // --- Config --------------------------------------------------- -$bar-thickness: var(--stroke-default); /// Épaisseur de la barre d'onglet actif. -$track-thickness: var(--stroke-sm); /// Épaisseur de la piste sous les onglets. -$nav-size: utils.rem-calc(40); /// Taille des boutons de défilement. -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus. - -// Themable indicator. -$bar-color: var(--ui-tabs-active-bar-color, var(--navigation-high-stroke-active)); /// Couleur de la barre active, surchargeable par instance. -$bar-size: var(--ui-tabs-active-bar-size, #{$bar-thickness}); /// Épaisseur de la barre active, surchargeable par instance. +$track-thickness: var(--ui-tabs-track-thickness, var(--stroke-sm)); /// Épaisseur de la piste sous les onglets. +$nav-size: var(--ui-tabs-nav-size, #{utils.rem-calc(40)}); /// Taille des boutons de défilement. +$focus-ring-width: var(--ui-tabs-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus. + +// `--ui-tabs-active-bar-*` : anciens noms, honorés une version (repli). +$bar-color: var(--ui-tabs-indicator-color, var(--ui-tabs-active-bar-color, var(--navigation-high-stroke-active))); /// Couleur de la barre d'onglet actif. +$bar-size: var(--ui-tabs-indicator-thickness, var(--ui-tabs-active-bar-size, var(--stroke-default))); /// Épaisseur de la barre d'onglet actif. :host { display: flex; diff --git a/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab.scss b/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab.scss index 65c824f..0b31408 100644 --- a/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab.scss +++ b/projects/ui-kit/navigation/ui-tabs/src/lib/ui-tab.scss @@ -10,8 +10,8 @@ // ===================================================================== // --- Config --------------------------------------------------- -$tab-min-height: utils.rem-calc(44); /// Hauteur minimale d'un onglet (cible tactile). -$focus-ring-width: utils.$action-focus-ring-width; /// Épaisseur de l'anneau de focus. +$tab-min-height: var(--ui-tabs-min-height, #{utils.rem-calc(44)}); /// Hauteur minimale d'un onglet (cible tactile). +$focus-ring-width: var(--ui-tabs-focus-ring-width, #{utils.$action-focus-ring-width}); /// Épaisseur de l'anneau de focus. :host { display: contents; diff --git a/projects/ui-kit/navigation/ui-tabs/ui-tabs.mdx b/projects/ui-kit/navigation/ui-tabs/ui-tabs.mdx index c228f8e..f41e8a6 100644 --- a/projects/ui-kit/navigation/ui-tabs/ui-tabs.mdx +++ b/projects/ui-kit/navigation/ui-tabs/ui-tabs.mdx @@ -111,7 +111,7 @@ Un onglet `disabled` n'est pas sélectionnable et est sauté par les flèches. ## Indicateur personnalisé L'indicateur d'onglet actif s'ajuste via deux variables CSS exposées : -`--ui-tabs-active-bar-color` et `--ui-tabs-active-bar-size`. +`--ui-tabs-indicator-color` et `--ui-tabs-indicator-thickness`. diff --git a/projects/ui-kit/navigation/ui-tabs/ui-tabs.stories.ts b/projects/ui-kit/navigation/ui-tabs/ui-tabs.stories.ts index d922196..b76f3b3 100644 --- a/projects/ui-kit/navigation/ui-tabs/ui-tabs.stories.ts +++ b/projects/ui-kit/navigation/ui-tabs/ui-tabs.stories.ts @@ -259,14 +259,14 @@ export const Disabled: Story = { // --- Custom Indicator ------------------------------------------------------ // Personnalisation de l'indicateur actif via les variables CSS exposées -// `--ui-tabs-active-bar-color` et `--ui-tabs-active-bar-size`. +// `--ui-tabs-indicator-color` et `--ui-tabs-indicator-thickness`. export const CustomIndicator: Story = { render: (args) => ({ props: args, template: box(` Design diff --git a/projects/ui-kit/styles/component-vars.scss b/projects/ui-kit/styles/component-vars.scss new file mode 100644 index 0000000..760b7cf --- /dev/null +++ b/projects/ui-kit/styles/component-vars.scss @@ -0,0 +1,953 @@ +// ===================================================================== +// @4sh/ui-kit — component variables. +// +// Copy into `src/styles/presets/`, import it from `main.scss`, change what you +// need. As shipped it changes nothing: it restates the defaults. +// +// @use 'presets/component-vars'; // src/styles/main.scss +// +// Load order is irrelevant: the kit only reads these names, never declares them. +// For a value shared by the whole kit (spacing, radius, control size), retune the +// design token instead — it follows brand, light/dark and viewport. +// +// Generated by `npm run docs:config`, values read from the compiled CSS — do not +// edit here. 598 variables; 19 more exist whose value depends on the +// rendered variant, listed in each component’s “Theming” section in Storybook. +// ===================================================================== + +:root { + + // ══════════════════════════════════════════════════════════════════ + // Actions + // ══════════════════════════════════════════════════════════════════ + + // ── ui-button ── + // Dimensions + --ui-button-height: var(--size-components-default); + --ui-button-height-small: var(--size-components-sm); + // Espacements + --ui-button-gap: var(--units-sm); + --ui-button-icon-stacked-gap: var(--units-xs); + --ui-button-icon-stacked-padding-x: var(--units-default); + --ui-button-icon-stacked-padding-y: var(--units-md); + --ui-button-padding-x: var(--units-default); + --ui-button-padding-x-small: var(--units-md); + // Bordures & rayons + --ui-button-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-button-radius-rounded: var(--radius-full); + --ui-button-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-button-font-size: var(--size-typography-text-default); + --ui-button-font-size-small: var(--size-typography-text-md); + + // ── ui-button-split ── + // Bordures & rayons + --ui-button-split-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + + // ── ui-link ── + // Dimensions + --ui-link-underline-thickness: from-font; + // Espacements + --ui-link-gap: var(--units-sm); + --ui-link-underline-offset: 0.15em; + // Bordures & rayons + --ui-link-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-link-radius: var(--radius-2xs); + // Typographie + --ui-link-font-size: var(--size-typography-text-default); + --ui-link-font-size-small: var(--size-typography-text-md); + + // ══════════════════════════════════════════════════════════════════ + // Base + // ══════════════════════════════════════════════════════════════════ + + // ── ui-icon ── + // Dimensions + --ui-icon-aspect-ratio: 1; + --ui-icon-min-size: 1em; + --ui-icon-size-default: var(--size-typography-icon-default); + --ui-icon-size-lg: var(--size-typography-icon-lg); + --ui-icon-size-md: var(--size-typography-icon-md); + --ui-icon-size-sm: var(--size-typography-icon-sm); + --ui-icon-size-xl: var(--size-typography-icon-xl); + + // ── ui-image ── + // Dimensions + --ui-image-placeholder-min-height: var(--units-xl); + --ui-image-placeholder-min-width: var(--units-xl); + // Bordures & rayons + --ui-image-placeholder-radius: var(--radius-default); + // Divers + --ui-image-object-fit: contain; + + // ══════════════════════════════════════════════════════════════════ + // Formulaires + // ══════════════════════════════════════════════════════════════════ + + // ── ui-autocomplete ── + // Dimensions + --ui-autocomplete-clear-size: var(--size-components-2xs); + --ui-autocomplete-group-height: 2rem; + --ui-autocomplete-group-height-small: 1.5rem; + --ui-autocomplete-input-min-width: 5rem; + --ui-autocomplete-option-height: 2.5rem; + --ui-autocomplete-option-height-small: 2rem; + --ui-autocomplete-panel-max-width: calc(100vw - var(--units-lg)); + --ui-autocomplete-scroller-max-height: 19.5rem; + --ui-autocomplete-scroller-max-height-small: 16rem; + // Espacements + --ui-autocomplete-header-padding-x: var(--units-sm); + --ui-autocomplete-header-padding-y: var(--units-xs); + --ui-autocomplete-option-gap: var(--units-sm); + --ui-autocomplete-option-padding-x: var(--units-default); + --ui-autocomplete-option-padding-x-small: var(--units-md); + --ui-autocomplete-panel-gap: var(--units-xs); + --ui-autocomplete-panel-padding: var(--units-xs); + --ui-autocomplete-tag-gap: var(--units-xs); + // Bordures & rayons + --ui-autocomplete-clear-radius: var(--radius-full); + --ui-autocomplete-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-autocomplete-option-radius: var(--radius-sm); + --ui-autocomplete-tag-radius: var(--radius-full); + // Typographie + --ui-autocomplete-font-size: var(--size-typography-text-default); + --ui-autocomplete-font-size-small: var(--size-typography-text-md); + --ui-autocomplete-group-font-size: var(--size-typography-text-md); + --ui-autocomplete-group-font-size-small: var(--size-typography-text-sm); + + // ── ui-checkbox ── + // Dimensions + --ui-checkbox-box-size: var(--ui-form-control-size, var(--size-components-2xs)); + --ui-checkbox-icon-size: var(--ui-icon-size-md, var(--size-typography-icon-md)); + // Espacements + --ui-checkbox-gap: var(--ui-form-control-gap, var(--units-sm)); + --ui-checkbox-label-gap: var(--units-2xs); + // Bordures & rayons + --ui-checkbox-box-radius: var(--radius-xs); + --ui-checkbox-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-checkbox-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-checkbox-font-size: var(--size-typography-text-default); + + // ── ui-datepicker ── + // Dimensions + --ui-datepicker-cell-min-height: var(--size-components-sm); + --ui-datepicker-day-size: var(--size-components-default); + --ui-datepicker-nav-size: var(--size-components-default); + --ui-datepicker-panel-max-width: calc(100vw - var(--units-lg)); + --ui-datepicker-panel-width: 22.5rem; + --ui-datepicker-time-btn-height: var(--size-components-2xs); + --ui-datepicker-time-btn-width: var(--size-components-xs); + --ui-datepicker-time-field-width: 3rem; + // Espacements + --ui-datepicker-buttonbar-gap: var(--units-sm); + --ui-datepicker-buttonbar-padding-top: var(--units-sm); + --ui-datepicker-cell-padding-x: var(--units-md); + --ui-datepicker-cell-padding-y: var(--units-sm); + --ui-datepicker-grid-gap: var(--units-xs); + --ui-datepicker-header-gap: var(--units-sm); + --ui-datepicker-month-divider-padding-left: var(--units-lg); + --ui-datepicker-month-gap: var(--units-sm); + --ui-datepicker-months-gap: var(--units-lg); + --ui-datepicker-panel-gap: var(--units-sm); + --ui-datepicker-panel-padding: var(--ui-overlay-panel-padding, var(--units-sm)); + --ui-datepicker-picker-gap: var(--units-xs); + --ui-datepicker-subheader-gap: var(--units-sm); + --ui-datepicker-time-gap: var(--units-sm); + --ui-datepicker-time-padding-top: var(--units-sm); + --ui-datepicker-time-unit-gap: var(--units-2xs); + --ui-datepicker-time-value-padding-x: var(--units-sm); + --ui-datepicker-time-value-padding-y: var(--units-2xs); + --ui-datepicker-title-padding-x: var(--units-sm); + --ui-datepicker-title-padding-y: var(--units-2xs); + --ui-datepicker-weekday-padding-y: var(--units-xs); + // Bordures & rayons + --ui-datepicker-cell-radius: var(--radius-sm); + --ui-datepicker-day-radius: var(--radius-full); + --ui-datepicker-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-datepicker-panel-radius: var(--ui-overlay-panel-radius, var(--radius-default)); + --ui-datepicker-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + --ui-datepicker-time-btn-radius: var(--radius-sm); + --ui-datepicker-time-value-radius: var(--radius-sm); + --ui-datepicker-title-radius: var(--radius-sm); + // Typographie + --ui-datepicker-cell-font-size: var(--size-typography-text-default); + --ui-datepicker-day-font-size: var(--size-typography-text-default); + --ui-datepicker-time-font-size: var(--size-typography-title-md); + --ui-datepicker-title-font-size: var(--size-typography-title-md); + --ui-datepicker-weekday-font-size: var(--size-typography-text-md); + // Divers + --ui-datepicker-day-opacity-disabled: 0.45; + --ui-datepicker-day-opacity-outside: 0.6; + --ui-datepicker-panel-shadow: var(--ui-overlay-panel-shadow, var(--shadow-default-md)); + + // ── ui-field ── + // Dimensions + --ui-field-height: var(--ui-form-field-height, var(--size-components-default)); + --ui-field-height-small: var(--ui-form-field-height-small, var(--size-components-sm)); + --ui-field-min-height-multiline: var(--ui-form-textarea-min-height, var(--size-components-sm)); + --ui-field-min-height-multiline-small: var(--ui-form-textarea-min-height-small, var(--size-components-sm)); + --ui-field-padding-y-auto-height: var(--units-xs); + // Espacements + --ui-field-box-gap: var(--ui-form-field-gap, var(--units-sm)); + --ui-field-footer-gap: var(--units-sm); + --ui-field-gap: var(--units-xs); + --ui-field-gap-small: var(--units-xs); + // Bordures & rayons + --ui-field-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-field-radius: var(--ui-form-field-radius, var(--radius-sm)); + --ui-field-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Divers + --ui-field-box-cursor: text; + --ui-field-focus-ring-opacity: 50%; + + // ── ui-file-upload ── + // Dimensions + --ui-file-upload-field-height: var(--ui-form-field-height, var(--size-components-default)); + --ui-file-upload-field-height-small: var(--ui-form-field-height-small, var(--size-components-sm)); + --ui-file-upload-zone-min-height: 112px; + --ui-file-upload-zone-min-height-small: 94px; + // Espacements + --ui-file-upload-browse-padding-x: var(--units-default); + --ui-file-upload-browse-padding-x-small: var(--units-md); + --ui-file-upload-content-gap: var(--units-sm); + --ui-file-upload-field-gap: var(--units-sm); + --ui-file-upload-field-padding-x: var(--units-md); + --ui-file-upload-gap: var(--units-default); + --ui-file-upload-messages-gap: var(--units-2xs); + --ui-file-upload-toolbar-gap: var(--units-sm); + --ui-file-upload-zone-content-gap: var(--units-xs); + --ui-file-upload-zone-gap: var(--units-lg); + --ui-file-upload-zone-gap-small: var(--units-sm); + --ui-file-upload-zone-padding: var(--units-default); + --ui-file-upload-zone-title-gap: var(--units-2xs); + // Bordures & rayons + --ui-file-upload-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-file-upload-radius: var(--ui-form-field-radius, var(--radius-sm)); + --ui-file-upload-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-file-upload-browse-font-size: var(--size-typography-text-default); + --ui-file-upload-browse-font-size-small: var(--size-typography-text-md); + --ui-file-upload-field-font-size: var(--size-typography-text-default); + --ui-file-upload-field-font-size-small: var(--size-typography-text-md); + --ui-file-upload-message-font-size: var(--size-typography-text-md); + --ui-file-upload-zone-hint-font-size: var(--size-typography-text-md); + --ui-file-upload-zone-hint-font-size-small: var(--size-typography-text-sm); + --ui-file-upload-zone-title-font-size: var(--size-typography-text-default); + --ui-file-upload-zone-title-font-size-small: var(--size-typography-text-md); + // Divers + --ui-file-upload-placeholder-opacity: 0.75; + + // ── ui-file-upload-list ── + // Dimensions + --ui-file-upload-item-media-size: 2.5rem; + --ui-file-upload-item-media-size-small: 2rem; + --ui-file-upload-item-progress-height: var(--stroke-default); + // Espacements + --ui-file-upload-item-gap: var(--units-md); + --ui-file-upload-item-gap-small: var(--units-sm); + --ui-file-upload-item-infos-gap: var(--units-2xs); + --ui-file-upload-item-media-gap: var(--units-sm); + --ui-file-upload-item-padding-x: var(--units-default); + --ui-file-upload-item-padding-x-small: var(--units-md); + --ui-file-upload-item-padding-y: var(--units-md); + --ui-file-upload-item-padding-y-small: var(--units-sm); + --ui-file-upload-item-remove-padding: var(--units-2xs); + // Bordures & rayons + --ui-file-upload-item-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-file-upload-item-radius: var(--radius-lg); + --ui-file-upload-item-radius-small: var(--radius-md); + --ui-file-upload-item-remove-radius: var(--radius-sm); + --ui-file-upload-item-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + --ui-file-upload-item-thumb-radius: var(--radius-sm); + // Typographie + --ui-file-upload-item-meta-font-size: var(--size-typography-text-sm); + --ui-file-upload-item-meta-font-size-small: var(--size-typography-text-sm); + --ui-file-upload-item-name-font-size: var(--size-typography-title-sm); + --ui-file-upload-item-name-font-size-small: var(--size-typography-text-md); + // Couleurs + --ui-file-upload-item-progress-color: var(--global-text-default); + + // ── ui-input ── + // Typographie + --ui-input-font-size-small: var(--size-typography-text-md); + --ui-input-unit-font-size: var(--size-typography-text-default); + + // ── ui-input-group ── + // Espacements + --ui-input-group-overlap: var(--ui-control-stroke-width, var(--stroke-default)); + // Bordures & rayons + --ui-input-group-radius: var(--ui-form-field-radius, var(--radius-sm)); + + // ── ui-input-group-addon ── + // Dimensions + --ui-input-group-addon-height: var(--ui-form-field-height, var(--size-components-default)); + --ui-input-group-addon-height-small: var(--ui-form-field-height-small, var(--size-components-sm)); + // Espacements + --ui-input-group-addon-gap: var(--ui-form-field-gap, var(--units-sm)); + --ui-input-group-addon-padding-x: var(--ui-form-field-padding-x, var(--units-md)); + // Bordures & rayons + --ui-input-group-addon-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-input-group-addon-font-size: var(--size-typography-text-default); + --ui-input-group-addon-font-size-small: var(--size-typography-text-md); + + // ── ui-input-mask ── + // Typographie + --ui-input-mask-font-size-small: var(--size-typography-text-md); + + // ── ui-input-number ── + // Dimensions + --ui-input-number-spinner-width: 1.875rem; + // Typographie + --ui-input-number-font-size-small: var(--size-typography-text-md); + --ui-input-number-unit-font-size: var(--size-typography-text-default); + + // ── ui-input-otp ── + // Dimensions + --ui-input-otp-cell-size: var(--size-components-default); + --ui-input-otp-cell-size-large: var(--size-components-xl); + --ui-input-otp-cell-size-small: var(--size-components-sm); + // Espacements + --ui-input-otp-gap: var(--units-sm); + // Bordures & rayons + --ui-input-otp-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-input-otp-radius: var(--ui-form-field-radius, var(--radius-sm)); + --ui-input-otp-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-input-otp-font-size: var(--size-typography-text-default); + --ui-input-otp-font-size-large: var(--size-typography-text-lg); + --ui-input-otp-font-size-small: var(--size-typography-text-md); + + // ── ui-input-tags ── + // Dimensions + --ui-input-tags-group-height: 2rem; + --ui-input-tags-input-min-width: 5rem; + --ui-input-tags-option-height: 2.5rem; + --ui-input-tags-option-height-small: 2rem; + --ui-input-tags-panel-max-width: calc(100vw - var(--units-lg)); + --ui-input-tags-scroller-max-height: 12.5rem; + --ui-input-tags-scroller-max-height-small: 11.25rem; + --ui-input-tags-tag-height: 1.5rem; + // Espacements + --ui-input-tags-option-gap: var(--units-sm); + --ui-input-tags-option-padding-x: var(--units-default); + --ui-input-tags-option-padding-x-small: var(--units-md); + --ui-input-tags-panel-gap: var(--units-xs); + --ui-input-tags-panel-padding: var(--units-xs); + --ui-input-tags-tag-gap: var(--units-xs); + --ui-input-tags-tag-gap-custom: var(--units-2xs); + // Bordures & rayons + --ui-input-tags-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-input-tags-option-radius: var(--radius-sm); + --ui-input-tags-tag-radius: var(--radius-full); + // Typographie + --ui-input-tags-font-size: var(--size-typography-text-default); + --ui-input-tags-font-size-small: var(--size-typography-text-md); + --ui-input-tags-group-font-size: var(--size-typography-text-md); + --ui-input-tags-tag-font-size: var(--size-typography-text-md); + + // ── ui-label ── + // Espacements + --ui-label-gap: var(--units-xs); + // Typographie + --ui-label-font-size: var(--size-typography-text-md); + --ui-label-font-size-small: var(--size-typography-text-sm); + + // ── ui-nudger ── + // Dimensions + --ui-nudger-value-min-width: 2.5rem; + --ui-nudger-value-min-width-small: 2rem; + // Espacements + --ui-nudger-gap: var(--units-default); + // Typographie + --ui-nudger-value-font-size: var(--size-typography-title-xl); + --ui-nudger-value-font-size-small: var(--size-typography-title-lg); + + // ── ui-radio ── + // Dimensions + --ui-radio-box-size: var(--ui-form-control-size, var(--size-components-2xs)); + --ui-radio-dot-size: var(--units-md); + // Espacements + --ui-radio-gap: var(--ui-form-control-gap, var(--units-sm)); + --ui-radio-label-gap: var(--units-2xs); + // Bordures & rayons + --ui-radio-box-radius: var(--radius-full); + --ui-radio-dot-radius: var(--radius-full); + --ui-radio-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-radio-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-radio-font-size: var(--size-typography-text-default); + + // ── ui-rating ── + // Espacements + --ui-rating-gap: var(--units-2xs); + --ui-rating-gap-lg: var(--units-2xs); + --ui-rating-gap-md: var(--units-2xs); + --ui-rating-gap-sm: var(--units-2xs); + --ui-rating-gap-xl: var(--units-2xs); + --ui-rating-icon-padding: var(--units-2xs); + // Bordures & rayons + --ui-rating-icon-radius: var(--radius-full); + // Couleurs + --ui-rating-icon-color: var(--form-low-content-default); + --ui-rating-icon-fill-color: var(--form-high-content-checked); + + // ── ui-segment-control ── + // Dimensions + --ui-segment-control-min-height: 2.75rem; + --ui-segment-control-min-height-small: 2.5rem; + --ui-segment-control-option-min-height: 2.25rem; + --ui-segment-control-option-min-height-small: 2rem; + --ui-segment-control-option-min-width: 2.25rem; + --ui-segment-control-option-min-width-small: 2rem; + // Espacements + --ui-segment-control-gap: 0; + --ui-segment-control-option-gap: var(--units-sm); + --ui-segment-control-option-padding-x: var(--units-default); + --ui-segment-control-option-padding-x-small: var(--units-md); + --ui-segment-control-padding: var(--units-xs); + // Bordures & rayons + --ui-segment-control-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-segment-control-option-radius: var(--radius-sm); + --ui-segment-control-radius: var(--radius-md); + --ui-segment-control-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + --ui-segment-control-thumb-radius: var(--radius-sm); + // Typographie + --ui-segment-control-font-size: var(--size-typography-text-default); + --ui-segment-control-font-size-small: var(--size-typography-text-md); + + // ── ui-select ── + // Dimensions + --ui-select-checkbox-icon-size: var(--ui-icon-size-sm, var(--size-typography-icon-sm)); + --ui-select-checkbox-size: 1.125rem; + --ui-select-clear-size: var(--size-components-2xs); + --ui-select-filter-height: var(--size-components-sm); + --ui-select-filter-height-small: var(--size-components-xs); + --ui-select-group-height: 2rem; + --ui-select-group-height-small: 1.5rem; + --ui-select-option-height: 2.5rem; + --ui-select-option-height-small: 2rem; + --ui-select-panel-max-width: calc(100vw - var(--units-lg)); + --ui-select-scroller-max-height: 19.5rem; + --ui-select-scroller-max-height-small: 16rem; + // Espacements + --ui-select-filter-gap: var(--units-sm); + --ui-select-filter-padding-x: var(--units-sm); + --ui-select-header-padding-x: var(--units-sm); + --ui-select-header-padding-y: var(--units-xs); + --ui-select-option-gap: var(--units-sm); + --ui-select-option-padding-x: var(--units-default); + --ui-select-option-padding-x-small: var(--units-md); + --ui-select-panel-gap: var(--units-xs); + --ui-select-panel-padding: var(--units-xs); + --ui-select-values-gap: var(--units-xs); + // Bordures & rayons + --ui-select-checkbox-radius: var(--radius-xs); + --ui-select-clear-radius: var(--radius-full); + --ui-select-filter-stroke-width: var(--stroke-sm); + --ui-select-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-select-option-radius: var(--radius-sm); + --ui-select-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + // Typographie + --ui-select-font-size: var(--size-typography-text-default); + --ui-select-font-size-small: var(--size-typography-text-md); + --ui-select-group-font-size: var(--size-typography-text-md); + --ui-select-group-font-size-small: var(--size-typography-text-sm); + + // ── ui-slider ── + // Dimensions + --ui-slider-handle-size: 1.375rem; + --ui-slider-length: 12rem; + --ui-slider-mark-size: 0.375rem; + --ui-slider-thickness: 0.5rem; + // Bordures & rayons + --ui-slider-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-slider-handle-radius: var(--radius-full); + --ui-slider-handle-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); + --ui-slider-mark-radius: var(--radius-full); + --ui-slider-track-radius: var(--radius-full); + + // ── ui-textarea ── + // Dimensions + --ui-textarea-min-height: var(--ui-form-textarea-min-height, var(--size-components-sm)); + --ui-textarea-min-height-small: var(--ui-form-textarea-min-height-small, var(--size-components-sm)); + // Espacements + --ui-textarea-padding-block: calc((var(--ui-textarea-min-height, var(--ui-form-textarea-min-height, var(--size-components-sm))) - 1.5em) / 2); + --ui-textarea-padding-block-small: calc((var(--ui-textarea-min-height-small, var(--ui-form-textarea-min-height-small, var(--size-components-sm))) - 1.5em) / 2); + --ui-textarea-padding-x: var(--ui-form-field-padding-x, var(--units-md)); + // Typographie + --ui-textarea-count-font-size: var(--size-typography-text-md); + --ui-textarea-count-font-size-small: var(--size-typography-text-sm); + --ui-textarea-font-size-small: var(--size-typography-text-md); + --ui-textarea-line-height: 1.5; + + // ── ui-toggle ── + // Dimensions + --ui-toggle-thumb-size: 1.25rem; + --ui-toggle-thumb-size-small: 1rem; + --ui-toggle-track-height: 1.5rem; + --ui-toggle-track-height-small: 1.25rem; + --ui-toggle-track-width: 2.75rem; + --ui-toggle-track-width-small: 2.25rem; + // Espacements + --ui-toggle-gap: var(--ui-form-control-gap, var(--units-sm)); + --ui-toggle-label-gap: var(--units-2xs); + --ui-toggle-thumb-offset: 1.25rem; + --ui-toggle-thumb-offset-small: 1rem; + --ui-toggle-track-padding: 0.0625rem; + --ui-toggle-track-padding-small: 0.0625rem; + // Bordures & rayons + --ui-toggle-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-toggle-thumb-radius: var(--radius-full); + --ui-toggle-track-radius: var(--radius-full); + --ui-toggle-track-stroke-width: var(--stroke-sm); + // Typographie + --ui-toggle-font-size: var(--size-typography-text-default); + --ui-toggle-font-size-small: var(--size-typography-text-md); + + // ══════════════════════════════════════════════════════════════════ + // Informatif + // ══════════════════════════════════════════════════════════════════ + + // ── ui-accordion-panel ── + // Dimensions + --ui-accordion-control-size: 2.5rem; + // Bordures & rayons + --ui-accordion-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + + // ── ui-alert ── + // Dimensions + --ui-alert-min-height: 3.5rem; + --ui-alert-min-height-large: 4.125rem; + // Espacements + --ui-alert-content-gap: var(--units-2xs); + --ui-alert-content-padding-right: var(--units-sm); + --ui-alert-focus-ring-offset: 0.125rem; + --ui-alert-gap: var(--units-sm); + --ui-alert-icon-padding-top: var(--units-2xs); + --ui-alert-padding-x: var(--units-md); + --ui-alert-padding-y: var(--units-sm); + // Bordures & rayons + --ui-alert-close-radius: var(--radius-xs); + --ui-alert-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-alert-radius: var(--radius-default); + --ui-alert-stroke-width: var(--stroke-default); + // Typographie + --ui-alert-text-font-size: var(--size-typography-text-md); + --ui-alert-text-font-size-large: var(--size-typography-text-lg); + --ui-alert-title-font-size: var(--size-typography-title-sm); + --ui-alert-title-font-size-large: var(--size-typography-title-md); + // Divers + --ui-alert-close-opacity: 0.7; + + // ── ui-avatar ── + // Dimensions + --ui-avatar-size: 3.5rem; + --ui-avatar-size-large: 4rem; + --ui-avatar-size-small: 2.625rem; + --ui-avatar-size-tiny: 2.125rem; + // Espacements + --ui-avatar-badge-offset-x: 25%; + --ui-avatar-badge-offset-y: -25%; + // Bordures & rayons + --ui-avatar-radius: var(--radius-full); + --ui-avatar-radius-square: var(--ui-avatar-square-radius, var(--radius-md)); + --ui-avatar-stroke-width: var(--stroke-default); + // Typographie + --ui-avatar-font-size: var(--size-typography-title-default); + --ui-avatar-font-size-large: var(--size-typography-title-xl); + --ui-avatar-font-size-small: var(--size-typography-title-md); + --ui-avatar-font-size-tiny: var(--size-typography-title-sm); + // Divers + --ui-avatar-badge-z-index: 1; + + // ── ui-badge ── + // Dimensions + --ui-badge-dot-size: 0.625rem; + --ui-badge-dot-size-large: 0.75rem; + --ui-badge-dot-size-small: 0.5rem; + --ui-badge-height: 1.75rem; + --ui-badge-height-large: 2rem; + --ui-badge-height-small: 1.375rem; + // Espacements + --ui-badge-gap: var(--units-xs); + --ui-badge-padding-x: var(--units-md); + --ui-badge-padding-x-large: var(--units-lg); + --ui-badge-padding-x-small: var(--units-md); + // Bordures & rayons + --ui-badge-radius: var(--radius-full); + --ui-badge-stroke-width: var(--stroke-sm); + // Typographie + --ui-badge-font-size: var(--size-typography-text-default); + --ui-badge-font-size-large: var(--size-typography-text-lg); + --ui-badge-font-size-small: var(--size-typography-text-md); + + // ── ui-chip ── + // Dimensions + --ui-chip-height: 2rem; + --ui-chip-height-small: 1.5rem; + --ui-chip-image-size: 1.5rem; + --ui-chip-image-size-small: 1.125rem; + // Espacements + --ui-chip-gap: var(--units-xs); + --ui-chip-gap-small: var(--units-2xs); + --ui-chip-image-offset: var(--units-2xs); + --ui-chip-padding-inline-end-removable: var(--units-2xs); + --ui-chip-padding-x: var(--units-sm); + --ui-chip-padding-x-small: var(--units-sm); + --ui-chip-remove-padding: var(--units-2xs); + // Bordures & rayons + --ui-chip-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-chip-image-radius: var(--radius-full); + --ui-chip-image-radius-square: var(--radius-xs); + --ui-chip-radius: var(--radius-full); + --ui-chip-radius-square: var(--radius-sm); + --ui-chip-remove-radius: var(--radius-full); + --ui-chip-stroke-width: var(--stroke-sm); + // Typographie + --ui-chip-font-size: var(--size-typography-text-default); + --ui-chip-font-size-small: var(--size-typography-text-md); + + // ── ui-empty-state ── + // Dimensions + --ui-empty-state-max-width: 40ch; + --ui-empty-state-media-size: var(--units-4xl); + --ui-empty-state-media-size-small: var(--units-2xl); + // Espacements + --ui-empty-state-actions-gap: var(--units-sm); + --ui-empty-state-gap: var(--units-lg); + --ui-empty-state-gap-small: var(--units-default); + --ui-empty-state-text-gap: var(--units-xs); + // Typographie + --ui-empty-state-desc-font-size: var(--size-typography-text-default); + --ui-empty-state-desc-font-size-small: var(--size-typography-text-md); + --ui-empty-state-title-font-size: var(--size-typography-title-default); + --ui-empty-state-title-font-size-small: var(--size-typography-title-md); + // Couleurs + --ui-empty-state-media-color: var(--global-text-muted); + + // ── ui-helper ── + // Espacements + --ui-helper-gap: var(--units-xs); + // Typographie + --ui-helper-font-size: var(--size-typography-text-default); + --ui-helper-font-size-small: var(--size-typography-text-md); + + // ── ui-progress-bar ── + // Dimensions + --ui-progress-bar-track-height: 0.5rem; + --ui-progress-bar-track-height-small: 0.25rem; + // Bordures & rayons + --ui-progress-bar-track-radius: var(--radius-xs); + --ui-progress-bar-track-radius-small: var(--radius-2xs); + // Motion + --ui-progress-bar-indeterminate-duration: 1.6s; + + // ── ui-read-only ── + // Dimensions + --ui-read-only-field-height: var(--ui-form-field-height, var(--size-components-default)); + --ui-read-only-field-height-small: var(--ui-form-field-height-small, var(--size-components-sm)); + // Espacements + --ui-read-only-label-gap: var(--ui-form-field-gap, var(--units-sm)); + // Typographie + --ui-read-only-font-size: var(--size-typography-text-default); + --ui-read-only-font-size-small: var(--size-typography-text-md); + + // ── ui-separator ── + // Espacements + --ui-separator-gap: var(--units-sm); + // Bordures & rayons + --ui-separator-stroke-width: var(--stroke-default); + --ui-separator-stroke-width-small: var(--stroke-sm); + // Typographie + --ui-separator-font-size: var(--size-typography-text-default); + --ui-separator-font-size-small: var(--size-typography-text-md); + + // ── ui-skeleton ── + // Dimensions + --ui-skeleton-circle-height: 6.25rem; + --ui-skeleton-circle-height-small: 4rem; + --ui-skeleton-circle-width: 6.25rem; + --ui-skeleton-circle-width-small: 4rem; + --ui-skeleton-rectangle-height: 6.25rem; + --ui-skeleton-rectangle-height-small: 4rem; + --ui-skeleton-rectangle-width: 6.25rem; + --ui-skeleton-rectangle-width-small: 4rem; + --ui-skeleton-text-height: 1.25rem; + --ui-skeleton-text-height-small: 0.75rem; + --ui-skeleton-text-width: 14rem; + // Bordures & rayons + --ui-skeleton-circle-radius: var(--radius-full); + --ui-skeleton-rectangle-radius: var(--radius-default); + --ui-skeleton-text-radius: var(--radius-xl); + // Couleurs + --ui-skeleton-background: var(--global-background-muted); + + // ── ui-spinner ── + // Dimensions + --ui-spinner-size: 2rem; + --ui-spinner-size-small: 1.25rem; + // Typographie + --ui-spinner-font-size: var(--size-typography-text-md); + --ui-spinner-font-size-small: var(--size-typography-text-sm); + // Couleurs + --ui-spinner-color: var(--informative-highlightlow-content-default); + + // ── ui-tag ── + // Dimensions + --ui-tag-height: var(--size-components-2xs); + --ui-tag-height-small: 22px; + // Espacements + --ui-tag-gap: var(--units-sm); + --ui-tag-gap-small: var(--units-xs); + --ui-tag-padding-x: var(--units-sm); + // Bordures & rayons + --ui-tag-radius: var(--radius-full); + --ui-tag-radius-square: var(--radius-sm); + --ui-tag-stroke-width: var(--stroke-sm); + // Typographie + --ui-tag-font-size: var(--size-typography-text-default); + --ui-tag-font-size-small: var(--size-typography-text-md); + + // ── ui-toast ── + // Bordures & rayons + --ui-toast-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + + // ── ui-toast-container ── + // Dimensions + --ui-toast-width: 22.5rem; + --ui-toast-width-expanded: 30rem; + // Espacements + --ui-toast-edge-gap: var(--units-lg); + --ui-toast-stack-gap: var(--units-sm); + // Divers + --ui-toast-z-index: 1100; + + // ── ui-tooltip ── + // Dimensions + --ui-tooltip-arrow-size: 0.5rem; + --ui-tooltip-max-width: 16rem; + // Espacements + --ui-tooltip-padding-x: var(--units-default); + --ui-tooltip-padding-y: var(--units-sm); + // Bordures & rayons + --ui-tooltip-radius: var(--radius-default); + --ui-tooltip-stroke-width: var(--stroke-sm); + // Typographie + --ui-tooltip-font-size: var(--size-typography-text-md); + + // ══════════════════════════════════════════════════════════════════ + // Mise en page + // ══════════════════════════════════════════════════════════════════ + + // ── ui-card ── + // Espacements + --ui-card-footer-gap: var(--units-sm); + --ui-card-gap: var(--units-md); + --ui-card-header-gap: var(--units-2xs); + --ui-card-padding: var(--units-lg); + // Bordures & rayons + --ui-card-radius: var(--radius-md); + --ui-card-stroke-width: var(--stroke-sm); + + // ── ui-drawer ── + // Dimensions + --ui-drawer-action-size: 2rem; + --ui-drawer-size-edge: 10rem; + --ui-drawer-size-side: 20rem; + // Espacements + --ui-drawer-header-gap: var(--units-sm); + --ui-drawer-panel-padding: var(--units-lg); + // Bordures & rayons + --ui-drawer-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-drawer-stroke-width: var(--stroke-sm); + // Couleurs + --ui-drawer-mask-background: var(--primitives-black-50); + // Divers + --ui-drawer-z-index: 1100; + + // ── ui-modal ── + // Dimensions + --ui-modal-action-size: 2rem; + --ui-modal-resize-handle-size: 16px; + // Espacements + --ui-modal-dialog-padding: var(--units-lg); + --ui-modal-header-gap: var(--units-sm); + --ui-modal-mask-padding: var(--units-lg); + // Bordures & rayons + --ui-modal-dialog-radius: var(--radius-md); + --ui-modal-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-modal-stroke-width: var(--stroke-sm); + // Couleurs + --ui-modal-mask-background: var(--primitives-black-50); + // Divers + --ui-modal-z-index: 1100; + + // ── ui-popover ── + // Dimensions + --ui-popover-arrow-size: 0.5rem; + --ui-popover-max-height: null; + --ui-popover-max-width: 20rem; + // Espacements + --ui-popover-padding: var(--units-md); + + // ══════════════════════════════════════════════════════════════════ + // Navigation + // ══════════════════════════════════════════════════════════════════ + + // ── ui-breadcrumb ── + // Espacements + --ui-breadcrumb-content-gap: var(--units-sm); + --ui-breadcrumb-item-gap: var(--spacing-default); + // Bordures & rayons + --ui-breadcrumb-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-breadcrumb-radius: var(--radius-2xs); + // Typographie + --ui-breadcrumb-font-size: var(--size-typography-text-default); + --ui-breadcrumb-font-size-small: var(--size-typography-text-md); + --ui-breadcrumb-line-height: 1.4; + --ui-breadcrumb-weight: var(--weight-bold); + + // ── ui-menu ── + // Dimensions + --ui-menu-header-height: 2rem; + --ui-menu-header-height-small: 1.5rem; + --ui-menu-item-height: 2.5rem; + --ui-menu-item-height-small: 2rem; + --ui-menu-min-width: 13.125rem; + --ui-menu-min-width-small: 10rem; + // Espacements + --ui-menu-group-indent: var(--units-default); + --ui-menu-item-padding-x: var(--units-md); + --ui-menu-item-padding-x-small: var(--units-sm); + --ui-menu-panel-padding: var(--ui-overlay-panel-padding, var(--units-sm)); + // Bordures & rayons + --ui-menu-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-menu-item-radius: var(--radius-sm); + // Typographie + --ui-menu-font-size: var(--size-typography-text-default); + --ui-menu-font-size-small: var(--size-typography-text-md); + --ui-menu-header-font-size: var(--size-typography-text-md); + --ui-menu-header-font-size-small: var(--size-typography-text-sm); + + // ── ui-sidebar ── + // Dimensions + --ui-sidebar-close-size: 2rem; + --ui-sidebar-rail-width: 4.5rem; + --ui-sidebar-width: 17.5rem; + // Espacements + --ui-sidebar-region-padding: var(--units-md); + // Bordures & rayons + --ui-sidebar-stroke-width: var(--stroke-sm); + // Couleurs + --ui-sidebar-backdrop-background: var(--primitives-black-50); + // Divers + --ui-sidebar-z-index: 1000; + + // ── ui-sidebar-menu ── + // Dimensions + --ui-sidebar-icon-slot-size: 1.25rem; + --ui-sidebar-item-height: 2.5rem; + --ui-sidebar-item-height-small: 2rem; + --ui-sidebar-separator-width: var(--stroke-sm); + // Espacements + --ui-sidebar-group-indent: var(--units-lg); + --ui-sidebar-item-padding-x: var(--units-md); + --ui-sidebar-item-padding-x-small: var(--units-sm); + // Bordures & rayons + --ui-sidebar-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-sidebar-item-radius: var(--radius-sm); + // Typographie + --ui-sidebar-font-size: var(--size-typography-text-default); + --ui-sidebar-font-size-small: var(--size-typography-text-md); + --ui-sidebar-header-font-size: var(--size-typography-text-sm); + --ui-sidebar-header-font-size-small: var(--size-typography-text-sm); + + // ── ui-step ── + // Bordures & rayons + --ui-stepper-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-stepper-marker-stroke-width: var(--stroke-sm); + --ui-stepper-separator-stroke-width: var(--stroke-default); + + // ── ui-step-item ── + // Dimensions + --ui-stepper-marker-size: 2.75rem; + // Bordures & rayons + --ui-stepper-rail-stroke-width: var(--stroke-default); + + // ── ui-tab ── + // Dimensions + --ui-tabs-min-height: 2.75rem; + + // ── ui-tab-list ── + // Dimensions + --ui-tabs-active-bar-size: var(--stroke-default); + --ui-tabs-indicator-thickness: var(--stroke-default); + --ui-tabs-nav-size: 2.5rem; + --ui-tabs-track-thickness: var(--stroke-sm); + // Bordures & rayons + --ui-tabs-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + // Couleurs + --ui-tabs-active-bar-color: var(--navigation-high-stroke-active); + --ui-tabs-indicator-color: var(--navigation-high-stroke-active); + + // ══════════════════════════════════════════════════════════════════ + // shared + // ══════════════════════════════════════════════════════════════════ + + // ── ui-config ── + // Dimensions + --ui-form-control-size: var(--size-components-2xs); + --ui-form-field-height: var(--size-components-default); + --ui-form-field-height-small: var(--size-components-sm); + --ui-form-textarea-min-height: var(--size-components-sm); + --ui-form-textarea-min-height-small: var(--size-components-sm); + // Espacements + --ui-form-control-gap: var(--units-sm); + --ui-form-field-gap: var(--units-sm); + --ui-form-field-padding-x: var(--units-md); + --ui-overlay-panel-padding: var(--units-sm); + // Bordures & rayons + --ui-avatar-square-radius: var(--radius-md); + --ui-control-stroke-width: var(--stroke-default); + --ui-focus-ring-width: var(--units-2xs); + --ui-form-field-radius: var(--radius-sm); + --ui-overlay-panel-radius: var(--radius-default); + // Motion + --ui-control-transition-duration: var(--transition-default); + --ui-control-transition-easing: var(--transition-easeinout); + // Divers + --ui-overlay-panel-shadow: var(--shadow-default-md); + + // ══════════════════════════════════════════════════════════════════ + // Tableaux + // ══════════════════════════════════════════════════════════════════ + + // ── ui-paginator ── + // Dimensions + --ui-paginator-button-size: 2rem; + // Espacements + --ui-paginator-bar-gap: var(--units-2xl); + --ui-paginator-blocks-gap: var(--units-default); + --ui-paginator-numbers-gap: var(--units-sm); + // Bordures & rayons + --ui-paginator-button-radius: var(--radius-default); + --ui-paginator-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + + // ── ui-table ── + // Dimensions + --ui-table-drop-indicator-width: var(--stroke-lg); + --ui-table-resizer-hit-width: var(--units-sm); + // Espacements + --ui-table-cell-padding: var(--units-default); + --ui-table-cell-padding-large: var(--units-xl) var(--units-default); + --ui-table-cell-padding-small: var(--units-sm) var(--units-md); + --ui-table-head-padding: var(--units-md) var(--units-default); + --ui-table-head-padding-large: var(--units-default); + --ui-table-head-padding-small: var(--units-sm) var(--units-md); + --ui-table-loading-padding-y: var(--units-3xl); + // Bordures & rayons + --ui-table-focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); + --ui-table-stroke-width: var(--stroke-sm); +} diff --git a/projects/ui-kit/styles/settings/_ui-config.scss b/projects/ui-kit/styles/settings/_ui-config.scss index ee2423c..5916ca3 100644 --- a/projects/ui-kit/styles/settings/_ui-config.scss +++ b/projects/ui-kit/styles/settings/_ui-config.scss @@ -1,67 +1,77 @@ // ===================================================================== // ui-config : shared structural constants for ui-* components. // -// Two levers: edit a value here (kit-wide) or override the LOCAL var in a -// component's .scss (that component only). No colors — themable colors are -// runtime design tokens. +// Three levers, from the broadest to the narrowest: the design token each value +// points at, the `--ui-` hook declared here (kit-wide, no recompile), and +// the LOCAL var of a component's .scss. No colors — themable colors are runtime +// design tokens. +// +// Components interpolate these values, so a hook added here propagates into +// every consumer on its own: `var(--ui-checkbox-box-size, var(--ui-form-control-size, +// var(--size-components-2xs)))` — component, then category, then token. // ===================================================================== // --- Global UI -------------------------------------------------------- -$focus-ring-width: var(--units-2xs); +$focus-ring-width: var(--ui-focus-ring-width, var(--units-2xs)); /// Épaisseur de l'anneau de focus, pour tout le kit. -$control-stroke-width: var(--stroke-default); -$control-transition-duration: var(--transition-default); -$control-transition-easing: var(--transition-easeinout); +$control-stroke-width: var(--ui-control-stroke-width, var(--stroke-default)); /// Épaisseur de bordure des contrôles. +$control-transition-duration: var(--ui-control-transition-duration, var(--transition-default)); /// Durée des transitions d'état des contrôles. +$control-transition-easing: var(--ui-control-transition-easing, var(--transition-easeinout)); /// Courbe des transitions d'état des contrôles. // --- Global : motion -------------------------------------------------- // Shared timing for the motion system (enter/leave presets + transitions). -// Durations/easings map to the `--transition-*` tokens; distance/scale are -// structural (never colors). Override per instance via the `--ui-motion-*` -// custom properties (see utils.motion-transition / base/_motion.scss). -$motion-duration-fast: var(--transition-fast); // 80ms — micro-feedback -$motion-duration-base: var(--transition-default); // 180ms — default -$motion-duration-slow: var(--transition-slow); // 280ms — large surfaces -$motion-easing-standard: var(--transition-easeinout); // symmetric (toggles) -$motion-easing-enter: var(--transition-easeout); // decelerate (appear) -$motion-easing-leave: var(--transition-easein); // accelerate (dismiss) -$motion-distance: 0.5rem; // slide offset (8px) -$motion-scale: 0.96; // zoom start/end scale +// Not hooked here: the `--ui-motion-*` custom properties already expose duration, +// easing, delay, distance and scale per instance (see utils/_motion.scss), and the +// kit-wide default is the `--transition-*` token these point at. +$motion-duration-fast: var(--transition-fast); /// Durée « micro-feedback ». +$motion-duration-base: var(--transition-default); /// Durée par défaut (apparition, disparition, collapse). +$motion-duration-slow: var(--transition-slow); /// Durée « grande surface ». +$motion-easing-standard: var(--transition-easeinout); /// Courbe symétrique (bascules). +$motion-easing-enter: var(--transition-easeout); /// Courbe décélérée (apparition). +$motion-easing-leave: var(--transition-easein); /// Courbe accélérée (disparition). +$motion-distance: 0.5rem; /// Distance du glissement des presets `slide`. +$motion-scale: 0.96; /// Échelle de départ / d’arrivée des presets `zoom`. // --- Global : overlay panels ------------------------------------------ -$overlay-panel-radius: var(--radius-default); -$overlay-panel-padding: var(--units-sm); -$overlay-panel-shadow: var(--shadow-default-md); -$overlay-panel-stroke-width: $control-stroke-width; -$overlay-panel-offset: 8px; +// `--ui-overlay-*` (utils/_overlay.scss) retunes ONE panel; these set the default +// for all of them. +$overlay-panel-radius: var(--ui-overlay-panel-radius, var(--radius-default)); /// Rayon des coins des panneaux flottants. +$overlay-panel-padding: var(--ui-overlay-panel-padding, var(--units-sm)); /// Gouttière interne des panneaux flottants. +$overlay-panel-shadow: var(--ui-overlay-panel-shadow, var(--shadow-default-md)); /// Ombre des panneaux flottants. +$overlay-panel-stroke-width: $control-stroke-width; /// Épaisseur de bordure des panneaux flottants (suit `--ui-control-stroke-width`). +$overlay-panel-offset: var(--ui-overlay-panel-offset, 8px); /// Décalage d'un panneau par rapport à son ancre. // --- Category : forms ------------------------------------------------- -$form-control-size: var(--size-components-2xs); -$form-control-gap: var(--units-sm); -$form-focus-ring-width: $focus-ring-width; +$form-control-size: var(--ui-form-control-size, var(--size-components-2xs)); /// Côté d'un contrôle carré (case à cocher, radio). +$form-control-gap: var(--ui-form-control-gap, var(--units-sm)); /// Espace entre un contrôle et son libellé. +$form-focus-ring-width: $focus-ring-width; /// Anneau de focus des champs (suit `--ui-focus-ring-width`). // Field -$form-field-height: var(--size-components-default); -$form-field-height-small: var(--size-components-sm); -$form-field-radius: var(--radius-sm); -$form-field-padding-x: var(--units-md); -$form-field-gap: var(--units-sm); -$form-field-stroke-width: $control-stroke-width; -$form-field-focus-ring-width: $form-focus-ring-width; +$form-field-height: var(--ui-form-field-height, var(--size-components-default)); /// Hauteur de la boîte de champ. +$form-field-height-small: var(--ui-form-field-height-small, var(--size-components-sm)); /// Hauteur de la boîte de champ, variante `small`. +$form-field-radius: var(--ui-form-field-radius, var(--radius-sm)); /// Rayon des coins de la boîte de champ. +$form-field-padding-x: var(--ui-form-field-padding-x, var(--units-md)); /// Inset horizontal de la boîte de champ. +$form-field-gap: var(--ui-form-field-gap, var(--units-sm)); /// Espace entre le contrôle et ses affixes. +$form-field-stroke-width: $control-stroke-width; /// Bordure de la boîte de champ (suit `--ui-control-stroke-width`). +$form-field-focus-ring-width: $form-focus-ring-width; /// Anneau de focus de la boîte de champ (suit `--ui-focus-ring-width`). // Textarea (multiline field box) -$form-textarea-min-height: var(--size-components-sm); -$form-textarea-min-height-small: var(--size-components-sm); -$form-textarea-padding-y: var(--units-default); -$form-textarea-padding-y-small: var(--units-md); +$form-textarea-min-height: var(--ui-form-textarea-min-height, var(--size-components-sm)); /// Hauteur minimale d'une boîte multiligne. +$form-textarea-min-height-small: var(--ui-form-textarea-min-height-small, var(--size-components-sm)); /// Hauteur minimale d'une boîte multiligne, variante `small`. +$form-textarea-padding-y: var(--ui-form-textarea-padding-y, var(--units-default)); /// Inset vertical d'une boîte multiligne. +$form-textarea-padding-y-small: var(--ui-form-textarea-padding-y-small, var(--units-md)); /// Inset vertical d'une boîte multiligne, variante `small`. // --- Category : actions ----------------------------------------------- -$action-focus-ring-width: $focus-ring-width; +$action-focus-ring-width: $focus-ring-width; /// Anneau de focus des actions (suit `--ui-focus-ring-width`). // --- Category : informative ------------------------------------------- // Avatar -$avatar-size-tiny: 34px; -$avatar-size-small: 42px; -$avatar-size-default: 56px; -$avatar-size-large: 64px; -$avatar-square-radius: var(--radius-md); -$avatar-group-overlap: var(--units-md); +// The four sizes stay raw numbers: they go through `rem-calc()`, which needs a +// number, not a `var()`. Each is already reachable per component through +// `--ui-avatar-size{,-tiny,-small,-large}`. +$avatar-size-tiny: 34px; /// Diamètre de l’avatar en taille `tiny` (nombre brut : passe par `rem-calc`). +$avatar-size-small: 42px; /// Diamètre de l’avatar en taille `small` (nombre brut). +$avatar-size-default: 56px; /// Diamètre de l’avatar en taille `default` (nombre brut). +$avatar-size-large: 64px; /// Diamètre de l’avatar en taille `large` (nombre brut). +$avatar-square-radius: var(--ui-avatar-square-radius, var(--radius-md)); /// Rayon des coins d'un avatar carré. +$avatar-group-overlap: var(--ui-avatar-group-overlap, var(--units-md)); /// Chevauchement des avatars d'un groupe. diff --git a/projects/ui-kit/table/ui-paginator/src/lib/ui-paginator.scss b/projects/ui-kit/table/ui-paginator/src/lib/ui-paginator.scss index ef0a2fa..a8e7910 100644 --- a/projects/ui-kit/table/ui-paginator/src/lib/ui-paginator.scss +++ b/projects/ui-kit/table/ui-paginator/src/lib/ui-paginator.scss @@ -4,20 +4,20 @@ // ui-paginator : co-located styles. All values come from design tokens. // ===================================================================== -$focus-ring-width: utils.$action-focus-ring-width; /// Anneau de focus des boutons. -$button-size: 32px; /// Taille des contrôles / min-width des numéros. -$button-radius: var(--radius-default); /// Rayon des boutons (carré arrondi). -$bar-gap: var(--units-2xl); /// Espacement entre les blocs de la barre. -$blocks-gap: var(--units-default); /// Espacement interne d'un bloc. -$numbers-gap: var(--units-sm); /// Espacement entre les numéros de page. +$focus-ring-width: var(--ui-paginator-focus-ring-width, #{utils.$action-focus-ring-width}); /// Anneau de focus des boutons. +$button-size: var(--ui-paginator-button-size, #{utils.rem-calc(32)}); /// Taille des contrôles / min-width des numéros. +$button-radius: var(--ui-paginator-button-radius, var(--radius-default)); /// Rayon des boutons (carré arrondi). +$bar-gap: var(--ui-paginator-bar-gap, var(--units-2xl)); /// Espacement entre les blocs de la barre. +$blocks-gap: var(--ui-paginator-blocks-gap, var(--units-default)); /// Espacement interne d'un bloc. +$numbers-gap: var(--ui-paginator-numbers-gap, var(--units-sm)); /// Espacement entre les numéros de page. // Base @mixin paginator-button { display: inline-flex; align-items: center; justify-content: center; - height: utils.rem-calc($button-size); - min-width: utils.rem-calc($button-size); + height: $button-size; + min-width: $button-size; border: none; border-radius: $button-radius; background-color: var(--navigation-high-surface-default); @@ -69,7 +69,7 @@ $numbers-gap: var(--units-sm); /// Espacement entre les num // First / previous / next / last. &-control { @include paginator-button; - width: utils.rem-calc($button-size); + width: $button-size; padding: 0 var(--units-sm); &:disabled { @@ -129,8 +129,8 @@ $numbers-gap: var(--units-sm); /// Espacement entre les num display: inline-flex; align-items: center; justify-content: center; - height: utils.rem-calc($button-size); - min-width: utils.rem-calc($button-size); + height: $button-size; + min-width: $button-size; color: var(--navigation-high-content-default); font-size: var(--size-typography-text-md); font-weight: var(--weight-bold); diff --git a/projects/ui-kit/table/ui-table/src/lib/ui-table.scss b/projects/ui-kit/table/ui-table/src/lib/ui-table.scss index 3db55cd..d1a3259 100644 --- a/projects/ui-kit/table/ui-table/src/lib/ui-table.scss +++ b/projects/ui-kit/table/ui-table/src/lib/ui-table.scss @@ -12,24 +12,24 @@ /// Densités de padding (`default`/`small`/`large`). $sizes: ( 'default': ( - cell-padding: var(--units-default), - head-padding: var(--units-md) var(--units-default), + cell-padding: var(--ui-table-cell-padding, var(--units-default)), + head-padding: var(--ui-table-head-padding, var(--units-md) var(--units-default)), ), 'small': ( - cell-padding: var(--units-sm) var(--units-md), - head-padding: var(--units-sm) var(--units-md), + cell-padding: var(--ui-table-cell-padding-small, var(--units-sm) var(--units-md)), + head-padding: var(--ui-table-head-padding-small, var(--units-sm) var(--units-md)), ), 'large': ( - cell-padding: var(--units-xl) var(--units-default), - head-padding: var(--units-default), + cell-padding: var(--ui-table-cell-padding-large, var(--units-xl) var(--units-default)), + head-padding: var(--ui-table-head-padding-large, var(--units-default)), ), ); -$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. -$stroke-width: var(--stroke-sm); /// Épaisseur des traits de séparation / gridlines. -$focus-ring-width: utils.$focus-ring-width; /// Anneau de focus (lignes, en-têtes). -$resizer-hit-width: var(--units-sm); /// Largeur de la zone de saisie du redimensionneur. -$drop-indicator-width: var(--stroke-lg); /// Épaisseur de l'indicateur de dépose (row reorder). -$loading-padding-y: var(--units-3xl); /// Hauteur d'air de la ligne d'attente (`loading`). +$size-default: map.get($sizes, 'default'); /// Raccourci vers l'entrée `default` de `$sizes`. +$stroke-width: var(--ui-table-stroke-width, var(--stroke-sm)); /// Épaisseur des traits de séparation / gridlines. +$focus-ring-width: var(--ui-table-focus-ring-width, #{utils.$focus-ring-width}); /// Anneau de focus (lignes, en-têtes). +$resizer-hit-width: var(--ui-table-resizer-hit-width, var(--units-sm)); /// Largeur de la zone de saisie du redimensionneur. +$drop-indicator-width: var(--ui-table-drop-indicator-width, var(--stroke-lg)); /// Épaisseur de l'indicateur de dépose (row reorder). +$loading-padding-y: var(--ui-table-loading-padding-y, var(--units-3xl)); /// Hauteur d'air de la ligne d'attente (`loading`). ui-table { display: block; diff --git a/scripts/component-vars.build.mjs b/scripts/component-vars.build.mjs new file mode 100644 index 0000000..c65f9dd --- /dev/null +++ b/scripts/component-vars.build.mjs @@ -0,0 +1,822 @@ +#!/usr/bin/env node +/** + * component-vars.build.mjs — emits the kit's customisation surface: + * + * projects/ui-kit/styles/component-vars.scss every `--ui-*` variable at its + * shipped value, ready to copy + * figma/component-vars.json the same list in DTCG form (same + * shape as src/design-tokens/*.json) + * + * VALUES come from the CSS Sass actually compiles, not from reading the SCSS: + * `rem-calc(44px - 2 * $gutter)` has to be evaluated, not guessed. DESCRIPTIONS come + * from the `///` comments, via the docs.config.mjs manifest. + * + * Usage: + * node scripts/component-vars.build.mjs # generate + * node scripts/component-vars.build.mjs --check # fail if stale or off-convention + */ + +import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'; +import { dirname, isAbsolute, join, relative, sep } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import * as sass from 'sass'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const KIT = join(ROOT, 'projects/ui-kit'); +const STYLES = join(KIT, 'styles'); +const MANIFEST_FILE = join(ROOT, 'storybook/generated/ui-config.json'); +const SCSS_OUT = join(STYLES, 'component-vars.scss'); +const JSON_OUT = join(ROOT, 'figma/component-vars.json'); +// Served by Storybook (staticDirs) for the doc page's download button. Byproduct: +// regenerated on every build, never committed. +const PUBLIC_COPY = join(ROOT, 'storybook/public/component-vars.scss'); + +const TOKENS_CONFIG = JSON.parse(readFileSync(join(ROOT, 'tokens.config.json'), 'utf8')); +const TOKENS_SRC = isAbsolute(TOKENS_CONFIG.sourceRoot) + ? TOKENS_CONFIG.sourceRoot + : join(ROOT, TOKENS_CONFIG.sourceRoot); + +// The Figma file that OWNS the variable collections (see CLAUDE.md): in the UI Kit +// they are `remote`, hence read-only. +const TARGET_FILE_KEY = 'lH4jhyZFkIeJ1Ob1tlY7Wm'; +const COLLECTION = 'component-vars'; +const REM_BASE = 16; + +// --- Naming vocabulary ------------------------------------------------------ +// `--ui-{family}[-{part}]-{property}[-{modifier}]`, modifier last — same rule as the +// tokens (`actions-high-surface-hover`). An unparsable name fails the build. + +const MODIFIERS = new Set([ + 'small', 'large', 'tiny', 'compact', 'dense', + '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', + 'active', 'disabled', 'hover', 'focus', 'pressed', 'checked', 'selected', 'indeterminate', + 'vertical', 'horizontal', 'inline', 'rounded', 'square', 'open', 'closed', 'collapsed', 'expanded', + 'flush', 'first', 'last', 'dark', 'light', 'overlay', 'inset', 'removable', + 'custom', 'multiline', 'side', 'edge', 'outside', 'default', +]); + +/** + * property → { French label, display group, Figma type + scopes }. + * `bindable: false` = no Figma variable equivalent (duration, easing, cursor…). + */ +const PROPERTIES = { + // Dimensions + size: { fr: 'Taille', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + height: { fr: 'Hauteur', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'min-height': { fr: 'Hauteur minimale', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'max-height': { fr: 'Hauteur maximale', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + width: { fr: 'Largeur', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'min-width': { fr: 'Largeur minimale', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'max-width': { fr: 'Largeur maximale', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + length: { fr: 'Longueur', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + thickness: { fr: 'Épaisseur', group: 'dimensions', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'aspect-ratio': { fr: 'Ratio', group: 'dimensions', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + + // Spacing + padding: { fr: 'Inset', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-x': { fr: 'Inset horizontal', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-y': { fr: 'Inset vertical', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-top': { fr: 'Inset haut', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-bottom': { fr: 'Inset bas', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-left': { fr: 'Inset gauche', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-right': { fr: 'Inset droit', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-inline': { fr: 'Inset horizontal', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-block': { fr: 'Inset vertical', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-inline-start': { fr: 'Inset de début', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + 'padding-inline-end': { fr: 'Inset de fin', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + gap: { fr: 'Espacement', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + indent: { fr: 'Retrait', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + overlap: { fr: 'Chevauchement', group: 'spacing', type: 'FLOAT', scopes: ['GAP'] }, + offset: { fr: 'Décalage', group: 'spacing', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'offset-x': { fr: 'Décalage horizontal', group: 'spacing', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + 'offset-y': { fr: 'Décalage vertical', group: 'spacing', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + distance: { fr: 'Distance', group: 'spacing', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'] }, + + // Borders & radii + radius: { fr: 'Rayon des coins', group: 'border', type: 'FLOAT', scopes: ['CORNER_RADIUS'] }, + 'stroke-width': { fr: 'Épaisseur de bordure', group: 'border', type: 'FLOAT', scopes: ['STROKE_FLOAT'] }, + 'border-width': { fr: 'Épaisseur de bordure', group: 'border', type: 'FLOAT', scopes: ['STROKE_FLOAT'] }, + 'focus-ring-width': { fr: "Épaisseur de l'anneau de focus", group: 'border', type: 'FLOAT', scopes: ['STROKE_FLOAT'] }, + + // Typography + 'font-size': { fr: 'Taille de police', group: 'typography', type: 'FLOAT', scopes: ['FONT_SIZE'] }, + 'line-height': { fr: 'Interligne', group: 'typography', type: 'FLOAT', scopes: ['LINE_HEIGHT'] }, + weight: { fr: 'Graisse', group: 'typography', type: 'STRING', scopes: ['FONT_WEIGHT', 'FONT_STYLE'] }, + + // Colours + color: { fr: 'Couleur', group: 'color', type: 'COLOR', scopes: ['ALL_FILLS'] }, + background: { fr: 'Fond', group: 'color', type: 'COLOR', scopes: ['FRAME_FILL', 'SHAPE_FILL'] }, + surface: { fr: 'Fond', group: 'color', type: 'COLOR', scopes: ['FRAME_FILL', 'SHAPE_FILL'] }, + stroke: { fr: 'Couleur de bordure', group: 'color', type: 'COLOR', scopes: ['STROKE_COLOR'] }, + shine: { fr: 'Teinte du reflet', group: 'color', type: 'COLOR', scopes: ['ALL_FILLS'] }, + fill: { fr: 'Remplissage', group: 'color', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + + // Motion + duration: { fr: 'Durée', group: 'motion', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + delay: { fr: 'Délai', group: 'motion', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + easing: { fr: 'Courbe de transition', group: 'motion', type: 'STRING', scopes: ['ALL_SCOPES'], bindable: false }, + scale: { fr: 'Échelle', group: 'motion', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + + // Misc + opacity: { fr: 'Opacité', group: 'misc', type: 'FLOAT', scopes: ['OPACITY'] }, + shadow: { fr: 'Ombre', group: 'misc', type: 'FLOAT', scopes: ['EFFECT_FLOAT'] }, + 'z-index': { fr: 'Plan de superposition', group: 'misc', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, + top: { fr: 'Décalage haut', group: 'spacing', type: 'FLOAT', scopes: ['WIDTH_HEIGHT'], bindable: false }, + cursor: { fr: 'Curseur', group: 'misc', type: 'STRING', scopes: ['ALL_SCOPES'], bindable: false }, + 'object-fit': { fr: 'Cadrage du média', group: 'misc', type: 'STRING', scopes: ['ALL_SCOPES'], bindable: false }, + columns: { fr: 'Colonnes', group: 'misc', type: 'FLOAT', scopes: ['ALL_SCOPES'], bindable: false }, +}; + +const GROUPS = [ + ['dimensions', 'Dimensions'], + ['spacing', 'Espacements'], + ['border', 'Bordures & rayons'], + ['typography', 'Typographie'], + ['color', 'Couleurs'], + ['motion', 'Motion'], + ['misc', 'Divers'], +]; + +/** A part that implies text: the colour then carries the TEXT_FILL scope. */ +const TEXT_PARTS = new Set(['label', 'text', 'content', 'title', 'subtitle', 'message', 'caption']); + +// --- 1. Values: read from the compiled CSS --------------------------------- + +function walk(dir, out = [], test = (n) => /^(ui|sp)-[\w-]+\.scss$/.test(n)) { + for (const e of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, e.name); + if (e.isDirectory()) { + if (e.name !== 'styles') walk(full, out, test); + } else if (test(e.name)) out.push(full); + } + return out; +} + +/** End of the `var(` opened at `start` (balanced parens). */ +function closingParen(text, start) { + let depth = 0; + for (let i = start; i < text.length; i++) { + if (text[i] === '(') depth++; + else if (text[i] === ')') { + depth--; + if (depth === 0) return i; + } + } + return -1; +} + +/** Every `var(--ui-x, default)` in a CSS string, nested ones included. */ +function extractHooks(css, out = new Map()) { + const re = /var\(\s*(--ui-[\w-]+)\s*(,|\))/g; + let m; + while ((m = re.exec(css))) { + const open = css.lastIndexOf('var(', m.index + 4); + const end = closingParen(css, open + 3); + if (end === -1) continue; + const inner = css.slice(open + 4, end); + const comma = inner.indexOf(','); + const fallback = comma === -1 ? null : inner.slice(comma + 1).trim(); + if (!out.has(m[1])) out.set(m[1], new Set()); + out.get(m[1]).add(fallback); + if (fallback) extractHooks(fallback, out); // hooks empilés (`--ui-icon-size`) + } + return out; +} + +const files = walk(KIT); +/** hook → { defaults:Set, files:Set } */ +const found = new Map(); +for (const file of files) { + const css = sass.compile(file, { loadPaths: [STYLES], style: 'expanded' }).css; + for (const [hook, defaults] of extractHooks(css)) { + if (!found.has(hook)) found.set(hook, { defaults: new Set(), files: new Set() }); + const entry = found.get(hook); + for (const d of defaults) entry.defaults.add(d); + entry.files.add(relative(ROOT, file).split(sep).join('/')); + } +} + +// --- 2. Metadata: read from the manifest (`///`) --------------------------- + +if (!existsSync(MANIFEST_FILE)) { + console.error('✗ storybook/generated/ui-config.json manquant — lance `npm run docs:config`.'); + process.exit(1); +} +const manifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf8')); + +/** hook → { role, component, category, entryPoint, family, scssVar, isMapEntry } */ +const meta = new Map(); +function collectMeta(resolved, path, ctx) { + if (!resolved) return; + if (resolved.cssVar?.startsWith('--ui-') && !meta.has(resolved.cssVar)) { + meta.set(resolved.cssVar, { ...ctx, scssVar: path, isMapEntry: path !== ctx.rowName }); + } + collectMeta(resolved.fallback, path, ctx); + for (const e of resolved.entries ?? []) collectMeta(e.value, `${path}['${e.key}']`, ctx); +} +for (const [component, data] of Object.entries(manifest.components)) { + if (!component.startsWith('ui-')) continue; + const parts = data.file.split('/'); + const i = parts.indexOf('ui-kit'); + const category = parts[i + 1]; + const entry = parts[i + 2]; + for (const row of [...data.vars, ...data.hooks]) { + collectMeta(row.default, row.name, { + role: row.role, + component, + category, + entryPoint: `@4sh/ui-kit/${category}/${entry}`, + family: entry.replace(/^ui-/, ''), + file: data.file, + rowName: row.name, + }); + } + // A custom property the component declares is keyed under its own name. + for (const row of data.hooks) { + if (row.name.startsWith('--ui-') && !meta.has(row.name)) { + meta.set(row.name, { + role: row.role, + component, + category, + entryPoint: `@4sh/ui-kit/${category}/${entry}`, + family: entry.replace(/^ui-/, ''), + file: data.file, + scssVar: row.name, + isMapEntry: false, + }); + } + } +} + +/** + * CROSS-CUTTING hooks: they belong to no component. Their `///` lives on the mixin + * that reads them (`styles/utils/_motion.scss`, `_overlay.scss`), which the doc + * generator does not scan — hence this list. They are meant for one element, never a + * global preset: a component with its own setting (ui-tooltip forces a fast duration) + * would win anyway. + */ +const SHARED_HOOKS = { + '--ui-motion-duration': 'Durée de la transition / de l’animation, pour un exemplaire.', + '--ui-motion-easing': 'Courbe de la transition, pour un exemplaire.', + '--ui-motion-delay': 'Délai avant démarrage, pour un exemplaire.', + '--ui-overlay-surface': 'Fond du panneau flottant, pour un exemplaire.', + '--ui-overlay-stroke': 'Couleur de bordure du panneau flottant, pour un exemplaire.', + '--ui-overlay-radius': 'Rayon des coins du panneau flottant, pour un exemplaire.', + '--ui-overlay-shadow': 'Ombre du panneau flottant, pour un exemplaire.', +}; + +/** + * Hooks the kit sets ON ITSELF (SCSS declaration, `setProperty`, `[style.--x]`), + * per component family. Declared by the family that reads it, it is plumbing: the + * component overwrites it, so a preset value would do nothing. Declared by a + * NEIGHBOURING family (ui-button-split reshaping a ui-button's corners), it stays a + * public handle. + */ +const declaredBy = new Map(); +for (const file of walk(KIT, [], (n) => /\.(scss|ts|html)$/.test(n))) { + const text = readFileSync(file, 'utf8'); + const family = /\/ui-kit\/[^/]+\/(ui-[^/]+)\//.exec(file)?.[1]?.replace(/^ui-/, '') ?? null; + const add = (name, kind) => { + if (!declaredBy.has(name)) declaredBy.set(name, new Map()); + declaredBy.get(name).set(family, kind); + }; + // Hardcoded in the SCSS: the component always overwrites. + for (const m of text.matchAll(/^\s*(--ui-[\w-]+)\s*:/gm)) add(m[1], 'scss'); + // Bound from an Angular input: only overwrites the instances that use it. + for (const m of text.matchAll(/setProperty\(\s*['"`](--ui-[\w-]+)/g)) add(m[1], 'input'); + for (const m of text.matchAll(/\[style\.(--ui-[\w-]+)(?:\.[\w%]+)?\]/g)) add(m[1], 'input'); +} + +/** + * Legacy names kept as a fallback for one version (see CHANGELOG). They sit in the + * middle of a default chain without being a real override level, so they are stripped + * before computing the shipped value. + */ +const DEPRECATED_ALIASES = new Set(['--ui-tabs-active-bar-color', '--ui-tabs-active-bar-size']); + +/** `var(--legacy, X)` → `X`, recursively, for deprecated aliases only. */ +function stripDeprecated(value) { + if (!value) return value; + let out = value; + for (const alias of DEPRECATED_ALIASES) { + let i; + while ((i = out.indexOf(`var(${alias},`)) !== -1) { + const end = closingParen(out, i + 3); + if (end === -1) break; + const inner = out.slice(i + 4 + alias.length + 1, end).trim(); + out = out.slice(0, i) + inner + out.slice(end + 1); + } + } + return out; +} + +// --- 3. Token index (to link the Figma aliases) --------------------------- + +const kebab = (parts) => + parts.filter(Boolean).join('-').toLowerCase() + .replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, ''); + +function leaves(node, path = [], out = []) { + if (!node || typeof node !== 'object') return out; + if ('$value' in node) return out.push({ path, token: node }), out; + for (const k of Object.keys(node)) if (!k.startsWith('$')) leaves(node[k], [...path, k], out); + return out; +} + +/** `--units-sm` → { collection: 'metrics', name: 'units/sm' } */ +const tokenIndex = new Map(); +/** Every valid DTCG reference (`{metrics.units.sm}`). */ +const tokenRefs = new Set(); +for (const col of TOKENS_CONFIG.collections) { + const modeSegs = new Set(); + for (const a of col.modeAxes ?? []) { + for (const k of Object.keys(a.map ?? {})) modeSegs.add(k); + if (a.default) modeSegs.add(a.default); + } + for (const f of col.files ?? []) { + for (const leaf of leaves(JSON.parse(readFileSync(join(TOKENS_SRC, f), 'utf8')))) { + const body = leaf.path.filter((p) => !modeSegs.has(p) && !/^mode[A-Z0-9]/.test(p)); + const cssVar = col.preserveCase + ? `--${[col.prefix, body.at(-1)].filter(Boolean).join('-')}` + : `--${kebab([col.prefix, ...body])}`; + if (!tokenIndex.has(cssVar)) { + tokenIndex.set(cssVar, { collection: col.id, name: body.join('/') }); + } + // DTCG reference as this script will write it — used to prove every emitted + // alias points at a token that really exists in the source files. + tokenRefs.add(`{${col.id}.${body.join('.')}}`); + } + } +} + +// --- 4. Name parsing + value ---------------------------------------------- + +function parseHook(hook, family) { + const rest = hook.replace(/^--ui-/, ''); + // A shared constant has no family: its whole name is `part` + `property`. + if (family !== null && !rest.startsWith(`${family}-`)) { + return { conforms: false, part: null, property: null, modifier: null }; + } + let segs = (family === null ? rest : rest.slice(family.length + 1)).split('-'); + const mods = []; + while (segs.length > 1 && MODIFIERS.has(segs.at(-1))) mods.unshift(segs.pop()); + let property = null; + for (let take = Math.min(3, segs.length); take >= 1; take--) { + const candidate = segs.slice(-take).join('-'); + if (PROPERTIES[candidate]) { + property = candidate; + segs = segs.slice(0, -take); + break; + } + } + return { + part: segs.length ? segs.join('-') : null, + property, + modifier: mods.length ? mods.join('-') : null, + conforms: Boolean(property), + }; +} + +const SINGLE_VAR = /^var\(\s*(--[\w-]+)\s*\)$/; +const NUMBER = /^(-?\d*\.?\d+)(px|rem|em|ch|%|s|ms|deg)?$/; + +/** + * Peels the `--ui-*` layers off a default chain to reach the value Figma has to hold. + * A component default now often routes through a shared constant + * (`var(--ui-form-field-height, var(--size-components-default))`): Figma only stores + * one value, and the meaningful one is the token at the end of the chain. + */ +function throughHooks(raw) { + let out = raw; + for (let guard = 0; guard < 6; guard++) { + const m = out?.match(/^var\(\s*(--ui-[\w-]+)\s*,/); + if (!m) return out; + const end = closingParen(out, 3); + if (end === -1 || end !== out.length - 1) return out; // not a single wrapping var() + out = out.slice(4 + m[1].length + 1, end).trim(); + } + return out; +} + +/** Figma value of a CSS default: token alias, literal, or not representable. */ +function figmaValue(rawInput, expectedType) { + const raw = throughHooks(rawInput); + if (!raw) return { type: 'NONE', reason: "hook sans défaut : point de surcharge pur, le composant ne pose rien." }; + + const single = raw.match(SINGLE_VAR); + if (single) { + const target = tokenIndex.get(single[1]); + if (target) { + return { type: 'ALIAS', collection: target.collection, name: target.name, cssVar: single[1] }; + } + return { type: 'UNSUPPORTED', raw, reason: `\`${single[1]}\` n'est pas un token du pipeline : rien à aliaser.` }; + } + + const num = raw.match(NUMBER); + if (num) { + const n = Number(num[1]); + const unit = num[2] ?? null; + if (expectedType === 'FLOAT') { + // The source tokens are all in px, so align on that (1rem = 16px) and drop the + // units a Figma variable cannot hold: `em`/`ch` depend on the element's font, + // `%` on its container. + if (unit === 'em' || unit === 'ch' || unit === '%') { + return { + type: 'UNSUPPORTED', + raw, + reason: `unité relative \`${unit}\` : sa valeur dépend du contexte de rendu, une variable Figma ne peut pas la porter.`, + }; + } + const px = unit === 'rem' ? n * REM_BASE : n; + return { + type: 'FLOAT', + value: px, + css: unit === 'rem' ? `${px}px` : raw, + unit: unit ?? 'nombre', + raw, + }; + } + return { type: 'STRING', value: raw }; + } + + if (/^#|^rgb|^hsl/.test(raw)) return { type: 'COLOR', value: raw }; + if (/^calc\(|\) var\(|var\(.*\) /.test(raw)) { + return { + type: 'UNSUPPORTED', + raw, + reason: /^calc\(/.test(raw) + ? 'expression calc() : une variable Figma ne porte pas de calcul.' + : 'raccourci CSS à plusieurs valeurs : une variable Figma n’en porte qu’une.', + }; + } + return { type: 'STRING', value: raw }; +} + +// --- 5. Assembly ---------------------------------------------------------- + +const problems = []; +const undocumented = []; +const hooks = []; + +/** + * Shared constants of `_ui-config.scss` (`--ui-focus-ring-width`, + * `--ui-form-control-size`…). They belong to no component: they sit between the token + * and the component hook, and reach every consumer because components interpolate + * them. Their role comes from the `///` there, via the manifest's `shared` section. + */ +for (const [scssVar, entry] of Object.entries(manifest.shared ?? {})) { + const hook = entry.default?.cssVar; + if (!hook?.startsWith('--ui-')) continue; + meta.set(hook, { + role: entry.role, + component: 'ui-config', + category: 'shared', + entryPoint: '@4sh/ui-kit/styles', + family: null, + file: 'projects/ui-kit/styles/settings/_ui-config.scss', + scssVar, + isMapEntry: false, + group: entry.group, + }); +} + +/** family → { component, category, entryPoint, family, file } */ +const familyIndex = new Map(); +for (const [component, data] of Object.entries(manifest.components)) { + if (!component.startsWith('ui-')) continue; + const parts = data.file.split('/'); + const i = parts.indexOf('ui-kit'); + const family = parts[i + 2].replace(/^ui-/, ''); + if (!familyIndex.has(family)) { + familyIndex.set(family, { + component: parts[i + 2], + category: parts[i + 1], + entryPoint: `@4sh/ui-kit/${parts[i + 1]}/${parts[i + 2]}`, + family, + file: data.file, + }); + } +} + +for (const [hook, { defaults, files: seen }] of [...found].sort(([a], [b]) => a.localeCompare(b))) { + const shared = SHARED_HOOKS[hook]; + let info = shared + ? { + role: shared, + component: hook.startsWith('--ui-motion-') ? 'système de motion' : 'panneaux flottants', + category: 'global', + entryPoint: hook.startsWith('--ui-motion-') ? '@4sh/ui-kit/motion' : '@4sh/ui-kit/overlay', + family: hook.startsWith('--ui-motion-') ? 'motion' : 'overlay', + file: hook.startsWith('--ui-motion-') + ? 'projects/ui-kit/styles/utils/_motion.scss' + : 'projects/ui-kit/styles/utils/_overlay.scss', + scssVar: null, + isMapEntry: false, + } + : meta.get(hook); + if (!info) { + // No `///`: only acceptable for plumbing the component sets on itself + // (`[style.--x]`, `setProperty`). Otherwise it is an undocumented public hook, + // which is an error. + const family = [...(declaredBy.get(hook)?.keys() ?? [])].find( + (f) => f && hook.startsWith(`--ui-${f}-`) + ); + const ref = family ? familyIndex.get(family) : null; + if (!ref) { + undocumented.push(`${hook} (${[...seen].join(', ')})`); + continue; + } + info = { role: null, ...ref, scssVar: null, isMapEntry: true }; + } + const parts = parseHook(hook, info.family); + if (!parts.conforms) { + problems.push( + `${hook} (${info.file}) — hors convention \`--ui-${info.family}[-partie]-propriété[-modifieur]\`.` + ); + } + const spec = PROPERTIES[parts.property] ?? {}; + const values = [...new Set([...defaults].map(stripDeprecated))]; + // A value that references another hook carries the chain with it, so declaring it + // has no side effect. What rules a variable out is having SEVERAL defaults (pinning + // one would flatten the other variants), or a default that resolves through a + // PRIVATE variable — `--_x` means nothing outside the component, so restating it + // globally would resolve to nothing and break the property. + const contextual = values.length > 1; + const privateDefault = values.some((v) => /var\(\s*--_/.test(v ?? '')); + const declKind = declaredBy.get(hook)?.get(info.family) ?? null; + const plumbing = declKind !== null; + + hooks.push({ + name: hook, + component: info.component, + category: info.category, + entryPoint: info.entryPoint, + family: info.family, + part: parts.part, + property: parts.property, + modifier: parts.modifier, + group: spec.group ?? 'misc', + description: + info.role && !info.isMapEntry + ? info.role + : `${spec.fr ?? 'Valeur'} de \`${info.component}\`${parts.part ? ` — ${parts.part}` : ''}${parts.modifier ? ` (variante \`${parts.modifier}\`)` : ''}.`, + descriptionSource: info.role && !info.isMapEntry ? 'scss' : 'derived', + scssVar: info.scssVar, + themable: !contextual && !plumbing && !shared && !privateDefault, + plumbing, + plumbingKind: declKind, + sharedHandle: Boolean(shared), + reason: + declKind === 'scss' + ? 'posée par le composant sur son propre élément : la déclarer dans un thème resterait sans effet.' + : declKind === 'input' + ? 'pilotée par une entrée Angular du composant : le thème ne vaudrait que pour les exemplaires qui ne s’en servent pas.' + : shared + ? 'poignée transverse (motion / panneaux flottants) : à poser sur l’élément visé — un composant qui a son propre réglage l’emporte.' + : contextual + ? `${values.length} défauts selon la variante rendue : en déclarer un seul écraserait les autres.` + : privateDefault + ? 'surcharge d’un exemplaire : son défaut passe par une variable interne au composant, qui ne résout rien depuis l’extérieur.' + : null, + default: values[0] ?? null, + defaults: values, + files: [...seen], + figma: { + bindable: spec.bindable !== false, + type: spec.type ?? 'STRING', + scopes: + spec.type === 'COLOR' && TEXT_PARTS.has(parts.part ?? '') ? ['TEXT_FILL'] : (spec.scopes ?? ['ALL_SCOPES']), + name: [info.family, parts.part, [parts.property, parts.modifier].filter(Boolean).join('-')] + .filter(Boolean) + .join('/'), + value: figmaValue(values[0] ?? null, spec.type ?? 'STRING'), + }, + }); +} + +// Figma name collision: a variable cannot also be a folder. +const figmaNames = new Set(hooks.map((h) => h.figma.name)); +for (const n of figmaNames) { + for (const other of figmaNames) { + if (other !== n && other.startsWith(`${n}/`)) { + problems.push(`Collision Figma : « ${n} » est à la fois une variable et un dossier (« ${other} »).`); + } + } +} + +// --- 6. component-vars.scss ----------------------------------------------- + +const CAT_LABELS = { + actions: 'Actions', + base: 'Base', + forms: 'Formulaires', + informative: 'Informatif', + layout: 'Mise en page', + navigation: 'Navigation', + table: 'Tableaux', +}; + +function themeScss() { + const themable = hooks.filter((h) => h.themable); + const perInstance = hooks.filter((h) => !h.themable); + + // Vertical alignment across the WHOLE file: one column of values. + const width = Math.max(...themable.map((h) => h.name.length)) + 1; + const line = (h) => ` ${h.name}:${' '.repeat(width - h.name.length)}${h.default};`; + + const L = []; + L.push('// ====================================================================='); + L.push('// @4sh/ui-kit — component variables.'); + L.push('//'); + L.push('// Copy into `src/styles/presets/`, import it from `main.scss`, change what you'); + L.push('// need. As shipped it changes nothing: it restates the defaults.'); + L.push('//'); + L.push('// @use \'presets/component-vars\'; // src/styles/main.scss'); + L.push('//'); + L.push('// Load order is irrelevant: the kit only reads these names, never declares them.'); + L.push('// For a value shared by the whole kit (spacing, radius, control size), retune the'); + L.push('// design token instead — it follows brand, light/dark and viewport.'); + L.push('//'); + L.push(`// Generated by \`npm run docs:config\`, values read from the compiled CSS — do not`); + L.push(`// edit here. ${themable.length} variables; ${perInstance.length} more exist whose value depends on the`); + L.push('// rendered variant, listed in each component’s “Theming” section in Storybook.'); + L.push('// ====================================================================='); + L.push(''); + L.push(':root {'); + + const byCategory = new Map(); + for (const h of themable) { + if (!byCategory.has(h.category)) byCategory.set(h.category, new Map()); + const comps = byCategory.get(h.category); + if (!comps.has(h.component)) comps.set(h.component, []); + comps.get(h.component).push(h); + } + + for (const [category, comps] of [...byCategory].sort(([a], [b]) => a.localeCompare(b))) { + L.push(''); + L.push(` // ══════════════════════════════════════════════════════════════════`); + L.push(` // ${CAT_LABELS[category] ?? category}`); + L.push(` // ══════════════════════════════════════════════════════════════════`); + + for (const [component, list] of [...comps].sort(([a], [b]) => a.localeCompare(b))) { + L.push(''); + L.push(` // ── ${component} ──`); + for (const [group, label] of GROUPS) { + const rows = list.filter((h) => h.group === group); + if (!rows.length) continue; + rows.sort((a, b) => a.name.localeCompare(b.name)); + L.push(` // ${label}`); + for (const h of rows) L.push(line(h)); + } + } + } + + L.push('}'); + + return `${L.join('\n')}\n`; +} + +// --- 7. figma/component-vars.json (DTCG, same shape as the token files) ---- + +/** DTCG `$type`, aligned on the vocabulary of the token files. */ +function dtcgType(value, figmaType) { + if (figmaType === 'COLOR') return 'color'; + if (value.type === 'ALIAS') return figmaType === 'FLOAT' ? 'dimension' : 'string'; + if (value.type === 'FLOAT') return /px|rem|em/.test(value.raw ?? '') ? 'dimension' : 'number'; + return 'string'; +} + +/** DTCG reference of an alias: `{metrics.units.sm}` (mode segment stripped). */ +function dtcgRef(value) { + return `{${value.collection}.${value.name.split('/').join('.')}}`; +} + +function figmaJson() { + // Internal plumbing is not a design decision: it has no place in a Figma + // collection (the component overwrites it anyway). + const importable = hooks.filter( + (h) => h.figma.bindable && !h.plumbing && !['UNSUPPORTED', 'NONE'].includes(h.figma.value.type) + ); + const skipped = hooks.filter( + (h) => !h.figma.bindable || h.plumbing || ['UNSUPPORTED', 'NONE'].includes(h.figma.value.type) + ); + + // Nested tree: `toggle/track/width` → toggle → track → width. + const tree = {}; + for (const h of importable) { + const path = h.figma.name.split('/'); + let node = tree; + for (const seg of path.slice(0, -1)) { + if (node[seg] && '$value' in node[seg]) { + problems.push( + `Collision DTCG : « ${seg} » est à la fois un token et un groupe (${h.figma.name}).` + ); + } + node = node[seg] ??= {}; + } + const leaf = path.at(-1); + const v = h.figma.value; + // An alias that does not resolve would import silently as an empty value. + if (v.type === 'ALIAS' && !tokenRefs.has(dtcgRef(v))) { + problems.push(`Alias mort : ${h.name} → ${dtcgRef(v)} n'existe pas dans src/design-tokens.`); + } + node[leaf] = { + $value: v.type === 'ALIAS' ? dtcgRef(v) : v.type === 'FLOAT' ? (v.css ?? String(v.value)) : String(v.value), + $type: dtcgType(v, h.figma.type), + $description: h.description, + $extensions: { + 'com.figma': { + resolvedType: h.figma.type, + scopes: h.figma.scopes, + codeSyntax: { WEB: `var(${h.name})` }, + }, + 'com.4sh.ui-kit': { + cssVar: h.name, + component: h.component, + entryPoint: h.entryPoint, + descriptionSource: h.descriptionSource, + ...(h.themable ? {} : { perInstance: true, note: h.reason }), + }, + }, + }; + } + + return { + $description: COLLECTION, + $extensions: { + 'com.4sh.ui-kit': { + generatedBy: 'scripts/component-vars.build.mjs', + what: + "Les hooks `--ui-*` des composants, au format DTCG, prêts à être importés dans UNE collection Figma dédiée. Ce ne sont pas des design tokens : ils appartiennent à un composant, et la plupart ne font que POINTER vers un token (alias `{collection.chemin}`).", + figmaCollection: COLLECTION, + targetFileKey: TARGET_FILE_KEY, + modes: ['Mode 1'], + counts: { + tokens: importable.length, + alias: importable.filter((h) => h.figma.value.type === 'ALIAS').length, + literal: importable.filter((h) => h.figma.value.type !== 'ALIAS').length, + skipped: skipped.length, + derivedDescriptions: importable.filter((h) => h.descriptionSource === 'derived').length, + }, + aliasTargets: [ + ...new Set( + importable.filter((h) => h.figma.value.type === 'ALIAS').map((h) => h.figma.value.collection) + ), + ].sort(), + skipped: skipped.map((h) => ({ + cssVar: h.name, + component: h.component, + reason: h.figma.value.reason ?? h.reason ?? 'sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio).', + })), + }, + }, + ...tree, + }; +} + +// --- 8. Write / check ----------------------------------------------------- + +const scssText = themeScss(); +const jsonText = `${JSON.stringify(figmaJson(), null, 2)}\n`; +const check = process.argv.includes('--check'); + +if (problems.length) { + console.error(`✗ ${problems.length} problème(s) de nommage :`); + for (const p of problems) console.error(` - ${p}`); + if (check) process.exit(1); +} +if (undocumented.length) { + console.error(`✗ ${undocumented.length} hook(s) sans commentaire \`///\` (invisibles dans le thème) :`); + for (const u of undocumented) console.error(` - ${u}`); + if (check) process.exit(1); +} + +if (check) { + const stale = [ + [SCSS_OUT, scssText], + [JSON_OUT, jsonText], + ].filter(([f, t]) => (existsSync(f) ? readFileSync(f, 'utf8') : null) !== t); + if (stale.length) { + console.error( + `✗ périmé(s) : ${stale.map(([f]) => relative(ROOT, f).split(sep).join('/')).join(', ')} — lance \`npm run docs:config\`.` + ); + process.exit(1); + } + console.log('✓ component-vars.scss et component-vars.json à jour.'); +} else { + mkdirSync(dirname(SCSS_OUT), { recursive: true }); + mkdirSync(dirname(JSON_OUT), { recursive: true }); + writeFileSync(SCSS_OUT, scssText); + writeFileSync(JSON_OUT, jsonText); + // Served by Storybook for the doc page's download button. + mkdirSync(dirname(PUBLIC_COPY), { recursive: true }); + writeFileSync(PUBLIC_COPY, scssText); + sass.compileString(scssText, { loadPaths: [STYLES] }); // never ship a broken file + const c = figmaJson().$extensions['com.4sh.ui-kit'].counts; + console.log( + `✓ component-vars.scss : ${hooks.filter((h) => h.themable).length} hooks exposés (+ ${hooks.length - hooks.filter((h) => h.themable).length} hors réglage global).` + ); + console.log( + `✓ figma/component-vars.json (DTCG) : ${c.tokens} tokens (${c.alias} alias, ${c.literal} valeurs), ${c.skipped} écartés.` + ); +} diff --git a/storybook/blocks/config-table.js b/storybook/blocks/config-table.js index e1b0945..c824115 100644 --- a/storybook/blocks/config-table.js +++ b/storybook/blocks/config-table.js @@ -107,6 +107,8 @@ function useResolvedVars(names) { // Texte brut : une infobulle native ne rend ni markdown ni HTML. const ORIGIN_TOOLTIP = 'Table générée par « npm run docs:config ». Le contrat, c’est le nom de la variable et son rôle. ' + + 'La colonne « Hook exposé » donne la custom property à poser pour retoucher la valeur sans forker ' + + 'le SCSS (mode package) : sur :root pour tous les exemplaires, sur un sélecteur pour une seule zone. ' + 'La colonne « Défaut (starter) » n’est que le réglage livré par ce starter : la rebinder ne demande ' + 'aucune reprise de doc. La valeur résolue est mesurée dans le thème, la marque et le viewport actifs.'; @@ -176,8 +178,27 @@ function chain(steps) { ); } +/** Un binding `var(--ui-x, défaut)` : le hook exposé au consommateur, sinon null. */ +function hookOf(resolved) { + return resolved?.cssVar?.startsWith('--ui-') ? resolved.cssVar : null; +} + +/** + * Cellule « Hook exposé » : le point de surcharge en mode package. Le composant ne + * déclare jamais ce nom, donc le poser n'importe où au-dessus (`:root`, un + * sélecteur, l'élément) suffit à gagner. + */ +function hookCell(resolved) { + const hook = hookOf(resolved); + if (!hook) return el('td', { style: { color: 'var(--sb-text-subtle)' } }, '—'); + return el('td', null, el('code', null, hook)); +} + /** Cellule « Défaut (starter) ». */ -function defaultCell(resolved) { +function defaultCell(row) { + // Derrière un hook, le réglage livré est son FALLBACK : c'est lui qu'on décrit + // (badge + chaîne d'indirections), le nom du hook ayant sa propre colonne. + const resolved = hookOf(row) ? (row.fallback ?? row) : row; const kind = resolved.steps.some((s) => s.via === 'shared') ? 'shared' : resolved.kind; // Map : lister les clés plutôt que le mot « map », que le badge dit déjà. const inlineItems = @@ -193,11 +214,7 @@ function defaultCell(resolved) { : el( 'code', null, - resolved.cssVar - ? // Un hook `var(--x, défaut)` : montrer le défaut, c'est lui qui s'applique - // tant que la custom property n'est pas posée par le consommateur. - `var(${resolved.cssVar}${resolved.fallback ? `, ${resolved.fallback.cssVar ? `var(${resolved.fallback.cssVar})` : resolved.fallback.literal}` : ''})` - : (resolved.literal ?? 'map') + resolved.cssVar ? `var(${resolved.cssVar})` : (resolved.literal ?? 'map') ); return el('td', null, head, badge(kind), chain(resolved.steps)); @@ -229,12 +246,12 @@ function valueCell(resolved, values) { } /** Lignes filles d'une map : une par entrée (`default.height → 44px`). */ -function mapRows(name, resolved, values, prefix = '') { +function mapRows(name, resolved, values, withHook, prefix = '') { const rows = []; for (const entry of resolved.entries ?? []) { const path = `${prefix}${entry.key}`; if (entry.value?.kind === 'map') { - rows.push(...mapRows(name, entry.value, values, `${path}.`)); + rows.push(...mapRows(name, entry.value, values, withHook, `${path}.`)); continue; } rows.push( @@ -247,6 +264,7 @@ function mapRows(name, resolved, values, prefix = '') { el('code', null, `${path}`) ), el('td', { style: { color: 'var(--sb-text-subtle)' } }, '↳ entrée de map'), + ...(withHook ? [entry.value ? hookCell(entry.value) : el('td', null, '—')] : []), entry.value ? defaultCell(entry.value) : el('td', null, '—'), entry.value ? valueCell(entry.value, values) : el('td', null, '—') ) @@ -255,7 +273,11 @@ function mapRows(name, resolved, values, prefix = '') { return rows; } -function table(caption, rows, values) { +/** + * `withHook` : la table des `$var` porte la colonne du hook exposé. La table des + * custom properties ne l'a pas — son nom de variable EST déjà le hook. + */ +function table(caption, rows, values, withHook) { return el( 'div', null, @@ -269,9 +291,10 @@ function table(caption, rows, values) { el( 'tr', null, - el('th', { style: { width: 230 } }, 'Variable'), + el('th', { style: { width: 200 } }, 'Variable'), el('th', null, 'Rôle'), - el('th', { style: { width: 240 } }, 'Défaut (starter)'), + ...(withHook ? [el('th', { style: { width: 250 } }, 'Hook exposé')] : []), + el('th', { style: { width: 200 } }, 'Défaut (starter)'), el('th', { style: { width: 130 } }, 'Valeur résolue') ) ), @@ -284,16 +307,69 @@ function table(caption, rows, values) { { key: row.name }, el('td', null, el('code', null, row.name)), el('td', null, inlineCode(row.role)), + ...(withHook ? [hookCell(row.default)] : []), defaultCell(row.default), valueCell(row.default, values) ), - ...mapRows(row.name, row.default, values), + ...mapRows(row.name, row.default, values, withHook), ]) ) ) ); } +/** + * — les constantes mutualisées de + * `_ui-config.scss` pour un groupe. Même contrat que ConfigTable : le rôle vient du + * `///`, la valeur est mesurée, et la colonne « Hook exposé » donne le nom à poser + * pour retoucher toute la catégorie sans recompiler. + */ +export function SharedConfigTable({ group, prefix, exclude }) { + const rows = React.useMemo(() => { + const skip = exclude ?? []; + return Object.entries(manifest.shared ?? {}) + .filter(([name, entry]) => entry.group === group) + .filter(([name]) => (prefix ? name.startsWith(`$${prefix}`) : true)) + .filter(([name]) => !skip.some((p) => name.startsWith(`$${p}`))) + .map(([name, entry]) => ({ name, role: entry.role, default: entry.default })); + }, [group, prefix, exclude && exclude.join('|')]); + + const names = React.useMemo(() => { + const all = []; + for (const row of rows) collectCssVars(row.default, all); + return [...new Set(all)].sort(); + }, [rows]); + + const [scopeRef, values] = useResolvedVars(names); + + if (!rows.length) { + return el( + 'p', + null, + el('strong', null, `SharedConfigTable : aucune constante pour le groupe « ${group} ». `), + 'Lance ', + el('code', null, 'npm run docs:config'), + '.' + ); + } + + return el( + 'div', + { ref: scopeRef }, + el( + 'p', + { style: { margin: '0 0 12px', fontSize: 12, color: 'var(--sb-text-subtle)' } }, + el( + 'span', + { title: ORIGIN_TOOLTIP, style: { cursor: 'help', borderBottom: '1px dotted currentColor' } }, + 'ⓘ générée depuis ', + el('code', null, manifest.$source.shared) + ) + ), + table(null, rows, values, true) + ); +} + export function ConfigTable({ of, only, hooks = true, label }) { const component = manifest.components[of]; @@ -347,9 +423,9 @@ export function ConfigTable({ of, only, hooks = true, label }) { ), // `label` : à passer quand une page documente plusieurs composants, pour // qu'on sache à quel `.scss` chaque table appartient. - vars.length ? table(label ? el('code', null, label) : null, vars, values) : null, + vars.length ? table(label ? el('code', null, label) : null, vars, values, true) : null, hookRows.length - ? table(label ? ['Custom properties exposées — ', el('code', { key: label }, label)] : 'Custom properties exposées', hookRows, values) + ? table(label ? ['Custom properties exposées — ', el('code', { key: label }, label)] : 'Custom properties exposées', hookRows, values, false) : null ); } diff --git a/storybook/docs/config/component-config.mdx b/storybook/docs/config/component-config.mdx index 2568201..047293e 100644 --- a/storybook/docs/config/component-config.mdx +++ b/storybook/docs/config/component-config.mdx @@ -32,6 +32,12 @@ composant. À la compilation (SASS) projects/ui-kit/styles/settings/_ui-config.scss + + Hooks --ui-* + La même valeur structurelle, mais retouchable sans recompiler le SCSS, le seul levier disponible en mode package + À l'exécution (CSS) + @4sh/ui-kit/styles/component-vars.scss + @@ -174,12 +180,66 @@ Rebinder une variable dans un projet ne demande donc aucune reprise de doc. Un seul composant (ex : anneau plus épais sur le radio uniquement) Remplacer la valeur de la variable locale en tête du .scss du composant. + + Sans accès au SCSS (mode package) ou une seule zone de l'écran + Poser le hook --ui-* de la valeur : sur :root pour tout le projet, sur un sélecteur pour cette zone. Voir ci-dessous. + -> Deux leviers, c'est tout. Les composants font @use 'utils' (qui expose -> `ui-config`) et lisent chaque valeur dans une variable **locale** ; éditer `_ui-config.scss` -> se répercute donc partout, et remplacer la variable locale n'affecte que le composant. +> Les deux premiers leviers sont des leviers de **compilation** : ils supposent d'avoir le SCSS +> du kit sous la main — donc le mode schematics, où le code des composants est copié dans le +> projet. En mode package le SCSS est déjà compilé : c'est le rôle des hooks. + +
+ +## Mode package : les hooks `--ui-*` + +En mode package, `_ui-config.scss` et les variables locales des composants sont **déjà +compilés** : les rebinder est impossible. Chaque valeur structurelle est donc lue *à travers* +une custom property, dont le défaut est le réglage livré : + +```scss +// dans ui-button.scss +$radius: var(--ui-button-radius, var(--radius-sm)); // ← le hook, puis le défaut +$sizes: ('small': (height: var(--ui-button-height-small, var(--size-components-sm)), …)); +``` + +Le composant ne **déclare** jamais ces noms : il ne fait que les lire. Les poser n'importe où +au-dessus suffit donc à gagner, du plus large au plus ciblé : + +```scss +:root { --ui-button-height-small: 24px; } /* tous les boutons small */ +.toolbar ui-button { --ui-button-height-small: 24px; } /* cette zone seulement */ +``` + +Trois règles pour choisir le bon levier : + +- **Une couleur** se retouche par son **design token** (`--actions-high-surface-default`), pas + par un hook : le token suit le clair/sombre et les 3 marques, et son contraste est vérifié. + Les rares hooks de couleur exposés (voile de masque, marqueur de chargement, reflet de + squelette, remplissage de notation) sont là pour repeindre **un exemplaire**, pas pour + rethémer. +- **Une valeur partagée par tout le kit** (espacement, rayon, taille de contrôle) se retouche + par son token `--units-*` / `--radius-*` / `--size-components-*` : inutile de poser 40 hooks. +- **Le hook** sert quand un seul composant, une seule taille ou une seule zone doit dévier. + +`@4sh/ui-kit/styles/component-vars.scss` rassemble **tous** les hooks réglables, déclarés à la valeur +livrée, groupés par catégorie → composant → type de propriété, valeurs alignées. On copie le +fichier dans le projet, on le charge après `styles.css`, et on change ce qu'on veut. Tel quel il +ne change rien : il redéclare les défauts. Il est téléchargeable depuis +**Spécifications → Thème & Système de Tokens**. + +Le fichier est **généré** depuis les `.scss` (`npm run docs:config`), et ses valeurs sont lues +dans le CSS réellement compilé — donc jamais périmées. Quelques hooks n'y figurent pas, parce +qu'un réglage global n'a pas de sens pour eux : ceux dont le défaut dépend de la variante rendue +(`--ui-button-radius` vaut un autre rayon en `rounded`) et ceux que le composant se pose +lui-même. La colonne « Hook exposé » de chaque composant les donne quand même. + +Chaque section « Theming » de composant affiche le hook de chaque valeur dans sa colonne +**« Hook exposé »**. Une poignée de custom properties `--ui-*` n'y figure pas : celles que le +composant se pose **à lui-même** pour piloter ses sous-éléments (les surcharger revient à lutter +contre lui) : `component-vars.scss` ne les propose pas.
diff --git a/storybook/docs/config/config-actions.mdx b/storybook/docs/config/config-actions.mdx index 2062054..0abc4fa 100644 --- a/storybook/docs/config/config-actions.mdx +++ b/storybook/docs/config/config-actions.mdx @@ -1,4 +1,5 @@ import { Meta } from '@storybook/addon-docs/blocks'; +import { SharedConfigTable } from '../../blocks/config-table'; @@ -11,21 +12,4 @@ les boutons / actions à la fois. > Consommée par : `ui-button`. Retour au > [fonctionnement & sommaire](?path=/docs/components-configuration--docs). - - - - - - - - - - - - - - - - - -
VariableRôleDéfautValeur
$action-focus-ring-widthAnneau de focus des boutons / actions$focus-ring-width (global)2px
+ diff --git a/storybook/docs/config/config-forms.mdx b/storybook/docs/config/config-forms.mdx index c0c434e..7c0d9e4 100644 --- a/storybook/docs/config/config-forms.mdx +++ b/storybook/docs/config/config-forms.mdx @@ -1,4 +1,5 @@ import { Meta } from '@storybook/addon-docs/blocks'; +import { SharedConfigTable } from '../../blocks/config-table'; @@ -12,99 +13,18 @@ globale et peut être surchargée pour **tous** les contrôles de formulaire à > `ui-input-mask`, `ui-input-number`, `ui-textarea`). Retour au > [fonctionnement & sommaire](?path=/docs/components-configuration--docs). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRôleDéfautValeur
$form-control-sizeTaille de la boîte checkbox / du cercle radiovar(--size-components-2xs)24px (22px mobile)
$form-control-gapEspace entre le contrôle et son labelvar(--units-sm)8px
$form-focus-ring-widthAnneau de focus des contrôles de formulaire$focus-ring-width (global)2px
$form-field-heightHauteur de la boîte de champ (input, select, etc.)var(--size-components-default)48px (44px mobile)
$form-field-height-smallHauteur de la boîte de champ variante smallvar(--size-components-sm)40px (36px mobile)
$form-field-radiusRayon de bordure de la boîte de champvar(--radius-sm)4px
$form-field-padding-xPadding horizontal de la boîte de champvar(--units-md)12px
$form-field-gapEspace entre les éléments dans la boîte de champ (input + affixes)var(--units-sm)8px
$form-field-stroke-widthÉpaisseur de bordure de la boîte de champ$control-stroke-width (global)2px
$form-field-focus-ring-widthAnneau de focus de la boîte de champ$form-focus-ring-width (catégorie forms)2px
$form-textarea-min-heightHauteur minimale de la boîte multiligne (ui-textarea)80px80px
$form-textarea-min-height-smallHauteur minimale multiligne variante small64px64px
$form-textarea-padding-yPadding vertical de la boîte multilignevar(--units-default)16px
$form-textarea-padding-y-smallPadding vertical multiligne variante smallvar(--units-md)12px
+## Contrôles (`$form-control-*`) + + + +## Boîte de champ (`$form-field-*`) + + + +## Multiligne (`$form-textarea-*`) + + + +## Focus + + diff --git a/storybook/docs/config/config-global.mdx b/storybook/docs/config/config-global.mdx index 125cf7f..0073a57 100644 --- a/storybook/docs/config/config-global.mdx +++ b/storybook/docs/config/config-global.mdx @@ -1,4 +1,5 @@ import { Meta } from '@storybook/addon-docs/blocks'; +import { SharedConfigTable } from '../../blocks/config-table'; @@ -10,97 +11,21 @@ Constantes structurelles qui s'appliquent à **tous** les composants interactifs > Fichier source : `projects/ui-kit/styles/settings/_ui-config.scss` · exposé via `@use 'utils'`. > Retour au [fonctionnement & sommaire](?path=/docs/components-configuration--docs). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRôleToken par défautValeur
$focus-ring-widthÉpaisseur de l'anneau de focus (box-shadow sur :focus-visible)var(--units-2xs)2px
$control-stroke-widthÉpaisseur de bordure des contrôles (bouton, boîte checkbox, cercle radio)var(--stroke-default)2px
$control-transition-durationDurée des transitions d'état (via control-transition())var(--transition-fast)80ms
$control-transition-easingCourbe des transitions d'étatvar(--transition-easeinout)ease-in-out
+ ## Panneaux flottants (`$overlay-panel-*`) Structure partagée des **panneaux flottants** (dropdown de `ui-menu`, calendrier de -`ui-datepicker`, futurs select/multiselect…), consommée via le mixin +`ui-datepicker`, select/multiselect…), consommée via le mixin `utils.overlay-panel($surface, $stroke)`. Les **couleurs** ne sont pas ici : chaque composant passe sa propre famille de tokens au mixin (menu → `navigation.*`, forms → `form.*`) — re-styler une famille n'impacte pas les autres. Échappatoire par instance via les custom properties `--ui-overlay-radius` / `--ui-overlay-shadow` / `--ui-overlay-surface` / `--ui-overlay-stroke`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRôleToken par défautValeur
$overlay-panel-radiusRayon du panneauvar(--radius-default)16px
$overlay-panel-paddingGouttière interne du panneauvar(--units-sm)8px
$overlay-panel-shadowOmbre portée du panneauvar(--shadow-default-md):
$overlay-panel-stroke-widthÉpaisseur de bordure du panneau$control-stroke-width2px
$overlay-panel-offsetÉcart déclencheur ↔ panneau (constante miroir côté TS : offsetY CDK)aligné sur --units-sm8px
+ -> Consommée par : `ui-menu`, `ui-datepicker`. +> Consommée par : `ui-menu`, `ui-datepicker`, `ui-select`, `ui-autocomplete`, `ui-popover`. ## Motion (`$motion-*`) @@ -108,63 +33,4 @@ Timing partagé du **système de motion** (presets d'entrée/sortie + transition Consommé par `utils.motion-transition()`, `base/_motion.scss` et la directive `[uiMotion]`. Surchargeable par élément via les custom properties `--ui-motion-*`. Voir **Foundations → Motion**. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRôleToken par défautValeur
$motion-duration-fastDurée « micro-feedback »var(--transition-fast)80ms
$motion-duration-baseDurée par défaut (enter/leave, collapse)var(--transition-default)180ms
$motion-duration-slowDurée « grande surface »var(--transition-slow)280ms
$motion-easing-standardCourbe symétrique (transitions d'état)var(--transition-easeinout)ease-in-out
$motion-easing-enterCourbe d'entrée (décélération)var(--transition-easeout)ease-out
$motion-easing-leaveCourbe de sortie (accélération)var(--transition-easein)ease-in
$motion-distanceDécalage des presets slide-* (structurel)0.5rem
$motion-scaleÉchelle de départ du preset zoom (structurel)0.96
+ diff --git a/storybook/docs/config/config-informative.mdx b/storybook/docs/config/config-informative.mdx index cffe6b4..7ca0fcb 100644 --- a/storybook/docs/config/config-informative.mdx +++ b/storybook/docs/config/config-informative.mdx @@ -1,4 +1,5 @@ import { Meta } from '@storybook/addon-docs/blocks'; +import { SharedConfigTable } from '../../blocks/config-table'; @@ -12,51 +13,9 @@ et `ui-avatar-group`. > Consommée par : `ui-avatar`, `ui-avatar-group`. Retour au > [fonctionnement & sommaire](?path=/docs/components-configuration--docs). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRôleDéfautValeur
$avatar-size-tinyDiamètre de l'avatar taille tinypx brut (spécifique avatar)34px
$avatar-size-smallDiamètre de l'avatar taille smallpx brut (spécifique avatar)42px
$avatar-size-defaultDiamètre de l'avatar taille defaultpx brut (spécifique avatar)56px
$avatar-size-largeDiamètre de l'avatar taille largepx brut (spécifique avatar)64px
$avatar-square-radiusRayon de bordure en forme squarevar(--radius-md):
$avatar-group-overlapChevauchement entre avatars empilés (ui-avatar-group)var(--units-md)12px
+ + +> Les quatre diamètres sont des **nombres bruts** : ils passent par `rem-calc()`, qui refuse une +> `var()`. Ils n'ont donc pas de hook à ce niveau — chacun reste atteignable par composant via +> `--ui-avatar-size`, `--ui-avatar-size-tiny`, `--ui-avatar-size-small` et +> `--ui-avatar-size-large`. diff --git a/storybook/docs/specifications/theme.mdx b/storybook/docs/specifications/theme.mdx index b66c5d2..d91f85a 100644 --- a/storybook/docs/specifications/theme.mdx +++ b/storybook/docs/specifications/theme.mdx @@ -7,6 +7,56 @@ import { Meta } from '@storybook/addon-docs/blocks'; Ce starter n'utilise **aucune librairie UI propriétaire**. Le thème repose entièrement sur des **design tokens** générés en variables CSS, consommés par les composants. +## Trois couches, trois mots + +Le mot « thème » désigne une seule de ces trois couches. Elles se superposent, de la plus +large à la plus étroite : + + + + + + + + + + + + + + + + + + + + + + + + + + +
CoucheCe qu'elle règlePortée
TokensLa base du système : couleurs, espacements, rayons, typoTout, partout
Marque & mode
(le « thème »)
Quelle palette, et clair ou sombre — en changeant la valeur des tokensTout, au runtime
HooksUne valeur structurelle d'un composant : hauteur du bouton + small, rayon de la carte, largeur du tiroir…Un composant, ou une seule zone de l'écran
+ +Un hook ne remplace pas un token : dans la grande majorité des cas il **pointe** vers un +token. Retoucher un espacement pour tout le kit = viser le token ; retoucher la hauteur du +seul bouton `small` = viser le hook. + +La couche « hooks » a elle-même deux étages, que la cascade CSS enchaîne toute seule : + +```css +var(--ui-checkbox-box-size, var(--ui-form-control-size, var(--size-components-2xs))) +/* ↑ le composant ↑ la catégorie ↑ le token */ +``` + +Les hooks de **catégorie** viennent de `_ui-config.scss` et portent son nom +(`$form-control-size` → `--ui-form-control-size`). Ils servent quand une valeur est partagée +par une famille de composants **et** que le token qu'elle vise sert ailleurs : ici +`--size-components-2xs` alimente aussi `ui-tag`, donc retoucher le token dépasserait la cible, +alors que `--ui-form-control-size` ne touche que la case à cocher et le radio. + ## Pipeline ``` @@ -101,3 +151,97 @@ const current = this.brand.currentBrand(); > Contrairement au thème, la marque n'est **pas persistée** : elle dérive du sous-domaine > (multi-tenant). L'explorateur **Foundations → Colors** permet de forcer marque + mode pour > prévisualiser les tokens, sans altérer le reste de l'UI Storybook. + +## Hooks de composant (`--ui-*`) + +Les tokens règlent le système ; les **hooks** règlent un composant. Chaque valeur +structurelle d'un composant (hauteur, inset, rayon, épaisseur de bordure, taille de police…) +est lue à travers une custom property dont le défaut est le réglage livré : + +```scss +// dans ui-button.scss +height: var(--ui-button-height-small, var(--size-components-sm)); +// ↑ le hook ↑ le défaut livré +``` + +Le composant ne **déclare** jamais ce nom : il ne fait que le lire. Le poser n'importe où +au-dessus suffit donc, et la portée se choisit par le sélecteur : + +```scss +:root { --ui-button-height-small: 24px; } /* tous les boutons small */ +.toolbar ui-button { --ui-button-height-small: 24px; } /* cette zone seulement */ +``` + +C'est le **seul levier disponible en mode package**, où le SCSS du kit est déjà compilé. +En mode schematics le code des composants est dans le projet : leurs variables SCSS sont +rebindables directement (voir **Components → configuration**). + +### Deux réflexes + +- **Une couleur** se retouche par son token sémantique (`--actions-high-surface-default`), + pas par un hook : le token suit la marque, le clair/sombre, et son contraste est vérifié. + Les rares hooks de couleur servent à repeindre **un exemplaire** (voile de masque, + marqueur de chargement, reflet de squelette). +- **Une valeur partagée par tout le kit** (espacement, rayon, taille de contrôle) se + retouche par son token `--units-*` / `--radius-*` / `--size-components-*` — inutile de + poser quarante hooks pour un même effet. + +### Le fichier de départ + +

+ + ⬇ Télécharger component-vars.scss + +

+ +**581 hooks** y sont déclarés à leur valeur livrée, groupés par catégorie → composant → +type de propriété (dimensions, espacements, bordures, typographie, couleurs), valeurs +alignées. + +On le copie dans un dossier **`presets/`** du projet et on l'importe depuis `main.scss` : + +``` +src/styles/ +├── main.scss +└── presets/ + ├── component-vars.scss ← le fichier livré, valeurs retouchées + └── shadcn.scss ← ou un préréglage nommé, à côté +``` + +```scss +// src/styles/main.scss +@use 'presets/component-vars'; +``` + +Tel quel, le fichier livré ne change rien : il redéclare les défauts. **L'ordre de +chargement n'a pas d'importance** — le kit ne *déclare* jamais ces noms, il ne fait que +les lire. (Une surcharge de **token**, elle, doit venir après `styles.css`, puisque le +kit y déclare déjà les tokens.) + +> Un préréglage n'a pas besoin d'être exhaustif : seules les lignes présentes s'appliquent, +> le reste garde le défaut du kit. Un exemple complet vit dans ce dépôt — +> `src/styles/presets/shadcn.scss` reproduit la métrique de shadcn/ui (contrôles 36/32, +> rayons 6/8/12, bordures 1px, texte 14px) en 166 lignes : 16 tokens pour l'échelle +> globale, 150 variables de composant pour le reste. Les couleurs n'y sont pas touchées. + +Il est aussi livré dans le package (`@4sh/ui-kit/styles/component-vars.scss`) et **généré** +depuis les `.scss` du kit, valeurs lues dans le CSS compilé : il ne peut pas dériver du +code. + +> Le hook de chaque valeur est indiqué dans la colonne **« Hook exposé »** de la section +> « Theming » de chaque composant. Les quelques hooks absents du fichier — valeur dépendante +> de la variante rendue, ou propriété que le composant se pose lui-même — y figurent aussi, +> avec leur rôle.