Semantic tone classes
Semantic tones give color a purpose. Instead of choosing a fixed blue or red for each page, you choose a tone such as info, success, or danger, then choose a role such as surface or button. PureStack resolves those roles from the active light or dark theme. Try the site's theme switcher while looking at the examples below: their class names stay the same while their palette values change.
The key distinction is selection versus painting:
tone--infoselects the info palette for an element and its descendants. On its own, it does not add a visible background, border, or text color.tone-fill-surface,tone-border-surface, andtone-text-surfacepaint a background, border color, and text color from the selected palette.b-1, padding, and radius classes supply the border width and shape. A tone border class sets color, but does not create a visible border by itself.
<div class="tone--info tone-fill-surface tone-border-surface tone-text-surface b-1 rounded-md p-3">
An information surface
</div> The same recipe works with another tone--* class without changing the paint classes.
All ten tones
Each card below uses the same surface, border, text, padding, and radius classes. Only the selected tone changes. The labels describe typical uses; your site skin controls the actual colors.
custom is a named palette lane, not an arbitrary CSS color string. A skin can give it product-specific values. ghost is also a complete tone with its own tokens; its exact appearance depends on the skin.
Compare roles within one tone
A tone contains more than one background and text color. These samples all select accent, but paint different roles from that palette:
<div class="tone--accent tone-fill-surface tone-border-surface tone-text-surface b-1 rounded-md p-3">
Regular surface
</div>
<div class="tone--accent tone-fill-surface-alt tone-border-surface-alt tone-text-surface-alt b-1 rounded-md p-3">
Alternate surface
</div>
<div class="tone--accent tone-fill-button tone-border-button tone-text-button b-1 rounded-md p-3">
Button role
</div>
<div class="tone--accent tone-fill-spotlight tone-border-surface-alt tone-text-surface-alt b-1 rounded-md p-3">
Spotlight field
</div> Keep fill, border, and text from the same role when you need a ready-made combination. Each role's colors are designed as a set within the active theme. The direct tone-fill, tone-border, and tone-text classes use the tone color itself; they are useful for accents, but they are not the same as a complete surface or button treatment.
spotlight is for large areas such as feature bands, callouts, and showcase panels. It is a calm field with a soft light, without the gloss and edge shading of surfaces and buttons, so it stays calm at any size. It pairs with the alternate surface border and text classes. Like every fill it has -hover, -active, -disabled, and -all forms: the light reaches further on hover and press, and goes out when disabled. Components accept it as variant="spotlight", for example <Panel tone="accent" variant="spotlight">.
Keep spotlights rare: one or two per page, for the pieces a visitor should notice first. Most surfaces should be quiet. tone-fill-flat, tone-fill-flat-alt, and tone-fill-flat-solid paint a role's single color without the gradient chrome, with the same -hover, -active, -disabled, and -all forms as the gradient fills. Components accept them as variant="flat", variant="flatAlt", and variant="flatSolid":
<Panel tone="neutral" variant="flat">A quiet card</Panel>
<BtnLink href="/guide/" variant="flatSolid">A flat button</BtnLink> The skin sets the field and the light color; the page decides where the light falls. It comes from the top left by default. Move it with spotlight-from-top, spotlight-from-top-right, spotlight-from-bottom-left, or spotlight-from-bottom-right, for example to keep it visible when a card covers that corner:
<Panel tone="accent" variant="spotlight" class="spotlight-from-top-right">
...
</Panel> Cards placed on a spotlight should not repeat its field. tone-fill-glass lets the field show through, lifted by a faint veil and lit by the same light, so a card reads as a lighter pane rather than a second color. The light follows the nearest spotlight-from-*, so cards and their spotlight are lit from the same side. Its veil thickens on hover and press, and thins without the light when disabled. Components accept it as variant="glass". A different tone on a glass card tints it without breaking the field:
<Panel tone="accent" variant="spotlight" class="spotlight-from-top-right">
<Panel variant="glass">Next review</Panel>
<Panel tone="secondary" variant="glass">Open decision</Panel>
</Panel> Tone-colored text and clipped fills
Use tone-text for the selected tone color. Use tone-text-bg-button when you want the button role's background treatment clipped into text. Both examples keep a neutral surrounding surface; the tone changes only for the label:
<strong class="tone--accent tone-text">Tone color</strong>
<strong class="tone--accent tone-text-bg-button">Clipped fill</strong> The clipped fill can be a solid color or a richer background, depending on the skin. tone-text-bg-surface and tone-text-bg-surface-alt use the corresponding content roles.
Compose a callout from plain markup
This callout uses the warning tone for its surface and an inset accent on the left. Its heading and body stay ordinary semantic HTML:
<div class="tone--warning tone-fill-surface tone-border-surface tone-text-surface tone-inset-l inset-size-2 b-1 rounded-md p-3">
<strong class="d-block mb-1">Review before publishing</strong>
<span class="fs-sm">Check links and preview the site at its final URL.</span>
</div> tone-inset-l adds a tone-colored rail without changing the surface border color. inset-size-2 controls its width using the utility spacing scale.
Change a tone inside another tone
Tone selection applies to descendants through current-palette CSS variables. A nested tone--* starts a new palette context for its own children. Here the note uses neutral and its inner status uses success:
All pages were generated.
<div class="tone--neutral tone-fill-surface tone-border-surface tone-text-surface b-1 rounded-md p-3">
<strong class="d-block mb-2">Build report</strong>
<div class="tone--success tone-fill-surface-alt tone-border-surface-alt tone-text-surface-alt b-1 rounded-md p-2">
<span class="text-eyebrow m-0">Completed</span>
<p class="m-0 fs-sm">All pages were generated.</p>
</div>
</div> The inner text-eyebrow also reads the current palette, so its subtle text color changes with tone--success. This is why tone classes are useful for composing a small visual system instead of setting each color separately.
Keep a region in one theme mode
Tones follow the page theme, so a neutral card is light in the light theme and dark in the dark theme. Add theme--dark or theme--light to keep a region in one mode while the page switches. The region gets that mode's full palette from the same skin and presets, and tone classes inside it work as usual. Here a dark product frame holds a light card in both themes:
<div class="theme--dark tone--accent tone-fill-surface-alt tone-border-surface-alt tone-text-surface-alt b-1 rounded-lg p-3">
<span class="text-eyebrow">Project view</span>
<div class="theme--light tone-fill-surface tone-border-surface tone-text-surface b-1 rounded-md p-3">
<strong class="d-block">Autumn launch</strong>
<span class="text-subtle fs-sm">Light card, dark frame, in either theme.</span>
</div>
</div> A theme--* region starts from the neutral tone of its mode and resets the inherited text color. Put tone--* on the same element or on a descendant. Rules that must win inside a region should rely on specificity, not on an outer @scope, because the region becomes the nearest theme scope.
Hover, active, and disabled states
Surface and button paint roles have state variants. Hover or press the first control; compare it with the disabled control. The -all suffix includes the rest, hover, active, and disabled treatments for that role.
<button type="button" class="tone--accent tone-fill-button-all tone-border-button-all tone-text-button-all b-1 rounded-md p-2">
Hover or press
</button>
<button type="button" disabled class="tone--accent tone-fill-button-all tone-border-button-all tone-text-button-all b-1 rounded-md p-2">
Disabled
</button> For individual states, use a suffix such as tone-fill-button-hover, tone-border-button-active, or tone-text-button-disabled. -hover applies on :hover. -active applies on :active or when the element has an .active class. -disabled applies on :disabled. The shorthand -all exists for surface, alternate surface, and button fill, border, text, and text-background roles. It is not a universal suffix for every tone class.
These classes paint states; they do not add button behavior or keyboard handling. Keep interactive elements as real buttons and links. Built-in components such as Btn already connect tone, variant, and states through their props.
Use a tone as a line or inset accent
A border-width class and tone-border give a plain block a tone-colored rule. tone-inset-l draws a left-side inset box shadow instead, using the current tone color:
<div class="tone--info tone-text tone-border bl-2 rounded-md p-3">
Border rule
</div>
<div class="tone--info tone-text tone-inset-l inset-size-4 rounded-md p-3">
Inset accent
</div> Other directions are tone-inset-r, tone-inset-t, tone-inset-b, tone-inset-x, and tone-inset-y. The inset-size-0 through inset-size-6 classes set --tone-inset-size using the utility spacing scale. Inset classes also have -hover and -active forms; they do not have -disabled or -all forms.
Class reference
| Class family | What it sets | Common choices |
|---|---|---|
tone--{name} | Current palette context for the element and descendants. | tone--neutral, tone--accent, tone--success |
tone-fill-{role} | Background. | canvas, surface, surface-alt, button |
tone-border-{role} | Border color. | surface, surface-alt, button |
tone-text-{role} | Text color. | surface, surface-alt, button |
tone-text-bg-{role} | Role background clipped into text. | surface, surface-alt, button |
tone-fill, tone-border, tone-text | The tone color directly. | No role suffix. |
tone-inset-{direction} | Inset shadow in the tone color. | l, r, t, b, x, y |
inset-size-{0..6} | Custom property controlling inset width. | inset-size-4 |
Not every role has every state. The direct tone-color classes and tone-fill-canvas have rest, hover, and active forms; tone-border also has -all. The surface, alternate surface, and button families have disabled and -all forms. Use the explicit examples above as templates when composing stateful markup.
Components and skins
For components, pass a semantic tone to the component instead of rebuilding its standard variant with classes:
<Panel tone="info" variant="surface">A note</Panel>
<Btn tone="danger" variant="solid">Delete item</Btn>
<Badge tone="success">Complete</Badge> Their variants combine border widths, radii, fill, border, text, and interaction classes. Use the raw tone utilities when you need a plain element or a custom combination. To change how a tone looks across the whole site, edit its tokens in a skin; the Themes guide explains the light and dark palette setup. The general Utility CSS classes guide covers spacing, typography, layout, and borders used in these examples.