[Overview](https://rickylabs.github.io/netscript/design-system/)[Guidelines](https://rickylabs.github.io/netscript/design-system/guidelines/)[Token contract](https://rickylabs.github.io/netscript/design-system/token-contract/)[Docs kit](https://rickylabs.github.io/netscript/design-system/docs-kit/)[Theme builder](https://rickylabs.github.io/netscript/create/)[Components](https://rickylabs.github.io/netscript/design-system/components/)[Create your own theme](https://rickylabs.github.io/netscript/design-system/create-your-own-theme/)[Studio](https://rickylabs.github.io/netscript/design-system/themes/studio/)[Tonal](https://rickylabs.github.io/netscript/design-system/themes/tonal/)[Bloom](https://rickylabs.github.io/netscript/design-system/themes/bloom/)[Cupertino](https://rickylabs.github.io/netscript/design-system/themes/cupertino/)[Liquid](https://rickylabs.github.io/netscript/design-system/themes/liquid/)[Fluent](https://rickylabs.github.io/netscript/design-system/themes/fluent/)[Brutal](https://rickylabs.github.io/netscript/design-system/themes/brutal/)[Filigrane](https://rickylabs.github.io/netscript/design-system/themes/filigrane/)[Terminal](https://rickylabs.github.io/netscript/design-system/themes/terminal/)[Pixel](https://rickylabs.github.io/netscript/design-system/themes/pixel/)[Tactile](https://rickylabs.github.io/netscript/design-system/themes/tactile/)

# Token contract

Theme architecture from the design guidelines. Read it with the [two governing rules](https://rickylabs.github.io/netscript/design-system/guidelines/#1-two-rules-that-override-everything) and the [compatibility contract](https://rickylabs.github.io/netscript/design-system/guidelines/#3-compatibility-contract-must).

## 2. Theme architecture

Figma and code describe the same system. Figma stores values in variable collections and modes. In code, the tokens build (#2414) stores tokens in three DTCG 2025.10 tiers and selects between contexts with a DTCG resolver file. This section maps one onto the other. Ownership follows the epic (#2412, decision 2): Figma owns values; code owns names, tiers, modes and component anatomy.

### 2.1 Three token tiers

| Tier | What it holds | Figma source | Code surface (provisional until #2414) | Who may read it |
| --- | --- | --- | --- | --- |
| **Primitive** | Raw values only: colour ramps (oklch with a hex fallback), dimensions, durations, font families. Radix 12-step ramps (gray, slate, sand, mauve, blue, violet, orange, indigo, tomato, lime, grass, amber, red, teal) and theme ramps (`ember`, `band`, `paper`, `glass`, `shadow`, `swiss-red`) | `Primitives` collection (Value mode) | Internal custom properties, e.g. `--ns-band-*` | The semantic tier only. Components never read primitives |
| **Semantic** | **Aliases only.** Every value references a primitive (or another semantic role), never a raw hex, px or ms. Core roles plus each style's namespaced roles (§2.4) | `Scheme` collection (Light, Dark). Its aliases are selected through the `Theme set` (`Theme` or `Theme B`), `Base gray`, `Base accent`, `Density` and `Studio flavor` modes (§2.2) | `--ns-*` (core) and `--ns-<theme>-*` (styles) | Component tokens and L2 component CSS |
| **Component** | Part and state tokens (`button.*`, `input.*`, `card.*`, …) that alias semantic roles, so heavy customization is a token edit, not a CSS override | Variable bindings on library components; there is no component collection yet | Named by the tokens build from the component anatomy spec (#2416) | That component's own CSS only |

- **Derived values.** Tints, hovers and other mixes are not raw values in the semantic tier. `$extensions.netscript.derive` describes them. CSS keeps `color-mix()`, and Figma receives the computed value.
- **Collections and modes are not tiers.** `Scheme`, `Theme`, `Theme B`, `Base gray`, `Base accent`, `Density` and `Studio flavor` modes are contexts the resolver chooses between (§2.2). `Theme set` only switches the aliases between `Theme` and `Theme B`. None of them adds a fourth tier.
- **Raw values inside Figma modes.** Some Theme modes still store raw values in slots that are semantically aliases, for example Tactile's `surface/{l,d}/*` and Fluent's `accent/{l,d}/solid`. Each theme's `DESIGN.md` records them. Before export, the tokens build (#2414) splits each one into a primitive and an alias.
- **Node-level exceptions are Figma-only.** A primitive or raw value bound directly on a Figma node is a temporary exception. The theme's `DESIGN.md` names the role that replaces it, pending in #2414. It never ships in L2 component CSS.
- **Layer ownership is unchanged.** L0/L1 own platform behaviour, attributes and prop getters. L2 registry components consume component and semantic tokens. An L2 component never imports another L2 component.

### 2.2 How Figma collections map to the resolver

| Figma collection (modes) | What it selects | Token resolver (#2414) | Root attribute (provisional) |
| --- | --- | --- | --- |
| `Scheme` (Light, Dark) | The semantic role values for each scheme | `scheme` modifier: `light`, `dark` | `data-scheme` |
| `Theme` (Studio, Ledger, Filigrane, Signal, Tonal, Glass, Brutal, Fluent, Tactile, Pixel) | Language values: fonts, type scale, radii, materials, shadow recipe, ring | `theme` modifier, with one context per **language preset** (§2.3), not one per Figma mode | `data-theme` |
| `Theme B` (Bloom, Cupertino, Liquid, Terminal, macOS 27, Apple Classic, Fluent 2 Web) | Language values, as for `Theme` | The same `theme` modifier | `data-theme` |
| `Theme set` (A, B) | Which Theme collection the semantic roles alias through: A selects `Theme`, B selects `Theme B` | Not a modifier; the `theme` modifier covers both Theme collections | — |
| `Density` (Comfortable, Compact, Dense, Airy, Chunky) | Control, page, section, card, table and badge spacing | `density` modifier. The token resolver specifies `comfortable` and `compact` only; `dense`, `airy` and `chunky` need a resolver schema change | `data-density` |
| `Studio flavor` (Utility, Cloud, Workshop, Playful, Ledger, Atelier, Signal) | Personality on top of the Studio language | Not a modifier. Each flavor expands into a Studio preset | `data-flavor` (Studio only) |
| `Base gray` (Neutral, Slate, Sand, Mauve) | Which gray ramp a theme sits on | Not a modifier. A preset parameter | `data-gray` |
| `Base accent` (Ink, Blue, Violet, Orange, Indigo, Tomato, Lime) | Which accent ramp drives `primary/*` | Not a modifier. A preset parameter | `data-accent` |
| `Primitives` (Value) | Raw ramps | The primitive tier; no modifier | — |

This Figma plan allows **10 modes per collection**, and `Theme` uses all 10. A second collection, `Theme B`, holds the modes for Bloom, Cupertino, Liquid and Terminal, and for the planned macOS 27, Apple Classic and Fluent 2 Web themes (#2443). The semantic roles alias through the `Theme set` collection: set A selects `Theme` and set B selects `Theme B`. Each language has its own mode.

The `Theme B` modes were seeded with copies of existing values: Bloom from Tonal; Cupertino, Liquid, macOS 27 and Apple Classic from Glass; Terminal from Signal; and Fluent 2 Web from Fluent. Each mode can diverge. Until a language's own values are set, its mode resolves to its seed values, which are prototype evidence only. Before a language ships, its values must come from its own mode, and the token export (#2414) must give each language preset its own complete core-role map. When both collections are full, a new language replaces a mode or needs a new Theme collection. The token resolver (#2414) has no mode limit in code.

### 2.3 Supported resolver axes

The axes are scheme × theme × flavor × density × gray × accent. **Not every permutation exists.** The unit of support is the **language preset**: a fixed choice of Theme values, density, gray and accent (and, for Studio, flavor), designed and reviewed in both schemes.

| Axis | Values | Where it applies | Supported today |
| --- | --- | --- | --- |
| `scheme` | light, dark | Every preset | Both schemes for every preset; each dark is designed, not inverted |
| `theme` | `studio`, `tonal`, `bloom`, `cupertino`, `liquid`, `fluent`, `brutal`, `filigrane`, `terminal`, `pixel`, `tactile` | — | One preset per language; Studio has seven, one per flavor |
| `flavor` | utility, cloud, workshop, playful, ledger, atelier, signal | Studio only | Each flavor fixes its own gray, accent and density (Studio `DESIGN.md` → *Flavors*). Every other language ignores it |
| `density` | comfortable, compact (token resolver); dense, airy, chunky (Figma only) | Studio flavors; a few languages set one explicitly | Fixed per preset; not a free user override yet |
| `gray` | neutral, slate, sand, mauve | Studio flavors | Fixed per preset. The other languages alias their own ramps inside their Theme mode and ignore it |
| `accent` | ink, blue, violet, orange, indigo, tomato, lime | Studio flavors | Fixed per preset, as for gray |

**Precedence.** The resolver applies `theme` (with `flavor` for Studio) as a preset that fixes density, gray and accent. `scheme` then selects the Light or Dark values. An app's override layer (#2414) applies last, on the semantic tier only. A preset ignores attempts to change an axis it fixes, and the builder (§8) offers only supported combinations. A free axis, such as any density on any language, needs a resolver schema change (#2414) and a contrast run for every new permutation.

**Fixed axes per language.** This table comes from each theme's `DESIGN.md` → *NetScript Mapping*. The mode assignments are read from the Figma library and are not yet verified in code (#2414). Modes in set B live in `Theme B` and still hold their seed values (§2.2).

| Language | Figma Theme mode | Density | Gray × accent |
| --- | --- | --- | --- |
| Studio | `Studio` | per flavor (Utility: compact) | per flavor (Utility: neutral × ink) |
| Tonal | `Tonal` | comfortable; M3 component sizes are fixed | mauve × violet, inside the mode |
| Bloom | `Bloom` (set B, seeded from `Tonal`) | none (literal geometry) | none; Bloom reads `bloom/*` |
| Cupertino | `Cupertino` (set B, seeded from `Glass`) | comfortable | slate × blue, inside the mode |
| Liquid | `Liquid` (set B, seeded from `Glass`) | comfortable | slate × blue, inside the mode; own values in `liquid/*` |
| Fluent | `Fluent` | none (WinUI defaults) | neutral gray × WinUI accent, inside the mode |
| Brutal | `Brutal` | none (comfortable default) | sand × amber, inside the mode |
| Filigrane | `Filigrane` | none | sand × tomato, inside the mode |
| Terminal | `Terminal` (set B, seeded from `Signal`) | compact | gray × lime, inside the mode |
| Pixel | `Pixel` | comfortable | sand-family × amber, inside the mode |
| Tactile | `Tactile` | none | none; Tactile reads `tactile/*` |

**Migration from today's selectors.** Code today (NS One) scopes light on `:root` and dark on `[data-theme='dark']`. The new model moves the scheme to `data-scheme` and gives `data-theme` to the language: `data-theme="dark"` becomes `data-theme="<language>" data-scheme="dark"`. Acceptance for the tokens build (#2414) requires that existing `--ns-*` names keep resolving or have a documented migration, and that NS One renders unchanged. The tokens build therefore either keeps `[data-theme='dark']` resolving as the default preset's dark scheme or ships that migration, with a `ui:migrate` step for app-owned copies. Never rename an existing `--ns-*` name without a migration.

### 2.4 Styles and token-only presets

There are two kinds of theme. Keep them apart.

- **Token-only preset.** Assigns values to existing roles: a Studio flavor, a gray or accent choice, an app's brand theme. It adds no names and no component sources. Most app themes are this kind.
- Style.

  Every theme language (Studio, Tonal, Bloom, Cupertino, Liquid, Fluent, Brutal, Filigrane, Terminal, Pixel, Tactile) is a style, in the sense shadcn uses the word: it restyles

  every

  component, including shape, borders, fills, shadows, decorations and inner markup, over one shared contract. A Pixel switch is Pixel's own switch, not the base switch with a smaller radius.

  - **The contract does not change between styles** (#2416): each component keeps its props, `data-part` names, `data-variant`, `data-size` and `data-state` values, slots, APG pattern and keyboard map. The contract tests run against every style.
  - **Tokens stay separate axes** under every style: scheme, gray, accent, radius and density.
  - A style declares:

    1. **Its component sources:** one per component, shipped as a registry `style` item (#2415).
    2. **A role manifest:** every namespaced role it adds, as `<theme>/<path>` → `--ns-<theme>-<path>`, each with a Light and a Dark alias. Until the tokens build has a machine-readable manifest, the list is the *Language roles* row in the theme's `DESIGN.md` → *NetScript Mapping*. The tokens build registers every listed role before the style ships; unknown names fail CI.
    3. **A complete core map:** every core semantic role still resolves under the style, so token-only presets and app-owned components keep working.
  - A style restyles its own components only, never app-owned markup outside them.
- **In Figma,** each style is a set of component sets named `<Style>/<Component>`, with the same property names, values and layer names as the base set, so an instance swap keeps every override. Variables carry the token axes; variants carry states and sizes inside a style.

Styles with namespaced roles today: `studio/*`, `liquid/*`, `tactile/*`, `bloom/*`, `cupertino/*`, `fluent/*` and `pixel/*`. Only that style's components read its namespaced roles. A Studio flavor is a token-only preset inside the Studio style.

### 2.5 Core semantic roles (every theme defines all of them, in Light and Dark)

The CSS custom property comes from each variable's code syntax. For example, `surface/bg` maps to `var(--ns-bg)` and `surface/card` to `var(--ns-card)`.

- **Surfaces:** `surface/bg`, `surface/surface`, `surface/card`, `surface/raised`, `surface/muted`, `surface/hover`, `surface/selected`, `surface/overlay`, `surface/container`, `surface/container-high`, `surface/container-highest`, `surface/inverse`.
- **Text:** `text/fg`, `text/card`, `text/muted`, `text/subtle`, `text/inverse`, `ink/fg`.
- **Borders:** `border/default`, `border/subtle`, `border/hover`, `border/strong`, `border/input`, `border/ring`, `border/ink`, `border/control`.
- **Intents** (each has `default`, `hover`, `fg`, `subtle`, `border`, `text`): `primary`, `accent`, `secondary`, `success`, `warning`, `destructive`, `info`. Primary also has `primary/container` and `primary/on-container`.
- **Actions and state:** `action/primary-bg`, `action/primary-fg`, `action/primary-border`, `state/disabled-container`, `state/disabled-fg`.
- Depth:

  - `elevation/ambient`, `elevation/key`, `effect/shadow`;
  - `bevel/{highlight,lip,edge,inset,solid-highlight,solid-lip,glow}`;
  - `material/{fill,fill-strong,stroke,track}`.
- **Language accents** (only meaningful in their theme, but always defined): `aurora/{1..4}`, `block/{yellow,blue,green,pink,purple,ink-text}`, `frame/ink`, `control/ink`.

### 2.6 Variants and states are data attributes

Modes, variants and states are data attributes (`data-variant`, `data-size` and `data-state`), not BEM modifier classes (epic Decision 4).

A style restyles every variant and may change a component's inner markup. It never changes the contract: the same props, parts and `data-*` values exist in every style (#2416). A style that needs a new variant value, such as Studio's card variants or Bloom's notched widget, adds it to the component spec first, so every style can render it.

[Read GUIDELINES.md](https://raw.githubusercontent.com/rickylabs/netscript/main/docs/design-system/GUIDELINES.md)
