Paradox Foundational DS

Getting started

Paradox Foundational Design System

The single source of truth for Paradox design values on the web (sirius): colors, typography, spacing, radius, and the ds/ components. Every specimen on this site renders from the code tokens themselves — what you see is what ships.

v1.0 pilot updated tokens: globals.css snapshot 2026-07-20 dark-first web only

How truth flows

Values are defined once in Figma and travel one way. If two links of the chain disagree, the code token file wins — and the divergence belongs in the drift register.

You're a human

Browse foundations and components; click any token name to copy it. Specimens are live — the glass buttons run the real engine. Sections marked soon are still migrating from preview.html.

You're an agent

Don't read this page, and don't parse its HTML. Two generated companions carry the same facts, far lighter: ds-facts.ndjson (140 KB — one line per section and per block, the answers) and ds-index.json (26 KB — the map and the read contract). One grep answers a question: grep -o '{"id":"ANCHOR".*' ds-facts.ndjson returns a single line with the sirius file to edit, the Figma node ids, every rendered state with the exact JSX or CSS it copies, every recipe a human can click as label/value pairs, the spec tables, the caption prose verbatim, and the DRIFTs. Median 1.4 KB.

Both are embedded in this page too (script#ds-facts, script#ds-index) so it stays self-sufficient when deployed — identical bytes, the same grep works on index.html. Truth order: token values come from sirius globals.css — this page renders them live, and if they disagree the CSS wins; component APIs from the sirius file each line names; geometry and intent from the Figma master; judgment from skills/px-design-bible/SKILL.md. Every divergence is a DRIFT entry, never a silent fix.

Before claiming a change is done: node tools/verify.js. It fails if a composite re-declares a primitive, if an icon was hand-written, if a copyable a human can click is missing from the facts, or if any of the three copies drift apart — they are all generated from this page. A clean console is part of the contract.

Reading this site

MarkerMeaning
validatedConfirmed by Maxime — safe to build on.
hypothesisWorking hypothesis — follow it, flag doubts in the PR.
DRIFT-nnnA documented Figma ↔ code divergence — expand it where it sits, or read the drift register.
errorA trap or breakage-level warning — read it before touching the area (e.g. the theming trap below).
theming trap
Sirius carries two inverted theming conventions: legacy shadcn (:root = light, .dark = dark) and the official DS (:root = dark, .light = light). This site follows the DS convention — dark is the default. Every color you touch must render under both themes.

Foundations

Color code truth · 2026-07-20

One warm brand scale — PX Heat — over calm neutral grounds, plus functional palettes. Warmth arrives in touches: heat is an accent system, never a background. Every swatch renders live from the code tokens; click a name for var(), a hex to copy it raw.

PX Heat — the brand scale

Seven steps, 0° → 600°. 150° is burgundy — THE accent: the primary brand moments, charts (via --ds-theme-chart-1…5, never raw heat), badge accents. The gaps in the scale (no 300/500) are the scale — never interpolate missing steps.

Functional palettes

neutral · green · amber · red, steps 50 → 950, plus the one-offs --ds-color-black, --ds-color-white, --ds-color-blue-pxs (PXS surfaces only — never a general-purpose blue). Semantic tokens map onto these: reach for --ds-theme-* first, primitives only when no intention fits — and flag it in the PR.

Opacity scales

The board defines six scales × 13 steps (8 → 96%), token pattern {base}-{n}%. In code today only --ds-opacity-neutral-* and --ds-opacity-accent-* exist, at 8/16/24/40/56/72. ⚠️ Steps 72–96 are all bound to the /64 variable in Figma — they render at 64% (marked *).

Alias variables (opacity-*-lighter/light/main/dark/darker) exist on top of the step scales — with inconsistent mappings across groups (exact values read from the master, 2026-07-23): primary lighter = 10% but accent/success/warning/destructive lighter = 16%; light/main/dark = 24/32/40% everywhere; darker = 48% on primary/accent but 56% on success/warning/destructive. Pick one ladder (DRIFT-010).

Gradients

Four PX Heat directional ramps + the title text gradient — all Figma color styles, zero code tokens yet. Displayed large on purpose: a 7-stop ramp is unreadable as a thumbnail. Click a ramp to copy its full CSS. Seven more library gradients (Navbar/blur/backdrop) live in the parent master file — fileKey still needed.

Bottom → TopStyle: Gradients/PX Heat/Bottom → Top · 0deg
Top → BottomStyle: Gradients/PX Heat/Top → Bottom · 180deg
Left → RightStyle: Gradients/PX Heat/Left → Right · 90deg
Right → LeftStyle: Gradients/PX Heat/Right → Left · 270deg
Clarity of mind

Gradients/Other/Text/foreground-title — neutral-50 → neutral-400, for ground titles. Click to copy the clip recipe.

Usage

var(--ds-theme-accent-bg)
DoSemantic token first — an intention, themable, and the hook catches nothing. Primitives only when no semantic token fits (then flag it in the PR).
#296dff
Don'tNo raw hex, ever — even “temporary”, even in prototypes. This blue shipped in pxlab and exists nowhere in the system (Bible anti-pattern).

Foundations

Typography Text board · validated

Two families with a hard pairing rule, one scale from 10 to 128px. Anton is not part of the system: it's a content-only brand asset (thumbnails, video) and never touches product, web or digital surfaces — its former code usages now render Clash (see DRIFT-022). The scale below renders from the live tokens — click a row to copy its size + line-height pair.

Two families

Design that lands

Clash Grotesk — headings, ≥ LG (18px). --ds-font-accent

One idea per sentence, like Athéna talks.

DM Sans — body and UI, ≤ Base (16px). --ds-font-default

pairing rule
≤ Base (16px) → DM Sans · ≥ LG (18px) → Clash Grotesk. The code doesn't express this rule — carry it yourself. In Figma the families are named “font-family-default” (= Clash) and “font-family-dark” (= DM Sans): “dark” is a family name, not a theme.

The scale

Traps

trap
The legacy Tailwind scale is redefined in sirius: text-base renders 18px while text-ds-base renders 16px. Always use the ds scale — never the bare Tailwind text utilities.

Foundations

Spacing 4px scale

A 4px-based scale with half-steps at the low end, plus the non-Tailwind steps 18 (72px) and 22 (88px). Click a token for var(), a value for raw px.

stance
Air before decoration (Bible stance #7, hypothesis): regular vertical rhythm on the scale; when in doubt, take the larger step. Never an in-between value “to adjust”.

Foundations

Radius & borders validated · 2026-07-20

Ten radius steps, four border widths, six breakpoints — and a per-component radius mapping that is spec, not taste.

Radius

Per-component mapping (ISO-verified): buttons 8px at every size · fields 8/8/6 (sm) · checkboxes 4/6 · message textarea 16 · full is a mobile pattern — no pills on web. Details live in Button § Anatomy.

radius by role · validated 2026-07-28
Radius follows role, not taste (Maxime) — measured constant across the 41-screen exemplar corpus, so a surface's corner is decided by what it IS, never by eye. Mixing two adjacent steps inside one block is what this table exists to prevent.
RoleStepToken
Buttons · fields8--ds-radius-md
Message bubbles12--ds-radius-lg
Inner cards16--ds-radius-xl
Containers20--ds-radius-2xl
Pills · badges · togglesfull--ds-radius-full
⚠️ Figma names these one tier higher than their value (its rounded-3xl is 20px = this scale's 2xl): map by value, never by name — see the drift register.
overlays · validated 2026-07-28
Two overlay widths, one scrim. Side panel 528 (8px inset) · modal 480, both over 72% black + 2px blur. Measured identical on every overlay of the corpus (share modal, preview panel, success modal). ⚠️ These are the desktop widths — an overlay stays responsive (Maxime, 2026-07-28): below desktop it adapts to the compact family, full width inside the mobile gutter. A fixed-width overlay that overflows a small viewport is a bug, not a spec.

Borders

TokenWidth
--ds-border-xs1px
--ds-border-sm2px
--ds-border-md4px
--ds-border-lg8px

Breakpoints

NameValue
mobile360px
tablet768px
desktop-sm1024px
desktop1280px
desktop-lg1440px
desktop-xl1600px

Foundations

Variables Mode collection · dark ⇄ light

The semantic layer exactly as Figma defines it: the Mode collection aliases Fondations tokens per theme — Dark is the default, Light remaps every alias. This is what agents and devs consume as --ds-theme-*. Both swatches below resolve live from the code tokens; click a code token to copy its var(). Groups flagged ❌ unused in Figma (33 + 2 typography) are deliberately not imported, per governance.

dual-mode rule
Every element declares its render class.Adaptive chrome — page UI uses Mode tokens only (--ds-theme-*), never raw neutrals: it must survive both columns of this table. ② Stage-locked specimens — component boards are specified on dark; their stages stay dark in both modes, on purpose. ③ Theme-agnostic data (alpha scales, code blocks) carries its own explicit backdrop. Before shipping anything new: toggle both modes and run the overflow audit at three breakpoints (REX checklist).

Not shown here: opacity scales and their aliases live in Color § Opacity; typography size lives in Typography § Scale; the custom group (8 variables) is not inventoried yet 🚧. Three Mode variables have no code token (DRIFT-023): popover foreground-paragraph, success-bg-transparent, warning-bg-transparent.

Primitives

Button validated · 2026-07-20

Buttons carry the actions of forms, dialogs, and screens. The web family is a rounded rectangle — 8px radius at every size — with a state system driven by opacity, not by color swaps. Eight variants, no more: there is no outline, no link, and no burgundy button. (Glass taxonomy reworked by Maxime, 2026-07-22: glass renamed glass dark, glass light and glass destructive introduced.)

primary
secondary
destructive
glass dark
glass light
glass destructive
ghost
ghost destructive
The eight families · one row per family: solid · glass · ghost · md, enabled (.88) board is specified on dark ground

Variants — the eight families

VariantUse forSurface
primaryThe one principal action of a view or dialogneutral-50 on dark · shadow
secondarySupporting actions next to a primaryneutral-900 · 8% border · shadow
glass darkActions floating over imagery — dark contextengine Dark Glass preset (brightness −0.3, blur .25)
glass lightActions over imagery — light emphasisbrightness +0.3 · neutral 10% tint layer
glass destructiveDestructive actions over imagerybrightness +0.3 · red 16% tint layer · red label
ghostTertiary, low-emphasis actionstransparent · 8% white on interaction
destructiveIrreversible actionsred-600 · shadow
ghost destructiveLow-emphasis destructive (e.g. remove row)transparent · red-600 text
DoOne primary per action group — demote the rest to secondary and ghost (the master's Action group pattern).
Don'tNo burgundy button — burgundy lives in badges, PX Heat moments, and charts (DRIFT-002). No pills on web — pills are a mobile-native pattern.

Anatomy — sizes & content slots

xs · 24
sm · 32
md · 36
lg · 40
Four sizes · radius 8px at every size — no invented in-betweensclick a specimen for its JSX · click a table row’s size for the full CSS recipe

Every button (not icon buttons) can carry a left icon, a right icon, a badge, or a combo — hidden by default behind the Figma properties Left Icon / Right Icon / Show Badge, so they're easy to miss. Slots exist on every variant and every state; geometry is state-invariant (verified on the master — states only modulate opacity). Icons are Lucide, 16px at every size; the badge is the “Glass Badge”: dark 56% pill (opacity/neutral-inverted/56), padding 8/2, radius full, XS 12/16 Medium uppercase, white.

label only
left icon · sparkles
right icon · arrow-right
badge
combo (rare)
Content slots · primary md · same slots on all eight variants order: icon · label · badge · icon
SizeHeightPadding ×LabelSlot gapIconRadius
xs24px10pxDM Sans Medium 12/166px16px8px
sm32px12pxDM Sans Medium 14/206px16px8px
md36px12pxDM Sans Medium 14/208px16px8px
lg40px12pxDM Sans Medium 14/208px16px8px

States — an opacity system

States don't swap colors; they modulate the whole button's opacity. Ghost variants add an interaction background on top.

enabled ·88
hover ·80
active 1
focus 1 + ring
disabled ·48
Primary through its five states
enabled
hover
active
focus
disabled
Ghost adds an 8% white background on interaction (ghost destructive: 10% red)
StateOpacityAdditions
enabled.88
hover.80ghost: bg white 8% · ghost destructive: bg red 10%
active1.00ghost interaction bg persists
focus1.003px ring, white 24% — red 24% on destructive family
disabled.48shadow removed

Glass — the liquid material

Glass exists for actions that float over imagery — the material is the point, and it only reads on a complex surface. Three variants (master's Buttons board, 2026-07-22): glass dark = the “Dark Glass” example of @ybouane/liquidglass, verbatim, radius adapted to 8; glass light = same with brightness +0.3 and a neutral 10% tint layer; glass destructive = light with the destructive-red 16% tint and label. Each variant runs through its five states below, over the WIP artboard’s photo (Paradox App, node 8209:117190). In Figma the variants carry effect style Glass/sm; without WebGL they fall back to the flat recipes below.

enabledhoveractivefocusdisabled dark light destr.
The glass matrix · three variants × five states · md · opacity scale .88/.80/1/.48judged over imagery — the artboard’s photo
Material controls — tune the engine (drives every glass specimen live)

The engine's own parameters, identical to the reference playground at liquid-glass.ybouane.com. Brightness is not a slider: it's fixed per variant (−0.3 dark · +0.3 light & destructive). Tune, compare, then “Copy recipe” — or reset.

Engine recipe — the implementable spec

This block exists because glass is the one variant with zero code expression in sirius (no token, no component — DRIFT-006): until ds/ grows a glass primitive, this config IS the handoff. The other variants don't need it — their values already live in globals.css as semantic tokens (table above).

/* glass dark — the official "Dark Glass" example, verbatim
   (only cornerRadius adapted: 50 → 8, radius-rounded-field) */
element.dataset.config = JSON.stringify({
  brightness: -0.3,
  blurAmount: 0.25,
  cornerRadius: 8
});

/* glass light — brightness flips to +0.3, plus a tint layer under the
   label: opacity/primary/lighter = rgba(250,250,250,0.10) */

/* glass destructive — light, with tint and label in destructive red:
   opacity/destructive/16% = color-mix(red-600 16%, transparent) · label red-600 */

/* flat fallbacks (no WebGL):
   dark  → radial-gradient(296.68% 50% at 50.25% 50%, white 0→4%)
           + rgba(23,23,23,.64) + blur(4px)
   light → rgba(250,250,250,.10) + blur(4px)
   destr → red-600 @ 16% + blur(4px) · label red-600
   all   → radius 8 · opacity .88 enabled */
DoGlass floats over imagery or video — the backdrop stays present through the material (course headers, media overlays, immersive moments).
Don'tNo glass on flat surfaces — on plain ground it's indistinguishable from a secondary (left). Use secondary or ghost there.

Action group

The master's composition pattern for a set of actions: tertiary (ghost) + secondary + principal, optionally paired with 40px secondary icon buttons. The group is modular — 2 or 3 text actions (always exactly one principal), plus 0, 1 or 2 icon buttons. Click a combo to preview it:

Action group · one principal, everything else demoted

Implementation — semantic tokens

The actionable group of the semantic layer backs buttons and inputs. Reach for these before any primitive — a semantic token is an intention. Values below resolve live; toggle the theme to see both renditions.

TokenResolves to (current theme)Role
--ds-theme-actionable-bg-primaryPrimary button surface
--ds-theme-actionable-fg-primaryPrimary button label
--ds-theme-actionable-bg-secondarySecondary button surface
--ds-theme-actionable-fg-secondarySecondary button label
--ds-theme-actionable-fg-tertiaryTertiary / meta text
--ds-theme-actionable-fg-disabledDisabled label
--ds-theme-actionable-muted-bgMuted control surface
--ds-theme-actionable-muted-fgMuted foreground — see DRIFT-012
--ds-theme-actionable-inputInput borders
--ds-theme-actionable-ringFocus ring
mobile
Mobile diverges on purpose. paradox-mobile buttons are pills (32/40/44/48, Base 16/24 type from sm) with their own state rows. Never transpose: no pills on web, no 8px rectangles on mobile. Mobile facts are preserved in foundations/mobile.md until the mobile reference exists.

Primitives

Icon Button validated · 2026-07-20

The compact sibling of Button: a square carrying a single Lucide glyph. Same eight families, same opacity state system, same 8px radius — squares 24/32/36/40 with a 16px glyph (20px on lg). One nuance of its own: ghost focus is a 2px border, not a shadow ring.

Variants — the eight families

Identical taxonomy, identical use cases — see the Button variants table. One square per family.

primary
secondary
destructive
glass dark
glass light
glass destructive
ghost
ghost destructive
The eight families · one row per family: solid · glass · ghost · md, enabled (.88)

Anatomy — sizes

xs · 24
sm · 32
md · 36
lg · 40
Four squares · glyph 16px (20px on lg) · radius 8px at every size click a specimen for its JSX · click a size for the CSS recipe
SizeSquareGlyphPaddingRadius
xs24px16px4px8px
sm32px16px8px8px
md36px16px10px8px
lg40px20px10px8px

States — the same opacity system

Same scale as Button — enabled .88 · hover .80 · active 1 · focus 1 + ring · disabled .48 — with one nuance of its own: ghost focus is a 2px border (--ds-color-neutral-500), not a shadow ring.

enabled ·88
hover ·80
active 1
focus + ring
disabled ·48
Primary through its five states
enabled ·88
hover ·80
active 1
focus + ring
disabled ·48
Ghost — focus swaps the ring for a 2px border

Glass — the liquid material

The same three materials as Button — the spec, the playground and the engine recipe live in Button § Glass. Judged over the same artboard photo:

enabledhoveractivefocusdisabled dark light destr.
The glass matrix — icon buttons · three variants × five states · md · opacity scale .88/.80/1/.48judged over imagery — the artboard’s photo

Implementation

src/components/ds/common/IconButton.tsx — same semantic actionable tokens as Button (table here). API: Icon (a Lucide component), variant, size, href. Click any specimen above for ready JSX. Glass variants staged on the sirius branch design/glass-button-variants.

Primitives

Badge Badge.tsx · 1:1

Badges inform on the status of a piece of data. Seven variants — including the burgundy accent and the px-heat glow, which live here and NOT in buttons — four sizes, two states (default / hover at 80%). Backgrounds are 56% transparent theme mixes with the Glass frost. Click any badge for its JSX.

primarysecondaryaccentsuccesswarningdestructivepx-heat xs Badge Badge Badge Badge Badge Badge Badge sm Badge Badge Badge Badge Badge Badge Badge md Badge Badge Badge Badge Badge Badge Badge lg Badge Badge Badge Badge Badge Badge Badge
Seven variants × four sizes · default state — hover = opacity .80 xs is 10px regular-case; sm/md/lg are 12px uppercase
SizePaddingLabelCaseRadius
xs2 / 810/14 Mediumregularfull
sm2 / 812/16 Mediumuppercasefull
md4 / 1012/16 Mediumuppercasefull
lg6 / 1212/16 Mediumuppercasefull

Liquid glass — over imagery

Badges are Glass Badges: over imagery they carry the exact same liquid material as the glass buttons — engine refraction under their 56% tint layer, pill-radius lens. px-heat is the exception (glow, no glass). Same engine, same playground parameters as Button § Glass.

Badge Badge Badge Badge Badge Badge
Liquid badges · md · engine refraction under the 56% tints · judged over the artboard’s photo

Optional slots (Figma props, all 12px): dot · left icon · avatar · closable ×. The px-heat glow recipe: px-heat inset glow — click to copy.

Primitives

Tag no ds/ · DRIFT-007

Versatile labels for categorization, filtering and metadata. Five color variants × three types (fill, dash, emphasis) × three sizes — square-ish radii, unlike the Badge's pill. Closest existing code (pxlab Tag.tsx, shadcn ui/badge.tsx) matches neither: this page is the spec.

neutralbranddangerwarningsuccess fill Label Label Label Label Label dash Label Label Label Label Label emphasis Label Label Label Label Label
Five variants × three types · md · fill and dash share tints, emphasis stays neutral-surfaced
Labelsm · 20 · r4
Labelmd · 24 · r6
Labellg · 28 · r8
Three sizes · heights 20/24/28 · radius 4/6/8 (one radius step per size)

Status indicator

A 14px dot embedded in tags or labels to reflect a page/update/action status — 2px ground-colored ring, 24% tinted fill, 8px glyph.

pending
published
deleted
warning
Status indicator · 14px · ring 2px ground · fill 24% mix

Primitives

Selection controls no ds/ · DRIFT-007

Four controls across three master boards: Checkbox (square, plus an indeterminate shape), Circle checkbox (the task checkbox — it adds a started state), Radio and Toggle. Two sizes each, the same opacity state system as Buttons — and selection is signalled by the PX Glow (radio dot, Heat checkbox, selected Radio item), never by a flat accent fill. ds/ components are staged on the sirius branch design/selection-controls — Checkbox, Radio, Toggle, ModeSwitch (tsc-clean, pending team review); the option card already exists as RadioOptionCard.

Checkbox

Two Figma components: _Checkbox base (the atomic box) and Checkbox (base + label + description). Two themes: White — near-white fill when checked — and PX Heat — checked lights the glow. The glyphs are the official Lucide check and minus, dead-centered, at a rendered 1.5px stroke.

uncheckedcheckedindeterminatepx heat sm · 16 md · 20
The box · sm 16 / radius 4 · md 20 / radius 6 · border white 8% in every state · checked fill neutral-50, glyph neutral-950 · Lucide icons at 12/14px
enabledhoverpressedfocusdisableduncheckedcheckedpx heat
States = the Buttons opacity system · hover ·80 · pressed ·88 · focus + 3px ring white 24% · disabled: box turns muted 800 at ·48 and the glyph flips to neutral-50 · PX Heat keeps its glow under the focus ring, and loses it disabled
With label + description — live · click to toggle, hover for the copy chip (copies the current state) · row gap 8, box wrapper py 2 · label md 16/24 / sm 14/20 Medium · description 14/20 neutral-600

Circle checkbox — the task checkbox

Same anatomy as Checkbox at radius-full, plus one property the square box doesn't have: isStarted. A started task drops the box entirely and shows the bare Lucide loader glyph — the full lifecycle reads left to right below.

to dostarteddoneindeterminatelive · click
Task lifecycle (md) · started = Lucide loader 20px neutral-400, no box, no border — the board is static, the spin is ours · all five interaction states of the square apply to every step

Radio

One board, one theme — because the checked signal is the PX Glow: a 3px-padded neutral-950 disc lit from inside by the 4-layer heat glow. No white dot anywhere.

uncheckedchecked sm · 16 md · 20
The dot is a glow · sm 16 / md 20 · radius full · border white 8% · checked = padding 3 → inner disc neutral-950 + Ring/Internal PX Heat Glow
enabledhoverpressedfocusdisableduncheckedchecked
Same state system · hover ·80 · pressed ·88 · focus ring 3px · disabled muted at ·48 (the glow dot goes out)
With label + description — live group · click to select (single choice), hover for the copy chip · identical layout contract to Checkbox

Toggle

Track sm 36×20 / md 44×24 at radius full with a 2px inset; knob neutral-50 with Shadow-dark/xs. OFF = neutral-900 track, no border. ON comes in two variantspx glow (dark track lit by the heat glow, the default) and neutral (neutral-200 light track). They replace the board's neutral-100 recipe, whose knob-on-track contrast was one neutral step (decision Maxime, 2026-07-24).

offon · px glowon · neutral sm · 36×20 md · 44×24
Off / two ON variants · knob sm 16 / md 20 · px glow: dark track + accent 56% veil + PX Glow · neutral: track neutral-200 + black-16% border · replaces the board recipe (decision 2026-07-24, DRIFT-026)
enabledhoverpressedfocusdisabledoffon · glowon · neutral
Same state system, both variants · hover ·80 · pressed ·88 · focus ring 3px (stacked over the glow) · disabled ·48 — the disabled ON track falls back to the dark surface, knob shadow off
With label + description — live · click to switch (top: glow, bottom: neutral), hover for the copy chip · toggle sits left, gap 8 · description 14/20 neutral-400 — one step lighter than Checkbox's, per the board
dark activelight active
Dark light mode switch — a sibling component on the Toggle board, not a Toggle variant · 32px pill (bg block, border white 8%, padding 2) · active cell = 28px neutral-50 circle + shadow 0 1px 1px · icons Lucide moon/sun 16 · staged as ds/ModeSwitch

Implementation

ControlGeometrySelected signalCopy the recipe
checkbox16 r4 / 20 r6 · icon 12/14neutral-50 fill, neutral-950 Lucide checkbox md
checkbox · px heatsame boxaccent 56% veil + heat glow, check neutral-50heat checked
circle checkbox16 / 20 · radius full+ started: bare Lucide loader neutral-400circle md
radio16 / 20 · radius full · p 3neutral-950 disc + PX Glow (no white dot)radio md
toggle36×20 / 44×24 · p 2 · knob 16/20track neutral-100 + black-8% border, knob righttoggle md
paste-ready
Click any specimen to copy its real ds/ JSX — <Checkbox>, <Radio>, <Toggle>, <ModeSwitch> (branch design/selection-controls) and <RadioOptionCard> (already in ds/). Token-level recipes stay one click away on the highlighted values in every caption and in the table above.
shared contracts
States are the Buttons opacity system verbatim: hover ·80, pressed ·88, focus 3px ring white 24%, disabled ·48 (+ muted box on checkbox/radio). Labels: row gap 8, control wrapper py 2, label Medium (md 16/24, sm 14/20), description 14/20. All glyphs are official Lucide (check, minus, loader, redo, moon, sun) at a rendered 1.5px stroke — stroke-width = 1.5 × 24 / size on a 24 viewBox.
DRIFT-025
On the boards, the md label of Checkbox/Radio binds the studio kit's font/family/content token (named "Inter"), while sm labels and every description bind Paradox/SM text styles (DM Sans). The web renders both as DM Sans — the master's md binding should be re-pointed at a Paradox style.

Primitives

Text input partial ds/ coverage

Five field types (default, leading dropdown, trailing dropdown, leading text, payment) × three sizes × six states (placeholder, filled, focus, destructive, success, disabled) — plus three textarea variants (text, tag, message) with their own state set, and two web-designed extensions (search, password — DRIFT-029). Heights 48/40/32, radius 8/8/6 (sm drops one step). Click a field for its JSX (default type) or its CSS recipe — and try the live one.

Five types

your.name@email.com
default
FR06 12 34 56 78
leading dropdown
$1,000.00USD
trailing dropdown
http://paradoxgroup.com
leading text
VISA4242 4242 4242 4242
payment
Five types · lg 48 · every type repeats the full state set on the board
your.name@email.com
lg · 48 · r8
your.name@email.com
md · 40 · r8
your.name@email.com
sm · 32 · r6
Three sizes · heights 48/40/32 · type Base/SM/SM · radius drops one step at sm
Rechercher une session…
search · leading lens · trailing clear
••••••••••
password · trailing visibility toggle
Search · password — web-designed extensions (DRIFT-029) · pure compositions of the default type: leading search lens with a trailing clear ×, and a masked value with the eye toggle — no new geometry, no new colors

States — with label & hint

Label*
your.name@email.com
This is a hint text to help user.
Label*
your.name@email.com
This is a hint text to help user.
Label*
your.name@email.com
This is a hint text to help user.
Label*
your.name@email.com
This is a hint text to help user.
Label*
your.name@email.com
This is a hint text to help user.
Label*
your.name@email.com
This is a hint text to help user.
Placeholder · focus (ring #737373 + 3px halo) · destructive · success · filled · disabled · asterisk = burgundy · filled steps text to the title color · disabled dims text and icons to 16% white — the six board states, complete
Label*Type in it — focus ring, caret and placeholder are the real spec.
Interactive · a real <input> wearing the exact lg recipe · focus = ring #737373 + 3px halo via :focus-within
SizeHeightPaddingTypeRadius
lg48px12Base 16/248px
md40px8 / 10SM 14/208px
sm32px8SM 14/206px

Textarea — three variants

Ce que je retiens de la session : rester focus sur une seule priorité.
text · lg 168 · 6 lines · r26/24
FocusClarté
tag · lg 120 · 4 rows · r26/24
Nouveau message
message · composer · r26/24
One textarea language (decisions 2026-07-24, DRIFT-030/031) · every variant: inner 2px stroke opacity/neutral/32 + the buttons' glass, r26 wrapping a glass-dark plate at r24 with an inner 1px actionable secondary-border · text/tag heights 166/130/100 + resizer · composer: placeholder SM white 48% · paperclip and mic are ghost IconButtons, circular in this context (live: hover/active = muted bg) · Expertise = a real DS Button glass-light sm (32px — flush with send/paperclip/mic) with the circular override (no-live fallbacks carry the engine rim as a 1px inner stroke) · send: neutral-50 circle, ·48 while empty · the hero composer is live: click Expertise for the drop-up picker (name + promise per item); selecting turns the button active with the name and a trailing × that clears it (DRIFT-032)
lgmdsm text
This is a textarea
lg · 168 · 6 lines
This is a textarea
md · 120 · 4 lines
This is a textarea
sm · 96 · 3 lines
tag
Focus
lg · 120 · 4 rows
Focus
md · 96 · 3 rows
Focus
sm · 72 · 2 rows
message
Nouveau message
lg · 146 · 4 lines
Nouveau message
md · 126 · 3 lines
Nouveau message
sm · 86 · 1 line
Three sizes × three variants — the line rule (decision 2026-07-24, DRIFT-031): height = chrome + n × line-height · text (deliberate writing): 6 / 4 / 3 lines of Base 24 → 168 / 120 / 96 · tag (a few chips, not an essay — and never below 2 rows: the r24 radius would turn it into a pill): 4 / 3 / 2 chip rows → 120 / 96 / 72 · message (compact entry point that grows): 4 / 3 / 1 lines of SM 20 over the 66px chrome (gap 12) → 146 / 126 / 86 · replaces the board's hand-drawn 166/130/100 and 140/122/88
Label*
This is a textarea
This is a hint text to help user.
Label*
This is a textarea
This is a hint text to help user.
Label*
La clarté vient de la contrainte : une seule priorité par session.
This is a hint text to help user.
Label*
This is a textarea
This is a hint text to help user.
With label + hint — the four states · placeholder · focus (border #737373 + 3px halo, same recipe as inputs) · filled (text steps to the title color) · disabled ·48 · label SM Medium + burgundy asterisk · hint SM neutral-400 · field shown at text md (120 = 4 lines) · identical at every size
Nouveau message
message · destructive
Nouveau message
message · success
Semantic states · destructive / success on the inner plate border, same recipe as inputs
circular buttons
How the exception is handled. The composer needs circular buttons; the desktop DS fixes IconButton at radius 8. Rule: shape is a contextual override, not a new component — same anatomy, sizes and state system, only the radius flips to full inside conversational surfaces (this composer). Staged as <IconButton shape="circle"> (default stays square) on design/selection-controls. Nothing else may use it without a decision.

⚠️ Board inconsistency (flagged): textarea border is white 8% where inputs use 16% — part of DRIFT-010. The dropdown/leading/payment types and every textarea variant have no ds/ component (DRIFT-007).

Primitives

Progress no ds/ · DRIFT-007

Task progress or passing time. Two variants — Primary (white fill) and PX Heat (the 270° heat ramp) — four track heights, radius full. The Slider derives from this recipe. shadcn's ui/progress.tsx does not implement this spec.

25%50%75%100% t4 · 4px
t6 · 6px
t8 · 8px
t12 · 12px
Primary · white fill on neutral 8% track · heights 4/6/8/12 · radius full
PX Heat · 75%
PX Heat variant · heat ramp fill on block-bg track · click the bar for the gradient

Primitives

Slider derived · no board

Born on the web (Maxime, 2026-07-23): the Progress bar anatomy — radius-full track + fill — plus a handle, in two fill variants (decision 2026-07-24, the same pair as Toggle ON): px glow, the official PX Heat ramp compressed into the fill, and neutral, a neutral-50 fill on a 16% track. Same four sizes as Progress. No Figma board yet (DRIFT-021) — the ds/Slider component is staged on the sirius branch design/selection-controls; click a size label to copy its JSX.

sm · track 4
md · track 6
lg · track 8
xl · track 12
Variant px glow · live — drag them · the official PX Heat ramp fills up to the value, coral at the edge · track neutral 16% thumb sizes are a working hypothesis 🚧
sm · track 4
md · track 6
lg · track 8
xl · track 12
Variant neutral · live — drag them · fill neutral-50 on a 16% track (stepped up from Progress's 8% for slider affordance)
SizeTrackThumbFill (px glow / neutral)Track color
sm4px12px 🚧heat ramp / neutral-50neutral 16%
md6px14px 🚧heat ramp / neutral-50neutral 16%
lg8px18px 🚧heat ramp / neutral-50neutral 16%
xl12px22px 🚧heat ramp / neutral-50neutral 16%
enabled
disabled ·48
States · disabled follows the opacity system · focus = the standard ring

Primitives

Rich text code off-spec · DRIFT-008

How every element of an article renders — the full stack from the master's Text component board: title label, H1 (default + visual block), H2, H3, body, lists, quote, small, inline code and code paragraphs, picture and video with legends, and three spacers. Quote/code use JetBrains Mono italic — official as an ultra-rare option strictly for code / transcript-style content (decision 2026-07-24, token --ds-font-mono to add). The off-spec flag is about the code: ui/markdown.tsx still renders teal h3s and a gray-* palette — this board is its restyle spec.

À propos de cette Loop

Ceci est un titre H1

Étape 1 sur 4

Ceci est un titre H1 (visual block)

Ceci est un titre H2

Ceci est un titre H3

Un paradoxe est une affirmation qui semble se contredire, mais qui peut néanmoins être vraie. C'est une idée qui défie la logique et le bon sens, et qui nous oblige à remettre en question nos hypothèses et nos croyances.

  1. Apprendre les contradictions.
  2. Les paradoxes temporels.
  3. Réussir à jongler l'impossible.
  • Navigue dans les complexités du paradoxe avec assurance.
  • Découvre comment les paradoxes stimulent la pensée créative.

Ton chemin vers la liberté financière commence ici.

Par exemple, après qu'il ait dit : "ça me tue intérieurement" tu aurais pu dire : "ça me tue intérieurement"

Par exemple, après qu'il ait dit : "ça me tue intérieurement" tu aurais pu dire : "ça me tue intérieurement"

The moral of the story is: never underestimate the power of a good laugh and always be careful of bad ideas.
Article picture
This is a legend.
Video frame 02:08 -06:18
This is a legend.

Un lien vers la méthode complète au fil du texte, du gras qui passe en Medium titre et de l'italique simple.


PrincipeEffet
Clarté d'espritMoins de bruit, plus de décisions nettes
Paradoxe assuméDeux vérités peuvent coexister
  • Écrire la première itération.
  • Confronter la version à un vrai lecteur.
The full board stack · title label 12/16 SemiBold uppercase over a 16% rule · H1 2xl / H2 xl / H3 lg (Clash Medium) · body & lists SM 14/20 · quote + code chips = JetBrains Mono italic 12/16 #dbbdc8 on burgundy 10% · small 12/16 neutral-200 · media: radius 16, black-8% border, Shadow-dark/sm, legend indented 8 · the video bar's hover timestamp-preview (thumbnail + times) is specced on the board, omitted from this static demo · the closing run — link, emphasis, table, task list (Circle Checkbox at sm) — is web-designed, board to backfill (DRIFT-028)
Spacer S · 12px Spacer M · 24px Spacer L · 56px
Spacers · three fixed article-flow gaps — S 12 · M 24 · L 56 · invisible in product, hatched here to be measurable
ElementStyleSpacing above
Title labelXS SemiBold uppercase · neutral-400 · 16% rulesection start
H1Clash Medium 24/3228px
H2Clash Medium 20/2824px (16 after H1)
H3Clash Medium 18/2820px (12 after H2)
body · listsDM Sans 14/2012px (8 after H3)
quoteburgundy 10% · border 56% · r616px
code chipmono italic 12/16 · #dbbdc8 · burgundy 10% · r2inline
smallDM Sans 12/16 · neutral-2008px
picture / videoradius 16 · black-8% border · Shadow-dark/sm · legend XS pl 824px
spacersS 12 · M 24 · L 56

Primitives

Icons Lucide

The icon system is Lucide — the full official library, vendored ( icons): 24px grid, stroke 2, round caps. The Figma board mirrors this set; when an icon is missing there, it exists here first. Sizes in use: 16px in buttons and inputs (20px in lg icon buttons), 12px in badges and tags. Click any icon to copy its lucide-react import.

Composites

Composites 6 of 11 · growing

The system has two levels. Primitives — every section above: the smallest possible controls (a button, a checkbox, a tag). Composites — this section: reusable blocks assembled from primitives (option cards, designed cards, repeatable UI chunks). A composite never restyles a primitive: it only arranges them — badges are the DS Badge (and copy as one), the CTA is the DS Button, the bar is DS Progress, markers are the DS Checkbox and Radio, every icon is resolved from the vendored Lucide set at runtime, and every string names a documented text style (text-ds-*, the same utility names as sirius). node tools/audit-composites.js fails the build if any of that slips.

New designed cards and blocks land here as they are extracted from the two Cards boards. Inventory and order: Course cards ✓ · Action cards ✓ · PX Agent ✓ · Tutorial + Identity ✓ · Loop cards ✓ (Large / Compact / Minimal / block + mobile card / compact mobile / Date / block + Indicator + Line) · Radio item ✓ · next: Event card, One thing cards (big / card / edit / timer), Streak (indicator + card), Athena tools, Long bloc card — plus the remaining mobile variants of the imagery families (Course started, Action, Community, Event).

Course cards

The catalog's flagship (boards ✅ Desktop cards + ✅ Mobile cards, code ds/cards/CourseCard.tsx). Anatomy: full-bleed visual under a 24% veil, glass badges top-right, and a Details panel sitting on a progressive scrim — gradient 0% → 56% at 41.35% → 96% with an 8px backdrop blur. Hover lifts the shadow to Shadow-dark/lg, clears the veil and reveals the CTA.

Course visualMentalFocus

Empowered

default · live hover
Course visualMentalFocus

Empowered

hover · exploded
Course visualMentalFocusNouveau

Empowered

started
Course visualMentalFocus

Empowered

inactive
Four states — the first card is live, hover it · 244×344 · r20 · border white 16% · Shadow-dark/md · badges = the DS Badge lg (accent 56% / the white on-media rendition — DRIFT-033, blur 4) · title Clash SemiBold XL · scrim progressive + blur 8 · started adds the Nouveau chip (white 16%) + Progress md 6px · inactive: border drops to 8%, veil 56%, content ·56 · hover: veil off, CTA (white button ·88) in, Shadow-dark/lg
Featured · 624×344 · light 8% veil (the visual leads), lg shadow at rest · title steps to Clash SemiBold 5XL 36/44 · meta row (Lucide clock + video 16, XS-Uppercase Medium neutral-200) with the Nouveau chip · description Base/24 clamped + inline CTA ·88 · same started / inactive deltas as the standard card
Course visualFinanceSignatureNouveau12 semaines24 modules

Psychologie de l’argent

mobile · 244×360
Mobile · the featured content squeezed into the standard footprint (chip above the meta row, Details p16/24, gap 12) · the CTA is always visible — no hover on touch · lg shadow at rest · states started / pressed mirror the desktop deltas

Action cards

Entry points to a practice — three types from the board (code ds/cards/ActionCard.tsx + CommunityCard.tsx). Tool: the visual blends into black (mix-blend-overlay) so any artwork reads on-brand. Loop: a flat card surface, no imagery — a live session slot with its capacity and schedule. Community: the Tool treatment carrying the PXL wordmark.

OutilNouveau

Pratique (Loop IA)

Un espace pour créer ou rejoindre un groupe de travail collectif et progresser entre pairs.

tool · 394×225
CommunautéParadox LearningPXL

Communauté PXL

Un espace pour échanger, progresser et partager autour des parcours, outils et pratiques de l’app.

community · 292×344
Tool · Community · r20 · border white 16% · Shadow-dark/lg · background = neutral-950 + visual in mix-blend-overlay · same progressive scrim + blur 16 as Course cards · badges = DS Badge (lg secondary top, sm primary chip) · Community adds the centered PXL wordmark and an arrow-up-right 24 next to the title
2 places restanteLoop02/05 9h30

Protocole - Tes premières expériences avec l’argent.

Participants : Laurence Gilles

loop · 394×196
Loop · the one flat type: block surface, no imagery, no blur · capacity badge = DS Badge lg primary carrying the shared 8px semantic dot in success · schedule meta = Lucide calendar 16 XS-Uppercase neutral-200 · title wraps (XL), participants SM one line · the Loop type and its capacity/meta slots are missing from ActionCard.tsx (DRIFT-034)

PX Agent cards

One card per agent of the PX roster — the Course card language at 292×272 with the agent's portrait. The default state is just the name; hover reveals the agent's promise and the CTA without lifting the shadow (the portrait stays the hero). No ds/ component yet (DRIFT-007).

PX Agent MartinRevenusAgent

Martin

Il identifie les leviers les plus efficaces pour générer rapidement plus de revenus à partir de vos compétences actuelles.

default · live hover
PX Agent MartinRevenusAgent

Martin

Il identifie les leviers les plus efficaces pour générer rapidement plus de revenus à partir de vos compétences actuelles.

hover · exploded
PX Agent MartinRevenusAgent

Martin

inactive
Default · hover · inactive — the first card is live · 292×272 · same r20 / border white 16% / progressive scrim + blur 16 as Course cards, but Shadow-dark/md at every state — hover reveals, it does not lift · badges = DS Badge lg (accent + secondary — the dark rendition here, per the board) · hover adds the promise (SM neutral-200) + white CTA ·88 · inactive: veil 56%, content ·56, border keeps 16%

Tutorial + Identity

The two composites that carry primitives instead of imagery (code ds/cards/TutorialCard.tsx + IdentityCard.tsx, both created for this pass with the ds/common/Progress.tsx they needed — see DRIFT-007). Same shell as the imagery families — r20, border white 16%, Shadow-dark/lg — on the ground surface. Tutorial is the onboarding checklist: header, Progress, and a step list masked at both ends. Identity splits one card into two columns: the stats side (archetype, streak strip, counters) and the orb side (Paradox Score).

Prise en main

Complétée à28%
  • Démarrer un cours signature
  • Participer à une session Loop
  • Terminer un micro-cours
  • Rejoindre la communauté Circle
  • Discuter avec un Agent
desktop · 399×344 — live, tick a step
Tutorial desktop · 399×344 — live, tick a step · padding 12/16, gap 20 · title Clash Medium LG 18/28 on card-fg-title · completion = the DS Badge px-heat md after an XS-Uppercase label · bar = Progress lg px-heat, the board's instance overriding the track to the block surface · rows r8 border white 8%: pending on the block surface (py12), done on the ground surface (py10) with the label struck through in neutral-600 · the marker is the Checkbox circle rendition used as a state marker — the row is the control, so its surface is bumped to muted (the DS neutral-900 would vanish on a neutral-900 row — DRIFT-035) · the list is masked at both ends — 19% / 90% (the board’s 47px / 223px translated into the real 246px window) — and keeps a real 8px scrollbar 4px off the card edge, exactly where the board draws it two board facts to confirm: the 28% counts steps beyond the visible five (the live card recomputes on the five), and the top fade ghosts the first row even at the top of the list — DRIFT-036 🚧

Identité Paradox

Stratège Financier

Tu as une lecture lucide et organisée de l’argent. Tes décisions tracent une trajectoire claire vers tes objectifs.

4 semaines d’apprentissage consécutives.
Record : 6
2Cours terminés141Minutes de vidéo VUES1Thématique
2Cours terminés141Minutes de vidéo VUES1Thématique
58.4Paradox Score
Identity desktop · 801×344 · one shell, px16, two equal columns each carrying its own vertical padding (stats 12/24 · orb 12/16) · archetype Base/Medium + SM/Regular neutral-400, dividers 1px neutral-900 · the streak strip is not a bar chart: 21 bars, all full height, only the fill changes — PX Heat Top → Bottom, the ramp's Low variant, then the block surface · counters row masked at 88% (XS-Uppercase Bold + Regular) · score Clash SemiBold 5XL on a 32px line + a 16px white glow, label SM tracking −0.56 · two board absolutes traded for theme tokens so the card inverts: the dividers (block-border, the board says neutral-900) and the score label (ground-fg-paragraph, the board says neutral-300) — one step each, code token wins · the orb's glow bleeds 92px past its 204.5×198.5 ring · Voir plus and the three actions are the live DS buttons (Button ghost sm + IconButton ghost sm, Lucide arrow-right / share-2 / download / info 16), all at the board's rest opacity 88 two open points: the Low bars' semantics (decay, or a partial week?) and the orb artwork, authored for the dark surface — there is no light rendition of it yet 🚧
Prochaine action
  • Terminer un micro-cours
  • Démarrer un cours signature
  • Participer à une session Loop
  • Terminer un micro-cours
  • Rejoindre la communauté Circle
  • Discuter avec un Agent
mobile · closed — live, tap the header

Prise en main

Complétée à28%
Prochaine action
  • Terminer un micro-cours
  • Participer à une session Loop
  • Terminer un micro-cours
  • Rejoindre la communauté Circle
  • Discuter avec un Agent
mobile · open
Tutorial mobile · 328 — the first card is live, tap its header · height follows content, pb16 · the header gains a 24px chevron-down / chevron-up (the whole row is the toggle — a bare 24px chevron is too small a target) · every mobile row is tighterdone py8 · pending py4, against 10/12 on desktop · closed: no list, just a PROCHAINE ACTION eyebrow and the next pending step, its label the only one stepped down to SM/Medium 14/20 · open: the full list back at 16/24, capped at 184px behind the same bottom fade — the first done step scrolls out of view first

Loop cards

The scheduling family — the one family that already had code, and not in ds/: components/(loop)/loops/LoopCardDesktop.tsx renders the Large and Compact renditions (compact) with the timeline (showTimeline), LoopMinimalCard.tsx the minimal one. The board splits it into four pieces: the Line connector, the Indicator (capacity), the Date separator and the card itself. Values below are the board's; a value-by-value audit of the existing implementation is still pending (DRIFT-037).

Loop Large

Session cover09h00 - 10h15Dans 33 min2 places restantes

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox
MentalCarrièreDébutant
available
Session cover09h00 - 10h15Dans 33 min1 place restante

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLouise Charrier, Toi et l’agent IA Paradox
ParticipeMentalCarrièreDébutant
available + participate
Loop Large · 832×170 · available / available + participate · three columns: cover (pl12 py12, image square r12 border white 8% under a neutral-inverted 8% veil), details (flex, pl16 pr8 py16, gap 16) and actions (pl8 pr16 py16) · top row gap 16: time XS/Medium · the countdown in warning · the Indicator · title Clash SemiBold XL 20/28 one line ellipsised · participants = 20px avatars overlapping by 7.5, each ringed 1.25px in the card surface, the last seat being the agent (PX Heat ramp Low + monogram) · categories = DS Badge xs: accent for topics, secondary for the level, success + a 12px check for Participe · CTA = Button primary md, its label switching to “Voir l’exercice” once you participate
Session cover09h00 - 10h15Dans 33 minComplet

Empowered - Décupler sa confiance en soi

Agent IA ParadoxJess Noireau, Gary Warion, Sylvie Benoit et l’agent IA Paradox
MentalCarrièreDébutant
full
Session cover09h00 - 10h15Dans 33 minComplet

Empowered - Décupler sa confiance en soi

Agent IA ParadoxMathilde Lefebre, Céleste Jean, Toi et l’agent IA Paradox
ParticipeMentalCarrièreDébutant
full + participate
full / full + participate · the two deltas are independent: the artwork desaturates (mix-blend luminosity) in both, but only plain full drops cover and details to 48% and the CTA to 88% — full + participate keeps full contrast: your own session stays readable even when nobody else can join · the Indicator turns red-600 · Complet

Loop Compact

09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox2 places restantes
MentalCarrièreDébutant
available
09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLouise Charrier, Toi et l’agent IA Paradox1 place restante
ParticipeMentalCarrièreDébutant
available + participate
09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxJess Noireau, Gary Warion, Sylvie Benoit et l’agent IA ParadoxComplet
MentalCarrièreDébutant
full
09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxMathilde Lefebre, Céleste Jean, Toi et l’agent IA ParadoxComplet
ParticipeMentalCarrièreDébutant
full + participate
Loop Compact · 856 wide · the four statesLoopCardDesktop with compact · the one card in the system with no chrome at all: no surface, no border, no shadow, no cover — a row in a list rather than a card · three rails: the hour (104, pt6, time then countdown stacked), the details (flex, gap 12) and the actions (128, pt4, right-aligned) · the title steps down to LG 18/28 (against XL on the Large card) and the CTA to Button sm (py6, against md) · participants and Indicator share one row here instead of stacking · same state rule as the Large card: only plain full dims (48% / CTA 88%), full + participate keeps full contrast

Loop Minimal

Mardi 25 février09h15 - 10h00

(Suite Live) Protocole - Tes premières expériences avec l’argent. Apprendre à mettre en place une épargne de sécurité sans y penser.

Agent IA ParadoxMentalCarrièreDébutant
enabled · live — hover it, tab to it
Mardi 25 février09h15 - 10h00

(Suite Live) Protocole - Tes premières expériences avec l’argent. Apprendre à mettre en place une épargne de sécurité sans y penser.

Agent IA ParadoxMentalCarrièreDébutant
hover · exploded
Mardi 25 février09h15 - 10h00

(Suite Live) Protocole - Tes premières expériences avec l’argent. Apprendre à mettre en place une épargne de sécurité sans y penser.

Agent IA ParadoxMentalCarrièreDébutant
focus · exploded
Loop Minimal · 504×68 — the first card is live: hover it, or tab to it · (70 in CSS: Figma draws the 1px border inside the frame, the browser outside it — the only place in this family where the two accountings differ) · LoopMinimalCard.tsx · the only Loop rendition with interaction states, and the only one at radius 8 with a white-8% border · enabled: ground surface + Shadow-dark/md · hover: the surface steps up to block, shadow unchanged · focus: block surface + the standard 3px white-24% ring, and the shadow drops · hour rail 104 with the day in neutral-50 over the slot in neutral-400 — the reverse emphasis of the Compact rail · the title is the one Loop title in DM Sans (SM SemiBold 14/20, not Clash), one line ellipsised · no names and no Indicator here: just the avatars and the categories, the category row fading out at 88% the board pins the avatar rings to the ground colour, so they stay dark on the hover surface — the site follows the card’s own surface instead

Block — a day of sessions

Aujourd’hui·Mardi
Session cover09h00 - 10h15Dans 33 min2 places restantes

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox
MentalCarrièreDébutant
Session cover09h00 - 10h15Dans 33 min2 places restantes

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox
MentalCarrièreDébutant
block · large — a day of sessions
Loop card block · 856×186 per block — how the pieces assemble (LoopCardContainer.tsx + LoopCardDesktop with showTimeline) · the connector runs down the left at 8px, then gap 16, then the container reserves 16px under the card — which is what keeps the dashed line visible between two sessions · the Date Large separator opens the day, its dot sitting on the same 8px rail so the whole column reads as one timeline · the board's block symbol uses the connector's default state; start and end are what you swap in at a day's first and last positions
Aujourd’hui·Mardi
09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox2 places restantes
MentalCarrièreDébutant
09h00 - 10h15Dans 33 min

Empowered - Décupler sa confiance en soi

Agent IA ParadoxLaurence Gilles, Martin Jakobin et l’agent IA Paradox2 places restantes
MentalCarrièreDébutant
block · compact — no rail at all
Compact block · 856×82 · the compact block is just the row — no connector, no container, no reserved space: its hour rail already carries the time, so the timeline would be redundant · the day is opened by the Date Compact separator and its 1px rule instead · rows are spaced by 24

Mobile renditions

Empowered - Décupler sa confiance en soi

09h00 - 10h152 places restantes
Agent IA ParadoxMentalCarrièreDébutant
default · live — press it, tab to it

Empowered - Décupler sa confiance en soi

09h00 - 10h152 places restantes
Agent IA ParadoxMentalCarrièreDébutant
default pressed · exploded

Empowered - Décupler sa confiance en soi

09h00 - 10h15Complet
Agent IA ParadoxMentalCarrièreDébutant
full

Empowered - Décupler sa confiance en soi

09h00 - 10h15Complet
Agent IA ParadoxMentalCarrièreDébutant
full pressed
Loop card mobile · 312 × min 136 — the first card is live: press it, or tab to it · radius 16 (against 20 on desktop, 8 on Minimal), border white 16%, Shadow-dark/lg, padding 16 · no cover and no CTA: the whole card is the target, so a single pressed state stands in for hover and focus at once — block surface + the same 3px white-24% ring as the Minimal card · the title is DM Sans Base SemiBold 16/24, one line ellipsised, with time and Indicator beneath it and the participants row pinned to the bottom (space-between, which is what the 136 minimum is for) · the category row tightens to gap 4 here — 8 on desktop — and fades at 86% · full dims text and participants to 48% but, unlike the desktop card, has no artwork to desaturate and no CTA to drop to 88%
Aujourd’hui·Mardi

Empowered - Décupler sa confiance en soi

09h00 - 10h152 places restantes
Agent IA ParadoxMentalCarrièreDébutant

Empowered - Décupler sa confiance en soi

09h00 - 10h152 places restantes
Agent IA ParadoxMentalCarrièreDébutant
block mobile · 328 — a day on a phone
Date mobile · 328×32 · and the mobile block · 328×152 · the Date keeps its Large rendition on mobile — dot, dashed tail and all — so the day still opens on the timeline rail · the block is the same assembly as desktop with one change: the gutter halves, 8 against 16, which is what lets a 312 card and its rail fit a 328 column · the container still reserves 16px under the card, so the dashed line stays visible between two sessions exactly as it does on desktop
09h00 - 10h152 places restantesAgent IA ParadoxMentalCarrièreDébutant
default · live — press it, tab to it
09h00 - 10h152 places restantesAgent IA ParadoxMentalCarrièreDébutant
default pressed · exploded
09h00 - 10h15CompletAgent IA ParadoxMentalCarrièreDébutant
full
09h00 - 10h15CompletAgent IA ParadoxMentalCarrièreDébutant
full pressed
Loop card compact mobile · 312×80 — the first card is live · (82 in CSS, the same border accounting as the Minimal card) · two things set this rendition apart from every other Loop card: it drops the title entirely — time, capacity, participants and categories only — and it replaces the CTA with a DS Radio md: this is the rendition you pick from, not the one you open · the radio's own block surface is visible here with no override, which is exactly why the Tutorial rows needed one (DRIFT-035): there the row was block, here the card is ground · pressed = block surface + the 3px white-24% ring, radio unchanged · full dims the details to 48% but leaves the picker at full contrast which reads as “a full session is still selectable” — intentional (waitlist) or an oversight? 🚧

Indicator · Date · Line

xs2 places restantes1 place restanteComplet
lg2 places restantes1 place restanteComplet
Indicator · two sizes × three states · xs is bare — an 8px dot (green-600 free · amber-600 last seat · red-600 full) plus an XS/Medium label, gap 6 · lg wraps it in a tinted glass pill: px12 py8, radius full, blur 6 over the matching semantic at 8% — the lightest tint in the system, and the only place it appears
Aujourd’hui·Mardi
large · timeline column
Aujourd’hui·Mardi
compact · bottom rule
Date · 856 wide · the day separator above a group of sessions: XS-Uppercase SemiBold for the day (“AUJOURD’HUI”), then neutral-400 for the middot and the weekday · large carries the timeline column (8px dot + the dashed line, content indented 8 and padded 16 below), compact drops it for a 1px white-16% rule under the row
default
end
start
first center
center
last center
Line · the six connector states · 2px dashed, dash 3/3 on actionable-muted-bg — the board’s neutral-800 in dark, inverted in light (the master strokes it with round caps; a repeating gradient squares them off — imperceptible at 2px) with an 8px neutral-600 dot · start opens a group (dot then line), first / center / last center place the dot at 28px in an 80px slot (empty above, both sides, empty below), default and end are the plain 186px runs default and end render identically — a duplicate variant in the master 🚧

Radio item — option card

From the Radio board: a 520px option card — featured icon, title + description, optional Glass Badge, radio top-right. Two states (default / selected) × two layouts (simple / with an inline input for the "custom answer" case). Selecting it relights the whole card.

Chaque semaine
Même jour, même heure. 6 sessions créées automatiquement.
default · simple
Chaque semaine
Même jour, même heure. 6 sessions créées automatiquement.
selected · simple
Chaque semaine
Même jour, même heure. 6 sessions créées automatiquement.
Créer6sessions à la suite.
default · with input
Chaque semaine
Même jour, même heure. 6 sessions créées automatiquement.
Créer6sessions à la suite.
selected · with input
Two states × two layouts · the simple pair is live — click a card to select it, hover for the copy chip · radius 16 · padding 16 · default: ground bg, border white 8% · selected: block bg, border white 16% + the PX Glow inset on the whole card · featured icon 40px radius 8 (Lucide redo 20) · title 16/24 Medium · description 14/20 neutral-400 · optional Glass Badge slot next to the title (white 16% pill · XS Medium uppercase) · the with input layout adds a 1px divider and an inline field (40×40, border white 16%, radius 8) indented 56px under the title · board copy kept verbatim

Implementation

What every card in this section shares, and what to reach for when building the next one.

shared contracts
The shell: radius 20 (16 on mobile, 8 on the Loop Minimal row), border white 16%, Shadow-dark/lg (md on the PX Agent card, at every state). Imagery cards add the veil (24%, 56% inactive, 8% featured) and the Details panel on a progressive scrim (0% → 56% at 41.35% → 96%) plus a masked progressive blur 16 — both layers carry the inner radius 0 0 19px 19px and the card isolates, because Chromium lets a backdrop-filter escape a rounded clip. States: the Buttons opacity system — inactive/full ·48, pressed and rest-CTA ·88, focus/pressed 3px ring white 24%. Participants: 20px avatars overlapping by 7.5, each ringed 1.25px in the card's own surface, the last seat being the agent (PX Heat ramp Low + the monogram).
reuse contract
A composite arranges primitives and never re-declares one. Badges are <Badge> and copy as one on click; the CTA is <Button>; the bar is <Progress>; markers are <Checkbox> / <Radio>; the capacity dot is the shared .pxdot (not the 14px ringed StatusIndicator); every glyph resolves from vendor/lucide.min.js at runtime through an icon slot ( .pxi[data-icon]) at a rendered 1.5px stroke — stroke-width = 1.5 × 24 / size; and every string names a documented text style ( text-ds-*, the same utility names as sirius). Three values legitimately escape the scale and say so in the CSS (board-override): the Paradox Score's 32px line, its label's −0.56 tracking, and the PXL wordmark's artwork lettering. node tools/audit-composites.js fails on any drift from this.
open questions
Four things need a decision before these families are locked: the Tutorial list's permanent top fade (DRIFT-036), the semantics of the Identity streak's two Low bars, the compact mobile Loop card keeping its picker at full contrast when the session is full, and the Line connector's default and end states rendering identically in the master.

Registers

Exemplars canon · validated 2026-07-28

Screen-level canon: the reference screens to imitate when you build a screen. Components are specced above; this register answers the next question up — how screens are composed. Source: Maxime's Figma frames (file uZfZ4lr4eJeDb4q5KeWDiz), 41 screens extracted and measured. Imitate the logic (hierarchy, rhythm, density, tone), never the pixels. The measured facts per screen — annotations verbatim, every value mapped to its token, layout, type, density — live one file per screen in exemplars/analysis/ — e.g. exemplars/analysis/home-desktop-large.md with its export in exemplars/captures/, same slug on both sides. Read the analysis file to build; look at the capture to compare.

read first
Three standing caveats.Design truth only — content and features have drifted, some blocks are unreleased; never read a frame as product truth. ② Copy and imagery are placeholder, written to fill the frames — never a copy reference (voice lives in the Bible §3). ③ The DS wins over Figma: where a frame contradicts a validated token, the DS value is right — build from the analysis files' mapped tokens, never by eyedropper.
method
A Figma workaround is an intention, not a mistake. Maxime builds them to show what he wants visually without breaking a component, inside Figma's constraints. So when a frame does something structurally odd — a zeroed section with a pasted overlay, a duplicated hidden block, an off-grid offset — never copy the mechanism and never dismiss it: read the intent and express it as a rule. Each card below carries its Figma constraint → rule translation. Per Bible hard rule 7 (global coherence), that rule then applies to every screen of the family.
What an exemplar teaches: layout, hierarchy, spacing rhythm, density, composition, focal point, accent discipline, responsive strategy. What it does not: interactive states, motion, copy — those stay with the hard rules and the sections above.

The responsive doctrine, seen on one screen

Home at six widths — the visual proof of Bible hard rule 8. From 1280 up the content column is capped and the margins absorb the extra width; at 1024 and below the cards keep their pixel size and the rows become peeking carousels. Responsiveness is how much of the next card shows, never a resize.

Home at 1600px
xl · 1600
Home at 1440px
large · 1440
Home at 1280px
desktop · 1280
Home at 1024px
small · 1024 — compact family starts here
Home at 768px
tablet · 768
Home at 360px
mobile · 360

The five exemplars

Home, desktop large
home-desktop-large · 6 breakpoints

E1 — Home

Why it's canon
The member dashboard reference: mixed card families (course, action, tutorial, event, identity) stacked in labelled sections, each a Ground Header over a horizontal card row. Uniform 20px radius across ~20 card instances of five types. The Paradox Score / streak block is the single focal point.
Figma constraint → rule
The tablet frame zeroes the course section and pastes an overlay carousel; desktop-small uses override frames with mask-cropped cards. Same intent both times: at ≤1024px card rows become peeking horizontal carousels at fixed card size. One carousel component for the whole compact family — never per-screen overlays, never a reflow that shrinks cards.
Also inside
The richest annotations of the corpus (19 verbatim) — the scroll-linked card animation spec and the streak-chart opacity rules, the only written motion intent we have. The mobile Identity/stats block is annotated 🚧 WIP, do not integrate; the logo 🚧 Wrong logo.

analysis/home-{desktop-large,desktop-xl,desktop,desktop-small,tablet,mobile}.md

Catalog, desktop large
catalog-desktop-large · 6 breakpoints + multi-topic

E2 — Catalog

Why it's canon
Browsing density and section rhythm. Course cards are a fixed size (244×344 standard, 624×344 featured) — wider screens add margin, never columns. Section vertical rhythm is a constant 488px step (440 section + 48 gap), holding through the 11-section multi-topic frame. Exactly one accent badge per visible card, never more, never a fill.
Figma constraint → rule
Tablet instances the mobile component at 768px, and every instance is internally named "@ Desktop large" whatever its width. Read as: tablet belongs to the compact family and follows the mobile composition — there is no third tablet grid to build. The naming carries no design meaning.
Also inside
Empty states exist but are annotated 🚧 EMPTY STATE ILLUSTRATION MISSING — the intent is there, the illustrations aren't. Hover, search and filters-open variants are captured.

analysis/catalog-*.md · 7 files incl. the multi-topic frame

Course detail, desktop large
course-detail-desktop-large · + mobile

E3 — Course detail

Why it's canon
A long content page with a sidebar. Desktop runs a genuine two-column split (modules flex-1 ≈848px + fixed 392px progress sidebar, 40px gap) that stacks full-width on mobile. Spacing is 100% on the DS scale with zero drift — the cleanest screen of the corpus on that axis. Title and description each drop one type tier from desktop to mobile; body copy stays 16px.
Figma constraint → rule
On mobile the hero title/meta block exists twice (one live over the image, one hidden below) — the hidden copy is the component's default content, kept rather than detached. Read as: on mobile the hero title overlays the image, exactly one title in the DOM. Render the overlay, drop the duplicate.

analysis/course-detail-{desktop-large,mobile}.md

Loop, desktop large
loop-desktop-large · 16 screens

E4 — Loop

Why it's canon
The broadest reference of the corpus — list shells, overlays, modals, chat and a long report page in one coherent family. Fixes the desktop list shell (Ground Header 64, pagination 64, footer 224) and the overlay vocabulary: 528px side panel, 480px modal, both over a 72%-black + 2px-blur scrim. Accent discipline at its strictest — burgundy is absent from three of four desktop screens and, on chat, marks only the member's own bubbles.
Figma constraint → rule
The share modal shows off-grid values (7 / 7.5px offsets, 0.714px borders) — artefacts of scaling a component in the frame. Read as: the close button sits at the standard 8px inset, borders are 1px. The JetBrains Mono Italic quote callout is the real validated exception for transcript content — not a licence for a third UI font.
Also inside
A hidden "Section compact" variant (82px cards) on both session lists — an unreleased density system.

analysis/loop-*.md · 16 files (preview, focus mode, share modal, analysis, sessions, chat, transcript, filters)

Loop creation, dispatch step
loop-creation-dispatch-desktop-large · 7 families

E5 — Loop creation

Why it's canon
The multi-step wizard reference. The whole New Loop flow is ONE master component whose steps are revealed by layer visibility, not a component per step — and the type hierarchy is deliberately flat: a single Clash headline over an otherwise uniform field of DM Sans 14px. Its State matrix sections inventory the empty, filled, error and recap states — currently the only state documentation in the corpus.
Figma constraint → rule
The disabled CTA is a raw 48% opacity class. That value is the DS disabled opacity — the intent is right, only the binding is raw. Read as: disabled = the DS .48 state, via its token.
Confetti is dead
The Success modal ships a full-frame confetti raster. Confetti is banned (Maxime, 2026-07-28) — gamified, childish, dated. The celebration primitive is the fire-crackle border effect, and the code already agrees: LoopCreateSuccessModal.tsx renders <BorderFireEffect>. Its palette is PX Heat extended into incandescence and it emits from the card border — it frames the artifact instead of covering the screen. The modal is canon for its layout only.

analysis/loop-creation-*.md · 7 families incl. dispatch, verbatim, thematique, success, detail drawer

How canon gets minted

Two routes, and nobody but Maxime mints. An agent may propose a candidate in a PR; only his explicit promotion counts. An exemplar without a WHY is a screenshot.

RouteWhat happensRefresh
Figma
(all five above)
Maxime hands over frames → export the capture, measure the values, map them to --ds-* tokens and ds/ components, flag what has no token → draft the annotations and a proposed WHY → Maxime validates the WHY in one line. No coded implementation required — the frame is the exemplar.Re-export when the frame changes.
HarnessA screen generated with the Bible, corrected by Maxime, shipped — then explicitly promoted, with captures, measurable annotations and the WHY.Monthly scripted re-capture and diff — running code rots.
quality reset
No shipped screen is canon. Everything built before the toolkit (2026-07-20) is presumed off-quality and will not be fixed retroactively (Maxime, 2026-07-27). Never imitate an existing screen — only the exemplars above. The one counter-example kept for teaching: MeditationScreen (mobile), 62 hardcoded hex values and blue/gold gradients entirely off PX Heat — every rule it breaks is a Bible hard rule.

Registers

Drift register

Every documented divergence between the Figma master, the code tokens, and the ds/ implementations — found during the ISO reproduction pass of 2026-07-20. Each entry carries what was expected, what was observed, and where it stands. Resolved entries stay: they're the history of the system.