This page renders AGENTS.md from the repository root, then each guide of agents/, so people and agents read the same rules.
Quantum Design System — agent guide
This repository is the monorepo of the Quantum Design System (Thales). It holds
technology-agnostic component specifications, DTCG design tokens, Markdown
documentation and a vanilla CSS reference implementation, plus an Astro docs site that
renders all of it so people can review a component before it is built in any technology, a
Figma plugin that draws every component in Figma, bound to variables recreated from the
tokens, a Penpot plugin that draws them in Penpot, bound to Penpot tokens, and themes that
bring the tokens to Bootstrap 6 and shadcn/ui through those frameworks' own theming APIs. The
packages are published on npm under @quantum-ds (@quantum-ds/tokens, @quantum-ds/components,
@quantum-ds/figma, @quantum-ds/penpot, @quantum-ds/bootstrap-theme, @quantum-ds/shadcn-theme).
You (an agent or a human) turn natural-language requests such as "a button must have a blue border using the primary color, in three variants" into coordinated updates across every artifact.
How these instructions are organised
- This file is loaded in every session (
CLAUDE.mdimports it). It holds only what every session needs: the system in brief, where to read more, the skills, the commands and the rules that always hold. - The guides in
agents/hold the detail. Before acting, read the guides the table "Read before acting" names for the task, in full. They are the source of truth for how to work; this file only summarises them. - Directory
AGENTS.mdfiles (packages/components/specs/,packages/tokens/,packages/components/,packages/figma/,packages/penpot/,apps/docs/content/,apps/docs/screens/,apps/docs/,scripts/) add local rules. Read the one of every directory you are about to edit. - Skills in
.claude/skills/run recurring workflows step by step (see "Skills"). - Changing these instructions: a rule goes in this file only when every session needs it and it fits in a line; everything else goes to its guide or directory file. All of them are rendered on the site's "How it works" page.
The system in brief
Four artifacts describe one component and must always agree (pnpm check proves it): the
spec packages/components/specs/components/<id>.spec.json (what it is, with its decision
log), the component tokens packages/tokens/src/components/<id>.tokens.json (every design
decision), the docs apps/docs/content/components/<id>.md (usage guidance, prose only) and
the web implementation packages/components/src/components/<id>/ (CSS reading only
component tokens, a pure HTML template). A foundation (packages/components/specs/foundations/,
apps/docs/content/foundations/) is a rule every component follows, such as color or the focus
ring. A screen (apps/docs/screens/) is a real-world page rebuilt only with the components
and tokens. The site in apps/docs/ is a viewer built with the design system: it never encodes
design knowledge of its own. A Figma blueprint (packages/figma/src/blueprints/<id>.mjs)
redraws each component in Figma: its layer tree, read from the web implementation, naming the
token slot of each field, bound to Figma variables recreated from the tokens (packages/figma/AGENTS.md).
raw (hidden primitives) <- semantic (what components read) <- component (per part)
packages/tokens/src/raw/** packages/tokens/src/semantic/** packages/tokens/src/components/<id>.tokens.json
packages/tokens/src/additions/semantic/**
The same decisions hold on every platform (web, ios, android, desktop) and in every
mode (light, dark): only raw and semantic values are overridden, in
packages/tokens/src/platforms/<id>/ and packages/tokens/src/modes/<id>/. The outputs
under packages/tokens/dist/ (built, not committed) come from pnpm tokens:build: the CSS the
repository reads, the platform trees and the syntax theme (scripts/build-tokens.mjs), and the
published CSS, Sass, JavaScript, Android, Figma, Penpot and Tokens Studio formats (Style Dictionary,
packages/tokens/style-dictionary.config.mjs). Details:
agents/model.md.
Read before acting
| The session is about… | Read |
|---|---|
| Understanding the system, finding where something lives | agents/model.md |
| Any change to a component, a foundation or a token: a request, a stated decision, a new component | agents/workflow.md, agents/conventions.md, agents/impact.md, then the directory files of what you edit (packages/components/specs/, packages/tokens/, packages/components/, apps/docs/content/) |
| Anything touching accessibility: a role, keyboard, naming, contrast, a cited statement | agents/accessibility.md, packages/components/specs/a11y/ |
| The Figma library and its local export, a Figma export, plugging tokens, where a token comes from | agents/token-sources.md, packages/tokens/AGENTS.md |
| Drawing the components in Figma, a Figma blueprint, the Figma plugin | agents/token-sources.md "Drawing a component in Figma", packages/figma/AGENTS.md |
| New releases of Figma, Penpot or Tokens Studio, whether the design tool exports are up to date, a workaround of a tool's limit | agents/design-tools.md, agents/design-tools.json |
| Drawing the components in Penpot, the Penpot plugin | agents/token-sources.md "Drawing a component in Penpot", packages/penpot/AGENTS.md, packages/figma/AGENTS.md (the blueprints) |
| Specs format export or import | agents/specs-interop.md |
| A screen | apps/docs/screens/AGENTS.md, agents/impact.md |
| The docs site | apps/docs/AGENTS.md |
| Scripts, the validator, the token build | scripts/AGENTS.md |
| Committing, a branch, a pull request, an issue (the end of every change) | agents/git.md |
Skills
| Skill | Use it when |
|---|---|
stepwise-pr |
Work needs several team decisions in a row: a draft pull request whose description is the plan, one step per session ("continue step N"). |
figma-alignment |
A component is compared with or improved from its local Figma export (agents/token-sources.md). Builds on stepwise-pr. |
review-sheet |
The team must compare renderings to decide: a focused before/after page and one precise question. |
worktree-preview |
Someone needs a local preview of a branch, a pull request or a worktree under .claude/worktrees/. |
backlog |
Out-of-scope work becomes an issue, a merged study becomes issues, or the open issues need syncing or labelling. |
framework-theme |
A theme of Quantum for a UI framework (Bootstrap, shadcn/ui, another one) is created as a @quantum-ds/<framework>-theme package from a mapping checked against the tokens and the specs, or regenerated after a token or spec change. |
design-tools-watch |
The releases of Figma, Penpot, Tokens Studio or DTCG need reviewing against the exports, or a check in the real tool comes back (agents/design-tools.md). |
Claude Code lists them by itself and invokes them by name; another agent reads
.claude/skills/<name>/SKILL.md.
Commands
| Command | What it does |
|---|---|
pnpm install |
Install the root tooling and every workspace package (packages/*, apps/*); installs the commit-msg hook. |
pnpm tokens:build |
Check the tokens and build packages/tokens/dist/ (not committed) from packages/tokens/src/: dist/css/** (raw.css, semantic.css, components/<id>.css and the index.css entry point), dist/platforms/<id>/<mode>.tokens.json and dist/shiki-theme.json (scripts/build-tokens.mjs), then the distribution formats (packages/tokens/style-dictionary.config.mjs). Run after any token change. |
pnpm components:build |
Copy the specs (JSON and the foundations' code samples) to packages/components/dist/, what @quantum-ds/components publishes; not committed, also run by pnpm build and before packing. |
pnpm tokens:figma-library |
Report every token recreated from the Figma library that has since been added, removed or changed; needs the local export (agents/token-sources.md). |
pnpm figma:export [id…] |
Build the Figma plugin into packages/figma/dist/ (not committed): it draws the components (all, or the given ones and those their instances use) as component sets bound to Figma variables recreated from the tokens (packages/figma/AGENTS.md). |
pnpm penpot:export [id…] |
Build the Penpot plugin into packages/penpot/dist/ (not committed): it writes the Penpot tokens and draws the components (all, or the given ones and those their copies use) as components with variants bound to them, from the Figma blueprints (packages/penpot/AGENTS.md). |
pnpm penpot:serve |
Serve packages/penpot/dist/ at http://localhost:4400, where Penpot's plugin manager installs manifest.json (Penpot loads plugins over HTTP). |
pnpm bootstrap-theme:build |
Build the tokens, then the Bootstrap 6 theme into packages/bootstrap-theme/dist/ (not committed): the theme as Bootstrap root custom properties and as Sass configuration, and demo.html, a self-contained page showing every Bootstrap component with it (packages/bootstrap-theme/AGENTS.md). |
pnpm shadcn-theme:build |
Build the tokens, then the shadcn/ui theme into packages/shadcn-theme/dist/ (not committed): the theme as shadcn CSS variables and as a registry theme item, and demo.html, a self-contained page showing every shadcn component with it (packages/shadcn-theme/AGENTS.md). |
pnpm validate |
Schema-validate specs and cross-check spec ↔ tokens ↔ docs ↔ web; check mirrored components render the same; check the docs site and the screens are built with the design system. |
pnpm specs:check |
Export every component to the Specs format and require a valid, lossless round trip (agents/specs-interop.md). |
pnpm specs:export [id…] |
Write <id>.specs.json + <id>.quantum.json sidecar to build/specs/. |
pnpm specs:import <file> [--sidecar <file>] |
Turn a Specs document into a draft spec + component tokens in build/specs-import/. Never writes into packages/components/specs/ or packages/tokens/src/. |
pnpm test |
Unit tests of the scripts' libraries (scripts/test/), of the Figma and Penpot plugins (packages/figma/test/, packages/penpot/test/) and of the Bootstrap and shadcn themes (packages/bootstrap-theme/test/, packages/shadcn-theme/test/). |
pnpm check |
tokens:build + validate + specs:check + test. Must pass before any change is considered done. |
pnpm impact [--base <ref>] [--files …] |
Every artifact linked to what changed (screens included), and whether it changed too. Fails when a component changed without a version bump (agents/impact.md). |
pnpm commits:check [--base <ref>] [--message "<text>"] |
Check commit messages against Conventional Commits (agents/git.md). |
pnpm a11y:wcag |
Regenerate packages/components/specs/a11y/wcag.json from the W3C's machine-readable WCAG 2.2 (network). |
pnpm new:component <id> --name "Name" --role <role> |
Scaffold the four artifacts for a new component and a starter Figma blueprint (to rewrite from the real CSS and template), build the tokens, register it in packages/components/src/index.css. |
pnpm dev |
Build tokens, then start the Astro docs site (apps/docs/) at http://localhost:4321. |
pnpm build |
Build tokens, the components package (packages/components/dist/), the Figma and Penpot plugins (packages/figma/dist/, packages/penpot/dist/), the Bootstrap and shadcn themes (packages/bootstrap-theme/dist/, packages/shadcn-theme/dist/) and the static docs site into apps/docs/dist/. |
pnpm preview |
Serve the built docs site locally. |
Rules that always hold
- A change is the whole set of artifacts linked to it, screens included: spec → component
tokens →
pnpm tokens:build→ CSS/template → Figma blueprint → docs → screens, thenpnpm checkandpnpm impact(agents/workflow.md, agents/impact.md). - Every stated decision goes through the decision loop in full and is recorded in the spec's
decisions[], one decision per commit (agents/workflow.md "Decisions"). - Accessibility targets WCAG 2.2 AA; every accessibility statement cites a trusted source you opened (agents/accessibility.md).
- No raw or semantic token beyond the Figma library and the listed additions without asking first (agents/token-sources.md).
- One development, one branch, one pull request against
main, Conventional Commits; a human reviews and merges. Out-of-scope work becomes an issue inThalesGroup/quantum(agents/git.md).
Do not
- Edit or commit anything under
packages/tokens/dist/. - Put literal colors or lengths in
packages/components/src/components/**/*.css. - Reference raw tokens from component tokens or semantic tokens from CSS.
- Encode component knowledge in
apps/docs/(variants lists, states, colors). The site derives everything from the four artifacts. - Style the site with literal colors or font values, raw tokens or component tokens, or give it
a look-alike of a control the design system has a component for (
apps/docs/AGENTS.md). - Duplicate spec data in docs (option lists as data). Docs explain why and when; the spec defines what.
- Add a dependency to
scripts/build-tokens.mjsand its libraries. The token checks and the CSS the repository reads are intentionally zero-dependency so they can be lifted into other tooling; the distribution formats belong topackages/tokens/style-dictionary.config.mjs(Style Dictionary). - Commit with
pnpm checkfailing. - Commit or push to
maindirectly, merge your own pull request, or enable auto-merge (agents/git.md). - State an accessibility behaviour, requirement or recommendation without a trusted source, or cite a source you have not read. Memory is not a source.
- Change one artifact and leave the ones linked to it out of sync. Screens are linked artifacts: a component or token change that leaves a screen broken or stale is not done.
- Write a
ds-*class by hand in a screen, restyle a component fromapps/docs/screens/screens.css, or reuse the original's visual design, images or logos in a screen (apps/docs/screens/AGENTS.md). - Add a screen without its source, its licence text and the copyright notice the licence requires.
- Fork component tokens per platform or per mode, or put a platform or mode value in a component
token file: override the raw or semantic token in
packages/tokens/src/platforms/<id>/, or the semantic token inpackages/tokens/src/modes/<id>/, instead. - Scope a statement to a platform because its source is web-only when the statement holds everywhere, or state a platform behaviour without that platform's trusted source.
- Create GitHub Pages, deploy the site anywhere, or add a deployment job to the workflows (agents/git.md).
- Open an issue anywhere other than the
ThalesGroup/quantumrepository (agents/git.md).
Model and repository map
What the four artifacts, foundations, screens, token tiers, platforms and modes are, and where everything lives. Part of the agent guide: start from the root AGENTS.md.
Mental model
Four artifacts describe one component. They must always agree; pnpm check proves it.
| Artifact | Path | Owns |
|---|---|---|
| Spec | packages/components/specs/components/<id>.spec.json |
What the component is: role, anatomy (parts with roles), properties, states, layout, accessibility, content rules, examples, implementation metadata, and the decision log that led there. Technology-agnostic. |
| Component tokens | packages/tokens/src/components/<id>.tokens.json |
Every design decision of the component (colors, dimensions, typography) as DTCG tokens aliasing semantic tokens. |
| Docs | apps/docs/content/components/<id>.md |
Usage guidance for designers and developers: when to use, variants meaning, content, accessibility. Prose only; no data that the spec already holds. |
| Web implementation | packages/components/src/components/<id>/<id>.css + <id>.template.mjs |
The reference rendering: vanilla CSS consuming only component tokens, and a pure HTML template render(props). |
Foundations
Some decisions belong to no single component but hold for all of them: the building blocks of the system (color, typography, spacing and sizing, shape, elevation and effects, motion, iconography), global rules such as the focus ring, and code syntax colors; text selection is planned. A foundation is that global specification, documented like a component but with no anatomy, role or properties:
| Artifact | Path | Owns |
|---|---|---|
| Foundation spec | packages/components/specs/foundations/<id>.spec.json |
The semantic tokens it owns (tokens), what it is as one rule per aspect (rules), the components that must follow it (appliesTo), its contrast requirements (computed in every platform × mode by pnpm validate), its cited accessibility statements, the specimens that draw the tokens it is built from (raw palettes and scales included) on its page, examples rendered with the components following it (or code samples next to the spec, in packages/components/specs/foundations/<id>/), decisions[] and changelog. Schema packages/components/specs/schema/foundation.schema.json. |
| Foundation docs | apps/docs/content/foundations/<id>.md |
Usage guidance, platform sub-headings, a sourced Accessibility section, like component docs. |
A foundation owns semantic tokens only (none yet when no decision gives its primitives a
meaning: elevation and effects); components follow it by aliasing them from their own component
tokens, and never re-specify it. Its specimens present raw and semantic tokens by group
pattern, so a new token appears on its page with no site change, and every raw and semantic
token must reach one foundation (owned or in a specimen; pnpm validate warns otherwise). The
site derives who follows a foundation from those aliases and renders each one under
Foundations. Foundations carry no web file today:
the components draw what they specify. A foundation that needs global CSS (::selection, code
blocks) will add it under packages/components/src/foundations/, imported from
packages/components/src/index.css.
Screens
A screen is a real-world page (a login page, an application shell, a dashboard) taken from an open-source project whose licence allows it and rebuilt only with the components and tokens of the design system. Screens prove that components hold together, list what the system still lacks (each screen's gaps, with their issues when some track them), and show the system on pages people recognise:
| Artifact | Path | Owns |
|---|---|---|
| Screen | apps/docs/screens/<source>/<block>.screen.mjs |
meta (name, section, description, changes from the original, design system gaps with their issues when some track them) and a pure render() that composes the components' own templates. |
| Source | apps/docs/screens/<source>/source.json + its licence file |
Where the screens come from: project, url, repository, commit, licence and copyright, copied verbatim. Shown on every screen. |
| Layout | apps/docs/screens/screens.css, apps/docs/screens/lib/ |
Layout only, on semantic tokens; shared building blocks (shells, forms, charts). |
Screens are consumers of the design system, like the docs site: every change to a component
or a token updates the screens that use it in the same change (agents/workflow.md, agents/impact.md, apps/docs/screens/AGENTS.md).
In Figma
@quantum-ds/figma (packages/figma/) draws the components in Figma. Each component has
a Figma blueprint (packages/figma/src/blueprints/<id>.mjs): its Figma layer tree, read from
the web implementation, naming the token slot behind each field and holding no value. The
plugin recreates the tokens as Figma variables (one collection per tier, the modes on the
semantic one, aliases kept) and expands each blueprint over every configuration of the spec,
binding in each variant the token the active-token rule picks, the one the CSS reads. Like the
screens, a blueprint is a consumer kept in step with its component: pnpm impact lists it,
pnpm test fails when a token slot is neither bound nor explained (packages/figma/AGENTS.md,
agents/token-sources.md "Drawing a component in Figma").
In Penpot
@quantum-ds/penpot (packages/penpot/) draws the components in Penpot from the Figma
blueprints of @quantum-ds/figma (packages/figma/src/blueprints/<id>.mjs) and the same engine:
nothing about a component is written for Penpot. It writes the Penpot token file the token build
publishes (packages/tokens/dist/penpot/tokens.json: one set per tier group and per mode, one
theme per mode, references kept) into the file's tokens, and translates each variant's Figma
fields to the Penpot token properties that drive them. What Penpot cannot bind is set to the
token's value and listed with the reason (packages/penpot/AGENTS.md, agents/token-sources.md
"Drawing a component in Penpot").
In Bootstrap and shadcn/ui
@quantum-ds/bootstrap-theme (packages/bootstrap-theme/) and @quantum-ds/shadcn-theme (packages/shadcn-theme/) bring the
design system to two web frameworks as themes, built from the tokens and nothing else: each one
is a mapping (src/mapping.mjs) from the framework's own theming API to Quantum token paths, that
its build turns into what the framework consumes (Bootstrap's root custom properties and Sass
configuration; shadcn's CSS variables and registry theme item), in light and dark. The mapping
respects the tiers: the framework's semantic layer (surfaces, text, borders, theme colors, type,
radii, spacing, focus) reads Quantum semantic tokens; its per-component layer (button sizes,
control borders, card surfaces) reads Quantum component tokens. A theme never restyles the
framework's components: what the API cannot express is listed in the package README, not forced.
Each package builds a self-contained dist/demo.html showing every component of the framework
with the theme, to compare with the reference rendering (packages/bootstrap-theme/AGENTS.md,
packages/shadcn-theme/AGENTS.md).
Tokens form three tiers with a strict reference direction:
raw (hidden primitives) <- semantic (what components read) <- component (per part)
packages/tokens/src/raw/** packages/tokens/src/semantic/** packages/tokens/src/components/<id>.tokens.json
packages/tokens/src/additions/semantic/**
Raw is the Figma library's color palettes and palette switch, which nobody reads directly. Semantic is what components and the site read: the Figma library's mapped colors and its Dimension, Effect and Typography scales (the library applies those directly; there is no semantic layer of sizes or text styles on top of them), plus the few tokens the design system adds.
The raw and semantic tokens come from two places, kept apart (agents/token-sources.md): packages/tokens/src/raw/,
packages/tokens/src/semantic/ and packages/tokens/src/modes/dark/ are recreated from
the Figma library as it is (paths made valid, same values, same aliases, errors
included); packages/tokens/src/additions/ and packages/tokens/src/platforms/ hold the few
tokens the design system adds, and only those the library lacks to render the components.
scripts/build-tokens.mjs compiles the tiers to CSS custom properties in
packages/tokens/dist/css/ (generated, not committed, never edited by hand), with an index.css
entry point importing every file in cascade order; Style Dictionary
(packages/tokens/style-dictionary.config.mjs) builds the published formats (CSS, Sass, JavaScript,
Android, Figma, Penpot, Tokens Studio, DTCG) next to it. The site in apps/docs/ is a viewer: it
renders what the four artifacts say and must never encode design knowledge of its own. It is also
built with the design system: its chrome reads semantic tokens only and renders the
design system's components wherever it shows a control they cover, so a new token or a new
component can change how the docs look (apps/docs/AGENTS.md, enforced by pnpm validate).
Platforms
Everything above is written once for every platform. packages/components/specs/platforms.json names the
platforms (web, ios, android, desktop) and the input modalities (pointer, touch,
keyboard). A state, an example, a keyboard entry, an accessibility guideline or a content
rule carries an optional platforms list when it applies to some platforms only; a state
carries inputs when only some inputs produce it (hover needs a pointer, focus-visible a
keyboard). Docs scope a paragraph with a ### <Platform name> sub-heading. Raw and semantic
tokens may be overridden per platform in packages/tokens/src/platforms/<id>/; the build resolves every
tier per platform and mode into packages/tokens/dist/platforms/<id>/<mode>.tokens.json, a DTCG tree any
technology can transform. Component tokens are never forked: the decisions are the same
everywhere, only the values differ. A technology (Flutter, React Native, Qt, JavaFX, a web
framework) is not a platform: it targets one or more platforms and consumes their outputs. The
web implementation is the only reference rendering; other platforms record their
implementation under implementations.<id>.
Modes
A mode is a context in which the same token paths take other values: today the color
modes light (the default) and dark, declared under modes in packages/tokens/tokens.config.json. The
default mode is the base tiers; every other mode is an overlay of the semantic tier in
packages/tokens/src/modes/<id>/ (same paths, aliasing raw tokens, no new path). Component tokens are never
forked per mode either: a component's color decisions hold in every mode, only the semantic
colors they alias change. Each mode carries a scheme (light or dark) that tells every
technology which system appearance selects it. Modes and platforms combine: every platform has
one resolved tree per mode; a token varies per platform or per mode, never both. On the web, the
data-theme attribute sets the mode of an element and its subtree: light, dark, or auto
(follows prefers-color-scheme); without it, the default mode applies (agents/conventions.md).
Repository map
AGENTS.md the agent guide (rendered on the site's "How it works" page)
agents/ the topic guides AGENTS.md routes to
agents/design-tools.json the design tools the exports target, and how far their releases are reviewed
CLAUDE.md imports AGENTS.md
.claude/skills/ agent skills for recurring workflows
.githooks/commit-msg Conventional Commits check, installed by pnpm install
.github/workflows/ CI (check, commits, impact, build) and the npm release workflow
package.json pnpm scripts (AGENTS.md "Commands"); pnpm workspace: packages/* and apps/*
scripts/ build-tokens, validate, impact, specs-bridge, new-component, commit-lint, figma-library, tests -> scripts/AGENTS.md
scripts/lib/paths.mjs the layout map every script and the docs site read their paths from
packages/tokens/ @quantum-ds/tokens -> packages/tokens/AGENTS.md
tokens.config.json tiers, platforms, modes, outputs, syntax theme
src/raw, src/semantic recreated from the Figma library (agents/token-sources.md)
src/additions/ raw, semantic and mode tokens the design system adds
src/components/<id>.tokens.json component tokens
src/platforms/<id>/ per-platform overrides of raw and semantic tokens
src/modes/<id>/ per-mode overrides of semantic tokens (dark)
style-dictionary.config.mjs the distribution formats (Style Dictionary)
dist/ GENERATED, not committed (pnpm tokens:build)
dist/css/ GENERATED CSS custom properties: raw.css, semantic.css, components/<id>.css, index.css (entry point)
dist/platforms/<id>/<mode>.tokens.json GENERATED resolved DTCG tree per platform and mode
dist/shiki-theme.json GENERATED syntax highlighting theme (Code foundation)
dist/tokens*.css, *.scss, tokens-es6*, variables-css.js, resources*.xml, figma/, penpot/, tokens_studio/, dtcg/
GENERATED distribution formats (style-dictionary.config.mjs)
packages/components/ @quantum-ds/components (publishes specs/ only) -> packages/components/AGENTS.md
specs/ component.schema.json + one spec per component -> packages/components/specs/AGENTS.md
specs/foundations/ global specifications shared by every component, foundation.schema.json
specs/a11y/ accessibility registry: trusted sources, WCAG 2.2 (generated), role profiles
specs/platforms.json platform and input vocabulary
dist/ GENERATED, not committed: the published specs (pnpm components:build)
src/ web reference implementation, not published (the docs site imports it by relative path)
src/index.css entry point of the vanilla CSS reference implementation (fonts, foundations, components)
src/fonts.css webfonts named by the font-family tokens (Google Fonts)
src/foundations/ global CSS of a foundation (code.css)
src/components/<id>/ hand-written CSS + HTML template
src/lib/template-utils.mjs escaping helpers + icon ligatures
packages/figma/ @quantum-ds/figma: the Figma plugin drawing every component (publishes dist/) -> packages/figma/AGENTS.md
build.mjs pnpm figma:export and the package build: writes dist/ (GENERATED, not committed)
src/plan.mjs, src/plugin.js the plan (variables, variants) and the plugin that draws it in Figma
src/blueprints/<id>.mjs the Figma layer tree of each component, read from its CSS and template (Penpot draws it too)
packages/penpot/ @quantum-ds/penpot: the Penpot plugin drawing every component -> packages/penpot/AGENTS.md
build.mjs, serve.mjs pnpm penpot:export (writes dist/, GENERATED, not committed) and pnpm penpot:serve
src/plan.mjs, src/plugin.js the plan (tokens, variants translated from the Figma plan) and the plugin that draws it in Penpot
packages/bootstrap-theme/ @quantum-ds/bootstrap-theme: the Bootstrap 6 theme built from the tokens -> packages/bootstrap-theme/AGENTS.md
build.mjs, src/mapping.mjs pnpm bootstrap-theme:build: the Bootstrap token -> Quantum token mapping, written to dist/ (GENERATED, not committed) with demo.html
demo/ the source of the demo page (every Bootstrap component, light and dark, theme on/off)
packages/shadcn-theme/ @quantum-ds/shadcn-theme: the shadcn/ui theme built from the tokens -> packages/shadcn-theme/AGENTS.md
build.mjs, src/mapping.mjs pnpm shadcn-theme:build: the shadcn variable -> Quantum token mapping, written to dist/ (GENERATED, not committed) with demo.html
demo/ the source of the demo page (a Vite + React + Tailwind app showing every shadcn component, light and dark)
apps/docs/ the docs site (Astro) -> apps/docs/AGENTS.md
content/components/, content/foundations/ one Markdown page per component and foundation -> apps/docs/content/AGENTS.md
screens/ real-world screens built with the design system, their sources and licences -> apps/docs/screens/AGENTS.md
src/ the viewer: pages, components, lib, styles
Change workflow
The change loop, where each kind of request goes, the decision loop, and the definition of done. Part of the agent guide: start from the root AGENTS.md.
The change workflow
Every request follows the same loop. Do not skip steps or stop halfway: a change that touches the spec but not the tokens, CSS, or docs leaves the repository inconsistent.
- Read first. Open the spec, the component tokens, the CSS and the docs of the component.
For a new component, read
packages/components/specs/components/button.spec.jsonand its siblings as the reference implementation of the pattern. - Map the impact. List every artifact linked to what you are about to change (
agents/impact.md). The change is the whole set, not the file the request names. - Locate the decision. Decide which layer the request changes (table below). Prefer the highest layer that expresses the intent: a change that should apply to every control belongs in semantic tokens, not in a component's tokens.
- Edit in order: spec → component tokens →
pnpm tokens:build→ CSS/template → Figma blueprint (packages/figma/src/blueprints/<id>.mjs, drawn in Figma and in Penpot: a new part, layout or token slot changes its layer tree; every slot is bound or listed innotDrawn) → docs. Bumpversionin the spec and add achangelogentry (newest first) for anything visible. A stated design decision is also recorded indecisions[](see "Decisions" below). Anything touching accessibility followsagents/accessibility.md: verify the source, then cite it. - Update the screens. Every screen rendering a changed component, or a component whose
tokens alias a changed token, is part of the change (
pnpm impactlists them): apply any template API change to it, drop a gap a new component or token now fills, and look at it. - Verify:
pnpm check, thenpnpm impact. Fix every error. Read warnings; they usually point at forgotten states, unused tokens or uncited references. Walk the impact checklist: every linked artifact is either updated or consciously left as is. - Look at it:
pnpm dev, open the component page, check the examples, the states matrix, the playground (and its token inspector: which token paints which part) and the accessibility contract on the Specification tab; then the Screens using it, at every frame width and in every mode. - Report what changed in each artifact, the sources behind any accessibility statement, and every linked artifact you deliberately left unchanged, with the reason.
Where does a change go?
| Request sounds like | Change |
|---|---|
| "Add a success intent" | Spec: new option on intent, example. Semantic tokens: the Figma library already maps global.bg.<emphasis>.functional.success.*, global.text.on-<emphasis>.functional.success.default, global.border.<emphasis>.functional.success.*; add a role only if it is missing, in packages/tokens/src/additions/semantic/. Component tokens: <id>.<part>.<property>.<emphasis>.success.<state> for every affected part/property/emphasis/state. CSS: one ladder rule per emphasis for [data-intent="success"]. Docs: row in the Intent table. Contrast computed for every new pair. |
| "The border must be the primary color" | Semantic tokens if it applies to all controls, otherwise component tokens: repoint <id>.container.border-color.* aliases. Rebuild. CSS unchanged (it only reads tokens). Docs: mention if the meaning changed. |
| "Buttons are too small on mobile" | The heights come from the Figma library's space.* in each component's tokens, the same on every platform; there is no control height token (an earlier decision removed them). A per-platform height is a decision to discuss first. |
| "Add a loading state" | Spec: state loading (trigger system), possibly property loading and a part indicator. Tokens for the indicator. CSS: [data-state="loading"] plus the native mechanism. Template: render the indicator. Docs: row in States. Accessibility: how the busy state is announced, cited from APG/WAI-ARIA (agents/accessibility.md). |
| "Make it accessible" / "What does a screen reader say?" | Read the role profile in packages/components/specs/a11y/roles.json and its APG pattern first. Answer and specify only from trusted sources (agents/accessibility.md); cite them in the spec, explain them in the docs. |
| "Every component must…" / "The focus ring…" / "Define the code colors" | A foundation (agents/model.md): the semantic tokens first, then packages/components/specs/foundations/<id>.spec.json (rules, specimens, contrast, accessibility, examples, decisions[]) and apps/docs/content/foundations/<id>.md. Components follow it by aliasing its tokens; appliesTo makes pnpm validate require it. Never re-describe it in each component. |
| "Add a new component" | pnpm new:component <id> --name ... --role ..., then fill every generated file. Add the component to packages/components/src/index.css (the scaffold does it). If the role has no profile in packages/components/specs/a11y/roles.json, write it first from the APG pattern and the WAI-ARIA role. Then the docs: every site control the component covers becomes the component (pnpm validate lists them), and the table in apps/docs/AGENTS.md says so. Then its Figma blueprint: the scaffold writes a starter one (packages/figma/src/blueprints/<id>.mjs) that only draws the scaffolded CSS; rewrite its tree from the real CSS and template (packages/figma/AGENTS.md). |
| "Hover does not exist on touch" / "This state only exists on iOS" | Spec: inputs on the state (pointer, touch, keyboard), or platforms when a platform genuinely lacks or adds the state. CSS and template only cover states that include web. Docs: say so in the States table. |
| "Support dark mode" / "This color is wrong in dark mode" | Semantic tokens of the mode: packages/tokens/src/modes/dark/ (same path as in packages/tokens/src/semantic/, aliasing a raw token; add the raw primitive if missing), or packages/tokens/src/additions/modes/dark/ for a token the design system added. Never a component token. Rebuild. Every component aliasing it (directly or through semantic aliases): contrast recomputed in that mode, version + changelog with the ratios. Docs only if the guidance changes. |
| "Add a high-contrast / dim mode" | A modes entry in packages/tokens/tokens.config.json (with its scheme) and its overlay directory packages/tokens/src/modes/<id>/. Rebuild: the web gets [data-theme="<id>"], every platform a <id>.tokens.json. Contrast of every component in the new mode. |
| "On iOS the target must be 44 pt" | A decision first: there is no control height token to override. Then an override in packages/tokens/src/platforms/ios/, never a component token. Rebuild. Spec: a guideline scoped platforms: ["ios"] citing Apple HIG (agents/accessibility.md). Docs: a ### iOS sub-heading. |
| "Add a Flutter / Qt / JavaFX implementation" | Not a platform. Pick the platforms it targets, consume packages/tokens/dist/platforms/<id>/<mode>.tokens.json (@quantum-ds/tokens/platforms/<id>/<mode>.tokens.json; one per mode, selected by the system appearance named by the mode's scheme; or packages/tokens/dist/css/ (@quantum-ds/tokens/css/index.css) for the web), record it under implementations.<platform> in the spec. |
| "Add a screen" / "Rebuild this page with the design system" | apps/docs/screens/AGENTS.md: a source with a licence that allows it (source.json + the licence text), then apps/docs/screens/<source>/<block>.screen.mjs rendering the components' templates, laid out by apps/docs/screens/screens.css on semantic tokens. Every gap goes to meta.gaps (its text; the issues tracking it when some exist). |
| "Change how the site looks" | apps/docs/ only, with semantic tokens and the design system's components. A meaning the semantic tier lacks becomes a semantic token (packages/tokens/src/additions/semantic/), never a site literal. Never solve a component styling problem in the site CSS. |
| "Plug the real tokens" / "Where does this token come from?" | See agents/token-sources.md. |
| "Draw the components in Figma" / "Update the Figma file" | pnpm figma:export (or pnpm figma:export <id>…) builds the plugin into packages/figma/dist/; run it in the Figma desktop app (agents/token-sources.md "Drawing a component in Figma"). A component that renders wrong in Figma is fixed in its blueprint, never in the Figma file: the next run would redraw it. |
| "Draw the components in Penpot" / "Update the Penpot file" | pnpm penpot:export (or pnpm penpot:export <id>…) builds the plugin into packages/penpot/dist/, pnpm penpot:serve serves it; install and run it in Penpot (agents/token-sources.md "Drawing a component in Penpot"). It draws the Figma blueprints: a component that renders wrong in Penpot is fixed in its blueprint or in the Penpot translation (packages/penpot/src/plan.mjs), never in the Penpot file. |
| "Compare a component with its Figma export" / "Align the button with Figma" | The local export in raw_content/components/ (agents/token-sources.md) is the candidate rendering; the repository stays the reference for the variants (axes and option names). List every difference in the pull request, then take them one at a time: show before and after on the site, the team decides, then the decision loop (below) and one commit. Map every raw value of the export to the semantic token that carries it (values a couple of RGB units apart are export noise); change only the component's tokens, never raw or semantic tokens, and ask before any addition. Keep the running list of departures from Figma in the pull request; when the last difference is decided, open one issue labelled upstream-figma-ds listing each departure with its values, the Figma variable to bind and the decision id, so the Figma library converges. |
Decisions: defining a component one statement at a time
Components are defined incrementally: someone states a decision ("the button has a blue border using the primary color", "there are three variants", "no icon on the small size"). Each statement goes through the decision loop below, in full, before the next one. It applies to every component. What a component shows before a decision confirms it is provisional: a decision may replace provisional content without superseding anything.
- Understand it. Restate the decision as a rule. Ask before applying when it is
ambiguous, contradicts an active decision, or would break WCAG 2.2 AA (
agents/accessibility.md): say which criterion it breaks, with its source, and propose a compliant alternative. - Store it in the spec's
decisions[], newest first: a stable kebabid,date,version(the one this change ships in),area,status: "active",text(the rule, present tense),rationaleonly when one was given (never invented),tokens(the paths orgroup.*that carry it),platformswhen scoped,refswhen accessibility is involved (required forarea: "accessibility"). A decision that replaces an earlier one lists it insupersedes, and the earlier one becomesstatus: "superseded"; old entries are never edited or deleted. The site renders the log on the component's Decisions tab. - Tokens. Create, update or delete semantic tokens when the decision is a system-wide
meaning (step 3 of the change workflow above), then component tokens for every affected part × option × state. Raw
tokens only when a needed primitive is missing (
agents/token-sources.md). Delete tokens nothing uses any more.pnpm tokens:build. - Spec. Anatomy, properties and options, states, layout, content rules,
examples[](add or change an example when the decision is visible), accessibility contract. Bump the minorversion(patch for a fix that changes no decision) and add achangelogentry naming the decision ids. - Web implementation. CSS rules for every new option and state, template parts. CSS still reads only component tokens.
- Docs. Tables (variants, sizes, states), guidance, platform sub-headings. Docs explain why and when; they do not copy the decision log.
- Examples, states matrix, playground and screens. They are generated from
examples[],propertiesandstates: check on the site that they show the decision. Never patch the site for one component; if the viewer cannot show a decision, open an issue (agents/git.md). Update and look at every screen rendering the component (pnpm impactlists them). - Accessibility check. For every affected pair, compute the contrast from the resolved
tokens in every mode × option × enabled state (text 4.5:1, WCAG 1.4.3; borders that identify the
control and the focus ring 3:1, WCAG 1.4.11) and record the ratios in the changelog entry.
Also review target size (2.5.8), focus visibility (2.4.7), meaning by color alone (1.4.1),
the role profile, and each platform's overlay. New statements cite sources you opened (
agents/accessibility.md). - Verify and look:
pnpm check,pnpm impact, then the site: Decisions tab, examples, states matrix, playground, Specification tab. - Report and commit. Report per artifact what the decision changed, the contrast ratios
and sources, and each linked artifact left unchanged with the reason. Commit the decision
on its own branch, as a Conventional Commit scoped to the component (
feat(button): blue border in the primary color;agents/git.md), push the branch and open a pull request with the labels that fit and a description a reviewer can follow (agents/git.md"Pull requests").
Definition of done
- Spec validates against the schema;
versionbumped andchangelogentry added. - Every stated decision recorded in
decisions[]; a replaced one markedsuperseded. - Component tokens cover every affected part × option × state; aliases point to semantic tokens.
-
pnpm tokens:buildrun (its outputs are not committed). - CSS has a rule for every option and every state; template renders every example.
- The Figma blueprint follows the CSS structure and binds or lists every token slot (
pnpm test, which draws it in Figma and in Penpot). - Docs updated (variants/sizes/states tables, content, accessibility).
- Contrast of every affected color pair computed from the resolved tokens, in every mode, and recorded. A foundation's pairs are declared in its
contrast[]and computed bypnpm validate. - A rule shared by every component lives in a foundation (
packages/components/specs/foundations/), not in each component; components following it alias its tokens. - Every new raw or semantic token reaches a foundation page: owned by a foundation or matched by one of its specimens (no
pnpm validatewarning). - Every accessibility statement cites a trusted source you actually opened (
agents/accessibility.md); the docs Accessibility section links the sources that back its explanations. - Anything platform-specific is scoped (
platforms,inputs,### <Platform>); universal statements really hold everywhere; platform overlays rebuilt. -
pnpm checkpasses with no errors. Warnings understood. -
pnpm impactpasses; every unchanged linked artifact is justified in the report (agents/impact.md). - Component reviewed in the site (examples, states matrix, playground, accessibility).
- Every screen using a changed component or token updated (API changes applied, filled gaps removed) and reviewed on the Screens tab, at every frame width and in every mode.
- The docs site still uses only semantic tokens, and the components covering its controls (a new component replaced its site look-alikes;
apps/docs/AGENTS.mdtable updated). - Committed on its own branch, never on
main, as a Conventional Commit scoped to the component or area (agents/git.md"Commits"). - Branch pushed and pull request opened with the labels that fit and a human-readable description (
agents/git.md"Pull requests"). - Final report: what was done, and what the reviewer must look at precisely (
agents/git.md"Pull requests").
Conventions
Identifiers, roles, the component token path grammar, states, platforms, modes and the web implementation rules. Part of the agent guide: start from the root AGENTS.md.
Conventions
Identifiers
- Component ids, part ids, option values, state ids: kebab-case (
icon-start,focus-visible). - Property ids: camelCase (
iconStart), because they become template props and, later, framework props. - One component = one id used everywhere: spec
id, token root group, docs file,packages/components/src/components/<id>/, CSS classds-<id>.
Roles (from Nathan Curtis' "component and part roles as composites")
- A component role says what the component is (
button,togglebutton,textbox,disclosure,alert,presentation…). It is a composite: it implies semantics, keyboard behaviour and ARIA for every platform, so implementations never re-derive them. - A part role says what a part does for the component (
container,label,icon,indicator,description,errormessage,panel…). - Both vocabularies live as enums in
packages/components/specs/schema/component.schema.json. Extending them is a deliberate act: edit the schema, explain the new role inpackages/components/specs/AGENTS.md. - The first anatomy part is always the root and has role
container.
Component token path grammar
<component>.<part>.<property>[.<option>][.<state>]
button.container.background.primary.hover
button.container.border-radius
button.label.typography.md
<part>must be a part id from the spec's anatomy.<property>is a kebab-case CSS-like property name (background,border-color,min-height,typography).<option>is an option value of an enum property (primary,sm);<state>a declared state id.- Which token applies to a configuration follows from the path alone: an option missing from
the path matches any value, a state without a token falls back to
default, the token naming the most options wins. Rule and checks:packages/tokens/AGENTS.md"Which token applies". - Values alias semantic tokens only. Never a raw token, never a literal.
- The generated CSS variable is the path joined with
-:--button-container-background-primary-hover. Typography composites expand to-font-family,-font-size,-font-weight,-line-height,-letter-spacing.
States vocabulary
default, hover, active, focus-visible, disabled, selected, checked, unchecked,
indeterminate, pressed, expanded, collapsed, invalid, readonly, loading,
dragging, visited, current. Every spec declares default. A state used in a token path
must be declared in the spec.
Platforms and inputs
- Vocabulary in
packages/components/specs/platforms.json; the schema enums mirror it andpnpm checkkeeps them equal. - Default is universal. Add
platformsonly when a platform genuinely differs, andinputsonly when a state depends on how people interact (hover, focus-visible). A statement whose source is web-only but whose content holds everywhere stays universal. - A platform statement cites that platform's trusted source (Apple HIG, Android developers, Material). Never infer a platform behaviour from another platform's documentation.
- Docs:
### Web,### iOS,### Android,### Desktopsub-headings, exactly those names. - Tokens: override in
packages/tokens/src/platforms/<id>/, same paths as the base tokens, only raw and semantic tiers, no new paths. Lengths stay in the DTCG unit (px, read as pt or dp). implementations.webis the reference rendering and stays required;ios,androidanddesktopare optional metadata (control,framework,source,notes).
Modes
- Vocabulary in
packages/tokens/tokens.config.json(modes): exactly onedefault(the base tiers), each with ascheme(lightordark). Overlays inpackages/tokens/src/modes/<id>/override semantic tokens only, same paths, aliasing raw tokens. A component token, a raw token or a new path in a mode is refused by the build. - Descriptions, docs and spec statements name modes by their meaning ("dark mode"), never by a
technology mechanism; the web mechanism belongs to
### Websub-headings andpackages/components/src/. - Web:
data-theme="<mode>"on any element switches its subtree;data-theme="auto"followsprefers-color-scheme; no attribute means the default mode. The generated CSS declares the semantic and component tokens on:root, [data-theme]so aliases resolve again in every context, and setscolor-scheme. Component CSS never mentions modes. - Other platforms: one resolved tree per mode,
packages/tokens/dist/platforms/<id>/<mode>.tokens.json; the technology selects the tree from the system appearance (the mode'sscheme).
Web implementation
- Root class
.ds-<id>on the native element named inimplementations.web.element. A form control with a label, a description or an error message as parts has a wrapper root and names the native control asimplementations.web.rolePart(packages/components/AGENTS.md"Form controls with a wrapper root"); the accessibility checks then apply to that element. - Enum properties become
data-<prop>="<option>"attributes; booleans become native attributes when one exists (disabled), otherwisedata-<prop>. - Parts are
.ds-<id>__<part>, optionally withdata-part="<part>". Decorative parts carryaria-hidden="true". - Every state selector is written twice through
:where(): the native pseudo-class and[data-state="<state>"], so the site can force any state for review. - CSS reads only
--<id>-*tokens and private--_*properties. No literals. - The template is a pure function
render(props) -> stringwith no DOM access, usable in Node and browsers. - No script ships in the web implementation: interactive behaviour comes from native HTML
(
<button>, popover,popovertarget). A pattern that needs script to be accessible (an ARIA menu's arrow keys) is not used; the docs site may use script for itself, never for a component. - A composite (Dropdown) renders its subcomponent part (
component: "button") with that component's template, and lists repeated parts with their ownpropertiesbehind alistproperty (packages/components/specs/AGENTS.md"Repeated parts and composite components").
Change impact
What else to update or review when an artifact changes. Part of the agent guide: start from the root AGENTS.md.
Change impact
Every change is propagated to everything linked to it, in proportion to its impact. pnpm impact computes the links from the diff (rules in the LINKS table of scripts/impact.mjs)
and prints a checklist; pnpm check then proves the mechanical part of the consistency.
| When this changes | Also update or review |
|---|---|
| Spec | Component tokens, CSS, template, docs; docs of components listing it in related; the spec and tokens of a component that renders the same as it (MIRRORS in scripts/lib/mirrors.mjs: the icon button mirrors the button); the screens rendering it. Version + changelog; decisions[] when a decision was stated. Its Figma blueprint (packages/figma/src/blueprints/<id>.mjs, which the Penpot plugin draws too) when a part, property or state changes. A new component: its starter Figma blueprint, rewritten from the CSS and template; the site controls it covers (apps/docs/src/, apps/docs/AGENTS.md table) and the screen gaps it fills (apps/docs/screens/). The framework themes' mappings (packages/bootstrap-theme/src/mapping.mjs, packages/shadcn-theme/src/mapping.mjs): a renamed or removed property, option, state or part fails their tests; a new one may deserve an equivalence. |
| Component tokens | pnpm tokens:build; CSS if variables appear or disappear; the Figma blueprint, which binds every slot or lists it in notDrawn (pnpm test); docs if a meaning changed; the tokens of a component that renders the same as it (pnpm validate fails until they resolve to the same values); the screens rendering it; the framework themes' mappings (a removed token path fails their tests, a changed value shows in their demos). Version + changelog. |
| CSS or template | Spec (version, changelog, implementations.web); docs if behaviour or rendering guidance changed; the Figma blueprint, whose layer tree follows the CSS structure; the CSS of a component that renders the same as it; the screens rendering it (a template API change is applied to each of them). |
| Docs | The spec must say the same thing; every WCAG criterion the docs rely on is cited in the spec. |
| Foundation spec | Its docs page; the semantic tokens it owns; the docs of every component following it. Version + changelog (pnpm impact fails otherwise); decisions[] when a decision was stated. |
| Foundation docs | The foundation spec must say the same thing; every WCAG criterion linked is cited in it. |
Foundation code sample (packages/components/specs/foundations/<id>/) |
The foundation spec: version + changelog if what the sample shows changed. |
| Raw or semantic tokens | pnpm tokens:build; packages/components/src/fonts.css if a font family changed; every component aliasing them (directly or through semantic aliases): version if its appearance changes, docs if contrast or emphasis changes; apps/docs/src/styles/site.css when the docs chrome reads a changed semantic token (check the site in every mode); the screens rendering a component that aliases them, and apps/docs/screens/screens.css when it reads them; every foundation owning, measuring or presenting them (contrast recomputed by pnpm validate, version if it changes; its specimens redraw by themselves, a token no foundation presents is a warning). The framework themes' mappings when a semantic token path goes (their tests fail) or its value changes (rebuild their demos). |
Platform overlay (packages/tokens/src/platforms/<id>/) |
pnpm tokens:build (packages/tokens/dist/platforms/<id>/*.tokens.json); every component aliasing the overridden tokens: version if its appearance on that platform changes, docs if the platform guidance changed. |
Mode overlay (packages/tokens/src/modes/<id>/) |
pnpm tokens:build (packages/tokens/dist/css/semantic.css, packages/tokens/dist/platforms/*/<id>.tokens.json); every component aliasing the overridden tokens: contrast in that mode recomputed, version + changelog with the ratios, docs if the mode guidance changed; the screens using them, in that mode. |
Distribution formats (packages/tokens/style-dictionary.config.mjs) |
pnpm tokens:build and a look at the changed outputs in packages/tokens/dist/; the list of outputs in packages/tokens/README.md, "Distribution formats" in packages/tokens/AGENTS.md, the package changelog. |
Screens layout or helpers (apps/docs/screens/screens.css, apps/docs/screens/lib/) |
Every screen: look at it on the site at every frame width and in every mode. |
Screen source (apps/docs/screens/<source>/source.json) |
Its licence file and every screen of the source (their pages show the source, licence and commit). |
packages/components/specs/platforms.json |
Schema enums (platform, input), packages/tokens/tokens.config.json platform ids, apps/docs/src/lib/platforms.ts, agents/conventions.md "Platforms and inputs", packages/components/specs/AGENTS.md. |
| Schema | Every spec, apps/docs/src/content.config.ts, the scaffold, the Specs bridge, the validator, packages/components/specs/AGENTS.md; the foundation schema reuses the component schema's definitions. |
packages/components/specs/a11y/ registry |
Every spec and docs page citing it; the scaffold (role profiles); packages/components/specs/AGENTS.md. |
packages/components/src/lib/template-utils.mjs |
Every template, every screen. |
| Scripts or libraries | Their tests, scripts/AGENTS.md, the root AGENTS.md or the agents/ guide when a command or obligation changes, the site when it uses the library. |
package.json scripts |
AGENTS.md "Commands" and .github/workflows/ci.yml. |
A guide in agents/ |
The root AGENTS.md ("Read before acting" and the rules it summarises); the skills in .claude/skills/ that cite it by path or section name. |
A skill (.claude/skills/<name>/SKILL.md) |
The "Skills" table of the root AGENTS.md. |
pnpm impact fails when a component's spec, tokens, CSS or template, or a foundation spec,
changed and its version did not move. Every other unchanged linked artifact is a review item: update it or say in the
report why it stays as it is. In CI it runs on pull requests against the target branch.
Accessibility
Trusted sources, citations, role profiles and what pnpm check enforces. Part of the agent guide: start from the root AGENTS.md.
Accessibility
Accessibility statements (behaviour, rendering, keyboard, naming, usage rules) come only from trusted sources, and every statement cites them. The target is WCAG 2.2 level AA; AAA criteria may be cited as extra quality but are not mandatory.
- Trusted sources are listed with their URL prefixes in
packages/components/specs/a11y/sources.json: W3C (WCAG 2.2 and its Understanding/Techniques, WAI-ARIA, APG, AccName, HTML-AAM, Core-AAM, ARIA in HTML, WAI tutorials), WHATWG HTML, MDN, RGAA, EN 301 549, Apple and Android/Material platform guidelines, and the Orange accessibility guidelines. A URL outside these prefixes failspnpm check. - Platform statements cite the platform's own trusted source: Apple HIG for iOS (and
macOS), Android developers and Material for Android. A guideline with
platformsis checked like any other; the site groups guidelines by platform. - Verify before citing. Open the page, confirm it says what you claim and that the anchor exists. If no trusted source supports a claim, reword it to what the sources say or drop it. Never cite from memory.
- Spec contract.
accessibilityholdsariaRole,name,keyboard[]andguidelines[]; each carriesrefs:wcag:<sc>(resolved againstpackages/components/specs/a11y/wcag.json, generated from the W3C's own data) or the id of an entry inaccessibility.references[]. Parts that are pure decoration aredecorative: true. - Role profiles.
packages/components/specs/a11y/roles.jsonstates what a component role obliges: ARIA role, accessible name, keys, focusability, the WCAG criteria that must be cited, and the native web elements that carry the role. Each profile cites its sources. A role without a profile cannot be specified; write the profile first from the APG pattern and WAI-ARIA. - Docs. The Accessibility section explains why, and links the trusted sources behind each explanation. Every WCAG criterion it links must be cited in the spec.
- Foundations. A foundation's
accessibility.guidelines[]andcontrast[]follow the same citation rules (no role profile: a foundation is not a component). Its contrast pairs are computed from the resolved tokens in every platform × mode and fail under their minimum. - What
pnpm checkenforces: uncited statements, untrusted URLs, unknown or removed WCAG criteria; the role profile (ARIA role, name, keys,focus-visiblestate, required criteria); the rendered HTML (native element with the role, native boolean attributes,aria-hiddenon decorative parts, a non-empty accessible name); the CSS (:focus-visiblefor focusable roles, native pseudo-class next to every forced state,prefers-reduced-motionwhen anything animates); the docs (sourced Accessibility section consistent with the spec). Checks cannot prove everything (contrast of real token pairs, screen reader output): review those on the site and cite the criterion.
Where the tokens come from
The Figma library, its local export, the additions, the Figma component exports, how to
plug another export, and how to draw a component in Figma. Part of the agent guide: start from the root AGENTS.md.
The local export
The Figma library is private. Its export lives in raw_content/ at the repository root, on the
machines that have it: git ignores the directory, so it is never committed, never quoted,
and never named in a spec, a token, the docs, the site or a commit message. Those say "the Figma
library". Nothing in pnpm check or CI reads it: pnpm tokens:figma-library and the tests that
read it skip without it. When the work needs it (a comparison with Figma, the origin of a token)
and it is missing, ask for it rather than guessing. A new worktree starts without it: copy it
from the main checkout.
Where the tokens come from
The raw and semantic tokens have two origins, kept in separate directories so that the origin of any token shows in its path:
- The Figma library. Its design tokens export (
raw_content/: Colors / Base, Colors / Palette Switch, Colors / Mapped light and dark, Dimension, Effect, Typography) is read-only reference material: nothing reads it at build time.scripts/figma-library.mjs --writerecreated it once as clean DTCG: Colors / Base and the Palette Switch inpackages/tokens/src/raw/(hidden primitives), Colors / Mapped, Dimension, Effect and Typography inpackages/tokens/src/semantic/(read by components and the site), the dark mapping inpackages/tokens/src/modes/dark/: the same paths, made valid only where a path or a CSS variable cannot carry them (⚠️ai→ai,semiBold→semi-bold,drop shadow→drop-shadow;brand.identity→brand.identity-base, whose CSS variable would collide with the mappedbrand-identity), the same values (lengths in px, opacity as 0-1), the same aliases, errors and limitations included (typos, aliases to colors it does not contain, kept as literals, contrast failures recorded as known issues of the Color foundation). Colors the library composes from a step and an opacity are written as the resulting color. The Palette Switch is in its Ash mode; Slate is not recreated yet. - Additions.
packages/tokens/src/additions/{semantic,modes/dark}/andpackages/tokens/src/platforms/hold the few tokens the design system adds because the Figma library lacks them to render the components: line heights, letter spacing, motion, the icon font,transparent, the focus ring, and the iOS and Android font families. Each file says so in its$description. Nothing else is added: no semantic layer of sizes, radii or text styles over the library's scales. A new addition is a decision to ask for, never a convenience. - Component exports.
raw_content/components/<id>-variants-raw.jsonis the Figma export of a component set of the Figma library, made with figma-cli: its variant properties and their defaults, then every variant with its auto layout (padding, gap, radius, size), fills, strokes and children (label text, icon instances). It holds no tokens:scripts/figma-library.mjsdoes not read it and nothing reads it at build time. Like the rest of the export, it is read-only and local. It is a candidate rendering a component is compared with, one difference at a time, through the decision loop (agents/workflow.md"Compare a component with its Figma export"). Cite what it shows (values, variant names) as "the Figma prototype", never its path.
These files are the starting point the system evolves from, by decisions (agents/workflow.md): edit them like
any token file. pnpm tokens:figma-library reports every token of the library that has since
been added, removed or changed; --write would undo that evolution, so it is not run again.
Plugging another export
- Drop the exported files into a tier directory (any file names, any nesting; every
*.jsonunder a tier'ssourcesinpackages/tokens/tokens.config.jsonis loaded and deep-merged). Delete the files they replace. Figma exports lengths as$type: "number"and opacity as a percentage: convert them todimension({ "value", "unit": "px" }) and to a 0-1 number, or the CSS gets unitless lengths.scripts/figma-library.mjsshows one way to do it. - If the export puts raw and semantic tokens in one file, split them by top-level group into
the two directories, or point both tier
sourcesinpackages/tokens/tokens.config.jsonat distinct subfolders. - Run
pnpm tokens:build. The only coupling between the tokens and the components is the set of semantic paths referenced bypackages/tokens/src/components/*.tokens.json. The build lists every unresolved alias; repoint those aliases to the equivalent semantic tokens. - Supported value shapes: legacy strings (
"#2563eb","16px") and DTCG 2024+ objects ({ "colorSpace", "components", "alpha", "hex" },{ "value", "unit" }),$typeinherited from groups, compositestypography,shadow,border, and aliases anywhere, including inside composites. Add a case toscripts/test/dtcg.test.mjsif you extend it. - Font families:
packages/components/src/fonts.cssloads every family the tokens name from Google Fonts. If the tokens name another family, update its URL (the validator checks it). - Platform values: anything that differs per platform (system font family, touch target
heights) goes to
packages/tokens/src/platforms/<id>/with the same paths as the base tokens. The build refuses an overlay that adds a path or touches a component token. - Modes: Figma exports one file per mode of a collection (
light.tokens.json,dark.tokens.json). The default mode's file goes topackages/tokens/src/semantic/, every other one topackages/tokens/src/modes/<id>/with amodesentry inpackages/tokens/tokens.config.json(scheme,sources). Keep only the semantic tokens whose value differs from the default mode; the build refuses a mode that adds a path, overrides a raw or component token, or overrides a token a platform overlay also overrides.
Drawing a component in Figma
pnpm figma:export [id…] goes the other way: from the repository to a Figma file. It builds the
Figma development plugin of @quantum-ds/figma into packages/figma/dist/ (not committed; pnpm build builds it too; run it again after any change to a spec or a token). In the Figma desktop
app, Plugins > Development > Import plugin from manifest… with
packages/figma/dist/manifest.json, then run Quantum components in a design file: its window
(built with the design system) shows the progress of each component and the warnings, and stays
open until closed. The package's own rules are in packages/figma/AGENTS.md.
Without ids it draws every component; with ids, those and the components their instances use.
- Variables. One collection per token tier:
Quantum / Raw(hidden from publishing, no scope),Quantum / Semantic(aLightand aDarkmode, from the token modes; a second mode needs a paid Figma plan) andQuantum / Components, holding only the tokens the drawn components reach. A variable is named after its token path (button/container/gap/medium), a typography token gives one variable per member (button/label/typography/medium/font-size), aliases stay aliases, and the web code syntax is the token's CSS variable. These are the repository's tokens, not the Figma library's own collections: the plugin never edits those. Running it again updates the values in place. The web values are exported: platform overlays are not. - Blueprints. A component is drawn from its blueprint,
packages/figma/src/blueprints/<id>.mjs: its Figma layer tree, read from the web implementation, naming the token slot that drives each field (format inpackages/figma/src/plan.mjs). A blueprint holds no value: in each variant, the engine binds the token the slot makes active for that configuration (activeTokens,scripts/lib/token-map.mjs), the one the CSS reads. A new component needs a blueprint (pnpm testfails without one;pnpm new:componentwrites a starter one, rewritten from the real CSS and template), and every token slot of a component is bound by its blueprint or listed in itsnotDrawnwith the reason; a changed CSS structure changes the blueprint. - The component set. On a page named after the component: one variant per option of every
enum property and per state (a state a property sets, such as
disabledorchecked, is a value of State, as in the spec's state list), plus the axes a blueprint adds when the layout depends on something else (the button's icon slots, whose padding differs). The default variant is the top-left one. Text properties carry the texts and icon glyphs (ligatures of the icon font, as on the web), BOOLEAN properties show the optional parts, focus rings are frames outside the border infocus-visibleonly, and a part that is another component (the dropdown's trigger) is an instance of it. A component set that already exists is kept as it is: delete it to draw it again, so that no instance loses its main component unnoticed. - What Figma cannot hold is set without a variable or left out, and listed in each
blueprint's
notDrawn: durations and easings (no variable type), a unitless line height (Figma reads a number variable as pixels, so it is set as a percentage), the icon font's fill, grade and optical size axes, values computed from a token (the outline radius, a check mark's size), and the widths the web takes from its container (a literal width). - Fonts. The plugin loads every family the tokens name. The icon font, Material Symbols Outlined, is not among Figma's fonts: install it on the machine (the variable font of google/material-design-icons) or icons are drawn as empty squares.
Drawing a component in Penpot
pnpm penpot:export [id…] does the same for Penpot. It builds the plugin of @quantum-ds/penpot
into packages/penpot/dist/ (not committed; pnpm build builds it too) and pnpm penpot:serve
serves it at http://localhost:4400, since Penpot loads a plugin over HTTP. In Penpot (2.18 or
later: the plugin API of tokens and variants), open a file, open the plugin manager (Ctrl + Alt + P),
install http://localhost:4400/manifest.json and run Quantum components: its window, the Figma
plugin's window with Penpot's theme, shows the progress and the warnings and stays open until
closed. The package's own rules are in packages/penpot/AGENTS.md. Without ids it draws every
component; with ids, those and the components their copies use.
- Tokens. The plugin writes
packages/tokens/dist/penpot/tokens.json, the Penpot token file the token build publishes, into the file's tokens: the setsFoundation,Component,Mode/LightandMode/Dark, and the themesMode / LightandMode / Dark, the default one active. It keeps the tokens the drawn components reach and every token they reference; a token is named after its path (button.container.gap.medium), keeps its Penpot type (spacing,sizing,borderRadius… from the property its path names) and its references, so switching the theme switches every component. Anumberoropacityvalue (1.5) is written as text ("1.5"): the token file keeps DTCG numbers, which Penpot's Tokens panel imports, but its plugin API takes only text for them. Running it again updates the values in place (a number the Tokens panel imported equals its text and is left as it is); a token of another type under the same name is left alone, with a warning. - Blueprints. Penpot draws the Figma blueprints (
packages/figma/src/blueprints/), expanded by the same engine: the same variants, bound to the same tokens. The plan then translates each Figma field to the Penpot token property that drives it (a padding, a gap along the layout direction, each corner radius, the stroke width, a minimum size of a layout child, the typography token of a text). A blueprint change is a change of both drawings; nothing is written for Penpot alone. - The component. On a page named after the component: every variant drawn as a board with a
flex layout, laid out in the Figma grid, made a component, then all of them combined into one
component with variants whose properties are the Figma variant properties. Penpot components have
no text or boolean properties: a copy overrides its texts and icon glyphs directly, and an
optional part is shown or hidden as the boolean's default says, which a copy changes. Focus rings
are rectangles outside the border in
focus-visibleonly, and a part that is another component (the dropdown's trigger) is a copy of it. A component that already exists is kept as it is. - What Penpot cannot hold is set to the token's value, and
dist/plan.jsonlists it per component (unbound, with the tokens, andapproximations): an icon's font size (its size is asizingtoken, which cannot drive a font size), the divider's thickness (a border width as a size), the minimum size of a component itself (Penpot honours it inside a layout only: the component is given that size when its content is smaller), a border on one side (a line along it, until the plugin API has per-side strokes), a baseline alignment (aligned to the end) and an arc (an open path). What Figma cannot hold either (durations, easings, the icon font's axes) is left out as in Figma (notDrawn). - Fonts. Penpot's fonts are its own catalog (Google Fonts, where Inter comes from) and the team's fonts: the plugin finds each family by name, and a text whose family is missing keeps Penpot's default font. The icon font, Material Symbols Outlined, must be found too (upload it as a team font if needed), or icons are drawn as empty squares; a warning says so in both cases.
Following the design tools
How the repository keeps up with the design tools it exports to (Figma, Penpot, Tokens Studio)
and the token format they share (DTCG): what each export relies on, where each tool publishes its
changes, how a review turns them into issues, and how a change that needs the real tool is checked
by a person. Part of the agent guide: start from the root AGENTS.md. The skill
design-tools-watch runs the review step by step.
Why
Every design tool export is written against what a tool did on the day it was written: the
@quantum-ds/tokens formats for Figma, Tokens Studio and Penpot
(packages/tokens/style-dictionary.config.mjs), and the plugins of @quantum-ds/figma and
@quantum-ds/penpot, which draw the components (agents/token-sources.md). The tools keep moving:
a release can break an export, fix a limit the code works around, or bring a feature worth using.
None of them is an npm dependency, so Dependabot never sees them (it does follow Style Dictionary).
The review is how those releases reach the backlog.
The registry
agents/design-tools.json lists one entry per tool:
exports: what the repository writes for it.reliesOn: every behaviour of the tool an export depends on, in a line each. A release that touches one of them is a finding; one that touches none is not. Keep it true: a new feature used by an export adds its line in the same change.sources: where the tool publishes its changes (release notes, changelogs, the plugin API's updates). Only these are read; a source that moves is updated here.minimum: the oldest version the exports support, when there is one.reviewed:{ "through", "on" }, the last release reviewed (a version, or the date of the last release note for a tool without versions) and the day of the review.nulluntil the first review.
reviewed moves forward only in the change that records a review, never by hand.
Workarounds in the code
Code that works around a tool's limit says so with a marker in its comment:
upstream(<tool>): <the limit>, the tool being an id of the registry (several, comma-separated, when
they share the limit):
// upstream(penpot): no baseline alignment; the end of the cross axis comes closest for one-line texts.
The marker goes where the workaround is decided (a table, a fallback, a conversion), once. It is
how a review finds what a release may let the code drop: git grep -n "upstream(" lists them, and
pnpm test checks that every marker names a tool of the registry. A workaround added later gets
its marker in the same change.
A review
The skill design-tools-watch runs it, for one tool or all of them, regularly (once a month, or
before a release of a package) and once at least when a tool announces a major version.
- Read every source of the tool from the release after
reviewed.throughup to the latest stable one (the last twelve months whenreviewedisnull). Betas and release candidates are read too, and reported apart as what is coming. - Match each change against
reliesOnand theupstream(…)markers. A change matching neither is not a finding; the review only counts them. - Classify each finding:
- Breaking: an export stops working or loses something (a type renamed, an API removed).
- Lifts a workaround: a marked limit is gone; the workaround can go, or be kept for the
versions before (then
minimumdecides). - Fixes a known issue: an open issue of the repository is fixed upstream.
- Opportunity: a feature that would let an export draw or bind more, or more simply.
- Coming: any of the above, in a beta or a release candidate only.
- Check what can be checked here: read the tool's source when it is open (Penpot, Tokens
Studio), run the token build and read its output, teach the plugins' stand-ins the new rule and
run
pnpm test. Each finding says how sure it is: confirmed (read in the tool's source or proven by a test) or to check in the tool (only the release note says it). - Report the findings, numbered, each with its source link, its class, the artifacts it
touches and how sure it is. Then, once the person who asked for the review agrees, one issue per
finding that needs work (skill
backlog), searching first for an issue that already covers it. Findings with no work (a fix for a problem the exports never had) stay in the report. - Record the review:
reviewedof each tool reviewed, and any newreliesOnline, in one commit on its own branch (docs(agents): review the design tool releases through <date>).
A check in the real tool
An agent cannot run Figma or Penpot. When a finding needs the real tool to be confirmed or ruled
out, its issue does not claim the impact: it says so, gives the person who will run it everything
needed to do it in one go, and is assigned to that person (the one who asked for the review, unless
they name someone else). Its body has a Check in
- Needs: the tool and version (a beta when the change is not released yet), the plan when it matters (a second variable mode needs a paid Figma plan), the fonts to install.
- Setup: the commands, from a clean checkout of the branch to check (
pnpm install, thenpnpm figma:export <id>orpnpm penpot:export <id>andpnpm penpot:serve), and how to load the plugin or import the token file (agents/token-sources.md). - Steps: numbered, each one action, with the component, variant, token or panel to open.
- Look at: what shows the impact (a variable's type, a token's value in the Tokens panel, a warning in the plugin window, a layer's binding), and what it should be if the export is right.
- Answers: what result confirms the impact and what rules it out.
- Report back: in a comment, the tool's version, what was seen (screenshots, the plugin window's warnings), and the answer.
When the result comes back, the issue is updated with it: confirmed, it becomes ordinary work;
ruled out, it is closed with the result. A stand-in rule taught from a release note is kept only
once the tool has confirmed it (packages/figma/AGENTS.md, packages/penpot/AGENTS.md).
Specs interoperability
Export to and import from Nathan Curtis' Specs format. Part of the agent guide: start from the root AGENTS.md.
Specs interoperability
Nathan Curtis' Specs format (npm
@directededges/specs-schema, CC BY 4.0) describes components extracted from Figma. This
repository's spec stays the source of truth; Specs is an export target and an import source.
scripts/lib/specs-bridge.mjs holds every mapping decision in tables at the top of the file.
- Export writes a valid Specs Component plus a
.quantum.jsonsidecar with what Specs has no slot for: accessibility, content, implementations, changelog, part metadata and tokens without a Specs style (focus outline, transition). Our property metadata and each token's exact path travel inside Specs$extensionsunder theio.quantumkey. - States become a Specs prop named
state(interaction and system states); a state set through a boolean property stays that boolean and appears ininvalidPropCombinations.stateis therefore a reserved property id. - Import with the sidecar restores the spec and tokens exactly. Without it (a Figma-generated file) it infers anatomy, properties, states and token paths, and leaves prose empty.
- Upgrading Specs: the schema version is pinned exactly in
package.json. To move, read the new ADRs in the Specs ADR index, bump the version, runpnpm specs:check, then adjust the tables inspecs-bridge.mjsand the tests inscripts/test/specs-bridge.test.mjsuntil it passes. - A
specs:checkerror on a valid spec is a bridge limitation, not a spec error: fix the mapping, never the spec. - Foundations (
packages/components/specs/foundations/) are not exported: Specs describes components. Their tokens travel with every component that aliases them.
Repository, commits and issues
Branches, pull requests, Conventional Commits, labels and issues. Part of the agent guide: start from the root AGENTS.md.
Repository, commits and issues
The repository is the GitHub repository
ThalesGroup/quantum.
Never commit to
main: one branch and one pull request per development. Start every change from an up-to-datemainon a new branch, commit there, push the branch tooriginand open a pull request againstmain(see "Branches" and "Pull requests" below). Never push tomain, never merge your own pull request and never enable auto-merge: a human reviews and merges.pnpm checkmust pass before every commit (AGENTS.md"Do not").Conventional Commits. Every commit message follows Conventional Commits 1.0.0 (see "Commits" below). The
commit-msghook refuses any other message, and CI checks every commit of a pull request (thecommitsjob).CI.
.github/workflows/ci.ymlruns on every pull request:check(pnpm check),commits(pnpm commits:check --base),impact(pnpm impact --base) andbuild(pnpm build, which uploads the docs site as an artifact, and a dry-run pack of every package);checkandbuildrun again on every push tomainandnextgen..github/workflows/release.ymlpublishes an alpha version of a package to npm (dist-tagalpha) with trusted publishing when a tag@quantum-ds/<package>@<version>-alpha.<n>is pushed on a commit ofnextgen; no GitHub release is created (CONTRIBUTING.md"Releases").No deployment. Never create GitHub Pages and never deploy the site, anywhere: no deployment job in the workflows.
pnpm buildandpnpm devare for local review only. The docs site will later be integrated into https://quantum.thalesdigital.io/ by the Quantum team.Out-of-scope work becomes an issue. When a conversation raises a new feature, a bug or an enhancement that is not directly part of the current work, open an issue for it right away instead of doing it or leaving it in the chat:
gh issue create --repo ThalesGroup/quantum --title "…" --body "…" --label <label>. The body stands alone: context, the artifacts involved (paths), what is expected. Report the issue URL.Labels. On every issue and every pull request, pick every label that fits from the repository's existing set (
gh label list --repo ThalesGroup/quantum):- Kind. A pull request takes the label of its commit type (
feat,fix,docs,ci,test,perf,build,chore,refactor), plusdependencieswhen it changes dependencies. An issue takesbug,enhancementorquestioninstead, never a commit type (docsaside, as an area). - Area, every one the change or the issue touches:
site(the docs website's code,apps/docs/and its screens),content(its guidance pages,apps/docs/content/),tokens(packages/tokens/),components(packages/components/: specs, CSS, templates),docs(README, CONTRIBUTING, the agent guides inagents/and the skills; the same label as the commit type, there is nodocumentationlabel). Scripts and workflows take no area label: the kind says it (build,ci). - Design tool, every one the change or the issue concerns:
figma(the Figma library, its export, the Figma token format, and@quantum-ds/figmainpackages/figma/),penpot(the Penpot token format, its import, and@quantum-ds/penpotinpackages/penpot/),tokens-studio(the Tokens Studio token format). A change topackages/figma/takesfigma, one topackages/penpot/takespenpot, on top of its kind. - Framework, every one the change or the issue concerns:
bootstrap(the Bootstrap theme,@quantum-ds/bootstrap-themeinpackages/bootstrap-theme/),shadcn(the shadcn/ui theme,@quantum-ds/shadcn-themeinpackages/shadcn-theme/). A change to a theme package takes its framework label on top of its kind. - Quantum NextGen. Every issue and pull request that concerns it (a pull request against
nextgen, an issue about its packages, specs, docs site, guides or skills) always takesnextgen.
The area is the part of the repository, not the commit scope:
fix(docs): …on the site's code takesfixandsite;docsgoes only on what touches the repository's own docs. Do not invent labels on the fly; create a new one only when asked.- Kind. A pull request takes the label of its commit type (
This repository only. Always pass
--repo ThalesGroup/quantum. Never open an issue or a pull request, or comment on one, in any other repository, including upstream projects (Specs, Astro, W3C…): describe the upstream problem in an issue here instead.
Branches
- One development, one branch: a component decision (
agents/workflow.md"Decisions"), a foundation, a script, a docs site feature, a docs rewrite. Unrelated changes go to separate branches and pull requests. - Name it
<type>/<scope>-<short-kebab-subject>, with the type and scope of its main commit (see "Commits"):feat/button-primary-border,fix/docs-token-filter,docs/agents-pr-workflow. - Branch from the latest
main(git fetch origin && git switch -c <branch> origin/main). Ifmainmoves before the merge, bring it in (rebase or mergeorigin/main), re-runpnpm checkand push again. - On a branch, check against
main:pnpm impact --base origin/mainandpnpm commits:check --base origin/main.
Pull requests
- Open it with
gh pr create --repo ThalesGroup/quantum --base main --head <branch> --title "…" --body "…" --label <label> [--label <label>…]. The title is the Conventional Commit header of the change (the main commit's header when there are several commits). - The description is written for a human reviewer who did not follow the conversation: plain
sentences, no internal jargon left unexplained, no pasted tool output. Use these sections:
- Why: the request or decision behind the change, in one or two sentences.
- What changes: per artifact (spec, tokens, CSS/template, docs, site, scripts), what
changed and the visible effect; decision ids, contrast ratios and the sources cited (
agents/accessibility.md). - Left unchanged on purpose: every linked artifact
pnpm impactlisted that was not updated, with the reason (agents/impact.md). - How to review: the precise places to look (files and lines, site pages, examples, states, modes), and the judgement calls the reviewer must confirm or reject.
- Checks:
pnpm checkandpnpm impact --base origin/mainresults. - Follow-ups: issues opened for out-of-scope work (links).
- Keep the description true: when review feedback adds commits, update it.
- When done, report in the conversation: what was done (branch, commits, pull request link, labels), and what the reviewer must review precisely, the same list as the "How to review" section.
Commits
<type>(<scope>): <subject>
[body: what and why, decision ids, contrast ratios]
[BREAKING CHANGE: what consumers must change]
Type says what the change is for the people consuming the design system:
Type Use for Spec version featA new or changed decision, component, property, option, state, token; a docs site feature; a new command minor fixA correction that changes no decision; a bug in a script or the docs site patch docsProse only: apps/docs/content/,AGENTS.md,agents/, READMEnone, or patch refactor·perf·styleRestructuring, speed, formatting: nothing visible changes none testTests only none buildToken pipeline, build scripts, package manifests none ci.github/workflows/, git hooksnone choreHousekeeping that fits nowhere else none revertReverts an earlier commit ( revert: <header of the reverted commit>)as reverted Scope names what changed: the component id (
button,icon-button) for a component change, the foundation id (focus) for a foundation change, several comma-separated when one decision spans components (feat(button,icon-button): color modes), otherwise an area:tokens,modes,platforms,foundations,specs,a11y,components(the components package),figma(the Figma package),penpot(the Penpot package),bootstrap-theme(the Bootstrap theme package),shadcn-theme(the shadcn/ui theme package),web(the web reference implementation),docs(the docs app and the guidelines),screens,scripts,agents,ci,deps,release. A component scope that does not exist fails the check; the list lives inscripts/lib/conventional-commits.mjs.Breaking changes (a removed option, property or token path, a renamed part, anything that makes consumers change their code or design files) take
!after the scope and aBREAKING CHANGE:footer saying what to change, and bump the spec's major version.Subject: imperative or a noun phrase stating the decision, no final period, header at most 100 characters. One decision, one commit (
agents/workflow.md"Decisions").Check a message before committing with
pnpm commits:check --message "<message>".pnpm installinstalls the hook (git config core.hooksPath .githooks); merge commits and git's ownfixup!/squash!/Revert "…"messages are let through. Commits made before the rule are not rewritten.