# Themes

VegaUI ships a separate theme layer in @vega-ui/theme-core. The package entrypoint is packages/theme/src/index.css, which imports both light.css and dark.css.

## Theme Model

Themes are class-based:

- `.light`: light theme semantic values
- `.dark`: dark theme semantic values

Each theme imports `@vega-ui/tokens-core` first, then maps raw token scales into semantic application variables such as:

- `--background-color`
- `--text-color`
- `--focus-color`
- `--surface-primary`
- `--surface-secondary`
- `--border-color`
- `--fills-primary`
- `--label-primary`

## Key Semantic Variables

These are the main variables application code and component styles should rely on first.

| Variable | Role | Typical Consumers |
| --- | --- | --- |
| `--background-color` | base app canvas color | app shell backgrounds, thumb-on-track contrast, custom page layout |
| `--text-color` | default content color | `Text`, `Heading`, field values, icon triggers, option rows |
| `--focus-color` | focus outline color | buttons, links, sliders, radios, checkboxes, segmented control |
| `--surface-primary` | primary container surface | `DialogContent`, `DrawerContent`, `SheetContent`, `PopoverContent` |
| `--surface-secondary` | floating or transient surface | `SelectListbox`, `TooltipContent`, `TooltipArrow` |
| `--border-color` | default structural border | `Card`, `Fieldset`, `TableRow`, `CheckboxCard` |
| `--border-color-hover` | interactive border emphasis | hover-state border progression for border-driven surfaces |
| `--color-error` | validation and error feedback | `TextField`, `TextArea`, `PinField`, `HelperText` |
| `--fills-primary` | accent-selected fill | checked `Checkbox`, checked `Radio`, page control progress |
| `--fills-secondary` | neutral track or subdued fill | `Progress`, `Meter`, `MeterStack`, `Slider`, `Code`, off `Switch` |
| `--label-primary` | strong indicator label | page control active indicators and derived emphasis fills |
| `--label-secondary` | muted secondary text | `Fieldset` legend text, `Code` foreground, secondary metadata |
| `--disable-text-color` | disabled primary content | disabled button text, disabled field values, disabled segmented items |
| `--disable-background-color` | disabled control surface | disabled `TextField`, `TextArea`, `Select`, `Checkbox`, `Switch`, `ButtonBase` |
| `--surface-shadow` | elevated surface shadow | `Dialog`, `Drawer`, `Sheet`, `Popover`, `Tooltip`, `SelectListbox` |

## Semantic Token Types

`Key Semantic Variables` is the quick-reference layer. This section is the classification layer: it groups theme variables by semantic family so the system is easier to extend and reason about.

| Token | Semantic Type | Meaning In UI |
| --- | --- | --- |
| `--fills-primary` | `accent-soft-fill` | primary accent fill for selected or active UI |
| `--fills-primary-hover` | `accent-soft-fill-hover` | hover state for the primary accent fill |
| `--fills-primary-active` | `accent-soft-fill-active` | active state for the primary accent fill |
| `--fills-secondary` | `neutral-track-fill` | neutral fill for tracks, rails, and subdued containers |
| `--fills-secondary-hover` | `neutral-track-fill-hover` | hover state for neutral track-like surfaces |
| `--fills-secondary-active` | `neutral-track-fill-active` | active state for neutral track-like surfaces |
| `--fills-tertiary` | `field-surface` | default background for text-entry and field-like controls |
| `--fills-tertiary-hover` | `field-surface-hover` | hover state for field surfaces |
| `--fills-tertiary-active` | `field-surface-active` | active state for field surfaces |
| `--fills-quaternary-hover` | `micro-surface-hover` | lightweight hover fill for icon actions and micro-interactions |
| `--fills-quaternary-active` | `micro-surface-active` | active state for micro-interactions |
| `--surface-primary` | `container-surface` | main elevated surface for containers such as dialogs or sheets |
| `--surface-secondary` | `floating-surface` | floating surface for listboxes, popovers, and tooltips |
| `--surface-shadow` | `elevation-shadow` | shadow for elevated surfaces |
| `--text-color` | `content-primary` | default readable content color |
| `--text-color-inverse` | `content-on-accent` | readable content color on accent-filled backgrounds |
| `--label-primary` | `label-strong` | strong UI label or indicator color |
| `--label-secondary` | `label-muted` | secondary label or metadata color |
| `--label-tertiary` | `label-subtle` | placeholder, helper, and subdued label color |
| `--border-color` | `border-default` | default structural border color |
| `--border-color-hover` | `border-hover` | hover state for structural borders |
| `--border-color-active` | `border-active` | active state for structural borders |
| `--disable-background-color` | `disabled-surface` | disabled background and surface fill |
| `--disable-text-color` | `disabled-content` | disabled primary text or value color |
| `--disable-label-text-color` | `disabled-subtle-content` | disabled secondary, helper, or placeholder text color |
| `--disable-border-color` | `disabled-border` | disabled border or indicator color |
| `--focus-color` | `focus-ring` | focus ring and outline color |
| `--color-error` | `feedback-error` | error and validation feedback color |
| `--overlay` | `backdrop-overlay` | backdrop tint for modal and overlay layers |
| `--separator-opaque` | `separator-strong` | visible separators, handles, and dividers |

## Semantic Families

Think about theme variables in these families first:

- `fills-*`: interactive fill semantics for selected, hovered, pressed, or field-like surfaces
- `surface-*`: container and floating surface semantics for overlays and elevated UI
- `text-*` and `label-*`: readable content and UI-label semantics
- `border-*`: structural boundary and border-emphasis semantics
- `disable-*`: disabled-state surface, content, and border semantics
- `focus-color`, `color-error`, `overlay`, `separator-*`: system and state-feedback semantics

## Semantic Types In Practice

These semantic types come from how the UI package actually uses the variables:

- `accent-soft-fill`: selected or checked states such as `Checkbox`, `Radio`, and page-control progress
- `neutral-track-fill`: tracks and neutral bars such as `Progress`, `Meter`, `MeterStack`, and `Slider`
- `field-surface`: input-like controls such as `TextField`, `TextArea`, `PinField`, and `SelectCombobox`
- `micro-surface-*`: low-emphasis hover and active surfaces for icon buttons, trigger buttons, and picker items
- `container-surface`: primary overlay or panel surfaces such as `DialogContent`, `DrawerContent`, and `SheetContent`
- `floating-surface`: floating content surfaces such as `SelectListbox`, `TooltipContent`, and `PopoverContent`
- `content-*`, `label-*`, `border-*`, and `disable-*`: text, label, border, and disabled-state semantics reused across the entire kit

Use this section to answer “what class of semantic token do I need here?”, then use `Key Semantic Variables` to pick the concrete variable name.

## How To Apply A Theme

Put the theme class on `<html>`:

```html
<html class="light">
```

`Dialog`, `Drawer` and `Sheet` render into `document.body`, so a class on an inner wrapper does not reach them.

## App Shell With Theme Switcher

```tsx
import { useEffect, useState } from 'react';
import { Button } from '@vega-ui/react';

type ThemeName = 'light' | 'dark';

export function ThemeSwitcher() {
  const [theme, setTheme] = useState<ThemeName>('light');

  useEffect(() => {
    document.documentElement.classList.remove('light', 'dark');
    document.documentElement.classList.add(theme);
  }, [theme]);

  return (
    <Button variant='secondary' appearance='ghost' onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Switch to {theme === 'light' ? 'dark' : 'light'}
    </Button>
  );
}
```

Give the page the theme background and text color:

```css
body {
  background: var(--background-color);
  color: var(--text-color);
}
```

A wrapper with another theme class themes its own subtree, but `Dialog`, `Drawer` and `Sheet` opened from it still take the theme of `<html>`.

## Theme Customization

The safest customization path is to define your own theme class and override semantic variables there.

```css
@import '@vega-ui/theme-core';

.brand-sunrise {
  --background-color: #fffaf3;
  --text-color: #251b10;
  --surface-primary: #fffdf9;
  --surface-secondary: #fff7ec;
  --border-color: #e7d7c2;
  --focus-color: var(--color-orange-accent-400);
  --fills-primary: color-mix(in oklab, var(--color-orange-accent-500) 16%, transparent);
  --fills-primary-hover: color-mix(in oklab, var(--color-orange-accent-500) 22%, transparent);
  --label-secondary: #7a6653;
}
```

Then put that class on `<html>` the same way as `.light` or `.dark`.

Customize semantic variables first. Reach for component-local variables only when the change should affect one component family rather than the whole application.

## Light vs Dark Behavior

Light and dark themes keep the same semantic variable names. The main difference is how those semantics map onto the raw token scale.

In particular, accent colors are inverted in dark mode. For example, `--color-blue-accent-100` points to a lighter raw blue in light mode and a darker raw blue in dark mode. That is why component styles should prefer semantic accent variables over raw palette steps.

## Guidance

- put the theme class on `<html>`, not on a wrapper or per component
- consume semantic variables in component CSS whenever possible
- avoid writing component-specific light/dark overrides when a semantic theme variable already exists
- customize semantic variables before touching component-local `--component-*` variables
