Skip to documentation content

Theme

Set up Honest UI's CSS-first theme system and understand how its semantic tokens, palettes, and scopes work.

Honest UI components get their color, typography, spacing, radius, and effects from CSS custom properties. The component code stays readable because each property describes a role—such as a primary background or regular body text—instead of repeating a raw value.

There is no required Honest UI theme provider. You own the stylesheet and choose how your application writes the theme attributes.

Start with the shared styles

Running init installs the shared style setup into the global stylesheet recorded in components.json:

npx honestui@latest init

Set a color theme and style on a shared ancestor. The document element is the simplest place for an application-wide theme:

<html data-theme="light" data-style="modern">
  <!-- Your application -->
</html>

Use data-theme="dark" for the dark color theme. Honest UI supplies modern and traditional style values; they currently change the radius scale while keeping the same type roles.

If a theme library controls your application, configure it to write light or dark to data-theme. Honest UI does not manage persistence, system-theme detection, or hydration for you.

Use tokens with Tailwind CSS

Honest UI components use Tailwind CSS v4 utilities with the CSS variables passed as arbitrary values. No plugin or Tailwind JavaScript config is required:

<div
  className="
    rounded-[var(--hui-radius-3)]
    border border-[var(--hui-color-border-base-primary)]
    bg-[var(--hui-color-background-base-secondary)]
    p-[var(--hui-space-5)]
    text-[var(--hui-color-foreground-base-primary)]
    shadow-[var(--hui-shadow-feather)]
  "
/>

The utility is generated at build time; the browser resolves the variable at runtime. That means the same class responds to light, dark, and nested theme scopes without a dark: duplicate. Read Tailwind CSS for custom-property shorthand, variants, type hints, and optional @theme inline aliases.

How the token system works

Every public token begins with --hui-; hui stands for Honest UI. The prefix makes ownership clear when Honest UI variables sit beside product tokens or variables from another library.

Tokens move from shared scales to roles used by components:

LayerExamplesWhat it controls
Palette values--hui-neutral-9, --hui-accent-10Theme-specific source colors
Semantic colors--hui-color-background-base-primary, --hui-color-foreground-danger-primaryMeaningful surface, content, border, and status roles
Spacing--hui-space-3, --hui-space-7Gaps, padding, control sizes, and layout rhythm
Radius--hui-radius-2, --hui-radius-fullCorners, pills, and circular shapes
Typography--hui-font-body, --hui-font-size-regularType families, sizes, weights, line heights, and tracking
Effects--hui-shadow-floating, --hui-focus-ringElevation, focus, motion, pressed feedback, and blur

Use semantic tokens in component code. Palette values are implementation details for building or replacing a color system; they should not decide what a component means.

.account-card {
  color: var(--hui-color-foreground-base-primary);
  background: var(--hui-color-background-base-secondary);
  border: 1px solid var(--hui-color-border-base-primary);
  border-radius: var(--hui-radius-4);
  padding: var(--hui-space-5);
  box-shadow: var(--hui-shadow-feather);
}

Theme attributes

The shared styles respond to four attributes. Put attributes that belong to one scope on the same element.

AttributeSupported valuesDefault behavior
data-themelight, darkRequired to resolve theme colors; also sets color-scheme for native controls
data-stylemodern, traditionalRequired to resolve radius tokens
data-accent-colororange, mintWithout an override, the theme uses the built-in indigo accent
data-gray-colorgray, mauve, slate, sageWithout an override, the theme uses gray

For example, this scope uses the dark theme with a mint accent and sage neutrals:

<section
  data-theme="dark"
  data-style="traditional"
  data-accent-color="mint"
  data-gray-color="sage"
>
  <!-- Every Honest UI token in this subtree resolves from this scope. -->
</section>

Nested scopes work through normal CSS inheritance. A child scope can preview a different theme without changing the rest of the page. Keep all four attributes together when a scope changes more than one dimension so its behavior is obvious in the markup.

Choose the right theme page

  • Colors explains semantic roles, palettes, overlays, and visualization colors.
  • Tailwind CSS shows how to use tokens in utilities and register shorter aliases.
  • Typography covers the LoveSans family, body and title scales, weights, line heights, and tracking.
  • Spacing documents the 17-step spacing scale and how to choose a step.
  • Radius compares the modern and traditional radius scales.
  • Effects covers shadows, focus rings, pressed feedback, motion, and blur.

Customize without losing meaning

Override a semantic token at the narrowest scope that owns the decision:

.billing-area {
  --hui-color-background-accent-emphasis: oklch(0.58 0.18 260);
  --hui-color-border-accent-emphasis: oklch(0.58 0.18 260);
}

Keep the token name when the meaning stays the same. Change component classes when the decision belongs to one component. Add a product token when the value represents a new reusable concept that Honest UI does not already name.

After any theme change, test default, hover, active, focus, disabled, and invalid states in both color themes. Check text and non-text contrast, visible keyboard focus, forced colors, reduced motion, and zoomed layouts before shipping.