# 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](https://github.com/AmbiqAI/helia-ui/issues/new), so that
every consumer gets the same answer rather than ten sites each solving it in
their own stylesheet.

## The dials

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`.

| Dial                                                      | What it changes                                                                                                                                                          | Accepts                                                                                                                                                                                       | Default                                                   | Read by                                                                                                                                                                                                                                                                                                     |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--helia-product-accent`                                  | The 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-secondary`                                | The 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-scale`                                    | Multiplies 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-scale`                                    | Multiplies 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-sans`                                       | Interface 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-mono`                                       | Code, 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-treatment`                                  | What 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. | `plain`                                                   | `EditorialBand`, 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-density`                                      | The 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.

## Where a dial may be set

`: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`:

```html
<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.

## Two sites, one system

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 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.
