Skip to content
helia-ui
Foundations
HELIA HUB

Site theme

Flair lives in these dials and nowhere else. A site copies @ambiqai/helia-ui/site-theme.css to src/styles/site-theme.css, loads it from customCss immediately after the package sheets, and sets the values below; everything else it might want to look different about is a package change, not a site one. A site that needs a rule outside this contract opens a component request against AmbiqAI/helia-ui, so that every consumer gets the same answer rather than ten sites each solving it in their own stylesheet.

Read out of site-theme.css itself: the declaration is the default and the comment above it is the description, so a dial that moves without this page moving fails validate.

DialWhat it changesAcceptsDefaultRead by
--helia-product-accentThe site’s accent. One value, not a vocabulary: a site carrying several product accents sets this per subtree rather than adding tokens.Any color, normally one of the --helia-accent-* hues rather than a fresh one, so the palette stays the palette.var(--helia-accent-slate)AsciiTerminal, for the prompt color. No card recipe draws an accent edge, so elsewhere a site passes this into Media as --accent.
--helia-accent-secondaryThe second accent, for a treatment that needs two: a gradient, a tone, a hero band. Leave it equal to the first and nothing pairs.Any color, on the same terms as the first accent.var(--helia-accent-slate)Badge tone secondary and LinkCard’s directional cue under the pointer. EditorialBand pairs it with the first accent when the hero treatment is gradient. Charts do not read it: the series ramp is --helia-chart-*, which is a palette rather than a dial.
--helia-radius-scaleMultiplies the whole radius scale at once.A unitless number. 0 is a square site, 1 is the package shape, above about 1.6 the pill and the card stop being distinct.1--helia-radius-xs, -sm, -md and -lg in semantic.css, and --radius in shadcn.css, so every helia-surface, every part that rounds its own chrome, and the React surfaces beside them. --helia-radius-pill is deliberately outside the scale.
--helia-motion-scaleMultiplies every motion duration. Reduced motion zeroes it regardless, so this is the site’s dial, not the visitor’s.A unitless number. 0 removes motion for everyone on the site; 1.5 is the slowest worth asking for.1--helia-motion-fast, -base and -slow, so helia-motion-lift, helia-motion-reveal, helia-motion-cue, helia-hover-surface, helia-button and the duration-* utilities the React parts write. Reveal and ShowcaseCarousel also read the number itself and skip their scripts when it is 0.
--helia-font-sansInterface and editorial type.A full font stack, not a family name on its own.var(--__sl-font, var(--helia-sl-font-system))The page frame through Starlight, and CodeBlock for its chrome.
--helia-font-monoCode, filenames, versions, transcripts.A full font stack, not a family name on its own.var(--__sl-font-mono, var(--helia-sl-font-system-mono))CodeBlock and AsciiTerminal.
--helia-surface-tint
--helia-surface-tint-amount
A hue mixed into every surface, and how much of it. The default mixes nothing, which is why the amount rather than the hue is what is zeroed.Any color, and a percentage. Keep the amount in single figures: this is a cast over the page, not a background color.transparent
0%
--helia-surface-paper, --helia-surface-card and --helia-surface-card-muted, so helia-surface and its variants, helia-chip--accent, helia-chip--static, helia-button--ghost, and the --card, --popover, --muted and --secondary aliases the React parts read.
--helia-hero-treatmentWhat the ground of a full-bleed band is made of. It is the site’s say rather than the caller’s, so a band takes its tone from the page and its treatment from the theme.plain, gradient, or tinted. plain is the surface as the palette leaves it; tinted pulls the first accent into it; gradient runs the first accent into the second across the band.plainEditorialBand, for the paper tone. A site’s own hero composition reads it too, which is why the package names the value rather than leaving each site to spell the choice its own way.
data-helia-densityThe card padding step, and nothing else. Prose rhythm and the page frame belong to Starlight.compact, comfortable, or the attribute left off.unset--helia-card-pad, so helia-surface--pad-card.

Every default is the package default restated, so a copy with nothing changed renders exactly what the package renders, and a site owns its flair as a diff against one file.

:root is the ordinary answer, and a site that sets them there needs nothing else. A section that wants its own treatment sets them on a wrapper instead, and the wrapper has to carry data-helia-theme:

<div data-helia-theme="warm" style="--helia-radius-scale: 1.6">
<!-- every surface under here is rounder -->
</div>

The attribute is a hook, not a value: the package never reads what it is set to. .helia-theme-scope does the same job where the markup cannot take an attribute. Either one is needed because a dial is not a token but the input to several. CSS substitutes a custom property at the element that declares it, so --helia-radius-md composed once on :root is a finished length by the time it inherits, and a scale set further down the tree would move nothing. semantic.css repeats those compositions on the two hooks, which is what lets a dial mean the same thing wherever it is set. It does not repeat them on *: that would charge every element on every page for a feature most pages never use.

Density is the exception and needs no hook. It is an attribute the package selects on rather than a value it substitutes, so it has always worked on any wrapper.

The same three cards and the same section header, under the hub’s theme and under a warmer one. Nothing below is a component variant: the difference is the values in a site-theme.css, set here on a wrapper so both can sit on one page.

The hub

Start building

Three cards, one section header, and nothing else changed.

Guide

Install the toolchain

Tutorial

Run the first example

Reference

Read the API

Every symbol, with the units and the error behavior.
  • --helia-product-accent: var(--helia-accent-slate)
  • --helia-accent-secondary: var(--helia-accent-slate)
  • --helia-radius-scale: 1
  • --helia-surface-tint-amount: 0%
  • --helia-hero-treatment: plain
  • density: unset

A warmer site

Start building

Three cards, one section header, and nothing else changed.

Guide

Install the toolchain

Tutorial

Run the first example

Reference

Read the API

Every symbol, with the units and the error behavior.
  • --helia-product-accent: var(--helia-accent-orange)
  • --helia-accent-secondary: var(--helia-accent-pink)
  • --helia-radius-scale: 1.6
  • --helia-surface-tint: var(--helia-accent-orange)
  • --helia-surface-tint-amount: 7%
  • --helia-hero-treatment: gradient
  • data-helia-density="comfortable"

The second theme lives in this site as a scoped example rather than in the package. A second theme is a thing a site has. Each panel is a site-theme.css with its selector changed from :root to a class, which is the only edit between these and the file a site would ship, and each carries data-helia-theme so the compositions re-derive inside it.