Skip to content

About

Reference implementation of the ASK design family // tier-demarcated tokens, self-hosted OFL fonts, canonical logo-ASK assets, preview cards

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

182 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Design System // ASK

design-system-ASK banner

Minimal design foundations for the ASK visual identity. Tokens remain irreducible; the repo may also carry artifact inheritance scaffolds and opt-in semantic primitives that show downstream surfaces how to consume them without redefining them.

order from chaos // beauty in systems

The line above is the ASK master tagline. It is canonically defined in brand-architecture.md (in ASK's context system, not this repo) — displayed here, not defined here. See the Voice section below for the protected-payload rule.

This repo, design-system-ASK, is the reference implementation of the ASK design family.

This repo conforms to visual-identity-system.md in ASK's canonical context as the source of truth. The files here are a downstream implementation; when the two disagree, the canonical file wins.


What this is

A foundational design system for ASK — a meta-brand expressed through a single wordmark, two diagonal gradients, and a deliberately small set of colors and weights. The system is reductive on purpose. Interface and interaction state is expressed through weight, opacity, and motion — never by introducing a hue outside the named set. The foundation-level opt-in exceptions are the ASK semantic-color primitives for data and architecture visualizations — Spectral State (element state) and Three Functions (structural function), below — not general UI color, and neither expands the palette. A separate pattern-local participant-identity vocabulary exists only inside message-archive; it is not a foundation primitive and does not open the unrestricted general palette.

Scope is the meta-brand. Sub-brand theming (production, builder, artist) layers on top of these tokens elsewhere; it does not live here.

Source materials

  • assets/logo-ASK.svg — primary vector wordmark, fill: currentColor
  • assets/logo-ASK-white.png — raster wordmark, white on transparent (light-mode pairing)
  • assets/logo-ASK-lavender-ASK.png — raster wordmark, lavender-ASK on transparent (dark-mode pairing)
  • Canonical spec: visual-identity-system.md (in ASK's canonical context, not in this repo)
  • Operator-side vector working source: ASK 9 4.ai (Illustrator file, not in this repo by design — production assets only)

Scope

This repo carries three kinds of material:

  1. Foundations — tokens, type, color, assets, and visual rules. The irreducible expression of the system and the inheritance source for other ASK-family surfaces.
  2. Artifact-inheritance scaffolds — small, auditable patterns, added when earned, that show consuming projects how to inherit the foundations without redefining them. A scaffold may be wholly static, or may carry self-contained client-side navigation and still freeze for audit; either way it inherits at generation time and seals.
  3. Surface patterns — contracts for live deployed surfaces: a page shell, or a live component a page mounts. They frame or run on a page that stays live, and they do not seal: a consumer references or vendors the pattern and re-syncs when it changes, rather than freezing an output for audit. surface-shell and diagram-interactive-radial are the instances.

The distinction in 2 versus 3 is the lifecycle, and it is load-bearing. An artifact scaffold produces something finished and frozen. A surface pattern produces something that keeps running. Do not apply sealing, freezing, or generation-time inheritance rules to a surface pattern.

The document register (surface-document.css) and the surface treatments (surface-treatments.css) serve both lifecycles: a live surface references or vendors them and re-syncs, and an artifact scaffold may inline them at generation time, after which the sealed copy does not re-sync. The surface treatments extend surface-panel.css, which authors the glass recipe they share, so a surface that uses them loads or inlines that file first.

These are consumption patterns, not components. They are not a generator, not a build pipeline, not an npm package, and not a component library. The catalog holds two artifact classes plus one separate surface-pattern group:

  • Class A — system / architecture diagram templates, in two kinds:
    • static: diagram-static-H (horizontal left→right top-aligned cascade), diagram-static-V (vertical top→down centered spine), diagram-static-SEQ (ordered top→down arrowed sequence — succession, not hierarchy), and diagram-static-FLOW (convergence flow — many sources converging into a resolved spec, realized, evaluated, governed, fed back) — structural; state-free. A page of any of the four may opt in to a shared responsive chrome that lays out its caption and legend together and, on a narrow or crowded canvas, closes them behind triggers in one control area with the HUD; an adopting consumer vendors diagrams-chrome.js with the bundle and surface-panel.css then surface-treatments.css for the triggers. Each pattern's README carries the contract.
    • interactive: diagram-interactive-spine — a navigable, stateful IA state surface that consumes the Spectral State primitive for node color. The taxonomy encodes static-vs-interactive, not just orientation (the interactive spine is also vertical).
  • Class B — project-output artifact templates: output-artifact (static document) and message-archive (sealed interactive archive — offline search + navigation over frozen content). Client-side navigation over embedded content does not make an artifact Class A and does not earn a new class.
  • Surface patterns — the separate group, not a third artifact class: surface-shell (the shared header, control slot, and flush-right footer that make a family of surfaces read as one artifact family with different payloads) and diagram-interactive-radial (a context-agnostic interactive radial map of a containment hierarchy, mounted on a live page). Each runs on a live page and seals nothing, so neither is a sealed output artifact, and the radial pattern is not a Class A scaffold: it is not inherited at generation time. The group is not "Class C" — it is a named group by lifecycle, not a taxonomy.

Artifact scaffolds — Class A and Class B only — inherit at generation time and freeze for audit. A sealed interactive artifact may retain client-side navigation over embedded content; it introduces no live data dependency. Downstream projects supply their own Tier 3 identity, their own source-truth posture, and their own content and domain structure. Hosting a scaffold here does not make this repo the owner of downstream project content.

The repo may also carry specialized opt-in foundation primitives beyond the core tokens — small, identity-free systems a surface loads only when it needs them. These are the foundation-level bounded exceptions; a pattern-level bounded vocabulary also exists (see below). The first foundation primitive is ASK Spectral State (spectral-state.css), a semantic state-color system for surfaces that encode element state. It is not general UI color, and the static scaffolds above do not use it; the interactive diagram-interactive-spine and the diagram-interactive-radial surface pattern are the state-bearing members that do.

Bounded color exceptions sit on three distinct axes, and none of them opens the general palette: semantic state — Spectral State and its sanctioned profiles (e.g. Evidence State); structural function — Three Functions; and participant identity — the Class B message-archive participant ramp. The first two are opt-in foundation primitives; the third is pattern-local — it lives inside patterns/message-archive/, is not a foundation token, and authorizes no use outside that pattern. No other values are available as unrestricted general-purpose colors.

A primitive may carry sanctioned profiles for adjacent semantic domains. The first is ASK Evidence State (evidence-state.css), an epistemic evidence-state vocabulary built on Spectral State: it reuses three Spectral State values by reference and adds two evidence-specific roles (weakened, not-yet-testable), leaving Spectral State's own vocabulary unchanged.

A second opt-in primitive, ASK Three Functions (three-functions.css), sits alongside Spectral State on a different axis: where Spectral State encodes an element's state, Three Functions encodes its function — legislative / executive / judicial, the three separation-of-powers roles read as color. It is a sibling primitive, not a Spectral State profile, and it expands no palette: the three roles bind to existing ASK values (magenta, the neutral white / lavender-ASK, cyan).

Accessibility and compliance scope

The ASK design system expresses ASK's approved visual identity. Its default design language makes no general claim of accessibility conformance, WCAG conformance, or regulatory compliance. Consuming its tokens or patterns does not establish such conformance for an application.

Accessibility and compliance requirements are application-specific. A documented requirement, or an explicitly authorized claim for a particular pattern, profile, or application, governs that bounded use only. It does not impose a system-wide redesign requirement and does not change the default ASK aesthetic.

The approved palette, typography, materials, and interaction treatments are not provisional choices awaiting a compliance audit. --fg-1 is the default light foreground; --fg-high-contrast is a bounded opt-in for separately registered uses, never the default and never selected automatically by font size. Existing high-contrast registrations and other expressly approved bounded treatments remain valid within their stated scope.

Advisor, executor, and generator conduct is governed by AGENTS.md §Accessibility and compliance scope discipline.


Index

Path What it is
colors_and_type.css CSS variables — colors, type, spacing, radii, motion
spectral-state.css + spectral-state.md + spectral-state.html ASK Spectral State — specialized opt-in state-color primitive (eight --state-* roles — seven neon signals plus neutral, which resolves to the theme foreground — on a 12-hue wheel), with a rendered visual key (spectral-state.html). For surfaces that encode element state; not general UI color. Layers on top of colors_and_type.css.
evidence-state.css + evidence-state.md + evidence-state.html ASK Evidence State — a sanctioned profile under Spectral State (epistemic evidence-state). supported / partially-supported / unresolved reuse Spectral State values by reference; weakened (muted 30° brown) and not-yet-testable (lavender-gray, dashed/hollow) are new roles. Rendered key in evidence-state.html. Layers on top of spectral-state.css; Spectral State's own vocabulary is unchanged.
three-functions.css + three-functions.md + three-functions.html ASK Three Functions — specialized opt-in function-color primitive (three --function-* roles: legislative magenta / executive theme-neutral white·lavender-ASK / judicial cyan), a sibling to Spectral State on the function axis (not a profile). Binds existing ASK values to roles; no palette expansion. Rendered key in three-functions.html. Layers on top of colors_and_type.css.
surface-panel.css Opt-in live-surface visual rule — the shared visual contract for live content panels: chrome (.surface-panel), primary label (.surface-panel-title), supporting copy (.surface-panel-support), and the interaction contract for a panel that is itself a link (a.surface-panel). It owns presentation only — no markup, no semantics, no template, no generated preview. Two semantic forms can share this appearance without sharing structure or behavior: a full-panel native link, and an inert panel containing consumer-owned links or actions. It deliberately does not own support-copy foreground, and it assigns no interaction to inert panels. It is also the one source of the glass recipe: the page material, panel material, free attachment and raised elevation classes of surface-treatments.css share its declarations by selector grouping, so an artifact scaffold that inlines the treatments inlines this file with them. Not a component, not a surface pattern, not an artifact scaffold
surface-action.css Opt-in live-surface visual rule — the shared visual contract for compact action controls, and the sibling of surface-panel.css on the action axis: the control itself (.surface-action), its foreground-only quieter variant (.surface-action--secondary), and one inherited hover / press / focus contract. It owns presentation and interaction only — never element type, destination, copy, action-row layout, or placement, so the same grammar styles an <a> that navigates and a <button> that acts without changing what either one is. Not for full-panel links, theme selectors, lightbox or gallery-overlay triggers, badges, or non-interactive labels — and not for inline text links, which surface-text-link.css owns. It also carries the attention edge adopted by class (.surface-attention-edge): a shaped interactive object that is not a compact action — an image card, a media launcher, an overlay control, a linked card in an SVG figure — takes the same hover and focus edge on its own anatomy, and nothing else of the compact action. Not a component, not a surface pattern, not an artifact scaffold
surface-text-link.css Opt-in live-surface visual rule — the shared visual contract for the unboxed textual traversal affordance (.surface-text-link): a link inside running prose or a structural title, which unlike a panel or a compact action has no geometry announcing that it is operable. It owns presentation only — no markup, no semantics, no destination, no layout, no template, no generated preview — and it deliberately declares no text color, so an ordinary link inherits its context's foreground and a role-colored anchor keeps its own. The resting underline is the emphasis accent at partial opacity — the accent's own hue at a reduced dose, never mixed toward the foreground; hover raises it to full opacity; focus is an independent high-contrast indicator. This is the default-ASK treatment and no strict AA non-text-contrast claim is made for its resting state in either theme — the module's own header carries the measured disposition. Opt-in by class on purpose: the excluded set — panels, actions, cards, gallery launches, marks, linked figures — is open-ended, and a missing class degrades to a visible foundation underline while a too-narrow deny-list would silently give a shaped object the textual grammar. Not a component, not a surface pattern, not an artifact scaffold
surface-document.css Opt-in visual rule for both lifecycles — the document register: document text roles on the foundation's own ladder (body on the Body step), the compositions that carry the space between them, the hierarchy rail, the passage rail of a quotation (violet) and a preformatted block (magenta) on the emphasis rail's geometry, the authorial callout composed with .surface-emphasis-rail (magenta), the section framing and the section synthesis composed as the same flat panel, quotation anatomy, preformatted and structured text with its declared peer groups, the entry title of an operable entry in a result list or record index, the document locator (inline code standing alone as a field value), the main + inspector composition of these parts, the action row an item of a repeated collection anchors at its end (.doc-actions--end), the table-of-contents entry (composed with surface-text-link.css, which a table of contents also loads), the dense table a consumer declares with table.doc-dense-table and the narrative table beside it, the document table composition a consumer adopts with table.doc-table directly inside a .doc-table-scroll box, and the overflow cue. It owns presentation only — no markup, no semantics, no template, no generated preview. A live surface references or vendors it and re-syncs; an artifact scaffold may inline it at generation time. Layers on top of colors_and_type.css. Not a component, not a surface pattern, not an artifact scaffold
surface-document-overflow.js Optional helper for surface-document.css: reports whether a preformatted block, or a document table's scroll box, is wider than its box, and on which side, so the stylesheet can draw the overflow cue. Without it a block or a table's box still scrolls and shows no cue
surface-treatments.css Opt-in visual rule for both lifecycles — separate surfaces composed from three independent axes (material, attachment, elevation), the separate-surface plane (.surface-separate), the disclosure (details.surface-disclosure, or, where the host keeps the region it controls, a controlled trigger: a native button carrying .surface-disclosure-trigger with aria-expanded and aria-controls, which takes the summary's grammar and none of the enclosure's geometry; its specimen sits beside the details specimens in surface-document.html), and content emphasis (.surface-emphasis, .surface-emphasis-rail, .surface-emphasis-chip). It owns presentation only — no markup, no semantics, no template, no generated preview. It extends surface-panel.css and loads after it: the page material, panel material, free attachment and raised elevation are that file's own declarations, shared by selector grouping. A live surface references or vendors both and re-syncs; an artifact scaffold may inline both at generation time. Not a component, not a surface pattern, not an artifact scaffold
surface-document.html Rendered visual key for the document register and the surface treatments
fonts/InterVariable.woff2 + italic Inter variable webfont, OFL
fonts/JetBrainsMono.woff2 + italic JetBrains Mono variable webfont, OFL
assets/logo-ASK.svg Vector wordmark, primary — fill: currentColor; the consuming surface sets currentColor to the mode-specific wordmark pairing
assets/logo-ASK-white.png Raster wordmark in #FFFFFF, on transparent (light-mode pairing / fallback)
assets/logo-ASK-lavender-ASK.png Raster wordmark in lavender-ASK (#D4C6E1), on transparent (dark-mode pairing / fallback)
favicon.svg · favicon-32.png · favicon.ico · apple-touch-icon.png Browser-icon package — the wordmark on the baked #D4C6E1 square. Tier-3 implementation assets; see Logo placement
tools/browser-icons.mjs Generates favicon.ico; --check verifies the package against assets/logo-ASK.svg
tools/check-custom-properties.mjs Fails when a var(--name) with no fallback cannot resolve — each vendorable stylesheet against its declared dependencies, each page against its own load graph. --check, --self-test; --root, --page and --dep run it against another repository's pages. Static only: it does not prove scope and does not resolve values built at runtime
tools/check-type-roles.mjs Fails when typography in its governed set of stylesheets and pages leaves the role system: a font size or tracking value that is neither a token nor a registered literal, a heading set at the Caption size or in uppercase, a heading carrying a label or control class, uppercase mono on a selector that is not registered, or uppercase tracking off its register's token without a registered exception and its reason; and (R7) a document-register role that departs from its role matrix in any rule on its selector, reading text off the Body step, a heading declared smaller than body, any other rule on a quotation that sets a type metric, a passage rail not declared by exactly one rule or set there through a longhand, a rule naming a quotation or block inside :is(), :where(), :not() or :has() that draws or recolors a rail rather than removing it, a rail off .surface-emphasis-rail's geometry, an emphasis rail off its registered magenta accent, a block rail whose magenta differs from the emphasis rail's words, a quotation rail off the registered emphasis violet or a violet modifier that drifts from it, a rendered check that pins another quotation or callout color or stops admitting the three sanctioned accents on an emphasis rail off document text, a hierarchy rail that is not the registered 1px --line-2 rail or is set by a second rule, or a rendered-page role or link-state matrix in tools/role-conformance.js that differs from its source. --check, --self-test. Static only: it does not follow the cascade or inheritance, and reads no file outside the governed set
tools/role-conformance.js · tools/check-role-conformance.mjs The rendered half of the document-register check. role-conformance.js runs in a page and fails when a foundation token departs from colors_and_type.css where the check is scoped or wherever a governed role, rail or contents list renders — the type steps, weights, leadings and tracking, the rail inset, both families and the three emphasis accents, and the theme roles --fg-1, --fg-3, --line-1 and --line-2, which must hold the owner's light or dark value (C0); an element carrying a role computes to anything but its matrix (C1); reading text leaves the Body step or a heading is smaller than body (C2); a quotation or preformatted block lacks its rail, leaves the shared geometry, takes any color but its role's or draws a second rail, an emphasis rail leaves the geometry, an emphasis rail on document text (an authorial callout) is not magenta, any other emphasis rail takes an accent outside the three sanctioned ones, or a hierarchy level is not the 1px --line-2 rail (C3); a contents link omits .surface-text-link or its resting underline, an anchor anywhere in a contents list omits .doc-toc-link, or a contents list holds body or lede text (C4); a cell or header of a table.doc-dense-table lacks its role or carries or contains body or lede text, or the dense role appears outside a marked table (C5); a retired class remains (C6); a link in document text, a label, a heading or a contents list omits .surface-text-link (C7); text inside a role changes its family, size or weight without a role of its own, or text nested inside a declared profile member leaves the member's metrics (C8); or a profile declaration is incomplete, miscounted, a list, names an element type rather than a class or id, or captures a governed role (C10); or a declared peer group in structured text is malformed, holds space inside it, or does not sit exactly one line from the next group, measured on its rendered lines (C11); a pass covers declared groups only, and the report counts structured blocks that declare none. check-role-conformance.mjs --url URL runs it in headless Chrome (no npm dependency; Node 22+), opens every disclosure, and then drives every governed text link and contents link with a real pointer and real Tab focus, failing when rest, hover, keyboard focus or the return to rest differs from surface-text-link.css, when a governed link carrying the class renders no box, or when a link a script added after the resting check lacks the class (C9). --profiles FILE.json supplies profile declarations — name, selector, owner, reason and expected count — which are validated before they exempt anything; --fixture URL runs tests/role-conformance-fixture.html. Every finding names a reason code. A page with no governed element is vacuous and fails, and so does a page that throws or fails to load
preview/styleguide.html Live token styleguide — the single canonical preview surface
styleguide-theme-control.js The style guide's forced-mode selector (auto / light / dark). Style-guide-only; not vendored, and not part of surface-shell. An inspection surface needs to hold a mode fixed; ordinary public surfaces follow the operating system and load nothing.
SKILL.md Agent-skill manifest for cross-tool reuse
CONSUMERS.md Known public downstream repos that consume these patterns/tokens — transparency record, not a customer list (private consumers tracked operator-side)
patterns/surface-shell/ Surface pattern — the shared page shell for a family of surfaces: identity-mark slot, surface identity, lede, optional status badge, optional control slot, and a flush-right footer of terminal compact actions. Header flush left, footer flush right; the shell owns the chrome, never the payload. It also ships an optional responsive-navigation runtime (surface-shell.js): where a surface authors a navigation source, the identity mark becomes the disclosure for a hierarchical panel — a drawer on desktop, a sheet on mobile — and a surface that declines it loads no script and acquires no navigation geometry. Tier-3-neutral, not ASK-neutral — it inherits Tier 1 + Tier 2 and ships the slot rather than a mark, so a consumer supplies only its own Tier 3
patterns/output-artifact/ Class B project-output artifact scaffold — consumption pattern for review packets, reports, dashboards
patterns/message-archive/ Class B sealed interactive archive scaffold — one template, two flavors, two-party/group modes, offline search and year navigation
patterns/_diagram-shared/ Canonical shared support for diagram engines — two members, each with its own target set: diagrams-text-layout.js, the text-layout contract consumed by diagram-static-H / -V / -SEQ, and diagrams-pointer.js, the pointer controller consumed by diagram-interactive-radial, diagram-interactive-spine and the four static patterns (diagram-static-H / -V / -SEQ / -FLOW). The text-layout member owns line breaking (delimiters longest-first, force-break, hard newline), measurement, role metrics — per-role cap, line height and the has-note predicate — and tspan emission; the consuming engines keep no private copy of any of them. A plane, not a mandate: the folder name confers no authority, each member declares its own target set, and diagram-static-FLOW, diagram-interactive-spine and diagram-interactive-radial are explicitly excluded from the text-layout member. Members are mirrored into the consuming bundles by tools/sync-diagram-shared.mjs; the mirrors are generated artifacts, never hand-edited, and --check exits non-zero on divergence
patterns/diagram-static-H/ Class A system / architecture diagram scaffold — horizontal left→right cascade; for architecture trees, topology maps, source-of-truth maps. Consumes the shared _diagram-shared/diagrams-text-layout.js contract, vendored as a generated byte-identical mirror; the engine keeps its own topology, placement, connector geometry and SVG envelope, because the three differ genuinely there
patterns/diagram-static-V/ Class A system / architecture diagram scaffold — vertical top→down centered spine; for inheritance chains and one-axis information-architecture diagrams. Consumes the shared _diagram-shared/diagrams-text-layout.js contract, vendored as a generated byte-identical mirror; the engine keeps its own topology, placement, connector geometry and SVG envelope, because the three differ genuinely there
patterns/diagram-static-SEQ/ Class A system / architecture diagram scaffold — ordered top→down sequence joined by arrows, left-aligned; for pipelines, workflows, lifecycles (succession, not hierarchy). Consumes the shared _diagram-shared/diagrams-text-layout.js contract, vendored as a generated byte-identical mirror; the engine keeps its own topology, placement, connector geometry and SVG envelope, because the three differ genuinely there
patterns/diagram-static-FLOW/ Class A system / architecture diagram scaffold — convergence flow: many normative sources converging into one resolved spec, realized, evaluated against an evaluation bus, governed, and fed back; for source-resolution / realization-governance topologies
patterns/diagram-interactive-spine/ Class A interactive diagram scaffold — navigable, stateful IA state surface (hover/click/inspector; pan, pinch and wheel zoom through the shared pointer controller). Consumes the Spectral State primitive for node color; state-bearing (unlike the static scaffolds)
patterns/diagram-interactive-radial/ Surface pattern — a context-agnostic interactive radial map of a containment hierarchy for a live page: a recursive, validated data contract with tested limits (depth 6, 2,500 placed nodes), a radial layout grammar, screen-space names with level of detail, pan / pinch / wheel through the shared pointer controller, Fit through the shared Fit helper, selection, a keyboard path over the hierarchy, a chooser where marks overlap, deep-link arrival, and six optional modules: a legend; a responsive chrome that shows the caption and legend in their corners while they fit and otherwise folds them behind triggers in the shared disclosure grammar beside the HUD, measured rather than set by one width; an inspector that reports a node or an undrawn record in text from the adapter's sections; search, facets and the membership they set, in a drawer; a PNG page plate and diagram plate; and a theme control for the instance that owns the document theme. Consumes Spectral State for state color. A consumer vendors the owner files byte-identical (and the foundation files its modules take) and supplies its data, its adapter (its domain words, fields, facets, plate words, and any override of the owner's generic control and announcement defaults) and its page; it seals nothing. It has two public expressions, each with a gallery preview: a reference specimen, the public content of the Consciousness + Free Will research map captured on 2026-10-02, and a synthetic parks composition, which also serves development and tests. Each supplies its own data and adapter; both run the same owner modules

Logo placement

The wordmark uses its own mode-specific brand pairing — it does not inherit the body text color — and it sits on the gradient, not on a fixed lavender-ASK block.

  • Light mode — #FFFFFF wordmark on the light gradient (the wordmark's brand pairing — not the #6A637F light-mode text).
  • Dark mode — #D4C6E1 (lavender-ASK) wordmark on the dark gradient (here it coincides with the #D4C6E1 dark-mode text).

In any UI surface — page, card, preview, component — the mark goes on the gradient. The fixed #D4C6E1 lavender-ASK field is only for the standalone exported asset (the JPG/vector deliverable). It is not a UI background. Do not place the wordmark on a flat lavender-ASK block anywhere in the system.

logo-ASK.svg is the primary reference: it paints with fill: currentColor, so one vector file can be used for both mode pairings when its container sets the correct wordmark color. The two PNGs are raster pairings/fallbacks.

Browser icons — the one other exported-asset instance

The prohibition above is about UI surfaces. The browser icon is not one: like the standalone exported deliverable, it is a fixed asset, and it carries the wordmark on the flat #D4C6E1 field by design — fitted to a square. It is the compact square application of the exported-asset treatment, not a third wordmark treatment and not a new logo.

File Role
favicon.svg 512 × 512, white wordmark on the baked #D4C6E1 square — the primary
favicon-32.png 32 × 32 raster fallback
favicon.ico root-probe carrier; a container around favicon-32.png, byte-for-byte
apple-touch-icon.png 180 × 180, iOS home screen

These are Tier-3 implementation assets. The visual decision stays with the identity canonical (visual-identity-system.md); this repo owns the reusable package, its verification, and the integration contract. The glyph geometry derives from assets/logo-ASK.svg without redraw — tools/browser-icons.mjs --check fails if any glyph path diverges from the wordmark, if the field or ink colour moves, if a fourth glyph or a wedge-only mark appears, or if the ICO stops matching the 32 px composition. Regenerate the ICO with node tools/browser-icons.mjs; it is byte-deterministic.

Two integration profiles, because ASK browser surfaces are not all origins.

A — deployed origin. The full four-file package at the origin root, root-absolute head paths:

<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">

/favicon.ico needs no declaration — it answers the browser's automatic root probe and covers pages that declare nothing.

B — package-local browser artifact. A mutable operator package a reader opens directly from its own directory rather than over an origin. Paths are relative to the artifact HTML at the package root, because a leading / there resolves to the filesystem or origin root, not to the package:

<!-- Package-local browser artifact — paths relative to this HTML file -->
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png">

Two files only. No apple-touch icon — there is no home-screen role. No root-probe guarantee is claimed under file://, so a package-local .ico is not required unless a concrete local-browser need is demonstrated. The visual bytes are the same canonical owner bytes; only the carrier subset and the path form differ.

A surface uses this package only where it is assigned ASK's own Tier 3. A surface that resolves a different Tier 3 supplies its own browser icon; carrying ASK's icon does not make a project ASK-the-entity, and surface-shell remains Tier-3-neutral — it ships a mark slot and supplies no favicon value. Downstream consumers vendor these exact bytes rather than redrawing or re-extracting a mark.

A web app manifest, PWA 192/512 icons, and mask-icon are not part of the package: none of the consuming surfaces is an installable app, and nothing here needs one.


Voice for this system's own surfaces

The README, card labels, and any docs in this repo speak in a calm, declarative, sentence-case voice. Quiet. Restrained. One idea per sentence. Plain present tense. Describe the system rather than narrating a maker.

Trait Treatment
Person Impersonal / declarative. Do not invent a studio "we" — ASK is a personal meta-brand, not a collective. Describe the system as a thing that exists, not a thing "we made".
Casing Sentence case in headings and body. UPPERCASE only for small labels (≤14px), tracked by role: Inter Caption at 0.14em, mono operative labels at 0.08em.
Length Short. One idea per sentence.
Punctuation Periods, em-dashes, commas. No exclamation marks. No "Introducing:" lead-ins.
Numerals Spell out one through nine in copy; figures in UI labels and prices.
Adjective hygiene No "revolutionary", "leading", "powerful", "next-gen", "AI-powered". Replace with one concrete noun.
Verbs Plain present tense. "The system uses Inter", not "we use Inter".
Emoji (scoped) No emoji in this design system's own surfaces (README, cards, docs). This rule applies to the system, not to ASK universally — ASK's broader personal contexts have a sanctioned exception (a single purple heart used as personal branding) which lives outside this repo. Do not propagate a blanket "ASK never uses emoji" rule.

Exception to the punctuation rule — protected payloads. The ASK tagline (order from chaos // beauty in systems) and its LinkedIn variant are protected payloads: fixed brand strings rendered verbatim, // intact. They are exempt from the punctuation and voice rules in the table above — the // is part of the string, not prose written in this system's voice. Never normalize it to an em-dash. Canonically defined in brand-architecture.md.

ASK's personal writing voice is a separate convention (terminalcore — slash-slash for em dashes, plus for ampersand, double-angle arrows). That voice belongs to ASK's personal channels — not the design system. Keep them strictly separate. The design system always speaks the calm sentence-case voice above.

Voice examples

Yes

  • "The system uses Inter for interface, JetBrains Mono for code."
  • "State is expressed through weight, opacity, and motion."
  • "Two diagonal gradients. A tight core set."

No

  • "We've built a revolutionary design system 🚀"
  • "Introducing: the ASK token library!"
  • "Our industry-leading typography stack ✨"

Visual foundations

Color

Backgrounds — two diagonal gradients. Both 45° (bottom-left → top-right); the lighter end is top-right in light, bottom-left in dark.

  • Light — linear-gradient(45deg, #D4C6E1 → #E2D3F0). Text is #6A637F (the approved dark-purple foreground).
  • Dark — linear-gradient(45deg, #201D26 → #0A090C). Text is #D4C6E1.

The gradient is fixed to the viewport (background-attachment: fixed), so scrolling reveals one continuous field.

Browser / under-page edge. A browser paints its own area beyond the document — the over-scroll pocket above and below the page — and derives it from a solid background colour, not from a gradient. It reads color-scheme for the same reason, to decide how to render scrollbars and form controls. Both signals are page-wide, so the foundation owns both on the root and nowhere else.

--bg-edge is the Tier-1 semantic role that carries the value. It binds existing palette values and creates none: --ask-lavender-light in light, --ask-ink-dark in dark. The base reset paints it on html beneath the gradient, so nothing visible on the page changes — the gradient still covers the viewport, and the solid shows only where the browser reaches past the document.

Two consequences worth stating, because both are easy to undo by accident:

  • The base reset uses the background-image longhand. The background shorthand resets background-color to transparent, which is precisely what left the canvas with no solid layer to derive an edge from.
  • The edge selectors match the root only. The general dark-mode blocks also match a descendant .theme-dark, which is right for local subtree theming and wrong here: custom properties inherit downward, so a .theme-dark on body would resolve dark for that subtree while html kept the light edge — dark content inside a white browser edge. A descendant .theme-dark therefore leaves the canvas alone, by design.

This is a foundation property, not a pattern feature. A pattern or surface must not declare background-color or color-scheme on the root to correct its own edge.

Core set — the named values. Everything fundamental is built from these:

Token Hex Use
--ask-fg-light #6A637F Default light-mode foreground (body text — the approved dark purple)
--ask-white #FFFFFF Light-mode wordmark (brand mark) — not body text
--ask-lavender-light #E2D3F0 Light gradient, top-right; also the light browser / under-page edge (--bg-edge)
--ask-lavender-dark #D4C6E1 lavender-ASK — light gradient start; dark-mode text
--ask-ink-light #201D26 Dark gradient, bottom-left; also the opt-in high-contrast foreground role (--fg-high-contrast). Approved uses are registered below — see High-contrast foreground — registered uses
--ask-ink-dark #0A090C Dark gradient, top-right; also the dark browser / under-page edge (--bg-edge)

High-contrast foreground — registered uses. --fg-high-contrast binds --ask-ink-light as an opt-in foreground role, and this list is its registry: a use is approved only if it appears here. The role never rebinds the default gradient-surface foreground ramp — --fg-1 / --fg-2 / --fg-3 stay as approved — it is never the default foreground, and it is never selected by font size. A bounded element or region may opt in where the ordinary roles do not carry enough contrast, and only by registration below. The list order is organizational, not chronological.

  1. message-archive pattern roles — one bounded use with two limbs:
    • participant ink and search-highlight text on the sanctioned colored fills;
    • in the AA-compliant light theme, essential page chrome, the archive title, and the focus indicator, where the ordinary foreground roles fail. In dark those page-level roles return to the normal dark foreground role.
  2. --fg-on-card — text on the fixed --surface-solid role, which does not flip with the theme and so takes a foreground that does not either.
  3. method-ASK D11 system-ASK hero — in the light theme, the figure-local diagram text roles opt into --fg-high-contrast after measured normal-text failure against the lavender gradient. Dark remains on the ordinary diagram roles. This is a bounded consumer-local use; it does not rebind --fg-1 / --fg-2 / --fg-3, alter the shared diagram patterns, or authorize another surface.

A new use requires all three of the following before merge: measured evidence that the ordinary foreground roles are insufficient for the exact bounded element or region; explicit ASK source-of-intent authorization; and registration in this list. Density, legal, tabular, accessibility, or a comparable context may create the pressure that justifies proposing a use — none of them authorizes one on its own.

A canonical inspection specimen may render the role to display and measure it. Inspection is not an additional registered application.

Surface. When a card or container needs a solid fill with more presence than a glass overlay:

Token Hex Use
--ask-surface #BFB3D4 Container fill, rest
--ask-surface-hover #C9BCDE Container fill, hover

UI accents (two, muted, secondary use). For dividers, low-emphasis fills, secondary borders:

Token Hex
--ask-ui-accent-1 #8B79A2 (muted plum)
--ask-ui-accent-2 #AE87C2 (lavender-mid)

Emphasis accents (three, sparing). Sanctioned for emphasis where the calm field needs a single hot point. Three — full stop. Not an invitation to introduce other hues.

Token Hex
--ask-emphasis-magenta #FF00FF
--ask-emphasis-violet #AA40FF
--ask-emphasis-cyan #00BEFF

Type

Inter is the default interface, display and explanatory family. JetBrains Mono is reserved for explicit technical, structural or operative roles, owned by selectors in their applicable module or pattern. Mono is never a generic body or support-copy substitute, and nothing becomes mono merely for being interface-facing — it enters where structure or precision matters. Family is chosen by the owning selector, never by payload text, HTML element type, capitalization, or the presence of //.

The current live-surface allocation:

Owner Selectors Role
foundation utility code · kbd · pre · samp · .mono · [data-mono] · .tabular code, technical and tabular literals
surface-action.css .surface-action compact operative control
surface-shell .surface-title · .surface-badge · .surface-nav-row structural title, status badge, panel hierarchy row
surface-document.css .doc-label · .doc-meta · .doc-quote > footer · .doc-code · .doc-pre · .doc-toc-list · .doc-toc-link · .doc-table-cell · the overflow cue operative label, metadata, quotation attribution, code, preformatted text, the table-of-contents list and its entries, and dense table cells in the document register
surface-treatments.css details.surface-disclosure > summary · .surface-disclosure-trigger · .surface-emphasis-chip disclosure trigger, controlled disclosure trigger, emphasis chip
a consuming surface its own explicitly named operative control e.g. the style guide's .surface-theme mode selector

Everything else on a live surface is Inter unless the owner of a selector says otherwise, and a generic utility class is not a license to make prose mono.

That table is the live-surface allocation, not a repo-wide census. The artifact and diagram patterns own additional pattern-local technical and structural mono selectors — diagram sublabels, stamps, theme tags, archive metadata — under the same principle: an explicit selector in the owning pattern. Those local contracts never make mono a page default, and they do not extend to any surface that has not adopted the pattern. The panel primary label sits between them in the table because it shares the locator's metric exactly, not because it is a third mono role: it is Inter. Both families are loaded locally as variable webfonts in fonts/ (OFL).

Type hierarchy is role-driven. The defined scale steps distinguish semantic roles; do not invent an ad-hoc size merely to add emphasis. Within a role, weight and foreground carry contrast.

Generic H1 and H2 roles use 400. H3 and both primary-label implementations use 300. Body uses 200; Small uses 300; Caption uses 400. The document register assigns weight by its own roles: document and section titles 400; subsection title 300; deep heading 500; lede, document body and quotation text 200; entry titles, preformatted text, table-of-contents entries and dense table cells 300; strong emphasis in document text 500. Light weights are deliberate. No thick typefaces.

Element name does not override the explicit role: an h1 or h2 carrying .surface-title or .surface-panel-title remains a primary label at 300, and conforming it to the generic heading weight is a defect rather than a repair.

Element name does not override a document-register role either. An h1 carrying .doc-title is a document title at 48 / 400. An h2 carrying .doc-section-title is a section title at 36 / 400, and an h3 carrying .doc-subsection-title is a subsection title at 28 / 300. An h4, h5, h6 or [role="heading"] element carrying .doc-deep-title is a deep heading at 24 / 500, at every depth. Each role is a complete text style — family, size, weight, leading, tracking, foreground and margin — so the element's own foundation rule contributes none of those seven. The foundation also sets text-wrap: pretty on h1/.h1, h2/.h2 and p/.body. The document register does not set text-wrap, so wrapping remains subject to the cascade and inheritance rather than being fixed by the text role. The generic H1 and H2 rows govern headings that carry no explicit role. Conforming a document-register heading to the generic heading size or weight is a defect rather than a repair. Uppercase mono is never a content heading, and a heading is never smaller than the prose it governs; depth is carried by the hierarchy rail, not by a smaller, lighter or recolored heading.

The 24px primary label over 18px supporting copy pair is a sanctioned distinction between two defined roles — not ad-hoc sizing, and not a case where a weight difference should be substituted for the size step. Both sit at 300, and the pair separates on role.

Role Family Weight Size / line-height Tracking
Display Inter 200 96 / 1.02 -0.035em
H1 Inter 400 48 / 1.12 -0.02em
H2 Inter 400 36 / 1.12 -0.02em
H3 Inter 300 28 / 1.20 -0.02em
Body Inter 200 24 / 1.45 0
Small Inter 300 18 / 1.40 0
Caption Inter 400 14, UPPERCASE 0.14em
Primary label — structural locator JetBrains Mono 300 24 / 1.16 -0.02em
Primary label — panel Inter 300 24 / 1.12 -0.02em
Compact action JetBrains Mono 300 14 / 1.20 0
Code / inline-code JetBrains Mono 300 0.9× host 0
Tabular numerals JetBrains Mono inherit inherit 0
Document title Inter 400 48 / 1.12 -0.02em
Section title Inter 400 36 / 1.12 -0.02em
Subsection title Inter 300 28 / 1.12 -0.02em
Deep heading — h4 and deeper Inter 500 24 / 1.12 0
Lede Inter 200 24 / 1.45 0
Document body Inter 200 24 / 1.45 0
Quotation text Inter 200 24 / 1.45 0
Entry title — the primary text of an operable entry Inter 300 24 / 1.45 0
Preformatted text JetBrains Mono 300 18 / 1.45 0
Table-of-contents entry JetBrains Mono 300 18 / 1.20 -0.02em
Dense table cell — table.doc-dense-table only JetBrains Mono 300 18 / 1.45, tabular numerals 0
Operative label JetBrains Mono 300 14 / 1.20, UPPERCASE 0.08em
Metadata · quotation attribution JetBrains Mono 300 14 / 1.40 0.08em

Inside document compositions, Caption sets 1.30 leading and no margin.

Inline code inside an operative label keeps its source case: the label's capitals never rewrite a code literal, and default-ASK is not DEFAULT-ASK. Plain text in a label, a unit such as mW included, keeps the capitals.

The document-register ladder. Document body is the foundation's Body step — the same 24 / 200 as a plain paragraph — and every document heading sits on a foundation step at or above it: the title on H1, the section title on H2, the subsection title on H3, and the deep heading on Body, where weight (500 against 200) and the space before it carry the distinction. No content heading is smaller than the prose it governs, and a quotation is body-sized at every length. The ladder is the foundation's own; the register adds no size. .doc-quote--display, which set a short quotation larger than its prose while the plain quotation sat smaller, is retired.

Conformance, in two halves. tools/check-type-roles.mjs rule R7 reads surface-document.css against one role matrix and fails when any rule on a role's selector declares another family, size, weight, leading, tracking or foreground, when reading text leaves the Body step, when a heading is declared smaller than body, when any other rule on a quotation sets a type metric, when a quotation or block rail is not declared by exactly one rule or is drawn or recolored anywhere else, when either passage rail leaves .surface-emphasis-rail's geometry (width, style, inset), when the emphasis rail leaves its registered magenta, when the block rail's magenta differs from the emphasis rail's words, when the quotation rail leaves the registered emphasis violet or the violet modifier's words, when the rendered checker pins another quotation color, lets an authorial callout take any color but the emphasis rail's magenta or stops admitting the three sanctioned accents on any other emphasis rail, when the hierarchy rail is not the registered 1px --line-2 rail, or when the rendered-page role matrix or link-state matrix drifts from its source. Geometry parity is enforced; each role's color is pinned, and no role is required to share another's. tools/role-conformance.js checks a rendered page: the foundation tokens, families, accents and theme roles are the owner's wherever governed text renders; every element that carries a role computes to the matrix, and text inside it keeps its family, size and weight (inline emphasis may change the weight) unless a valid profile declares it (and text inside a profile member keeps the member's); each quotation and preformatted block is covered by exactly one rail of the shared geometry — its own, in its role's color, or the nearest railed passage around it, whose role names the passage — each emphasis rail on document text, an authorial callout, draws the geometry in magenta and any other emphasis rail in one of its three accents, and each hierarchy level draws the 1px neutral rail; every table-of-contents link composes .surface-text-link and renders its underline, and every anchor in a contents list is a contents link; the cells of a declared dense table carry their roles and the dense role appears nowhere else; no contents list or dense cell holds document body; no retired class remains; a link inside document text, a label or a heading carries .surface-text-link; and each declared peer group in structured text opens on its label, holds no space inside it and sits exactly one line from the next, as measured on its rendered lines. tools/check-role-conformance.mjs runs it headless on any served page and then proves each governed link's rest, hover, keyboard-focus and return-to-rest states with real input, and tests/role-conformance-fixture.html proves each failure branch fails with its own reason code. Among the limits tools/role-conformance.js lists, two bear on what a pass compares. A valid profile exempts its members' own text: it is reported with its computed metrics and compared against no role. And the descendant check (C8) compares family, size and weight only, never letter case; case is compared by the role-metric check (C1), for the four of the fourteen matrix roles that declare one. A page with no governed element is reported vacuous, never passing.

Rails: shared geometry, meaning in the color. A quotation, a preformatted block and an authorial callout are all set apart on one geometry — a 2px solid rule and a --space-4 inset — and every one of those outer rails is an emphasis accent, so each reads as a boundary in both themes. The color says whose passage it is. A quotation is represented voice — words presented as someone's, including an earlier statement by the same author or speech the prose introduces — and its rail is the emphasis violet, declared as .surface-emphasis--violet declares it; it records whose words these are, not verification, evidence, endorsement or approval. The document's own thesis, posed question or analytical contrast within its argument is an authorial callout: .surface-emphasis-rail, magenta, composed onto document body text. An emphasis rail on document text — one that carries document body or lede or a text composition (.doc-group, .doc-prose, .doc-section, .doc-titled, .doc-labeled), or holds any of those, a quotation or a block — is a callout and always magenta; the violet and cyan modifiers stay valid on any other emphasis rail, and the rendered check holds each to that. A preformatted block's line structure is its content: its rail is magenta, declared exactly as .surface-emphasis-rail declares it. Who is speaking is the consuming surface's editorial assignment — never the element, the font, boldness or a pair of quotation marks. Neutral is structure only: the hierarchy rail stays a 1px neutral rule that structures a section, a list or a block, and no set-apart passage takes a neutral outer rail. A passage inside a railed passage draws no second rail, so one logical passage has one outer rail and the outermost role names it; a quoted code excerpt is a .doc-pre inside a .doc-quote and keeps the one violet rail. A section synthesis — the compression that closes a part — is not railed: it is a flat panel (.surface-separate + .surface-material-panel + .surface-attach-free + .surface-elevation-flush + .doc-group) with a .surface-emphasis-chip carrying .surface-emphasis--magenta and its text on .doc-body. A section framing — the statement that frames a whole part before its sections, the part's thesis or the question space it opens — takes the same flat panel and chip, its chip naming the framing. It opens the part, after the part's introduction; it looks forward where a synthesis looks back, and concludes nothing. A thesis or question set apart inside the argument stays an authorial callout. The neutral quotation rail is retired (2026-09-19): it made a set-apart passage look ghosted.

Profiles are declared, not waved through. A consumer's own named role — a count numeral, say — is a profile declaration: { "name", "selector", "owner", "reason", "expected_count" }. The check validates it before it exempts anything: every field present, one selector ending in a class, id or attribute, exactly the declared number of matches, and no match that carries or contains a governed role or passage. A declaration that fails exempts nothing, so a profile cannot hide the defect it was widened over.

Document compositions carry the space between roles. Every document role sets margin: 0. .doc-flow (--space-8), .doc-section and .doc-titled and .doc-group (--space-3), .doc-prose (--space-4) and .doc-labeled (--space-2) set it as a gap; a section nested in a section leads by --space-4 more. .doc-hierarchy is the neutral hierarchy rail: it carries a whole section, one rail per level, to any depth, and a structured preformatted block uses the same rail for indentation that is structure. data-lead-lines records one to three blank source lines. A grouped diagram declares each peer group as a .doc-pre-group, and successive groups sit exactly one line apart (Structured text, below). .doc-quote and .doc-pre draw the passage rail on .surface-emphasis-rail's geometry — a 2px solid rule and a --space-4 inset — each in its own role's color: a quotation is represented voice and takes the emphasis violet; a preformatted block takes the magenta emphasis rail, with no variation; an authorial callout, .surface-emphasis-rail on document text, is magenta too. So a quotation, a block and a callout each read as one set-apart unit — violet represented voice, magenta the document's own, the block's mono its line structure; a passage inside a railed passage draws no second rail. .doc-toc-list is a table of contents (a nested contents list that is also a .doc-hierarchy is one level down) and .doc-toc-link its entry, always composed as class="doc-toc-link surface-text-link": the register owns the entry's type, the text-link module its interaction. A dense table is one the consumer declares: in a table.doc-dense-table, .doc-table-cell is the value and .doc-label the header, and .doc-table-cell appears nowhere else; a table without the marker — a narrative comparison, a prose table — is not the dense role: it is a narrative table, its headers on .doc-label and its cells on .doc-body. A table's geometry stays with the consuming surface unless the table adopts the document table composition (below).

Text keeps a clear reading area. A decorative boundary stops at its label (surface-document.html #reading-area-routed). A meaning-bearing connector keeps its ends, direction and path (#reading-area-relation). Both are demonstrations, not module classes.

The primary-label role names a thing the system has — a surface, a route, a panel, a named primitive — rather than setting prose. Its shared core is 24 / 300 / -0.02em, on Body rather than an H-step because a label is a locator and not display type, and its default leading is 1.12 — which the panel implementation takes and the structural locator overrides. It has two implementations, and each owns its family through its selector.

The two implementations differ in family and in leading. They share everything else. The shared core — 24 / 300 / -0.02em — is what makes a primary label read as one object across surfaces, so it is precisely not what separates them. .surface-title is JetBrains Mono on 1.16; .surface-panel-title is Inter on --lh-heading (1.12). The 1.16 is a metric the shell owns for a wrapping, underlined structural locator, and the panel label — a single line, never underlined — has no such condition to earn it.

Both surface-shell title forms take 1.16: the plain title and the breadcrumbed title alike. Those two forms differ in landmark and in linkability — never in leading, and never in family. The breadcrumb's older pattern-local 1.35 was retired: it had been measured against an underline that was a border, and survived the change to a text decoration by inertia. Nothing carries it now.

.surface-title in the surface-shell pattern is the structural locator and is mono. That covers both of the pattern's title forms — the plain <h1> and the breadcrumbed title — because both carry .surface-title. .surface-panel-title in surface-panel.css is the panel primary label and is Inter, and it stays Inter whatever the label says — a panel may name something technical or structural, and may carry // or any other terminalcore grammar, without changing family. Those two selectors are the canonical implementations; neither is conformed to the other, and the shared size, weight and tracking are what still make a primary label read as one object across surfaces. No family is ever derived from a payload string.

Supporting copy under either stays on the Small Inter step, so that pair separates on size rather than weight. colors_and_type.css is unchanged by this role: its generic .mono utility remains for code, technical and tabular use, and the mono structural-locator exception is applied only through .surface-title. The compact action (.surface-action) is a separate mono exception, with its own metric and its own canonical selector — it is not an implementation of this role.

The compact-action role is the label on a small control — a chip, a route action, a preview or README button. .surface-action in surface-action.css is its canonical implementation. It is Caption-sized, not the Caption role, and the distinction is load-bearing: Caption is 14px Inter at 400, uppercased, on 0.14em tracking, and it labels things; the compact action is 14px mono at 300, sentence- or lower-cased, on zero tracking, and it is something you click. Conforming a compact action to Caption's uppercase, tracking, or weight — or to Inter — is a defect rather than a repair. This role also does not govern the 18px Inter CTA specimen in the style guide, which is a separate and deliberately different object.

Tracking is assigned by role and never chosen per artifact. Two small uppercase registers exist and never trade values: the Inter Caption at 0.14em and mono operative labels at 0.08em. A label takes its tracking from its role, not from its size — Caption-sized is not the Caption role. Specialized patterns — the Class A diagram scaffolds and the message archive — own pattern-local label metrics that do not extend to any other surface; a diagram scaffold's .caption overlay is one of those, not the Caption role. A new value enters this account only with a named role.

Tracking Token Roles
-0.02em --tracking-tight H1 · H2 · H3 · document title (.doc-title) · section title (.doc-section-title) · subsection title (.doc-subsection-title) · table-of-contents entry (.doc-toc-link) · structural locator (.surface-title) · panel primary label (.surface-panel-title) · panel hierarchy row (.surface-nav-row)
0 --tracking-normal Body · Small · supporting copy (.surface-panel-support) · lede (.doc-lede) · document body (.doc-body) · entry title (.doc-entry-title) · quotation text (.doc-quote > p) · deep heading (.doc-deep-title) · preformatted text (.doc-pre) · dense table cell (.doc-table-cell) · compact action (.surface-action) · code and inline code, the document locator included — .doc-code declares no tracking, so a locator stands only where its container sets none · tabular numerals
0.08em --tracking-wide mono operative labels and metadata (.doc-label, .doc-meta) · status badge (.surface-badge) · overflow cue (.doc-pre and .doc-table-scroll ::after) · quotation attribution (.doc-quote > footer) · disclosure trigger (.surface-disclosure > summary, .surface-disclosure-trigger) · emphasis chip (.surface-emphasis-chip)
0.14em --tracking-caption Caption — Inter, uppercase (.caption) · the pattern gallery's group and card-class labels (.group-label, .card .cls in patterns/index.html) — Inter uppercase labels that share Caption's register and tracking but keep their own weight and foreground, so they are not the Caption role · the style guide's section labels (.sg-h in preview/styleguide.html) — Inter uppercase labels in the same register that keep the 200 weight they inherit, so they are not the Caption role either
-0.035em --tracking-display Display

Registered exception. The style guide's .badge component specimen is Inter, uppercase, at --tracking-wide. It is neither the Caption role nor a mono operative label, and it is not a precedent for either register.

Spacing & layout

  • 4-px base unit. Tokens at 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 / 128.
  • 12-column grid, 96px outer margin on desktop.
  • Generous whitespace. If a layout feels "done", strip another section.

Radii

  • xs 4 · sm 8 · md 14 (default) · lg 22 · xl 32 · pill 999
  • Pills only on interactive elements. Square corners on hairlines and dividers.

Borders, shadows, transparency

  • Hairlines at rgba(white, 0.45) on light gradient and rgba(lavender-ASK, 0.22) on dark. Always 1px. No passage rail takes the hairline value: a quotation's rail is the emphasis violet, and a neutral outer rail on a set-apart passage is retired.
  • Shadows are long and soft. Three steps: sm (1px), md (24px), lg (60px). No hard drop shadows, no inner shadows, no colored shadows. Zero-blur interaction rings — the focus glow, the attention edge's 0.5px ring, and the inset 1px ring a borderless adopting object draws where a border would sit — are edge paint, not elevation shadows, and this line does not govern them. The content-emphasis bloom (surface-treatments.css) is emphasis paint, not elevation: a persistent, role-assigned accent whose dose is set per theme. This line does not govern it either.
  • Glass cards — the preferred container on the gradient field, selected on three separate axes. Material: the --surface-glass fill (white at 14% on light, lavender-ASK at 6% on dark) with a 1px --line-1 hairline, plus a 20px backdrop blur where the material blurs — the page material (.surface-material-page) blurs; the panel material (.surface-material-panel, used by the disclosure) does not. Attachment: corner treatment only — radius-lg (22) on every corner of a free-standing surface (.surface-attach-free); a surface joined at its top or bottom edge rounds only its free corners (.surface-attach-top, .surface-attach-bottom); a surface joined along its full width rounds none (.surface-attach-full-width); a chip takes radius-pill (.surface-attach-chip). Attachment sets no fill, blur, edge or shadow. surface-shell's navigation panel realizes the same corner rule pattern-locally and also drops the border on its joined edge; it does not consume these classes. Elevation: chosen per role — .surface-panel rests on shadow-md; a surface that is not a content panel is flush (.surface-elevation-flush) unless its role raises it (.surface-elevation-raised). surface-panel.css is the complete component selecting page material, free attachment and raised elevation, and it authors that recipe once: the matching axis classes share its declarations by selector grouping, while the linked panel's hover and focus rules and the composed emphasis shadow name shadow-md again, because a box-shadow list cannot compose across rules. The gradient shows through.

Containment

Three questions, asked in order; the first two inform the third and do not decide it. Semantic identity — is this a distinct section, quotation, finding or decision? Interaction identity — does it need its own control, focus behavior or navigation target? Presentation requirement — does the reader need a separate visual plane, or only grouping, attribution, indentation, spacing or a local boundary? Only the third selects a treatment, from least to most: ordinary flow (the default) · subordinate grouping (.doc-hierarchy — spacing and one quiet rule; no wash, blur or shadow) · quotation anatomy (.doc-quote — the passage rail, body-sized text and attribution; not a container) · disclosure (details.surface-disclosure — a trigger and an expanded region) · separate surface (.surface-separate with one material, one attachment and one elevation — earned when the object overlays, scrolls independently, arrives from outside the reading flow, or is a whole unit set apart for emphasis or comparison). An addressable subsection stays in ordinary flow; an attributed quotation takes no surface.

Hover, press, focus

  • Hover — opacity drops 1.0 → 0.92, and/or the border changes; a closed disclosure's trigger changes its foreground only; an open one already rests at the raised foreground. No arbitrary new hue is introduced: where a border changes color, it resolves either to something the element already carries or, for the shaped populations below, to the palette's emphasis magenta. The treatment is role-driven, and the roles genuinely differ — do not conform one to another:

    role hover treatment
    generic anchors (foundation a) foreground-bound — the text-edge border resolves to currentColor
    compact actions (.surface-action) and full-panel links (a.surface-panel) attention edge — the complete border turns pure magenta (--ask-emphasis-magenta) in both themes, with an apparent 1.5px edge in light (the 1px border plus a 0.5px zero-blur ring outside the box) and 1px in dark; no glow and no layout change; a full-panel link's ring composes with its resting --shadow-md. Keyboard-visible focus takes the same edge — see §Focus
    objects that adopt the edge (.surface-attention-edge) — a shaped interactive object that is neither of the above, such as an image card, a media launcher, an overlay control or a linked card in an SVG figure the attention edge alone, on the anatomy the object has: its own border; on a borderless control (.surface-attention-edge--borderless), an inset 1px ring where a border would sit, with the same 0.5px ring outside in light; in SVG, the stroke of the shape the link marks (.surface-attention-edge-shape), an apparent 1.5px in light and 1px in dark: a figure drawn below its own size declares that scale as --surface-attention-edge-scale, and the stroke widens to hold the edge; at its own size and above, the stroke is 1.5 and 1 units and scales with the figure, for a shape that rests on the diagram box's 1-unit stroke. The class brings no opacity drop, press or motion; the object's own hover stays its own, an anchor's foundation hover included. Keyboard-visible focus takes the same edge
    unboxed textual links (.surface-text-link) and breadcrumb links (.surface-title a) the magenta text decoration rises from partial to full opacity; element opacity held at 1 so the 0.92 drop reads as press
    disclosure triggers (details.surface-disclosure > summary, .surface-disclosure-trigger) foreground-only — on a closed disclosure the trigger and its indicator rise from --fg-2 to --fg-1; an open disclosure's trigger already rests at --fg-1, so hover changes nothing; no border, no opacity drop, no press
    the identity mark (a.surface-mark) declared opacity only; nothing else changes
    shell footer destinations none of their own — they are compact actions and take the compact-action row

    Two border treatments, split by role. A generic anchor's hover belongs to running text, so its text-edge border stays with its own inherited color. A compact action and a full-panel link are shaped objects a reader targets; in their hover, magenta carries attention, and each keeps its own anatomy — the complete pill border, or the complete panel border with its ring composed on the resting shadow. The magenta lasts only while the object is hovered or focused. It is never a selected, current, filtered, error or other persistent state; the same palette value carries persistent, context-governed meanings elsewhere, and a persistent semantic border — a diagram mark's state, a status encoding — keeps its own paint and does not acquire this treatment; a linked card in a figure takes the edge only while its link is hovered or focused. A persistent emphasis carried by a compact action, a full-panel link or an adopting object is overwritten while the object is hovered or focused, and needs its own consumer composition; surface-treatments.css's content emphasis is not composed onto either.

    The reason is scale. A --line-2 rest border is the faintest line the system has, so brightening it to --line-1 reads as almost no response on the light gradient. The magenta border gives a shaped object one attention response in both themes. No non-text contrast claim is made for the edge.

    Hover and focus share this paint and nothing else. They remain independent states answering different questions, so a shaped object that is hovered and focused shows one edge rather than two competing indicators, and the 0.92 opacity drop stays hover's alone. Two objects, one hovered and one focused, show the same edge; only hover's opacity drop tells them apart. An adopting object takes no drop from this rule, so its own hover, where it has one, is what tells its states apart.

    The list is closed. Compact actions, full-panel links and objects that adopt the edge share the attention treatment — one contract: compact actions and adopting objects share one set of declarations in surface-action.css, and the full-panel link keeps its own in surface-panel.css, composed with its resting shadow — and generic anchors alone keep the foreground-bound one; a future bordered control inherits neither by being interactive, and joins only by being named here or by adopting .surface-attention-edge. The class is opt-in and never inferred. It gives the edge alone, so an adopting object without a hover behavior of its own looks the same hovered as focused. It removes the browser's focus ring, so an object adopts it only with an anatomy that paints: its own border, the borderless ring, or a marked shape. The borderless ring paints beneath the object's content, so it suits a control whose content stays clear of its edge. A consumer rule that sets the object's border color, or its marked shape's stroke, on hover or focus, later in source or at a higher specificity, overrides the edge and is retired on adoption. --surface-attention-edge-scale is a geometric input, not a token: the effective uniform scale from the marked shape's own units to CSS pixels on the page, a plain positive number, 1 where nothing declares it, supplied by whatever scales the figure. Only the SVG attention stroke reads it. An adopting object with a resting or hover shadow restates it with the ring in its own rules, as the full-panel link composes with its own. The edge reaches only the page's own document: a control inside an embedded document from another origin, such as a video player's own buttons, keeps that document's focus indicator. Inert panels do not hover. A disclosure trigger joins neither shared treatment; its rows in the hover and focus tables are its own.

  • Press — transform: scale(0.97), 120ms ease-out. No darker fill. Exception: inline text that wraps. A scale press needs a transformable box, and giving one to a wrapping link changes how its text breaks — a segment wider than its column stops fragmenting and swells to the full column. The fragmenting population is exactly .surface-text-link and breadcrumb links (.surface-title a); they press without geometry, hover raising the underline to full opacity and holding element opacity at 1, so on them the 0.92 drop reads as press rather than as hover. The identity mark is a box and keeps the scale; so do footer destinations, through surface-action.css rather than through a rule of the shell's. patterns/surface-shell/README.md carries the state model. Reduced motion. Under prefers-reduced-motion: reduce, compact actions and full-panel links drop their transitions and their press scale; their hover and focus paint still applies, instantly. The shell's own reduced-motion rules stay the shell's.

  • Focus — never the browser default. Across the live-surface modules and the shell there are four anatomies by role, which are not interchangeable; artifact patterns such as message-archive own pattern-local indicators:

    role focus indicator
    compact actions (.surface-action), full-panel links (a.surface-panel) and objects that adopt the edge (.surface-attention-edge) the same attention edge as hover — magenta border, apparent 1.5px light / 1px dark, composed with the module's resting shadow (--shadow-md on a full-panel link; a compact action has none; an adopting object with a shadow of its own restates it). An adopting object takes the edge on its own anatomy, as in the hover table. No glow. No non-text contrast claim is made for this edge
    the identity mark (.surface-mark — the anchor, or the runtime <button> trigger) and the shell's navigation rows (a.surface-nav-row) the 0 0 0 1px white, 0 0 0 4px rgba(white, 0.25) glow
    unboxed textual links (.surface-text-link) and breadcrumb links (.surface-title a) a fragment-native indicator — their own decoration recolored to --fg-1 at 2px thickness and 2px offset, so one underline renders in every state
    disclosure triggers (details.surface-disclosure > summary, .surface-disclosure-trigger) a --fg-1 outline around the trigger, 2px wide and --space-1 out, with the trigger at --fg-1 and its indicator turned --ask-emphasis-magenta; the outline shows in every case: open or closed, and when the trigger leads with a mark; no edge, no glow

    Why a shaped object shares hover's edge. Hover and focus are independent states — focus persists when the pointer leaves, hover goes with it — but nothing in this contract requires a shaped control's focus to look different from its hover — unlike the textual links below, where focus is a separate safety channel — and giving a shaped control two competing indicators means deciding which one wins every time both are live. Sharing the paint removes that question without merging the states: the 0.92 opacity drop still belongs to hover alone, and the edge composes with resting elevation rather than replacing it, so a focused panel does not lose its shadow. The composition is with the module's own resting shadow: a consumer that sets a different one restates it in its own hover and focus rules.

    Why wrapping text cannot take either. Anything drawn around the box fails on fragmented inline text — sliced it opens at the cuts, cloned it becomes one ring per line rather than a single typographic indicator, and a rectangular outline is one shape only where the fragments happen to overlap horizontally. The identity mark has no border to recolor, so it keeps the glow. Footer destinations are compact actions and take the shaped-control edge through surface-action.css's shared hover and focus rule; so does the navigation panel's close control, whose edge the shell suppresses after a pointer-opened entry exactly as it suppresses a row's glow. patterns/surface-shell/README.md records the measurements.

Motion

  • Easing — cubic-bezier(0.22, 1, 0.36, 1) for entries; cubic-bezier(0.65, 0, 0.35, 1) for cross-fades.
  • Durations — 120 / 220 / 420 / 720ms. 220 covers almost everything.
  • Default transition is opacity and transform. Avoid layout transitions and bouncy easings.

Aesthetic anchors

Linear.app, Stripe, Apple. Structural elegance, mathematical clarity, refined light typography. Avoid: playful or bloated visuals, thick typefaces, emotional/empathic design tropes.

Catalog dispositions

Each rule file, key, page and pattern in the catalog carries one disposition against the type roles, document compositions and surface treatments above. The attachment axis spans two rule files and has its own row. A disposition describes the item as it is.

  • conforming — it follows the shared rules that apply to it.
  • extended — it carries a shared value added for those rules.
  • new — it is a source of those rules, or their rendered key.
  • specialized — it keeps pattern-local rules that differ from the shared ones, and its row names them. They are not sanctioned variants for any other surface.
  • migration pending — A required migration remains open; this is not a final specialization or conformance disposition.
  • generated — a tool writes it from canonical sources, and it is never edited by hand.
Item Disposition Detail
colors_and_type.css extended --tracking-caption (0.14em) carries the Caption role's tracking, and .caption uses it
surface-panel.css extended the one authored source of the glass recipe: the page material, panel material, free attachment and raised elevation classes share its declarations by selector grouping; the linked panel's hover and focus rules and the composed emphasis shadow name its raised-elevation token again
surface-action.css · surface-text-link.css conforming the compact-action and textual-link roles, and the attention edge adopted by class, as described above
surface-document.css · surface-document-overflow.js · surface-treatments.css · surface-document.html new the document register, its optional overflow helper, the surface treatments and their rendered key. The key renders every attachment value, labeled pairs (.doc-labeled), long headings from the section title to the eighth level that wrap within their column, grouped structured text at two, five and ten peer groups and under a single label, an ungrouped block keeping a recorded blank line, the section framing beside the synthesis, a repeated collection whose grid, column count, order and words it marks as consumer-owned and whose items of unequal copy end their action rows on one bottom edge, records beside an inspector with a page-local adapter, a source address standing as a field value beside a URL in prose, and narrative, dense and header-bearing tables in the document table composition beside a dense table in its consumer's own geometry
the attachment axis new five values, corner treatment only: .surface-attach-free in surface-panel.css; .surface-attach-top, .surface-attach-bottom, .surface-attach-full-width and .surface-attach-chip in surface-treatments.css. A surface chooses its attachment independently of its material and its elevation
spectral-state.* · evidence-state.* · three-functions.* conforming the Spectral State, Evidence State and Three Functions primitives. Their rendered keys set the page title and the section titles through .doc-title and .doc-section-title, and their page-local labels take the role tracking tokens
patterns/surface-shell/ specialized its navigation panel keeps its own material for navigation composed over another surface: the --surface-glass-2 fill with a 14px blur. It realizes the attachment corner rule pattern-locally — the desktop drawer rounds its bottom corners, and the mobile sheet its top corners. It also drops the border on its joined edge. It does not consume the attachment classes
patterns/_diagram-shared/ conforming the one canonical text-layout source for the H, V and SEQ static diagram engines, and the pointer controller of the radial and spine patterns, which carries no type. tools/sync-diagram-shared.mjs generates its mirrors, and --check verifies them. The caps and line heights it owns serve those diagrams' own register; fonts and letter-spacing stay with each engine's stylesheet
patterns/diagram-static-H/ · patterns/diagram-static-V/ · patterns/diagram-static-SEQ/ · patterns/diagram-static-FLOW/ specialized a compact register of their own in diagrams.css: 9–15px text, tracking from -0.01em to 0.18em, and a 10px caption overlay at 0.06em that is not the Caption role. The four diagrams.css copies are byte-identical and shared by convention; no tool keeps them in step
patterns/diagram-interactive-spine/ specialized a compact register for its top bar, nodes, inspector, legend and controls: 8.5–15px text, and glass panels with a 14px radius and a --surface-glass-2 edge. Its caption band keeps --tracking-wide (0.08em) rather than Caption's tracking, and its mono uppercase labels (node groups, inspector and legend headings, field labels) keep 0.14–0.16em
patterns/diagram-interactive-radial/ specialized a compact register for its bar, its screen-space names, HUD, legend, caption, chooser, inspector and facets drawer: 8–23px text (the root's name 23px, depth-1 names 16px, deeper names 13px, leaf names 11px at 200, count lines 9px and identifiers 8px in mono at 0.1em), and glass panels with the 14px radius and a --surface-glass-2 edge. Its caption band keeps --tracking-wide (0.08em), and its mono uppercase labels (the legend and chooser headings at 0.16em, the tier readout at 0.1em, the chooser's zoom action at 0.08em) are pattern-local. It stands outside tools/check-type-roles.mjs by declared disposition, as the other diagram patterns do
patterns/message-archive/ specialized its participant-identity ramp, its focus outlines and its chrome metrics are pattern-local. Its sticky control bar blurs at 20px, the shared material value; its day-heading chips keep 8px
patterns/output-artifact/ conforming the Class B document scaffold on the document register. Its title, lede, section titles, body and list text, labels, metadata, code, quotation, preformatted block, dense and narrative tables and disclosure take the register's roles and treatments, loaded as the four modules in the sealed-use order below; its tables adopt the document table composition; and the rendered check passes on its generated preview. Its own CSS sets no type metric, no text foreground and no screen geometry on an adopted table. One named print adaptation of its own relaxes, in print only, the composition rules that rely on scrolling, so that an adopted table can fit the printed column; it writes every other geometry rule on an element the register may also style at zero specificity. It draws only the geometry the register leaves to a consumer — the column, the metadata strip's and provenance footer's layout, list geometry, the geometry of a table that does not adopt the composition, and rules — in the Class B line-intensity token --artifact-line, stronger than the foundation hairline in light. That token never reaches a table the composition draws, a text foreground, a passage rail or the disclosure's panel; it lies outside the type roles, compositions and treatments a disposition measures, and it is no variant for another surface. tools/check-type-roles.mjs governs its template's typography declarations
patterns/_preview/ generated tools/gen-pattern-previews.mjs writes it from the canonical templates, and --check verifies it
index.html · patterns/index.html · preview/styleguide.html conforming tools/check-type-roles.mjs governs all three. The tracking account above records the gallery's and the style guide's page-local Inter labels, and the style guide's registered .badge exception

Adopting the document register

A surface adopts the register by replacing raw sizes with roles. A raw value maps by what the text is, never by the nearest size. These rules are generic; a surface's own selector map stays with that surface.

Raw pattern Role
a 10–12px mono uppercase label .doc-label
the same label used as an accent status flag .surface-emphasis-chip — the words in the primary text role, the accent on its border
10–12px mono metadata, not uppercase .doc-meta
inline code sized in em .doc-code, inside a sized role
a raw 36px or 48px title .doc-title or .doc-section-title, chosen by its level in the document, not by its old size
a code or diagram block at a raw size .doc-pre — it draws the passage rail
a grouped diagram whose peer groups are told apart by typed blank lines one .doc-pre-group per peer group in a .doc-pre--structured block, declared by the surface from its own source
a quotation set larger or smaller than its prose .doc-quote — body-sized; .doc-quote--display is retired
a set-apart passage on a faint neutral rail its role's accent: .doc-quote violet for represented voice; .surface-emphasis-rail magenta for the document's own callout
a section synthesis on a rail, or in supporting-copy size inside a card the flat synthesis panel with a magenta-bordered chip and .doc-body text
a part's opening thesis or question, set as a callout or as a synthesis the section framing: the same flat panel and chip, after the part's introduction, its chip naming the framing
a bare anchor in a table of contents .doc-toc-link.surface-text-link inside .doc-toc-list
a result's or record index entry's primary text set as body prose or as a panel label .doc-entry-title — its identifier and supporting fields take .doc-meta; the entry's element, interaction and row geometry stay the consumer's
a detail panel beside a record list, with smaller type of its own main + inspector: the same roles in both regions, the inspector's prose on .doc-body, and the inspector a separate flat surface on the three axes; the regions, selection and the route back stay the consumer's
a source address, DOI or path set as a field value in a hand-made mono style code.doc-code standing alone as the value of a .doc-labeled field — the document locator; a followed address is a .surface-text-link inside it, an identifier stays .doc-meta, and a URL in running prose stays a body link
action rows lined up across a grid's items by fixed heights, spacers or per-item offsets .doc-actions--end on each item's .doc-actions, in items that stretch to their grid row; the grid and the items' heights stay the consumer's
a dense technical table set as body prose table.doc-dense-table, with .doc-table-cell for values and .doc-label for headers; a narrative table is not dense and stays unmarked
a prose table at raw sizes a narrative table: .doc-label headers and .doc-body cells, unmarked
a table's hand-set rules, padding and sideways scroll the document table composition, adopted by class: table.doc-table directly inside a .doc-table-scroll box, .doc-table-num on a numeric column; a surface that must not scroll a table sideways keeps its own geometry
a disclosure summary used as a label details.surface-disclosure > summary
control sizes inside an interactive application, and glyph sizes set in em not text roles

Structured text. A preformatted passage whose indentation is structure is one scroll box, .doc-pre.doc-pre--structured. Its lines sit in .doc-pre-part elements. The lines beneath a line sit in a .doc-hierarchy, one rail per level of indentation, nested in source order. A level is assigned by what the lines mean to one another; spaces that only align columns are not levels. A block takes .doc-pre on purpose, never through a blanket pre or code selector. Peer groups. A grouped diagram — peer groups, each a label with the lines beneath it — declares each group as a .doc-pre-group: a direct child of the block, opening on its label part, in a block that holds nothing else at its top level. Successive groups sit exactly one line apart, whatever their number; inside a group nothing is set apart, so the label sits on its lines. The consuming surface declares the groups; how it finds them in its own source — a template, a table, a separator its own format defines — is its adapter, never a shared rule. No rule and no check here reads a group from blank lines, capitals, font or line count, an adapter never finds one merely from capitals, font or line count, and verbatim code or a quoted payload keeps its own blank lines. Outside declared groups, data-lead-lines on a part or a rail records one to three blank source lines before it. On a group's first line it adds nothing — the one line between groups stands for it, and before the first group it is dropped; anywhere else inside a group it splits the group, and the rendered check reports it (C11). The whole block sits behind one magenta block rail; its hierarchy rails stay neutral.

Document table. A table takes its geometry from the register only when the consumer adopts the composition by class: table.doc-table as the direct child of a .doc-table-scroll box, with .doc-table-num on a numeric column's header and cells. Both markers are needed: a table marked without its box takes none of it, and a table nested inside a composition cell takes none of its cell rules. The composition draws the fainter rule under every row and the hairline under the header row, and sets the cells' spacing. It aligns a numeric column to the end on tabular figures, keeps a narrative table at a 32rem measure and a dense value on one line, and lets code break only at its own opportunities. A table wider than its column scrolls inside its own box, with its header row in place, and the page never scrolls sideways. Nothing implies the composition: not table.doc-dense-table, not a text role on a cell, not the table element. The consumer names each scroll box as a region. The composition is not a compact layout and has no stacked narrow-width layout; a surface that needs either keeps its own geometry and does not adopt it. It also carries no print rules and does not address multi-level, spanning or nested headers or interactive header controls; a shared treatment of any of these is deferred to the document register. A surface that prints an adopted table adapts it with print rules of its own, as the output-artifact template does, and a surface that needs a complex or interactive header keeps its own geometry.

Main + inspector. Records in a main region beside an inspector for the one selected are a composition of delivered parts, with no selector of their own. Both regions keep one set of text roles, so a role keeps its treatment wherever it occurs and the inspector's prose stays .doc-body at the Body step however narrow its region. A record's name is an entry title in the main region and the view's heading in the inspector, because there it governs the fields beneath it. Each view composes .doc-titled, .doc-group and .doc-labeled. The inspector is a separate surface chosen on the three axes — .surface-separate, .surface-material-panel, .surface-attach-free and .surface-elevation-flush, the flat panel of the section synthesis. The regions' grid, proportions and breakpoint, the inspector's sticky place and independent scroll, the regions' order at a narrow width, selection, the current-item indication, focus handling and the route back all belong to the consuming surface. surface-action.css's magenta is attention only and never marks the current item. The register's compositions and .surface-action set display, so a consumer that hides a view or control with the hidden attribute supplies its own guard. The key demonstrates the composition with a page-local adapter (surface-document.html #records-inspector); nothing in it is a shared inspector controller.

Document locator. A source address, DOI or path that stands as a field value is inline code on its own: code.doc-code as the value of a .doc-labeled field, inside no other text role. It is an assignment, not a role, and has no class, metric or foreground of its own. Standing alone, .doc-code is mono at the Caption step, 300, in --fg-2, and breaks anywhere, so a long address wraps inside its field. It declares no leading, tracking or case: a locator takes them from where it stands, so it keeps its literal form only where its container sets none. Inside a label it would take the label's tracking and a smaller size, 0.9em of the label's (the register keeps code's source case there); inside a caption or a disclosure trigger, their capitals and tracking; inside a panel title, its tracking. So a locator never sits in any of them, and apart from that one case rule the role enforces none of this in any ancestry. A followed address is a .surface-text-link inside the code element. An address inside running prose stays a body link, an identifier that names a record stays .doc-meta, and which tokens link is the consumer's rule. An address in a page header's chrome is outside this assignment.

Action row at an item's end. In a repeated collection, .doc-actions--end on an item's .doc-actions places the row at the item's end. It works where the item is a flex column that stretches to its grid row's height: a .surface-panel item in a grid that keeps the default align-items. The auto start margin takes whatever block-axis space the item leaves. In grid rows sized to their content, that is only the space a taller neighbor leaves, so the tallest item's row still follows its copy by the item's gap; where the consumer sizes rows otherwise, every item's row may move. The guarantee is precise: the action rows of one grid row share their bottom edge, wherever each item is at least as tall as its content. When neighboring rows wrap to different numbers of lines, their tops do not match; the wrapped row's top sits higher by its extra lines. The grid, its rows and their heights stay the consumer's, and nothing is fixed, filled or offset. A row without the modifier keeps its place after its copy. Where the row has no free block-axis space, in a block container or a column no taller than its content, the modifier moves nothing; in any flex or grid context that leaves it space, it takes that space. It is adopted by class only, never through a bare element or .doc-actions itself. It sets only the row's place; the controls stay surface-action.css's. tests/doc-actions-end-fixture.html guards it.

Overflow helper. surface-document-overflow.js is optional. Without it a wide block, or a document table's box, still scrolls and shows no cue. With it, the helper marks which sides of a wide block or box hold hidden content, and the stylesheet draws the cue.

Sealed use. An artifact scaffold may inline the register at generation time. patterns/output-artifact/ is the owner's scaffold for this use: its template links the modules, and the consuming renderer inlines them at seal time. After the foundation it already carries, it inlines surface-text-link.css, which a table of contents or any governed link requires, and then surface-document.css. An artifact that also uses the surface treatments inlines surface-panel.css, surface-text-link.css, surface-document.css and surface-treatments.css, in that order — the order in which surface-document.html loads them. Each is inlined verbatim: none carries url() or @import. The sealed copy does not re-sync, and the helper stays optional.

Accent. An emphasis accent comes from the consuming surface's information architecture: the role its content plays there selects .surface-emphasis--magenta, .surface-emphasis--violet or .surface-emphasis--cyan. The treatment accepts that accent and never chooses one. In a document, an emphasis rail on document text is the authorial callout and takes magenta (Rails, above).


Iconography

The default is no icons. The wordmark, the type, and the gradient carry the identity.

When a UI absolutely needs an icon for affordance, treat it as an exception:

  • Stroke-only, 1.25 weight, round joins, round caps.
  • currentColor only — never colored, never two-tone, never filled.
  • Sizes 16 / 20 / 24 / 32 / 40. Scale up to make it louder; never add weight.
  • No unicode symbols as glyphs (no ✓, ★, →). Use SVG.
  • No third-party icon library by default. If a handful are genuinely needed, draw them and drop in assets/icons/.

Theme by embedding surface

Every diagram package generates and retains both theme variants (-light and -dark). The embedding surface selects the default:

  • Repository documentation and operator-system diagrams default to dark.
  • Substack and other published long-form editorial diagrams default to light.
  • A stated local exception may override the default for a specific figure.

This rule selects which existing render is embedded. It does not suppress, rename, or replace the alternate-theme export.


Consumers

Public downstream repos consume these patterns and tokens by reference. The known public consumers are tracked in CONSUMERS.md.

Consuming a pattern does not fork it. The two material kinds do not share a lifecycle:

  • Artifact-inheritance scaffolds. A downstream consumer vendors the applicable canonical artifacts at a pinned commit, supplies its own Tier 3 identity and content, and generates, seals, stamps where the pattern requires, and freezes its output for audit. design-system-ASK owns the engine, CSS, and export script; that consumer owns its source data, chrome, generation, sealing, and the frozen output artifact.
  • Surface patterns. These frame or run on live deployed surfaces. A consumer takes one by same-repo reference or as a pinned local vendored copy, and nothing seals — Tier 3, payload, deployment, and local adaptations stay with the consumer, and it re-syncs when the owner contract changes. design-system-ASK owns the shared pattern contract and its canonical files.

Neither route uses a CDN or a runtime hot-link. Ordinary downstream artifact consumers take the foundations through a local pinned _dsa-tokens/ mirror; the ASK front door is the exception recorded in CONSUMERS.md — it vendors the foundations (tokens, fonts, wordmark) directly rather than via a _dsa-tokens/ mirror, and carries ASK's own Tier 3.


Caveats / known gaps

  • The .ai vector working source lives operator-side, not in this repo (production assets only). The repo carries the primary vector at assets/logo-ASK.svg (fill: currentColor) and two PNG pairings.
  • Foundations are present. The Class A diagram scaffolds (horizontal patterns/diagram-static-H/, vertical patterns/diagram-static-V/, sequence patterns/diagram-static-SEQ/, convergence-flow patterns/diagram-static-FLOW/, and interactive patterns/diagram-interactive-spine/) and the Class B project-output scaffold (patterns/output-artifact/) are implemented. Both surface patterns are implemented: patterns/surface-shell/, which this repo's own three public surfaces consume, and patterns/diagram-interactive-radial/. Landed public consumers, downstream and first-party alike, are recorded in CONSUMERS.md. No public production Class B output-artifact surface has used this scaffold end-to-end yet. A private operator-internal consumer has exercised the Class B output-artifact flow end-to-end under its v2 contract; contents remain firewalled. urban-observatory has exercised and sealed the v3 document-register path end-to-end through its Class B renderer; that sealed review artifact remains operator-side rather than a public production surface.

License

Copyright 2026 Andrew S Klug // ASK

Licensed under the Apache License 2.0 // see LICENSE

About

Reference implementation of the ASK design family // tier-demarcated tokens, self-hosted OFL fonts, canonical logo-ASK assets, preview cards

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages