Couleurs
Les descriptions viennent de Figma. Elles portent les ratios de contraste mesurés — c'est la doctrine d'usage du système, pas de la décoration.
Fonds
--color-bg-default#FFFFFF ← --ramp-whiteThe page ground. This system stays on white; there is no dark mode.
--color-bg-surface#F3F0FF ← --ramp-accent-25Lavender mist. Raised surfaces that sit on the ground: cards, inputs, dialogs.
--color-bg-section#ECE1FF ← --ramp-accent-50Section lavender. Tints a whole region or slide, never a card. Supports the brand's one-tint-per-section rule.
--color-bg-feature#3F2BFF ← --ramp-accent-500A full-bleed indigo panel. Use white text on it (7.00:1).
--color-bg-celebrate#E1FF91 ← --ramp-highlight-200A full-bleed lime panel, reserved for genuine celebration. Use default text on it (12.32:1).
--color-bg-neutral#F3F2F5 ← --ramp-neutral-100Fond neutre teinté : une barre d’onglets, un panneau. Ne remplace pas bg/surface.
--color-bg-selection#D8D2FF ← --ramp-accent-100Fond du texte sélectionné.
Bordures
--color-border-default#D8D2FF ← --ramp-accent-100Pale lavender hairline, never grey. Aliases color/accent/100 since 27/08, when it was darkened so a chip could be seen at all: with no border, an unselected chip on the page ground was literally invisible. Decorative only at 1.44:1 on white — never let a border be the sole separator between two pieces of content. It does not reach the 3:1 that WCAG 1.4.11 asks of the visible boundary of a control; in this ramp only accent/300 does (3.23:1), at the price of a far more present indigo on all 33 places that read this variable.
--color-border-strong#E4E1E8 ← --ramp-neutral-300The heavier rule used to frame a photograph.
--color-border-focus#3F2BFF ← --ramp-accent-500The keyboard focus ring. 7.00:1 on the ground, well past the 3:1 asked of UI boundaries.
--color-border-subtle#D8D2FF ← --ramp-accent-100Trait décoratif, jamais seul séparateur.
--color-border-focus-on-accent#FFFFFF ← --ramp-whiteAnneau de focus sur un fond indigo.
--color-border-danger#DC2626 ← --ramp-red-500Contour d’un champ en erreur.
--color-border-control#665BFF ← --ramp-accent-400Le contour visible d’un champ, d’une case ou d’un chip. 3:1 minimum sur le sol, la surface et la section (4,66 / 4,15 / 3,72).
--color-border-accent#3F2BFF ← --ramp-accent-500Liseré d’accent : le contour d’un tag.
--color-border-hover#A9A5B0 ← --ramp-neutral-500Bordure d’un champ survolé. Décorative : 2,41:1 sur le sol.
Feedback
--color-feedback-warning-bg#FFF3D1 ← --ramp-sun-100Teinte pâle de la famille.
--color-feedback-warning-border#FFD264 ← --ramp-sun-300Contour décoratif de la famille.
--color-feedback-warning-text#6B4403 ← --ramp-sun-800Texte sur la teinte pâle et sur le sol.
--color-feedback-warning-solid#F7BA2E ← --ramp-sun-400Pastille, icône, remplissage fort.
--color-feedback-info-bg#DDF7F3 ← --ramp-teal-100Teinte pâle de la famille.
--color-feedback-info-border#84DDD1 ← --ramp-teal-300Contour décoratif de la famille.
--color-feedback-info-text#0E524D ← --ramp-teal-800Texte sur la teinte pâle et sur le sol.
--color-feedback-info-solid#22A89B ← --ramp-teal-500Pastille, icône, remplissage fort.
--color-feedback-success-bg#E4F6E9 ← --ramp-green-100Teinte pâle de la famille.
--color-feedback-success-border#9CDDAF ← --ramp-green-300Contour décoratif de la famille.
--color-feedback-success-text#205433 ← --ramp-green-800Texte sur la teinte pâle et sur le sol.
--color-feedback-success-solid#48A66B ← --ramp-green-500Pastille, icône, remplissage fort. Pas du texte courant : 3,02:1 sur le sol.
--color-feedback-danger-bg#FFECE9 ← --ramp-red-100Teinte pâle de la famille.
--color-feedback-danger-border#FFB5AC ← --ramp-red-300Contour décoratif de la famille.
--color-feedback-danger-text#801F1B ← --ramp-red-800Texte sur la teinte pâle et sur le sol.
--color-feedback-danger-solid#DC2626 ← --ramp-red-500Pastille, icône, astérisque, texte court : le rouge plein. 4,83:1 sur le sol.
Texte
--color-text-default#2F2D2E ← --ramp-neutral-900Warm near-black, never pure black. 13.67:1 on the ground.
--color-text-muted#6A676D ← --ramp-neutral-600The one quiet text step. 5.56:1 on the ground, 4.96:1 on surface. On section lavender it is 4.45:1 — large text only there.
--color-text-accent#3F2BFF ← --ramp-accent-500Accent text: links, eyebrows, numerals. Safe at body size (7.00:1 on the ground).
--color-text-on-accent#FFFFFF ← --ramp-whiteText on an indigo fill. 7.00:1.
--color-text-on-accent-soft#D8D2FF ← --ramp-accent-100Secondary text on an indigo fill. 4.86:1 — passes, but leave it for supporting copy.
--color-text-on-highlight#2F2D2E ← --ramp-neutral-900Text on a lime fill. 12.32:1.
--color-text-disabled#A9A5B0 ← --ramp-neutral-500Texte d’un élément inactif. 2,41:1, exempté : jamais pour du contenu.
--color-text-on-danger#FFFFFF ← --ramp-whiteTexte sur un fond de danger.
--color-text-on-success#2F2D2E ← --ramp-neutral-900Texte sur un fond de succès.
--color-text-on-warning#2F2D2E ← --ramp-neutral-900Texte sur un fond d’avertissement.
--color-text-accent-hover#3525E5 ← --ramp-accent-600Texte d’accent survolé. 8,24:1 sur le sol.
--color-text-secondary#4A4750 ← --ramp-neutral-700Texte courant dense : le corps d’un dialogue. 9,10:1 sur le sol. Entre text/default et text/muted.
Rampe neutre
--ramp-neutral-100#F3F2F5Tinted fill, wireframe fill.
--ramp-neutral-200#EDECEFThe backdrop around a slide.
--ramp-neutral-300#E4E1E8Image frame, wireframe border.
--ramp-neutral-400#C9C5D0Wireframe line. Not a text colour.
--ramp-neutral-500#A9A5B02.41:1 on white — decorative only. Never use for text, at any size.
--ramp-neutral-600#6A676DThe quiet text step. See color.text.muted.
--ramp-neutral-700#4A4750Dense body copy. 9.10:1 on the ground.
--ramp-neutral-800#3D393EText on a neutral-100 fill. 10.16:1.
--ramp-neutral-900#2F2D2EThe primary ink. See color.text.default.
Rampe accent — indigo
--ramp-accent-25#F3F0FFTeinte pâle de l’accent. Valeur de bg/surface.
--ramp-accent-50#ECE1FFTeinte de l’accent. Valeur de bg/section.
--ramp-accent-100#D8D2FFTinted fill behind accent text; secondary ink on indigo.
--ramp-accent-200#B2A8FFDerived step.
--ramp-accent-300#8C7DFFChart tints, muted accents.
--ramp-accent-400#665BFFDerived step.
--ramp-accent-500#3F2BFFThe brand indigo. Everything accent runs through this.
--ramp-accent-600#3525E5Hover state for indigo fills.
--ramp-accent-700#2C1ECCPressed state for indigo fills.
--ramp-accent-800#1C0991Text on an accent-100 fill. 9.74:1.
--ramp-accent-900#0D0759Derived step, deepest.
Rampe highlight — lime
--ramp-highlight-100#F3FFD7The palest lime fill.
--ramp-highlight-200#E1FF91The brand lime. This ramp's base sits at 200, not 500, because the lime is a surface and not an ink.
--ramp-highlight-300#CFED7BDerived step.
--ramp-highlight-400#B6D558Derived step.
--ramp-highlight-500#9EBA41Derived step. Not the brand value — see 200.
--ramp-highlight-600#7E972ADerived step.
--ramp-highlight-700#60741ADerived step.
--ramp-highlight-800#44530EText on a highlight-100 fill. 8.07:1. Reach here when lime has to carry text.
--ramp-highlight-900#2B3505Derived step, deepest.
Typographie
Familles
- "Poppins", system-ui, sans-serif
--font-family-headingGeometric sans, set at bold. Headings, buttons, brand marks. Free from Google Fonts; already in the Google Slides font picker.
- "Inter", system-ui, -apple-system, sans-serif
--font-family-bodyNeo-grotesque. Body copy and everything in the interface. Free from Google Fonts.
Graisses
- Concevoir, tester, décider — puis recommencer.
--font-weight-regular — 400 - Concevoir, tester, décider — puis recommencer.
--font-weight-medium — 500 - Concevoir, tester, décider — puis recommencer.
--font-weight-semibold — 600 - Concevoir, tester, décider — puis recommencer.
--font-weight-bold — 700
Échelle de tailles
- Concevoir, tester, décider — puis recommencer.
--font-size-display — 2.625remH1, and the headline on a title slide.
- Concevoir, tester, décider — puis recommencer.
--font-size-title — 2remH2, and a section divider slide.
- Concevoir, tester, décider — puis recommencer.
--font-size-subtitle — 1.5625remH3.
- Concevoir, tester, décider — puis recommencer.
--font-size-heading — 1.25remH4, and a dialog title.
- Concevoir, tester, décider — puis recommencer.
--font-size-brand — 1.125remThe wordmark in a header bar.
- Concevoir, tester, décider — puis recommencer.
--font-size-body-lg — 1.0625remA lead paragraph, one notch above body.
- Concevoir, tester, décider — puis recommencer.
--font-size-subheading — 1remH5.
- Concevoir, tester, décider — puis recommencer.
--font-size-body — 0.9375remThe default reading size.
- Concevoir, tester, décider — puis recommencer.
--font-size-ui — 0.875remButtons, inputs, nav links, table cells. Deliberately one size so a button and a field sit level side by side.
- Concevoir, tester, décider — puis recommencer.
--font-size-body-sm — 0.8125remDense body copy, card body, and the H6 eyebrow. H6 and small body share this size — that is intentional, not a duplicate.
- Concevoir, tester, décider — puis recommencer.
--font-size-caption — 0.75remField labels.
- Concevoir, tester, décider — puis recommencer.
--font-size-label — 0.6875remTable headers, card metadata, figure captions. Anything at this size must use text.default or text.muted — nothing dimmer passes AA here.
- Concevoir, tester, décider — puis recommencer.
--font-size-micro — 0.625remThe uppercase kicker on a card. The smallest size in the system.
Interlignes
Concevoir, tester, décider — puis recommencer. Un système de design ne vaut que par ce qu'il rend évident au moment de décider. Concevoir, tester, décider — puis recommencer.
--font-line-height-tight — 1.12Concevoir, tester, décider — puis recommencer. Un système de design ne vaut que par ce qu'il rend évident au moment de décider. Concevoir, tester, décider — puis recommencer.
--font-line-height-snug — 1.2Concevoir, tester, décider — puis recommencer. Un système de design ne vaut que par ce qu'il rend évident au moment de décider. Concevoir, tester, décider — puis recommencer.
--font-line-height-body — 1.55
Interlettrages
- Interlettrage
--font-letter-spacing-tight — -0.015em - Interlettrage
--font-letter-spacing-normal — 0em - Interlettrage
--font-letter-spacing-wide — 0.02em - Interlettrage
--font-letter-spacing-wider — 0.08em - Interlettrage
--font-letter-spacing-widest — 0.1em
Espacements
--space-xs — 4pxThe gap inside a small control.
--space-sm — 8pxButton padding, the gap between stacked labels, table cell padding.
--space-md — 12pxCard padding and the default gap between related items. The most-used step in the system.
--space-lg — 16pxDialog padding, the gap between groups, the space above and below a rule.
--space-xl — 24pxThe gap between blocks on a page.
--space-2xl — 32pxThe gap between sections. The largest step used inside a component.
--space-layout-48 — 48pxVertical rhythm between page sections.
--space-layout-64 — 64pxSlide padding. The top and bottom margin of a 16:9 slide.
--space-layout-96 — 96pxA generous section break, or the left margin of a title slide.
--space-layout-128 — 128pxThe largest step. Full-bleed breathing room.
Rayons
--radius-sm — 6pxSmall chrome: a page-number badge, a code block, a swatch.
--radius-md — 14pxThe system's signature radius. Cards, inputs, dialogs — anything that holds content.
--radius-lg — 28pxA roomy floating panel, and the frame around a photograph.
--radius-pill — 999pxFully round ends. Buttons, tags and segmented controls are always pills in this system.
Contrôles
La géométrie d'un contrôle de formulaire — ce qu'un espacement ne sait pas dire. Une case à cocher n'a pas la taille d'un écart, et une piste d'interrupteur n'a pas celle d'un rembourrage : ces trois mesures ont leur propre famille parce qu'elles décrivent un objet qu'on vise, pas un intervalle entre deux choses.
Elles sont peu nombreuses et c'est voulu : tout ce qui se compose depuis un espacement reste composé. La largeur d'une piste vaut space/xl + space/lg — 40 exactement — et n'a donc pas de variable.
--control-size — 18pxThe side of a compact control's box — radio, checkbox. Both are drawn at 18.
--control-track-height — 22pxThe height of a switch track. Its width needs no variable: 40 is space/xl + space/lg exactly.
--control-field-gap — 5pxThe vertical gap between a field's label, its box and its counter. Not the gap between a compact control and its word — that one is space/sm.
--control-height — 44pxHauteur d'un champ d'une ligne (Text field, Select, Multi Select, recherche de liste). Alignée sur button/height/sm — un champ et un bouton posés côte à côte tombent au même niveau. Le rembourrage vertical n'est pas appliqué : c'est la hauteur qui centre le texte.
Traits
Deux crans, et rien de plus : le trait au repos, et la marque de focus. Le fichier Figma en dessinait un troisième — 1,5, sur les deux zones de dépôt — quand 1 est dessiné 217 fois ailleurs. Un même geste fait deux fois n'est pas une échelle, et les deux nœuds ont été redessinés.
--border-width-default — 1pxThe resting stroke of a field, a box, a chip. The system's only one: drawn 217 times across five pages.
--border-width-focus — 2pxThe focus mark. Every control that carries one draws it at 2. Supersedes button/focus-ring-width, which draws nothing — the Button has no Focused state.
Opacités
Une seule valeur, pour un seul rôle : ce qu'un objet désactivé perd. Le fichier en dessinait trois — 0,4 sur les contrôles compacts, 0,5 sur les surfaces, 0,6 sur les champs de texte — pour la même intention. Elles ont été ramenées à une.
Elle se stocke en pourcentage dans Figma, comme un interligne : c'est ce que la liaison de variable attend côté dessin. npm run tokens divise par 100.
--opacity-disabled — 0.5How far a disabled object fades, in percent — Figma stores opacity the way it stores line-height. One value for all of them, where the file had drawn 40, 50 and 60 for the same intent.
Ombres
Figma n'a pas de type de variable « ombre » : une variable y est couleur, nombre, texte ou booléen. Chaque partie est donc nommée seule et liée à l'effet dans le fichier — le dessin et le code lisent la même valeur, et rien ne peut diverger. npm run tokens les remet bout à bout. Un composant utilise la ligne composée, jamais les parties.
--shadow-md--shadow-md-y8px--shadow-md-blur24px--shadow-md-spread0px--shadow-md-colorrgb(47 45 46 / 0.06)
--shadow-lg--shadow-lg-y22px--shadow-lg-blur9px--shadow-lg-spread-14px--shadow-lg-colorrgb(47 45 46 / 0.1)
--shadow-lg-invert--shadow-lg-invert-y-30px--shadow-lg-invert-blur30px--shadow-lg-invert-spread-14px--shadow-lg-invert-colorrgb(47 45 46 / 0.16)
La couche de contact
Toutes les ombres du système posent la même seconde couche, très près de la surface. Ce n'est pas une ombre : elle est nommée une fois et entre dans chacune des précédentes.
--shadow-contact-y1pxVertical offset. The contact layer, shared by every shadow in the system. It is also shadow/sm on its own — the faintest lift there is. Tinted with the text ink, never black.
--shadow-contact-blur2pxBlur radius. The contact layer, shared by every shadow in the system. It is also shadow/sm on its own — the faintest lift there is. Tinted with the text ink, never black.
--shadow-contact-spread0pxSpread. The contact layer, shared by every shadow in the system. It is also shadow/sm on its own — the faintest lift there is. Tinted with the text ink, never black.
--shadow-contact-colorrgb(47 45 46 / 0.04)The contact layer, shared by every shadow in the system. It is also shadow/sm on its own — the faintest lift there is. Tinted with the text ink, never black. No shadow in this system has a horizontal offset, so none is named.
Les couleurs d'ombre ne suivent pas l'encre. Elles valent ramp.neutral.900 à trois opacités, mais un alias Figma emporte l'opacité avec la couleur : on ne peut pas pointer l'encre et n'en changer que la transparence. Les trois sont donc des couleurs littérales. Si l'encre du système change, elles sont à reprendre à la main — c'est le seul point du groupe qui puisse dériver.
Aucune ombre n'a de décalage horizontal. Le 0 de tête n'est pas un token : c'est une règle du système, écrite dans build-tokens.mjs avec la couche de contact. Le jour où une ombre en demande un, il devient une variable comme les autres.
Mouvement
Trois durées et trois courbes. La règle tient en une ligne : un changement de couleur prend quick, un déplacement prend default, une entrée ou une sortie prend slow. Survolez une piste pour rejouer le mouvement — la pastille voyage avec le token de sa ligne, pas avec une valeur écrite ici.
Durées
À courbe constante — easing.standard.
--motion-duration-quick — 140ms140ms. Colour changes and fades — anything that does not travel: a hover fill, an opacity swap. Do NOT use it for movement; at this speed a path cannot be read.
--motion-duration-default — 200ms200ms. Movement inside the screen: something that slides from one place to another while staying visible. The nav pill travels on this one.
--motion-duration-slow — 320ms320ms. Things that enter or leave the screen — a dialog, a drawer. Not yet used: it waits for the contact dialog. Do NOT use it for a hover; it would feel broken.
Courbes
À durée constante — duration.slow, assez longue pour qu'on lise la différence.
--motion-easing-standard — cubic-bezier(0.4, 0, 0.2, 1)An object that moves while staying on screen: it leaves briskly and settles. The default curve — reach for this one first, and pair it with duration.default.
--motion-easing-enter — cubic-bezier(0, 0, 0.2, 1)Something arriving: full speed at the start, gentle landing. Pair with duration.slow. Only for what comes from outside the screen.
--motion-easing-exit — cubic-bezier(0.4, 0, 1, 1)Something leaving: gentle start, then gone. Pair with duration.slow. Never on something that stays — it reads as a departure.
Mouvement réduit. Sous prefers-reduced-motion: reduce, ce sont les trois durées qui tombent à 0ms, dans tokens.css. Aucun composant n'a à connaître la préférence, et les pistes ci-dessus s'arrêtent aussi. Le système ne supprime pas les états — un onglet courant reste signalé, un bouton survolé reste survolé : il supprime le trajet.
Layout
Métadonnées des 4 modes Figma. Ce sont les seuls tokens qui varient d'un mode à l'autre — redimensionnez la fenêtre pour les voir changer. Ils sont informatifs : rien ne les consomme pour l'instant.
--layout-device--layout-device-width--layout-root-font-size--layout-scale