Canonical · Web primary

Foundations

A semantic contract derived from the production Quiet Sky layer—not a copy of the frontend’s mixed global stylesheet.

CanonicalVerified 2026-09-03Source: web/src/app/globals.css

Color

Semantics before pigments

Dark is the default. Light is an explicit data-theme="light" override. Components consume semantic names; primitive colors never appear inside component rules.

Canonical

Every swatch below is rendered from the token, so nothing on this page can drift from tokens.css. Each label carries the runtime name and the Tailwind utility. Toggle the theme in the header to see both resolutions.

Surfaces

Page canvas--bg · bg-canvas
Raised--surface-1 · bg-surface-1
Recessed--surface-2 · bg-surface-2
Inset panel--surface-3 · bg-surface-3

Alpha surfaces

Hover and tracks--fill · bg-wash
Progress track--track · bg-track
Blurred bar--glass · bg-glass
Agent bubble--bubble-agent

These four are alpha values and read as invisible on an opaque card, so they sit on a checkerboard. --fill is a hover and press wash. --track is the unfilled part of a meter and is deliberately a step stronger than --fill; all three current prototypes make that distinction.

Content

Primary content sits on --text-1, the Tailwind utility text-content-1.

Secondary content sits on --text-2, the utility text-content-2.

Tertiary content sits on --text-3, the utility text-content-3.

Content tokens are shown as type rather than as blocks, because legibility at real size is the only thing that matters about them. Tailwind reserves the --text-* namespace for font sizes, which is why the colours surface as content-1/2/3.

Line

Default hairline--qs-border-soft · border-line
Emphasis, controls--qs-border-strong · border-line-strong

Brand

Accent fill--qs-accent · bg-brand
Accent wash--accent-tint
Accent hairline--accent-border
Accent text
Accent on surface--accent-text

Focus ring

The ring is drawn statically here so it is visible in a screenshot. In use it appears on :focus-visible only, at 2px with a 3px offset, from --focus-ring.

Collision-safe Web contract. Use bg-brand, text-brand, border-line, and border-line-strong. The generic bg-accent and border-border names are currently owned or overwritten by legacy/shadcn layers.

Danger

Fill--danger
Danger text
On surface--danger-text
Wash--danger-tint
Hairline--danger-border

Success

Fill--ok
Success text
On surface--ok-text
Wash--ok-tint
Hairline--ok-border

Both state families follow the same four-token shape: a fill, an on-surface text value with enough contrast to read at 12px, a wash, and a hairline. --ok and --ok-text differ, because the mint that reads as a filled dot is not legible as text.

Chart ramp

chart-1chart-2chart-3chart-4chart-5

One sequence, not five independent colours. The anchor flips between themes so that --chart-5 always reads as “most”: the ramp runs dark to light on night and light to dark on the light canvas.

Buttons and bubbles

Primary button

--btn-primary-bg and --btn-primary-fg. The primary fill is ink, not brand: cyan is reserved for state, not for the loudest button on the page.

Message bubbles

Rescheduled to Thursday and told the two guests who had declined.
Thanks, go ahead.

--bubble-agent, --bubble-user-bg, and --bubble-user-fg. The flat corner points at the speaker's own side.

Elevation

Soft
--elev-softshadow-soft
Cards and rows at rest
Card
--elev-cardshadow-card
Popovers, modals, toasts

Two structurally identical panels. The well behind them is the page canvas, because on a recessed surface neither shadow is visible at all. There are exactly two elevations, and depth is never used to signal state.

Typography

Onest, seven functional rungs

Sentence case is the default. Weight creates hierarchy before color. Quiet Sky Web surfaces must use collision-safe names for body, caption, and micro.

Canonical

Title 1

28 / 34 · page titles

text-title-1

Title 2

19 / 25 · card and modal titles

text-title-2

Title 3

15 / 21 · row titles

text-title-3

Body

14 / 21 · product default

text-qs-body

Small

13 / 19 · navigation and secondary rows

text-small

Caption

12 / 17 · timestamps and metadata

text-qs-caption

Micro

11 / 15 · chips and compact labels

text-qs-micro

Every rung above is rendered from its token, so a size printed here cannot disagree with tokens.css. Sizes are the only thing the scale fixes; weight and colour stay free.

Why three rungs carry a qs- prefix

A legacy AIC @theme block later in globals.css redefines three of the seven Tailwind size utilities. The collision-safe names exist so a Quiet Sky surface can opt out. Each pair below renders the intended value beside what the generic utility actually produces in the shipping build.

Intended · text-qs-body

Thursday review moved

Two guests declined, so the agent proposed Thursday and is waiting on a reply.

Row title 15 / 21, body 14 / 21. The title reads as a title.

Collided · text-body

Thursday review moved

Two guests declined, so the agent proposed Thursday and is waiting on a reply.

Body renders 15 / 20, identical in size to the row title above it. Hierarchy collapses to weight alone.

Body

text-body renders 15 / 20 instead of 14 / 21, matching text-title-3. Use text-qs-body.

globals.css:269-300

Caption

text-caption is repointed to 11px instead of 12 / 17, colliding with micro. Use text-qs-caption.

globals.css:269-300

Micro

text-micro renders 10 / 14 instead of 11 / 15. Use text-qs-micro.

globals.css:269-300

Shape & space

Nested geometry stays legible

Use the radius scale by role, not by preference. A Quiet Sky card is 16px; the 28px card style still found in legacy product code is a deviation.

Canonical

Radius

tight 8 · control 12 · inner 14 · card 16 · frame 20 · pill 99. The runtime names are --radius-tight … --radius-pill, matching production.

rounded-tight … rounded-pill · globals.css:214-219

Spacing

A 4px rhythm. The rhythm is the rule; the six variables are a convenience for this reference package only.

--qs-space-1 … --qs-space-6

Regular density

44px rows · 24px section gap · 16px card padding · 11/18px cells

:root

Compact density

36px rows · 16px section gap · 12px card padding · 7/14px cells

[data-density="compact"]

Radius ramp

tight 8rounded-tight
--radius-tight
control 12rounded-ctrl
--radius-ctrl
inner 14rounded-inner
--radius-inner
card 16rounded-card
--radius-card
frame 20rounded-frame
--radius-frame
pill 99rounded-pill
--radius-pill

Pick by role, not by preference. An inner element takes the rung below its container, which is why a 14px group nests inside a 16px card without the corners fighting. The 28px card treatment still present in legacy product code is a deviation, not a seventh rung.

Spacing rhythm

4px · tightest gap, icon to label
8px · related controls in a group
12px · items in a list
16px · card padding, sibling cards
20px · generous card padding
24px · between sections
Portable convenience, not a production token

The grid behind the bars is 4px. Production ships no canonical Quiet Sky spacing scale: it has a legacy AIC family at 4/8/16/24/32/48 (globals.css:1069-1074) and a second at 2/4/8/12/16/20 (globals.css:900-905). The qs- prefix on these six keeps them clear of both. For rows, cards, and sections use the density tokens below instead, which is where the real contract lives.

Density

Regular · :root

Today
Design review10:00
Standup notes11:30
Vendor call15:00

Compact · [data-density="compact"]

Today
Design review10:00
Standup notes11:30
Vendor call15:00

--row-h

44px regular, 36px compact. The minimum height of a list row.

globals.css:57-68

--cell-y / --cell-x

11/18px regular, 7/14px compact. Row and cell padding.

globals.css:57-68

--pad-card

16px regular, 12px compact.

globals.css:57-68

--gap-section

24px regular, 16px compact.

globals.css:57-68

Density changes space only. Type sizes, radii, stroke weights, and colours are identical in both panels above, and the two panels use the same markup. That constraint is what keeps compact mode from becoming a second design.

Motion & layers

Quiet, brief, and interruptible

The tokens exist in production; broad enforcement remains incomplete and is tracked in conformance.

Canonical target

Timing

Fast 200ms · base 250ms · slow 300ms · reveal 500ms. Use the brand ease for spatial transitions.

cubic-bezier(.22,1,.36,1)

Reduced motion

Respect prefers-reduced-motion; remove nonessential movement and preserve state changes.

Required

Layer scale

base 0 · raised 100 · rail 200 · popover 300 · drawer 400 · scrim 500 · modal 600 · toast 700 · tooltip 800

Partially adopted

The brand ease at each duration

200ms · --t-fast · hover, press, colour

250ms · --t-base · most spatial transitions

300ms · --t-slow · panels and drawers

500ms · --t-reveal · first appearance only

All four run cubic-bezier(.22, 1, .36, 1): fast departure, long settle, no overshoot. Nothing in Quiet Sky bounces. The track behind each dot is --track.

Motion is suppressed because this browser reports prefers-reduced-motion: reduce. The four durations are 200ms, 250ms, 300ms, and 500ms, all on cubic-bezier(.22, 1, .36, 1). State changes still happen; only the movement is removed.

Layer scale

A closed scale with 100 between rungs, so a new surface never needs an arbitrary number. A toast outranks a modal deliberately: a confirmation must remain readable above the thing that triggered it. Adoption in production is partial and tracked on the conformance page.

Implementation files

Use the portable contract

What the bridge maps

Reproduced here so the mapping is readable without opening the file. Nothing below is exercised by this page. Note the renames: Tailwind reserves --text-* for font sizes, so the three content colours become content-1/2/3, and the three names legacy layers overwrite get collision-safe utilities.

/* colour, abridged */
--color-canvas:        var(--bg);          /* bg-canvas */
--color-surface-1/2/3: var(--surface-1/2/3);
--color-content-1/2/3: var(--text-1/2/3);  /* NOT text-1: --text-* is a size namespace */
--color-wash:          var(--fill);        /* bg-wash */
--color-track:         var(--track);       /* bg-track */
--color-brand:         var(--qs-accent);   /* bg-brand, not bg-accent: shadcn owns --accent */
--color-line:          var(--qs-border-soft);    /* border-line, not border-border */
--color-line-strong:   var(--qs-border-strong);
--color-danger/-text/-tint/-border, --color-ok/-text/-tint/-border
--color-chart-1 … --color-chart-5

/* radius, literal values */
--radius-tight: 8px;  --radius-ctrl: 12px;  --radius-inner: 14px;
--radius-card: 16px;  --radius-frame: 20px; --radius-pill: 99px;

/* the three collision-safe type rungs */
--text-qs-body: 14px;    --text-qs-body--line-height: 21px;
--text-qs-caption: 12px; --text-qs-caption--line-height: 17px;
--text-qs-micro: 11px;   --text-qs-micro--line-height: 15px;

@custom-variant light ([data-theme="light"] &);