Colors

10 families × 8 shades. Families inherit base chroma; semantics override for consistent vibrance. Hover tokens are derived at runtime via oklch(from var(--X-medium) …).

primary

ultra-light--primary-ultra-light
light--primary-light
semi-light--primary-semi-light
medium--primary-medium
semi-dark--primary-semi-dark
dark--primary-dark
ultra-dark--primary-ultra-dark
hover--primary-hover

secondary

ultra-light--secondary-ultra-light
light--secondary-light
semi-light--secondary-semi-light
medium--secondary-medium
semi-dark--secondary-semi-dark
dark--secondary-dark
ultra-dark--secondary-ultra-dark
hover--secondary-hover

tertiary

ultra-light--tertiary-ultra-light
light--tertiary-light
semi-light--tertiary-semi-light
medium--tertiary-medium
semi-dark--tertiary-semi-dark
dark--tertiary-dark
ultra-dark--tertiary-ultra-dark
hover--tertiary-hover

accent

ultra-light--accent-ultra-light
light--accent-light
semi-light--accent-semi-light
medium--accent-medium
semi-dark--accent-semi-dark
dark--accent-dark
ultra-dark--accent-ultra-dark
hover--accent-hover

base

ultra-light--base-ultra-light
light--base-light
semi-light--base-semi-light
medium--base-medium
semi-dark--base-semi-dark
dark--base-dark
ultra-dark--base-ultra-dark
hover--base-hover

neutral

ultra-light--neutral-ultra-light
light--neutral-light
semi-light--neutral-semi-light
medium--neutral-medium
semi-dark--neutral-semi-dark
dark--neutral-dark
ultra-dark--neutral-ultra-dark
hover--neutral-hover

success

ultra-light--success-ultra-light
light--success-light
semi-light--success-semi-light
medium--success-medium
semi-dark--success-semi-dark
dark--success-dark
ultra-dark--success-ultra-dark
hover--success-hover

warning

ultra-light--warning-ultra-light
light--warning-light
semi-light--warning-semi-light
medium--warning-medium
semi-dark--warning-semi-dark
dark--warning-dark
ultra-dark--warning-ultra-dark
hover--warning-hover

info

ultra-light--info-ultra-light
light--info-light
semi-light--info-semi-light
medium--info-medium
semi-dark--info-semi-dark
dark--info-dark
ultra-dark--info-ultra-dark
hover--info-hover

danger

ultra-light--danger-ultra-light
light--danger-light
semi-light--danger-semi-light
medium--danger-medium
semi-dark--danger-semi-dark
dark--danger-dark
ultra-dark--danger-ultra-dark
hover--danger-hover

Contrast tokens

--on-{family} is the text color for anything sitting on --{family}-medium(and its hover shade). Picked at build time from the family's medium lightness; a non-flipping primitive, so it stays correct in dark mode where a neutral would invert.

Text on primary--primary-medium--on-primary
Text on secondary--secondary-medium--on-secondary
Text on tertiary--tertiary-medium--on-tertiary
Text on accent--accent-medium--on-accent
Text on base--base-medium--on-base
Text on neutral--neutral-medium--on-neutral
Text on success--success-medium--on-success
Text on warning--warning-medium--on-warning
Text on info--info-medium--on-info
Text on danger--danger-medium--on-danger

Shadows

Five elevation steps. Ink alpha is multiplied by --shadow-strength at runtime (1 in light mode, stronger in dark) so the same token reads on both surfaces.

--shadow-xs
--shadow-sm
--shadow-md
--shadow-lg
--shadow-xl

Spacing

Fluid between mobile and desktop via clamp(). Resize the window to see tokens recompute.

--space-3xs
--space-2xs
--space-xs
--space-sm
--space-md
--space-lg
--space-xl
--space-2xl
--space-3xl

Section spacing

Larger vertical rhythm for hero areas and major layout blocks. Desktop ×3, mobile ×2 of the standard spacing scale.

--section-space-3xs
--section-space-2xs
--section-space-xs
--section-space-sm
--section-space-md
--section-space-lg
--section-space-xl
--section-space-2xl
--section-space-3xl

Typography

7 fluid type steps. Base 18px desktop / 16px mobile; scale ratio 1.25 / 1.2. Two display steps sit above the heading ramp: heroes use --display-*, content headings use --h1…--h6 (ramp below).

--display-1The quick brown fox
--display-2The quick brown fox
--text-2xsThe quick brown fox
--text-xsThe quick brown fox
--text-smThe quick brown fox
--text-mdThe quick brown fox
--text-lgThe quick brown fox
--text-xlThe quick brown fox
--text-2xlThe quick brown fox

Heading ramp: --h1…--h6

Real h1–h6 elements with nothing but the base.scss defaults: size from --h*, tight leading on h1/h2 and snug below, tracking on h1/h2. Heroes use --display-* instead, so this ramp stays sized for content.

--h1

The quick brown fox

--h2

The quick brown fox

--h3

The quick brown fox

--h4

The quick brown fox

--h5
The quick brown fox
--h6
The quick brown fox

Leading & tracking

Line-height only via --leading-*, letter-spacing only via --tracking-* — component CSS never uses raw numbers. Samples render the live token.

TokenUseSample
--leading-noneSingle-line controls: buttons, badges, icon boxes, toggles.Two lines of text
set at this leading
--leading-tightDisplay sizes and headings.Two lines of text
set at this leading
--leading-snugSub-heads, compact UI titles, multi-line controls.Two lines of text
set at this leading
--leading-normalBody copy, ledes, card text.Two lines of text
set at this leading
--leading-relaxedLong-form prose.Two lines of text
set at this leading
--tracking-tightDisplay sizes, h1, big stat numbers.Tracked sample
--tracking-snugh2, brand wordmarks, bold UI titles.Tracked sample
--tracking-wideUppercase eyebrows and labels (the eyebrow mixin).Tracked sample

Stacks: .flow / .prose

.flow puts rhythm between stacked siblings (> * + * gets amargin-block-start of --flow-space, with more space above a heading than below it). .prose is .flow plus the readable measure (--measure). Containers trim their last child's margin rather than relying on padding + margin adding up.

A first paragraph inside a .prose block. Its width is capped at the measure, so the line never runs long enough to lose the reader on the way back.

A heading in the flow

The heading gets more space above it than below, which is what groups it with the paragraph that follows rather than the one before.

A third paragraph closes the block. The container trims its bottom margin, so the frame's own padding is the only thing between this line and the edge.

Border radius

--radius-sm
--radius-md
--radius-lg
--radius-xl
--radius-2xl
--radius-full

Section shell

Section shell

The page building block: a full-bleed outer that owns the gutter and vertical rhythm, an inner wrapper that owns the max-width, and an optional head. This one is tone raised, width wide, default rhythm.

Pages compose Sections inside the bare <main>; the first one clears the fixed header by itself and the closing CTA is rendered by the layout. The width prop mirrors the framework's width ladder:

widthTokenUse
site--site-widthDefault. The header pill's column, so sections line up with it.
wide--content-widthOne step in from the pill; grids and media that shouldn't touch the site edge.
content--content-width-mdReading / marketing width for text-led sections.
narrow--content-width-smHero copy, CTAs.
prose--content-width-xsLong-form text.

tone="brand" · width="content" · aside slot

A head with an aside

Eyebrow, title and sub stack on the left; whatever fills the aside slot sits beside them, bottom-aligned, and the default slot runs full width underneath.

The aside is for an intro that belongs next to the heading rather than under it. On narrow viewports it drops below the head.

Default-slot content follows the head at the full inner width. Unclassed lists, paragraphs and tables pick up the base defaults here.

Primitive elements

Styled by base.scss — safe defaults for paragraphs, links, lists, quotes, tables and form controls. Headings have their own ramp under Typography.

Lists

  • Unclassed lists keep their markers and item spacing.
  • Nested lists indent one step.
    • Second level
    • Still spaced by li + li
  • Classed lists get only the reset and style themselves.
  1. Ordered lists share the same rules.
  2. Numbers render in the padding.

Block elements

A quote picks up an inline-start rule, italic, and the dark neutral.


Keys render as keycaps: Ctrl + K. Code stays inline: --space-md.

TokenValue
--radius-md12px
--radius-lg16px

Text

A short paragraph of body copy. Line height and margin come from base.scss. Inline links inherit the primary color and switch to the semi-dark shade on hover.

Monospace via code: var(--primary-medium).

Form controls

Buttons

Ready to build?

Clone the starter and see how little configuration it takes to ship something clean.