# CSS Custom Properties and Theme Customization
taga11y uses CSS custom properties (CSS variables) scoped to the `.taga11y` block for all visual styling. This design enables runtime theme switching and per-instance customisation without JavaScript CSS manipulation.

## Custom Property Reference
All custom properties are defined on the `.taga11y` block and inherited by child elements.

### Colors
The 16 color properties are **6 independent (base) tokens** and **10 derived tokens**. Each derived token is declared once on `.taga11y` as `var(--<base>, <fallback>)`, so it automatically tracks its base value across light, OS-dark, and forced `data-theme` modes. **Overriding a base token cascades to every token derived from it**, unless that derived token is itself overridden (per-token opt-out — see [Custom Theme Example](#custom-theme-example)).

Base (independent) tokens — set these to retheme the whole component:

| Property | Default (light) | Default (dark) | Used By |
|---|---|---|---|
| `--taga11y-color-bg` | `#ffffff` | `#000000` | Wrapper background |
| `--taga11y-color-text` | `#000000` | `#ffffff` | Body text, labels, options |
| `--taga11y-color-border` | `#767676` | `#898989` | Input/chip region border |
| `--taga11y-color-border-focus` | `#0060df` | `#5fb3f5` | Focus ring on input |
| `--taga11y-color-tag-bg` | `#efefef` | `#101010` | Chip background |
| `--taga11y-color-error-border` | `#c62828` | `#ef9a9a` | Error chip/region border |

Derived tokens — resolve from a base token by default; override directly only to break the link:

| Property | Derived from | Default (light) | Default (dark) | Used By |
|---|---|---|---|---|
| `--taga11y-color-tag-text` | `--taga11y-color-text` | `#000000` | `#ffffff` | Chip text |
| `--taga11y-color-tag-border` | `--taga11y-color-tag-text` (chained) | `#000000` | `#ffffff` | Chip inset ring |
| `--taga11y-color-tag-remove-hover` | `--taga11y-color-border-focus` | `#0060df` | `#5fb3f5` | Chip remove button hover text |
| `--taga11y-color-suggestion-bg` | `--taga11y-color-bg` | `#ffffff` | `#000000` | Dropdown background |
| `--taga11y-color-suggestion-active-bg` | `--taga11y-color-border-focus` | `#0060df` | `#5fb3f5` | Active/hovered suggestion background |
| `--taga11y-color-suggestion-active-text` | `--taga11y-color-bg` | `#ffffff` | `#000000` | Active/hovered suggestion text |
| `--taga11y-color-suggestion-border` | `--taga11y-color-border-focus` | `#0060df` | `#5fb3f5` | Dropdown (listbox) border |
| `--taga11y-color-error-bg` | `--taga11y-color-bg` | `#ffffff` | `#000000` | Error chip/region background |
| `--taga11y-color-error-text` | `--taga11y-color-text` | `#000000` | `#ffffff` | Error chip/region text |
| `--taga11y-color-spinner` | `--taga11y-color-border-focus` | `#0060df` | `#5fb3f5` | Loading spinner border |

### Typography and Layout
| Property | Default | Used By |
|---|---|---|
| `--taga11y-radius` | `2px` | Border radius on chips, input, dropdown, error region |
| `--taga11y-border-width` | `1px` | Resting border width on the input/chip region and the error region |
| `--taga11y-outline-width` | `2px` | Focus ring width on the input/chip region and the dropdown (listbox) border |
| `--taga11y-shadow` | `none` | Box shadow on the chip region |
| `--taga11y-font-family` | `inherit` | Font family for all component text |
| `--taga11y-font-size` | `1em` | Font size for all component text |
| `--taga11y-spacing` | `1px 2px` | Padding on input region, options, loading indicator |
| `--taga11y-tag-padding-block` | `1px` | Vertical padding on `.taga11y__tag` — also mirrored onto `.taga11y__input`'s `padding-block` to keep input and chip the same outer height |
| `--taga11y-tag-padding-inline` | `4px` | Horizontal padding on `.taga11y__tag` |
| `--taga11y-tag-label-gap` | `2px` | Flex gap inside a chip between `.taga11y__tag-label` and `.taga11y__tag-remove` |

### Row-height stability under chip padding overrides
`.taga11y__input` mirrors `--taga11y-tag-padding-block` (vertical axis only; horizontal padding stays `0`). The input's outer height therefore matches a chip's outer height by construction — adding a chip never grows the wrapper's row height under default tokens, and overriding `--taga11y-tag-padding-block` scales the row uniformly without leaving the input visually shorter than the chips. The wrapper still grows naturally when the chip row wraps to a second visual line.

### Box-sizing
The component subtree (`.taga11y`, all descendants, and their `::before`/`::after` pseudo-elements) is reset to `box-sizing: border-box`. The reset is scoped to the component, so elements outside `.taga11y` on the host page are unaffected by whatever box-sizing the host has chosen.

## Theme Modes

### Automatic (OS preference)
By default, taga11y follows the operating system's color scheme preference:
```css
/* Light mode (default) — values set on .taga11y */
/* Dark mode — values set under @media (prefers-color-scheme: dark) */
```

### Forced theme via `data-theme` attribute
The `theme` option in the constructor or via `settings()` sets a `data-theme` attribute on the wrapper:
```js
// Force dark mode
const widget = new Taga11y(input, { theme: 'dark' });
// Force light mode
widget.settings({ theme: 'light' });
// Return to OS preference
widget.settings({ theme: null });
```
- `data-theme="dark"` applies dark mode values regardless of OS setting.
- `data-theme="light"` applies light mode values regardless of OS setting.
- `null` (default) removes the attribute, letting `prefers-color-scheme` take over.
The OS dark-mode block uses a plain `.taga11y` selector (specificity `(0,1,0)`), so `[data-theme="light"]` (`(0,2,0)`) always wins over it. Consumers can override dark-mode defaults without `!important` by adding `@media (prefers-color-scheme: dark) { .taga11y { --taga11y-color-bg: custom; } }` after the library stylesheet.

## Custom Theme Example
Set just the base tokens — the derived tokens follow automatically (e.g. `suggestion-bg`/`error-bg` track `bg`, `spinner`/`suggestion-active-bg` track `border-focus`). One explicit derived override below (`--taga11y-color-tag-text`) demonstrates per-token opt-out: it wins over the value it would otherwise inherit from `--taga11y-color-text`.
```css
.taga11y--custom {
  /* Base tokens — cascade to all derived tokens */
  --taga11y-color-bg: #fafafa;
  --taga11y-color-text: #333333;
  --taga11y-color-border: #1a73e8;
  --taga11y-color-border-focus: #1a73e8;
  --taga11y-color-tag-bg: #e8f0fe;
  --taga11y-color-error-border: #d93025;

  /* Explicit derived override — opts out of the --taga11y-color-text link */
  --taga11y-color-tag-text: #1a73e8;

  --taga11y-radius: 6px;
  --taga11y-shadow: 0 1px 3px rgb(0 0 0 / 0.12);
}
```
```js
const widget = new Taga11y(input, { suggestions: ['Tag 1', 'Tag 2'] });
widget._dom.wrapper.classList.add('taga11y--custom');
```
Custom properties cascade normally, so you can also override them on a parent element to affect multiple taga11y instances:
```css
.theme-blue .taga11y {
  --taga11y-color-suggestion-active-bg: #1565c0;
  --taga11y-color-border-focus: #1565c0;
}
```

## Reduced Motion
The `@media (prefers-reduced-motion: reduce)` media query disables all animations and transitions within the component, including the loading spinner, chip appear/dismiss, dropdown open/close, and error region transitions. No custom property is needed — this is handled automatically by the stylesheet.

## CSS Class Reference
| Class | Purpose |
|---|---|
| `.taga11y` | Root wrapper element |
| `.taga11y--disabled` | Applied when the component is disabled |
| `.taga11y__label` | Visible label element |
| `.taga11y__tags` | Flex container for chips and input |
| `.taga11y__tag-list` | Unordered list of chips |
| `.taga11y__tag` | Individual chip (list item) |
| `.taga11y__tag--error` | Error chip modifier |
| `.taga11y__tag-label` | Chip text span |
| `.taga11y__tag-remove` | Chip remove button |
| `.taga11y__input` | Combobox input |
| `.taga11y__listbox` | Dropdown suggestion list |
| `.taga11y__option` | Individual suggestion |
| `.taga11y__loading` | Loading indicator |
| `.taga11y__loading-spinner` | CSS spinner element |
| `.taga11y__loading-text` | Loading text |
| `.taga11y__error` | Error region (role="alert") |
| `.taga11y__announcer` | Visually-hidden live region |
| `.taga11y__hidden` | Hidden form input |
