code
```
### Font-size caveat
The design system sets `html { font-size: 62.5% }` so that **1rem = 10px**. If your app also uses Tailwind, shadcn/ui, or Bootstrap, add `html { font-size: 100%; }` after the Tale UI import. See [framework-integration.md](../packages/css/docs/framework-integration.md) for the full workaround.
---
## Dark Mode / Light Mode
### Three-layer system
| Priority | Trigger | Selector |
|----------|---------|----------|
| 1 (lowest) | Default | `html:not([data-color-mode="dark"])` — light mode when no attribute is set |
| 2 | OS preference | `@media (prefers-color-scheme: dark)` + `html:not([data-color-mode="light"])` — auto-dark unless explicitly overridden to light |
| 3 (highest) | Explicit attribute | `html[data-color-mode="dark"]` — always dark regardless of OS |
> **Common mistake:** Do not toggle dark mode by *removing* the `data-color-mode` attribute. Removing the attribute does not mean "light mode" — it means "no explicit preference", which falls back to OS preference via `prefers-color-scheme`. If the user's OS is set to dark mode, removing the attribute keeps the page dark. Always set the attribute to either `"dark"` or `"light"` explicitly.
### What happens in dark mode
- All `--neutral-*` shades **invert** (light ↔ dark)
- All `--color-*` shades **invert** (5 ↔ 100, 10 ↔ 90, etc.)
- `--brand-*` does **NOT** invert — it is palette-only
- `--text-color`, `--display-color`, `--mono-color` automatically adjust
### Setting it up
**Option A — OS preference only (no toggle)**
Add an inline script in `` before any CSS to avoid a flash of wrong theme:
```html
```
**Option B — with a toggle**
```tsx
function useDarkMode() {
const [dark, setDark] = React.useState(() => {
const stored = localStorage.getItem('color-mode');
if (stored) return stored === 'dark';
return window.matchMedia('(prefers-color-scheme: dark)').matches;
});
React.useEffect(() => {
const mode = dark ? 'dark' : 'light';
document.documentElement.setAttribute('data-color-mode', mode);
localStorage.setItem('color-mode', mode);
}, [dark]);
return [dark, setDark] as const;
}
```
**Option C — scoped dark section**
```html
```
---
## Component Catalogue
All components are imported from `@tale-ui/react/{name}`. BEM base classes are applied automatically — you only need extra `className` when overriding specific modifiers not exposed as props.
Components that accept variant/size props apply the BEM modifier class for you:
```tsx
// → class="tale-button tale-button--primary tale-button--sm"
// → class="tale-input tale-input--lg"
// → class="tale-radio tale-radio--sm"
```
### Form Controls
| Component | Import path | Key classes |
|-----------|------------|-------------|
| Button | `@tale-ui/react/button` | `.tale-button`, `--primary`, `--neutral`, `--ghost`, `--danger`, `--sm`, `--md`, `--lg` |
| Input | `@tale-ui/react/input` | `.tale-input`, `--sm`, `--lg` |
| Checkbox | `@tale-ui/react/checkbox` | `.tale-checkbox` |
| Checkbox Group | `@tale-ui/react/checkbox-group` | — |
| Radio | `@tale-ui/react/radio` | `.tale-radio` |
| Radio Group | `@tale-ui/react/radio-group` | — |
| Switch | `@tale-ui/react/switch` | `.tale-switch` |
| Toggle Button | `@tale-ui/react/toggle-button` | `.tale-toggle-button`, `--sm`, `--md`, `--lg` |
| Toggle Button Group | `@tale-ui/react/toggle-button` | `.tale-toggle-button-group` |
| Select | `@tale-ui/react/select` | `.tale-select__trigger`, `__popup`, `__item` |
| Combobox | `@tale-ui/react/combobox` | `.tale-combobox__input`, `__popup`, `__item` |
| Autocomplete | `@tale-ui/react/autocomplete` | `.tale-autocomplete__input`, `__popup`, `__item` |
| Number Field | `@tale-ui/react/number-field` | `.tale-number-field` |
| Slider | `@tale-ui/react/slider` | `.tale-slider` |
### Layout
| Component | Import path |
|-----------|------------|
| Accordion | `@tale-ui/react/accordion` |
| Disclosure | `@tale-ui/react/disclosure` |
| Tabs | `@tale-ui/react/tabs` |
| Scroll Area | `@tale-ui/react/scroll-area` |
| Separator | `@tale-ui/react/separator` |
### Overlay
| Component | Import path |
|-----------|------------|
| Dialog | `@tale-ui/react/dialog` |
| Alert Dialog | `@tale-ui/react/alert-dialog` |
| Popover | `@tale-ui/react/popover` |
| Drawer | `@tale-ui/react/drawer` |
| Tooltip | `@tale-ui/react/tooltip` |
| Preview Card | `@tale-ui/react/preview-card` |
### Navigation
| Component | Import path |
|-----------|------------|
| Menu | `@tale-ui/react/menu` |
| Context Menu | `@tale-ui/react/context-menu` |
| Menubar | `@tale-ui/react/menubar` |
| Navigation Menu | `@tale-ui/react/navigation-menu` |
| Toolbar | `@tale-ui/react/toolbar` |
### Feedback & Display
| Component | Import path |
|-----------|------------|
| ProgressBar | `@tale-ui/react/progress-bar` |
| Meter | `@tale-ui/react/meter` |
| Avatar | `@tale-ui/react/avatar` |
### Form Structure
| Component | Import path |
|-----------|------------|
| Field | `@tale-ui/react/field` |
| Fieldset | `@tale-ui/react/fieldset` |
| Form | `@tale-ui/react/form` |
### Utilities
| Export | Import path | Purpose |
|--------|------------|---------|
| Container | `@tale-ui/react/container` | Sets `--color-*` vars for a named/random palette |
| CSP Provider | `@tale-ui/react/csp-provider` | Content Security Policy nonce injection |
| I18nProvider | `@tale-ui/react/i18n-provider` | Locale and text direction (wraps React Aria's I18nProvider) |
| `mergeProps` | `@tale-ui/react/merge-props` | Merge multiple prop objects |
| `useRender` | `@tale-ui/react/use-render` | Custom render hook |
---
## Data Attributes for Styling
Components expose state via data attributes. Use these in CSS selectors:
| Attribute | Meaning |
|-----------|---------|
| `data-disabled` | Component is disabled |
| `data-open` | Popup / disclosure is open |
| `data-closed` | Popup / disclosure is closed |
| `data-checked` | Checkbox, radio, or switch is checked |
| `data-unchecked` | Checkbox, radio, or switch is unchecked |
| `data-selected` | Item is selected (select, combobox) |
| `data-highlighted` | Item has keyboard/pointer highlight |
| `data-focus-visible` | Keyboard focus is visible |
| `data-side="top\|bottom\|left\|right"` | Popup placement side |
| `data-starting-style` | Enter animation start |
| `data-ending-style` | Exit animation start |
| `data-popup-open` | Trigger element while its popup is open |
---
## Component Composition Patterns
Tale UI components use two composition patterns. Choose based on whether the component has built-in label/description parts.
### Pattern A: Compound components with built-in parts
Most form controls (Input, TextField, Select, Combobox) have their own Label, Description, and ErrorMessage parts:
```tsx
import { Input } from '@tale-ui/react/input';
Email addressWe'll never share your email.
```
React Aria automatically links the label to the input via `aria-labelledby` and the description via `aria-describedby`.
### Pattern B: Field wrapper for custom or plain controls
When using a plain `` or a component that doesn't have built-in label parts, wrap it with Field:
```tsx
import { Field } from '@tale-ui/react/field';
PasswordMust be at least 8 characters.This field is required.
```
### When to use which
| Situation | Use |
|-----------|-----|
| Using a Tale UI form control (Input, Select, etc.) | Pattern A — use the component's built-in `.Label`, `.Description` parts |
| Wrapping a plain ``, `