# Enma

> Enma is one design core with six themes on top (Graphite, Glass, Paper, Mineral, Enamel, Ember), each with a light and a dark mode. This file has everything an agent needs to build UI in Enma: rules, themes, token roles, class recipes for plain HTML and the React component API. Docs: https://enma.manishmandal.com

## Enma design

Enma is one core (spacing, type scale, components, states, motion, accessibility, rules) with six themes on top. A theme changes only the surface: color, fonts, radius, depth and how presses feel. Every screen should feel calm, precise and a little bit crafted.

### When to use this skill

- Building any UI for one of Manish's projects: React and Next.js apps, React Native, Chrome and VS Code extensions, freelance work, Claude artifacts.
- Making a mockup or prototype "in Enma".
- Reviewing a screen against Enma's rules.

For project setup, states, accessibility and the definition of done, also use the `enma-frontend` skill.

### 1. Pick the theme

Use the default for the project type. Override only with a reason, and write the decision down (`UI: Enma · ember · dark (home), toggle: no`).

| Project type                                    | Theme      | Home mode |
| ----------------------------------------------- | ---------- | --------- |
| B2B SaaS, dev tools, dashboards, internal tools | `graphite` | dark      |
| Consumer iOS-style apps, lifestyle, wellness    | `glass`    | light     |
| Learning, reading, writing, content             | `paper`    | light     |
| Finance, premium, trust-heavy, client work      | `mineral`  | light     |
| Playful side projects, landing pages, social    | `enamel`   | light     |
| Fitness, media, night-time use                  | `ember`    | dark      |

Every theme has light and dark; use the home mode unless the product needs a toggle. A project may change only three things: the accent color, the display font and the radius dial. Anything bigger is a new theme in the system. Details: [references/themes.md](references/themes.md).

### 2. Follow the core rules

- **One hero per screen:** a hero card with the big number, or a display headline. Everything else stays quiet.
- **One spark per view:** `--e-accent` marks one or two things (the highlighted bar, a status dot, the active tab). Never a background.
- **One primary button per view.** Siblings are `secondary` or `ghost`.
- **Numbers are the signature:** key figures use `Stat` in the theme's number face with tabular figures; units sit outside the number ("12,480 kg"); amounts right-align in tables.
- **Group with tone, not lines:** `raised` panels inside `surface` cards. Hairlines separate rows and table cells only (Paper is the exception: it is built from rules).
- **Smooth corners that nest:** `xl` frame, `lg` card, `md` group, `sm` input.
- **Physical motion, used sparingly:** presses spring back, numbers count up and bars grow once on first view, nothing loops, everything is instant under reduced motion.
- **Density follows the surface:** mobile is comfortable (44px targets, `lg` buttons, 16 to 20px padding); B2B is compact (`sm` toolbar controls, 13px table text, 12px labels).

### 3. Use tokens and roles, never raw values

Style everything with the `--e-*` roles, which always point at the active theme: `--e-bg`, `--e-surface`, `--e-raised`, `--e-field`, `--e-line`, `--e-line-strong`, `--e-ink`, `--e-ink-2`, `--e-primary`, `--e-on-primary`, `--e-accent`, `--e-on-accent`, `--e-accent-ink`, `--e-accent-soft`, `--e-positive`, `--e-positive-soft`, `--e-negative`, `--e-negative-soft`, `--e-hero`, `--e-on-hero`, `--e-on-hero-2`, `--e-track`, `--e-focus`, radius `--e-r-sm|md|lg|xl|control`, `--e-shadow-card`, fonts `--e-font-display|body|num|meta`. Shared scales: `--space-*`, `--control-sm|md|lg`, `--touch-min`, `--duration-*`, `--easing-*`. What each role means and the type scale: [references/tokens.md](references/tokens.md).

### 4. Build from components

Use the existing components before writing custom UI: `EnmaRoot`, `Button`, `IconButton`, `Icon`, `Badge`, `Card`, `Stat`, `Bars`, `LineChart`, `Sparkline`, `Donut`, `ListGroup`, `Table`, `TextField`, `Switch`, `Checkbox`, `SegmentedControl`, `TabBar`, `Avatar`, `Progress`, `Toast`. When to use each: [references/components.md](references/components.md). Without React (HTML, artifacts, other frameworks), use the class recipes: [references/recipes.md](references/recipes.md).

Missing something (dialog, menu, popover, combobox)? Build it on an accessible primitive (Radix UI or React Aria on web) and style it with Enma roles.

### 5. Write in Enma's voice

- Sentence case everywhere. Verbs first on actions: "Start workout", "New invoice", "Export".
- Say the outcome: "Invoice sent", "Saved". Never "successfully", "please" or exclamation marks.
- Errors say what happened and what to do, in one sentence: "The card was declined. Try another."
- Numbers carry units and context: "+12.4% this month", "4 × 8 · 80 kg".
- Placeholders are real examples ("Acme Labs"), not "e.g." or a repeat of the label.
- Status is always a word too ("Paid", "Overdue"), never color alone.

### Never

- Raw hex values or the raw per-theme tokens (`--ember-accent`) in product code.
- Two themes on one screen, or a theme re-tinted beyond the tweak budget.
- Blue-to-purple gradients, emoji as icons, cards with a colored left border, decorative blobs outside the canvas, glow on anything but the hero or primary.
- More than one primary button, hero or highlighted bar per view.
- Status shown by color alone, gray text below 4.5:1, focus rings removed.
- Animating everything. Motion marks a press, a first view or a change, nothing else.

### Reference

- Docs: https://enma.manishmandal.com (every page in every theme)
- Everything in one file: https://enma.manishmandal.com/enma.md

## Themes

Every theme defines the same roles in both modes, so code never changes when the theme does. Set the theme on the root: `<html data-enma="ember" data-theme="dark">` and `class="e-canvas"` on `<body>`, or `<EnmaRoot theme="ember">` in React.

| Theme      | Use it for                                      | Home  | Feel                                                                                |
| ---------- | ----------------------------------------------- | ----- | ----------------------------------------------------------------------------------- |
| `graphite` | B2B SaaS, dev tools, dashboards, internal tools | dark  | Precise instrument. Mono numerals, one electric lime signal.                        |
| `glass`    | Consumer iOS-style apps, lifestyle, wellness    | light | Frosted, layered, very round. Coral spark.                                          |
| `paper`    | Learning, reading, writing, content             | light | Editorial. Italic serif numbers, hairline rules, sharp corners, one vermilion mark. |
| `mineral`  | Finance, premium, trust-heavy, client work      | light | Calm and grounded. Forest hero, brass accent on sage.                               |
| `enamel`   | Playful side projects, landing pages, social    | light | Loud on purpose. Ink outlines, hard offset shadows, yellow and pink.                |
| `ember`    | Fitness, media, night-time use                  | dark  | Cinematic. A warm light source, amber on the key numbers and actions.               |

### Faces

| Theme    | Display              | Body            | Numbers                              |
| -------- | -------------------- | --------------- | ------------------------------------ |
| Graphite | Inter Tight 600      | Inter Tight     | JetBrains Mono 500                   |
| Glass    | Hanken Grotesk 700   | Hanken Grotesk  | Hanken Grotesk 700                   |
| Paper    | Instrument Serif 400 | Instrument Sans | Instrument Serif italic, scaled 1.36 |
| Mineral  | Sora 600             | Manrope         | Sora 600                             |
| Enamel   | Space Grotesk 700    | Space Grotesk   | Space Grotesk 700                    |
| Ember    | Geist 600            | Geist           | Geist 600                            |

Load only your theme's fonts (`@enma-design/css/fonts/<theme>.css` or `enma-design/fonts/<theme>`).

### Shape and depth

| Theme    | Radius sm / md / lg / xl / control | Depth                                                 |
| -------- | ---------------------------------- | ----------------------------------------------------- |
| Graphite | 8 / 12 / 18 / 30 / 10              | 1px ring plus a top highlight; long soft drop in dark |
| Glass    | 12 / 18 / 26 / 40 / pill           | Frosted blur, white inner highlight, soft drop        |
| Paper    | 2 / 4 / 6 / 10 / 3                 | None. Borders and rules only                          |
| Mineral  | 10 / 14 / 22 / 36 / 14             | White top highlight, soft green-tinted drop           |
| Enamel   | 10 / 14 / 18 / 28 / 14             | 2px ink outlines, hard 6px offset shadow              |
| Ember    | 10 / 16 / 24 / 38 / pill           | Hairline ring, warm glow behind the hero and primary  |

### Signatures you get for free

- The hero card: Graphite's dark slab, Glass's frosted panel, Paper's ruled block, Mineral's forest panel with a brass ring, Enamel's yellow plate, Ember's glow.
- The press: a spring scale on most themes, a hard push into the shadow on Enamel.
- The canvas: Graphite's dot grid, Glass's three soft light sources, Ember's warm glow. Never recreate them by hand.

### Tweak budget

A project may change the accent color, the display font and the radius dial, in one `overrides.css` loaded after Enma. The selectors must mirror the theme map so the override wins everywhere:

```css
[data-enma='ember'],
[data-enma='ember'][data-theme],
[data-enma='ember'] [data-theme]:not([data-enma]) {
  --e-r-control: 14px;
}
```

Re-check contrast for any color you change: text 4.5:1, controls, focus and chart marks 3:1.

## Tokens and roles

`tokens.json` (in `@enma-design/tokens`) is the source of truth. Product code reads the `--e-*` roles; the raw per-theme tokens (`--ember-accent`, `--paper-radius-lg`) exist only so the theme maps can point at them.

### Color roles

Every theme defines every role in light and dark.

| Role            | Variable            | Use                                                                                  |
| --------------- | ------------------- | ------------------------------------------------------------------------------------ |
| `bg`            | `--e-bg`            | The canvas behind everything.                                                        |
| `surface`       | `--e-surface`       | Cards, sheets, the phone frame, table wrappers.                                      |
| `raised`        | `--e-raised`        | Grouped content inside a surface: list groups, inner panels, hover rows.             |
| `field`         | `--e-field`         | Input and secondary-button fill.                                                     |
| `line`          | `--e-line`          | Hairlines between rows and table cells. Decorative, never a control's only boundary. |
| `line-strong`   | `--e-line-strong`   | Control borders: inputs, secondary buttons, unchecked boxes. 3:1 or more.            |
| `ink`           | `--e-ink`           | Primary text and icons on bg, surface, raised and field. 4.5:1 or more.              |
| `ink-2`         | `--e-ink-2`         | Secondary text, labels, meta and placeholders. 4.5:1 or more.                        |
| `primary`       | `--e-primary`       | The call to action fill. One per view.                                               |
| `on-primary`    | `--e-on-primary`    | Text and icons on primary.                                                           |
| `accent`        | `--e-accent`        | The spark: highlighted bar, status dot, active mark. Once or twice per view.         |
| `on-accent`     | `--e-on-accent`     | Text on an accent fill.                                                              |
| `accent-ink`    | `--e-accent-ink`    | Accent as text or icon (links, active tab) on surfaces or accent-soft.               |
| `accent-soft`   | `--e-accent-soft`   | Tinted background behind accent-ink (accent badges, avatar fallback).                |
| `positive`      | `--e-positive`      | Positive status text, on positive-soft or surface.                                   |
| `positive-soft` | `--e-positive-soft` | Background of positive badges and deltas.                                            |
| `negative`      | `--e-negative`      | Negative status and error text, on negative-soft or surface.                         |
| `negative-soft` | `--e-negative-soft` | Background of negative badges and danger buttons.                                    |
| `hero`          | `--e-hero`          | Fill of the one hero card per screen.                                                |
| `on-hero`       | `--e-on-hero`       | Text and the big number on the hero.                                                 |
| `on-hero-2`     | `--e-on-hero-2`     | Secondary text on the hero.                                                          |
| `track`         | `--e-track`         | Unfilled chart bars and progress tracks on the hero.                                 |
| `focus`         | `--e-focus`         | The keyboard ring: 2px with a 2px gap in the page color, 3:1 or more.                |

Never put `ink-2` on accent or hero fills. Status always carries a word too.

### Type scale

One scale for every theme; the theme picks the faces. Use the helper classes instead of setting families by hand.

| Style      | Size / line | Class                    | Face                     |
| ---------- | ----------- | ------------------------ | ------------------------ |
| display-xl | 48 / 52     | `e-display-xl`           | display, once per screen |
| display    | 34 / 40     | `e-display`              | display                  |
| title      | 22 / 28     | `e-title`                | display                  |
| heading    | 17 / 24     | `e-heading`              | body, 600                |
| body-lg    | 16 / 24     | `e-body-lg`              | body                     |
| body       | 14 / 20     | `e-body`                 | body                     |
| label      | 13 / 18     | `e-label`                | body, 500                |
| caption    | 12 / 16     | `e-caption`              | body, ink-2              |
| micro      | 11 / 14     | (axis and tab labels)    | body                     |
| num-hero   | 56          | `Stat size="hero"`       | number face, tabular     |
| num-kpi    | 30          | `Stat size="kpi"`        | number face, tabular     |
| num-inline | 13          | `e-meta`, table `is-num` | meta face, tabular       |

Also `e-eyebrow` (small label, uppercase in Paper) and `e-num` (number face on any element). Display sizes track tight; body text never does. Paper sets display sizes larger (56, 40, 27) and numbers 1.36 times larger.

### Spacing, sizes and radius

- Spacing is a 4px grid: `--space-1` 4, `--space-2` 8, `--space-3` 12, `--space-4` 16, `--space-5` 20, `--space-6` 24, `--space-8` 32, `--space-10` 40, `--space-12` 48, `--space-16` 64, plus `--space-half` 2.
- Controls: `--control-sm` 32, `--control-md` 40, `--control-lg` 48. Touch targets `--touch-min` 44. Icons 16, 20 and 24.
- Radius nests: `--e-r-xl` frame, `--e-r-lg` card, `--e-r-md` group, `--e-r-sm` input, `--e-r-control` buttons and segmented controls.

### Motion

- Durations: `--duration-instant` 90ms (hover color), `--duration-fast` 140ms (borders, focus), `--duration-base` 220ms (switches, menus), `--duration-slow` 360ms (sheets), `--duration-press` 450ms (spring return), `--duration-chart` 900ms (bars grow), `--duration-count` 1100ms (numbers count up).
- Curves: `--easing-out` (entering, settling), `--easing-spring` (press release, toggles), `--easing-snap` (quick B2B changes), `--easing-chart` (bars growing).
- Animate only `transform` and `opacity`. Everything collapses to instant under `prefers-reduced-motion`.

### Shadows

`--e-shadow-card` lifts surfaces and `--e-shadow-cta` sits under the primary button. Paper has none. Never invent shadows a theme does not define.

## Class recipes (plain HTML)

The same markup the React components render. Load `@enma-design/css` (or inline `enma.css` in an artifact), set the root and use these classes. Class prefix `e-`.

```html
<html lang="en" data-enma="paper" data-theme="light">
  <body class="e-canvas">
    ...
  </body>
</html>
```

```html
<!-- Buttons: variant primary | secondary | ghost | danger, size sm | md | lg -->
<button type="button" class="e-btn e-sq e-btn--primary e-btn--md">Start workout</button>
<button type="button" class="e-btn e-sq e-btn--secondary e-btn--sm">Export</button>

<!-- Icon button: always an aria-label -->
<button
  type="button"
  class="e-iconbtn e-sq e-iconbtn--ghost e-iconbtn--md"
  aria-label="Settings"
  title="Settings"
>
  <svg
    class="e-icon"
    width="20"
    height="20"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="1.75"
    stroke-linecap="round"
    stroke-linejoin="round"
    aria-hidden="true"
  >
    <path d="M12 5v14M5 12h14" />
  </svg>
</button>

<!-- Cards: e-card--hero for the one hero, e-card--raised inside a card, e-card--frame for phone shells -->
<div class="e-card e-sq e-card--hero e-card--pad-lg">
  <div class="e-stat e-stat--hero">
    <div class="e-stat__label">Weekly volume</div>
    <div class="e-stat__row">
      <span class="e-stat__value"
        ><span aria-hidden="true">12,480</span><span class="e-visually-hidden">12,480 kg</span></span
      >
      <span class="e-stat__unit" aria-hidden="true">kg</span>
      <span class="e-badge e-badge--positive e-stat__delta">+8%</span>
    </div>
  </div>
  <!-- add the class is-in after load to make the bars grow; the label is the chart's text summary -->
  <div
    class="e-bars is-in"
    style="height:64px;margin-top:18px"
    role="img"
    aria-label="Weekly volume: Monday 40, Tuesday 72, Wednesday 100"
  >
    <i class="e-bars__bar" style="height:40%"></i><i class="e-bars__bar" style="height:72%"></i
    ><i class="e-bars__bar is-on" style="height:100%"></i>
  </div>
</div>

<!-- Badge: tone neutral | accent | positive | negative -->
<span class="e-badge e-badge--positive"><span class="e-badge__dot" aria-hidden="true"></span>Paid</span>

<!-- List group -->
<ul class="e-list e-sq">
  <li>
    <div class="e-list__row">
      <span class="e-list__text"><span>Bench press</span></span
      ><span class="e-list__meta">4 × 8 · 80 kg</span>
    </div>
  </li>
  <li>
    <a class="e-list__row e-list__row--action" href="/settings"
      ><span class="e-list__text"><span>Settings</span></span></a
    >
  </li>
</ul>

<!-- Text field -->
<div class="e-field">
  <label class="e-field__label" for="ws">Workspace name</label>
  <div class="e-field__control e-sq">
    <input class="e-field__input" id="ws" placeholder="Acme Labs" aria-describedby="ws-hint" />
  </div>
  <div class="e-field__hint" id="ws-hint">Shown on invoices.</div>
</div>

<!-- Table: is-end right-aligns, is-num uses tabular figures, is-muted for quiet columns -->
<div class="e-table-wrap e-sq">
  <table class="e-table">
    <caption class="e-visually-hidden">
      Invoices
    </caption>
    <thead>
      <tr>
        <th scope="col">Customer</th>
        <th scope="col" class="is-end">Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Northwind</td>
        <td class="is-end is-num">$1,240.00</td>
      </tr>
    </tbody>
  </table>
</div>

<!-- Switch and checkbox -->
<label class="e-switch"
  ><input class="e-switch__input" type="checkbox" role="switch" checked />
  <span class="e-switch__track" aria-hidden="true"><span class="e-switch__thumb"></span></span
  ><span>Rest timer</span></label
>
<label class="e-check"
  ><input class="e-check__input" type="checkbox" />
  <span class="e-check__box" aria-hidden="true"
    ><svg
      class="e-icon"
      width="14"
      height="14"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2.5"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M5 12.5l4.5 4.5L19 7" /></svg></span
  ><span>Warm-up done</span></label
>

<!-- Progress -->
<div class="e-progress">
  <div class="e-progress__head">
    <span id="goal">Weekly goal</span><span class="e-progress__value">4 of 5 sessions</span>
  </div>
  <div
    class="e-progress__track"
    role="progressbar"
    aria-labelledby="goal"
    aria-valuemin="0"
    aria-valuemax="100"
    aria-valuenow="80"
    aria-valuetext="4 of 5 sessions"
  >
    <div class="e-progress__fill" style="width:80%"></div>
  </div>
</div>

<!-- Toast: role status, or alert for errors -->
<div class="e-toast e-sq e-toast--positive" role="status">
  <span class="e-toast__icon" aria-hidden="true"
    ><svg
      class="e-icon"
      width="16"
      height="16"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2.25"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M5 12.5l4.5 4.5L19 7" /></svg
  ></span>
  <div class="e-toast__body">
    <div class="e-toast__title">Invoice sent</div>
    <div class="e-toast__desc">Northwind will get it in a minute.</div>
  </div>
</div>
```

Type helpers: `e-display-xl`, `e-display`, `e-title`, `e-heading`, `e-body-lg`, `e-body`, `e-label`, `e-caption`, `e-eyebrow`, `e-num`, `e-meta`. `e-visually-hidden` keeps text for screen readers only.

Your own elements: use only `--e-*`, `--space-*`, `--control-*`, `--duration-*` and `--easing-*`.

```css
.screen {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
}
.panel {
  background: var(--e-surface);
  border-radius: var(--e-r-lg);
  box-shadow: var(--e-shadow-card);
}
```

## Components

Each component exists in React (`@enma-design/react` or `enma-design/react`) and as plain HTML classes (see recipes). Props match `index.d.ts`; the full API is in `enma.md`.

### Layout and containers

- **EnmaRoot:** sets the theme (`data-enma`) and mode (`data-theme`) for everything inside. One at the app root. `mode="home"` uses the theme's best mode; `canvas` paints the theme background. Don't nest roots to restyle one card.
- **Card:** `surface` (default container), `raised` (a group inside a surface), `hero` (the one hero per screen, flips text to on-hero and shows the theme's signature), `frame` (phone-level shell). Padding `none`, `sm` 12, `md` 16, `lg` 20. Don't stack cards inside cards inside cards.

### Actions

- **Button:** `secondary` by default, `primary` at most once per view, `ghost` for toolbars and low emphasis, `danger` for destructive actions. `size="lg"` with `block` for the main mobile action, `sm` in dense toolbars. `loading` shows a spinner and ignores clicks. Labels are verbs in sentence case, never "Click here" or "Submit".
- **IconButton:** one icon in a square button for toolbars and headers. `label` is required (accessible name and tooltip). Use a text button when a word is clearer.
- **Icon:** outline icons on a 24px grid, 1.75 stroke, `currentColor`, sizes 16, 20, 24. Give `label` only when it stands alone and carries meaning. In apps use Lucide at the same stroke. Never emoji.

### Numbers and data

- **Stat:** a number with label, unit, delta and sub line in the theme's number face. `hero` for the one big number (inside a hero card), `kpi` for tiles, `inline` inside rows. Counts up once on first view; screen readers get the full value at once. Units outside the number. Don't animate values that update every second.
- **Bars:** a short series (up to about 14 values) with one highlighted bar (default the last). No axes or gridlines. Always has a text summary.
- **LineChart:** a series over time, with an optional dashed comparison series ("this period vs last") and a soft area fill. Show a few axis labels, not every point.
- **Sparkline:** a word-sized trend beside a number, in a table cell or a KPI tile.
- **Donut:** one share of a whole (a goal, a course, a quota), with the value in the center. One per group, never a pie of many slices.
- **Progress:** a labelled bar for goals, uploads and limits, 0 to 100, with `valueText` like "4 of 5 sessions". Not for indeterminate waits; at most three stacked.
- **Table:** B2B data with quiet headers, hairline rows and right-aligned tabular numbers (`align: 'end'`, `numeric`). Status in a Badge. Always a `caption`. No zebra stripes, vertical borders or centered numbers. `framed={false}` inside a card.
- **Badge:** one or two words of status or a delta ("Paid", "+8%"), tones `neutral`, `accent`, `positive`, `negative`. A word, never color alone; at most two per row.

### Lists and navigation

- **ListGroup:** grouped rows on a raised panel (settings, exercises, recent items) with `ListGroup.Row` (`title`, `description`, `meta`, `leading`, `trailing`, `onClick` or `href` for a chevron). Rows are at least 44px. Don't mix actionable and static rows without a cue.
- **TabBar:** bottom navigation for mobile apps, three to five destinations, with labels. Never actions like "New" in the tab bar.
- **SegmentedControl:** two to four mutually exclusive views of the same content (Week, Month, Year). One tab stop; arrow keys move. Not for navigation between unrelated pages.

### Forms

- **TextField:** a labelled input with optional icon, hint and error. The error replaces the hint and says what to do ("Enter a full email address."). `sm` in toolbars, `lg` on mobile forms. Placeholders are real examples.
- **Switch:** an on/off setting that applies immediately. Use Checkbox when there is a Save button.
- **Checkbox:** a choice confirmed later, or a checklist item.

### Feedback and people

- **Toast:** a short confirmation or error after an action ("Invoice sent"), `neutral`, `positive` or `negative`, with at most one action. Bottom center on mobile, bottom right on desktop, about 4 seconds. Never the only place for a critical error.
- **Avatar:** a person's photo or initials, `sm` 28, `md` 36, `lg` 48. No decorative rings.

## Component API (React)

Generated from `packages/react/src/types.ts`.

### EnmaRoot

Sets the theme (data-enma) and mode (data-theme) for everything inside. Put one at the app root.

Also accepts: React.HTMLAttributes<HTMLElement>.

- `theme`: `EnmaTheme` (default 'graphite'). Which theme. Default 'graphite'.
- `mode`: `EnmaMode` (default 'home'). 'home' uses the theme's best mode (Graphite and Ember dark, the rest light). 'inherit' leaves data-theme to an ancestor. Default 'home'.
- `canvas`: `boolean` (default true). Paint the theme canvas (bg plus Glass glows, Ember light, Graphite dot grid). Default true.
- `as`: `keyof React.JSX.IntrinsicElements`

### Button

Also accepts: React.ButtonHTMLAttributes<HTMLButtonElement>.

- `variant`: `'primary' | 'secondary' | 'ghost' | 'danger'` (default 'secondary'). primary: one per view. secondary: default. ghost: toolbars and low emphasis. danger: destructive.
- `size`: `'sm' | 'md' | 'lg'` (default 'md')
- `block`: `boolean`
- `loading`: `boolean`
- `iconStart`: `IconName`
- `iconEnd`: `IconName`
- `as`: `'button' | 'a'` (default 'button')
- `href`: `string`

### IconButton

Also accepts: React.ButtonHTMLAttributes<HTMLButtonElement>.

- `icon`: `IconName` (required)
- `label`: `string` (required). Required accessible name, also shown as the tooltip.
- `variant`: `'ghost' | 'secondary' | 'primary'` (default 'ghost')
- `size`: `'sm' | 'md' | 'lg'` (default 'md')

### Icon

- `name`: `IconName` (required)
- `size`: `'sm' | 'md' | 'lg' | number` (default 'md')
- `strokeWidth`: `number` (default 1.75)
- `label`: `string`. Give a label only when the icon carries meaning on its own.
- `className`: `string`
- `style`: `React.CSSProperties`

### Badge

Also accepts: React.HTMLAttributes<HTMLSpanElement>.

- `tone`: `Tone` (default 'neutral')
- `dot`: `boolean`
- `children`: `React.ReactNode`

### Card

Also accepts: React.HTMLAttributes<HTMLElement>.

- `variant`: `'surface' | 'raised' | 'hero' | 'frame'` (default 'surface'). surface: default container. raised: grouped inner panel. hero: the one hero per screen. frame: phone-level container.
- `padding`: `'none' | 'sm' | 'md' | 'lg'` (default 'md')
- `as`: `keyof React.JSX.IntrinsicElements`

### Stat

Also accepts: React.HTMLAttributes<HTMLDivElement>.

- `label`: `React.ReactNode`
- `value`: `number | string` (required). A number counts up on first view; a string renders as is.
- `decimals`: `number` (default 0)
- `prefix`: `string`
- `suffix`: `string`
- `unit`: `string`
- `delta`: `string`
- `deltaTone`: `'positive' | 'negative' | 'neutral' | 'accent'` (default 'positive')
- `sub`: `React.ReactNode`
- `size`: `'hero' | 'kpi' | 'inline'` (default 'kpi'). hero: the one big number per screen. kpi: tiles. inline: inside rows.
- `animate`: `boolean` (default true)

### Bars

- `data`: `number[]` (required)
- `labels`: `string[]`
- `highlight`: `number` (default the last one). Index of the highlighted bar. Default: the last one.
- `max`: `number`
- `height`: `number` (default 64)
- `label`: `string` (default lists every label and value). Accessible summary. Default lists every label and value.
- `className`: `string`
- `style`: `React.CSSProperties`

### ListRow

`title` is a React node here, so the DOM's string `title` attribute is left out.

Also accepts: Omit<React.HTMLAttributes<HTMLElement>, 'title'>.

- `title`: `React.ReactNode`
- `description`: `React.ReactNode`
- `meta`: `React.ReactNode`
- `leading`: `React.ReactNode`
- `trailing`: `React.ReactNode`
- `href`: `string`

### ListGroup

Also accepts: React.HTMLAttributes<HTMLUListElement>.

- `children`: `React.ReactNode`

### TableColumn

- `key`: `string` (required)
- `label`: `React.ReactNode` (required)
- `align`: `'start' | 'end'`
- `numeric`: `boolean`
- `muted`: `boolean`
- `render`: `(row: R) => React.ReactNode`

### Table

- `columns`: `TableColumn<R>[]` (required)
- `rows`: `R[]` (required)
- `rowKey`: `string`
- `caption`: `string`
- `framed`: `boolean` (default true). Draw the surface, border and shadow around the table. Default true.
- `className`: `string`
- `style`: `React.CSSProperties`

### TextField

Also accepts: Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>.

- `label`: `React.ReactNode`
- `hint`: `React.ReactNode`
- `error`: `React.ReactNode`
- `icon`: `IconName`
- `size`: `'sm' | 'md' | 'lg'`

### Switch

Also accepts: React.InputHTMLAttributes<HTMLInputElement>.

- `label`: `React.ReactNode`

### Checkbox

Also accepts: React.InputHTMLAttributes<HTMLInputElement>.

- `label`: `React.ReactNode`

### SegmentedControl

- `options`: `{ value: string; label: React.ReactNode }[]` (required)
- `value`: `string`
- `defaultValue`: `string`
- `onChange`: `(value: string) => void`
- `label`: `string`. Accessible name of the group.
- `block`: `boolean`
- `className`: `string`
- `style`: `React.CSSProperties`

### TabBar

- `items`: `{ key: string; label: string; icon: IconName }[]` (required)
- `active`: `string`
- `defaultActive`: `string`
- `onChange`: `(key: string) => void`
- `showLabels`: `boolean` (default true)
- `label`: `string` (default 'Main')
- `className`: `string`
- `style`: `React.CSSProperties`

### Avatar

- `name`: `string` (required)
- `src`: `string`
- `size`: `'sm' | 'md' | 'lg'` (default 'md')
- `className`: `string`
- `style`: `React.CSSProperties`

### Progress

- `value`: `number` (required)
- `label`: `React.ReactNode`
- `valueText`: `string`
- `showValue`: `boolean` (default true)
- `className`: `string`
- `style`: `React.CSSProperties`

### Toast

- `title`: `React.ReactNode` (required)
- `description`: `React.ReactNode`
- `tone`: `'neutral' | 'positive' | 'negative'` (default 'neutral')
- `icon`: `IconName`
- `action`: `{ label: string; onClick?: () => void }`
- `className`: `string`
- `style`: `React.CSSProperties`

### LineChart

A line or area chart for a series over time, with an optional comparison series.

- `data`: `number[]` (required)
- `compare`: `number[]`. A second series drawn as a quiet dashed line, for "this period vs last period".
- `labels`: `string[]`. Axis labels under the chart. Show a few (first, middle, last) rather than every point.
- `height`: `number` (default 160). Height in px. Default 160.
- `area`: `boolean` (default true). Fill the area under the line with a soft tint of the series color. Default true.
- `min`: `number` (default 0, or the lowest value when the data goes below 0). Lowest value on the scale. Default 0, or the lowest value when the data goes below 0.
- `max`: `number` (default the highest value). Highest value on the scale. Default: the highest value.
- `label`: `string` (default describes the range, the latest value and the peak). Accessible summary. Default describes the range, the latest value and the peak.
- `className`: `string`
- `style`: `React.CSSProperties`

### Sparkline

A word-sized line chart for a trend beside a number.

- `data`: `number[]` (required)
- `width`: `number` (default 96). Width in px. Default 96.
- `height`: `number` (default 28). Height in px. Default 28.
- `label`: `string` (default describes the first and latest values). Accessible summary. Default describes the first and latest values.
- `className`: `string`
- `style`: `React.CSSProperties`

### Donut

A ring for one share of a whole: a goal, a course, a quota.

- `value`: `number` (required). 0 to 100.
- `label`: `string`. What the ring measures, shown in the center under the value and used as the accessible name.
- `valueText`: `string` (default the rounded percentage). Text for the value, for example "4 of 5". Default: the rounded percentage.
- `size`: `number` (default 96). Diameter in px. Default 96.
- `thickness`: `number` (default 10). Ring thickness in px. Default 10.
- `className`: `string`
- `style`: `React.CSSProperties`
