# Mittsu (@a3tai/mittsu) — Full reference
> Source-first Svelte 5 component and chart library tuned to feel native on macOS / iOS. The CLI drops raw .svelte + .css files into your project — you own the source, override CSS-variable tokens (the --uin-* namespace), and fork without waiting on a release. Components ship with BEM class names, no Svelte scoping, and headless behavior from bits-ui where it matters. Designed with LLM-friendly APIs: named variants, plain data arrays, predictable prop shapes.
This file concatenates the README and every doc page so an LLM can ingest
the full Mittsu reference in a single fetch. Per-component prop tables and
live examples live on the website at the linked URLs.
- Site: https://mittsu.dev
- Repository: https://github.com/a3tai/mittsu
---
# README
# @a3tai/mittsu
> **Native-ish UI primitives. Copy into your project. Own the source.**
[](#status)
[](LICENSE)
[](https://svelte.dev)
[](ROADMAP.md)
[Website](https://mittsu.dev) · [GitHub](https://github.com/a3tai/mittsu) · Sponsored by [A3T](https://a3t.ai)
A shadcn-style component library tuned to feel native on macOS / iOS,
extensible to React and Vue, and shipped through a CLI that drops raw
source into your project. Tokens are CSS variables; chrome is BEM
classes; the moment you need to change something, you fork the file.
## Quick start
```bash
# In your Svelte 5 project:
npx @a3tai/mittsu@latest init
npx @a3tai/mittsu@latest add button badge spinner
```
Then in your code:
```svelte
```
## Why another component library?
| | shadcn/ui | Mantine / Chakra | **@a3tai/mittsu** |
|---|---|---|---|
| Distribution | Copy via CLI | npm dependency | **Copy via CLI** |
| Framework | React | React | **Svelte 5 (React/Vue planned)** |
| Aesthetic | Tailwind, neutral | Branded | **macOS/iOS native, retunable** |
| Theming | Tailwind classes | Theme provider | **CSS variable tokens** |
| Headless deps | Radix | None / own | **Bits UI (where needed)** |
| AI-native components | none | none | **First-class** |
| You own the source | yes | no | **yes** |
## What's in the box
### Foundation
`Button` · `Input` · `SearchInput` · `Textarea` · `Label` · `Spinner` · `Card` · `Separator`
### Display
`Badge` · `Chip` · `Lamp` · `ProgressBar` · `Stat` · `Kbd`
### Navigation & Actions
`NavItem` · `Tabs` · `SegmentedControl` · `StatusRow`
### AI-native
`ChatMessage` · `ChatStream` · `PromptInput` · `TypingDots` · `ToolCallCard` ·
`ToolResultCard` · `AgentTimeline` · `CitationLink` · `DiffView` · `AIBadge`
Built for the shapes LLM APIs already return: `ToolCallCard` and
`AgentTimeline` take the tool-call array straight off a message, `DiffView`
takes the flat line list a model emits for an edit, and `ChatStream` handles
the streaming semantics (`aria-busy` while tokens arrive, auto-follow that
yields when the reader scrolls up).
### Charts
`AreaChart` · `LineChart` · `BarChart` · `DonutChart` · `RadarChart` ·
`RadialChart` live in `packages/charts` and use D3 for scales, paths, arcs,
radar geometry, and stacking. Shared `ChartFrame` and `ChartTooltip` primitives
keep legends, metrics, tooltips, and accessible data tables consistent.
94 components ship today; the sections above are a selection. A further 29 are
queued on the [roadmap](ROADMAP.md), grouped by:
Foundation · Display · Navigation · Actions · Form · Overlay · Layout
· Data · Media · **AI-native** · Marketing · Collaborative · macOS
extras · Hardware/desktop chrome.
## Showcase
A full SvelteKit demo lives in [`apps/web`](apps/web/). Run locally:
```bash
npm install
npm --workspace apps/web run dev
```
Pages: `/` (landing), `/components` (kitchen sink), `/charts` (chart gallery),
`/docs` (getting started).
## Repo layout
```
mittsu/
├── README.md · LICENSE · ROADMAP.md
├── docs/ ← markdown reference docs
├── .claude/skills/ ← repo-aware AI skills (add-component, port-component, release, …)
├── packages/
│ ├── cli/ ← @a3tai/mittsu CLI (Node, zero-dep)
│ └── svelte5/ ← @a3tai/mittsu-svelte5 component sources
│ ├── tokens/ ← design tokens (CSS variables)
│ ├── lib/ ← cn() utility
│ ├── components/ ← one folder per component (.svelte + .css)
│ └── registry.json ← CLI manifest
└── apps/
└── web/ ← SvelteKit showcase + future public site
```
## Architecture decisions
- **Per-component CSS file**, BEM-style class names, no Svelte scoping.
Consumers import CSS once globally; classes can be overridden at any
layer. CSS travels with the component when copied.
- **Composition over duplication**. `SearchInput` composes `Input` +
`Kbd`; the CLI follows component dependencies and pulls siblings
along automatically. No magic re-exports.
- **Tokens, not theme files**. 37 CSS variables under the `--uin-*`
namespace cover color, type, spacing, radius, motion, and focus.
Override at any scope to retheme. Dark mode via `prefers-color-scheme`
+ `data-theme="dark"` opt-in.
- **CLI is zero-dep Node**. `npx @a3tai/mittsu` doesn't pay an
install hop the first time someone tries it.
- **Imports rewritten on copy**. `../../lib/cn` is rewritten to point at
whatever path you've configured in `components.json`, so the file
Just Works after copy.
- **Headless where it matters**. Dialog, Popover, Tooltip, Combobox,
Calendar will compose [`bits-ui`](https://bits-ui.com/) — same role
Radix plays for shadcn — when they ship.
## Documentation
| Page | What it covers |
| --- | --- |
| [docs/getting-started.md](docs/getting-started.md) | Install, init, add your first component |
| [docs/cli.md](docs/cli.md) | Full CLI reference |
| [docs/tokens.md](docs/tokens.md) | Every design token + theming recipes |
| [docs/architecture.md](docs/architecture.md) | How the registry, CLI, and rewriter fit together |
| [docs/component-authoring.md](docs/component-authoring.md) | Writing a new component the right way |
| [docs/contributing.md](docs/contributing.md) | PR conventions, review checklist |
| [docs/components/](docs/components/) | Per-component reference pages |
| [ROADMAP.md](ROADMAP.md) | The full ~155-component catalog |
| [PLAN.md](PLAN.md) | Wave-based build order for the next-batch components |
## Status
**Pre-release.** APIs may change. The CLI is functional against a
local registry; the public hosted registry is not yet up. Not
recommended for production use until the v0.1 tag.
## Contributing
PRs welcome. See [`docs/contributing.md`](docs/contributing.md). The
quickest way to contribute a new component is via the
[`add-component`](.claude/skills/add-component/SKILL.md) Claude skill —
it scaffolds the .svelte + .css + registry entry from a single prompt.
## License
MIT. See [LICENSE](LICENSE).
---
# docs/getting-started.md
# Getting started
This guide walks you from "empty project" to "first component
rendering" in about five minutes.
## Prerequisites
- Node.js ≥ 18
- A Svelte 5 project (SvelteKit or any Svelte 5 + Vite setup)
## 1. Initialize
From your project root:
```bash
npx @a3tai/mittsu@latest init
```
This does three things:
1. Writes `components.json` at your project root (CLI config).
2. Copies the design tokens (`tokens.css`) into your project.
3. Copies the `cn` class-merger utility into your project.
After init, your tree looks like:
```
your-project/
├── components.json
├── src/lib/styles/tokens.css ← imported once
├── src/lib/utils/cn.ts ← used by components
└── ...
```
You can override any of those paths via `components.json` (see
[CLI reference](cli.md#configuration)).
## 2. Import the tokens once
Pull the tokens into your global stylesheet or app entry. For a
SvelteKit project, the cleanest spot is `src/routes/+layout.svelte`:
```svelte
```
Or `src/app.html` / `src/app.css`. The tokens declare ~37 CSS
variables under the `--uin-*` namespace; every component uses them.
## 3. Add components
Pull components in à-la-carte:
```bash
npx @a3tai/mittsu@latest add button badge spinner
```
Sibling dependencies follow automatically. Adding `search-input`
brings `input` and `kbd` along, for example. Each component lands as a
folder containing the `.svelte` source and a `.css` file:
```
src/lib/components/ui/
├── button/
│ ├── button.svelte
│ └── button.css
├── badge/
│ ├── badge.svelte
│ └── badge.css
└── spinner/
├── spinner.svelte
└── spinner.css
```
## 4. Wire the component CSS
Each component's CSS is global (no Svelte scoping). You can import
all of them at once from your app entry, or import per-route /
per-component. The simplest path is global:
```css
/* src/app.css */
@import './lib/styles/tokens.css';
@import './lib/components/ui/button/button.css';
@import './lib/components/ui/badge/badge.css';
@import './lib/components/ui/spinner/spinner.css';
```
## 5. Use the components
```svelte
New
```
That's it. Every component is plain Svelte 5 + token-driven CSS, so
forward attributes, bind values, and slot snippets work normally.
## Where next?
- [CLI reference](cli.md) — every command + flag
- [Design tokens](tokens.md) — full token list + theming recipes
- [Component authoring](component-authoring.md) — when you fork a
component or write a new one
- [Architecture](architecture.md) — how the registry / CLI / rewriter
fit together
---
# docs/cli.md
# CLI reference
The `@a3tai/mittsu` CLI is a zero-dependency Node script. Every
command is invokable via `npx @a3tai/mittsu` or, after a global
install, just `mittsu`.
## Commands
### `init`
```bash
mittsu init [flags]
```
Writes `components.json`, copies `tokens.css` into `paths.tokens`, and
copies `cn.ts` into `paths.lib`. Idempotent — re-running on an existing
project is a no-op unless you pass `--force`.
| Flag | Effect |
| --- | --- |
| `--registry ` | Override the registry source |
| `--target ` | Run against a project other than `cwd` |
| `--components-dir ` | Default for `paths.components` (where `add` will land files) |
| `--tokens-dir ` | Default for `paths.tokens` |
| `--lib-dir ` | Default for `paths.lib` |
| `--force, -f` | Overwrite existing files |
| `--dry-run, -n` | Print actions without writing |
### `add `
```bash
mittsu add button badge spinner [flags]
```
Copies one or more components into `paths.components//`. The CLI:
1. Resolves component names against the registry.
2. Recursively pulls in any **sibling** components a name depends on
(e.g. `search-input` → `input` + `kbd`).
3. Fetches each file (HTTPS or local path) and writes it to the
consumer's project.
4. Rewrites `../../lib/` imports so copied components resolve shared
utilities from wherever `paths.lib` is configured.
5. Leaves sibling-component imports alone (since the consumer keeps
the same `/.svelte` shape).
Flags: same as `init`. `--force` overwrites existing files. Registries can
also declare `externalDependencies`; the CLI prints the install command after
copying those components.
### `list`
```bash
mittsu list [--registry ]
```
Prints every component the registry exposes, sorted alphabetically,
with a one-line description.
### `help`
```bash
mittsu help
```
Prints command summaries.
### Global flags
| Flag | Effect |
| --- | --- |
| `--version, -v` | Print version and exit |
## Configuration
The CLI reads config from `components.json` at the consumer project
root. `init` writes it; `add` reads it.
```json
{
"framework": "svelte5",
"registry": "https://raw.githubusercontent.com/a3tai/mittsu/main/packages/svelte5/registry.json",
"paths": {
"components": "src/lib/components/ui",
"tokens": "src/lib/styles",
"lib": "src/lib/utils"
}
}
```
Lookup precedence:
1. CLI flag (highest priority)
2. `components.json` value
3. Hard-coded defaults (lowest)
## Local development
If you've cloned the repo and want to test the CLI against the in-tree
registry without publishing:
```bash
node packages/cli/bin/mittsu.js list \
--registry ./packages/svelte5/registry.json
node packages/cli/bin/mittsu.js add search-input \
--registry ./packages/svelte5/registry.json \
--target /tmp/mittsu-smoke
```
This is the same path CI uses for end-to-end verification.
## Registry format
The registry is a single JSON file. Structure:
```json
{
"name": "@a3tai/mittsu-svelte5",
"version": "0.0.1",
"framework": "svelte5",
"tokens": { "files": ["tokens/tokens.css"] },
"lib": { "files": ["lib/cn.ts"] },
"components": {
"button": {
"name": "button",
"description": "Primary action element …",
"files": [
"components/button/button.svelte",
"components/button/button.css"
],
"dependencies": ["lib/cn"]
}
}
}
```
`dependencies` lists either `lib/` (handled by the lib block) or
the `name` of another component the registry exposes. Cyclic deps are
not detected today; don't write any.
## Exit codes
| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Unknown command, missing args, registry fetch failure, write failure |
---
# docs/tokens.md
# Design tokens
Every visual property in `@a3tai/mittsu` reads from a CSS variable
under the `--uin-*` namespace. The defaults map to macOS system
materials so components feel at home in a desktop app without
additional theming work; override at any scope to retheme.
## Where they live
After `init`, the file lives at `/tokens.css` (default
`src/lib/styles/tokens.css`). Import it once from your app entry —
that pulls every token into the global `:root` scope.
## The full set
### Color — foreground
| Variable | Default (light) | Default (dark) | Use |
| --- | --- | --- | --- |
| `--uin-fg` | `rgba(0,0,0,.88)` | `rgba(255,255,255,.92)` | Primary text |
| `--uin-fg-mute` | `rgba(0,0,0,.62)` | `rgba(255,255,255,.65)` | Secondary text |
| `--uin-fg-dim` | `rgba(0,0,0,.42)` | `rgba(255,255,255,.42)` | Captions, hints |
| `--uin-fg-faint` | `rgba(0,0,0,.18)` | `rgba(255,255,255,.18)` | Disabled |
### Color — lines
| Variable | Default (light) | Default (dark) | Use |
| --- | --- | --- | --- |
| `--uin-line` | `rgba(0,0,0,.10)` | `rgba(255,255,255,.10)` | Hairline borders |
| `--uin-line-strong` | `rgba(0,0,0,.18)` | `rgba(255,255,255,.20)` | Emphasized borders |
### Color — materials (translucent surfaces)
| Variable | Use |
| --- | --- |
| `--uin-bg-base` | App background |
| `--uin-bg-panel` | Solid panel surface |
| `--uin-mat-window` | Window background material |
| `--uin-mat-sidebar` | Sidebar material |
| `--uin-mat-panel` | Panel material |
| `--uin-mat-row` | Row / card material |
| `--uin-mat-hover` | Hover-state overlay |
| `--uin-mat-selected` | Selected-row material |
### Color — accents
| Variable | Light | Dark | Use |
| --- | --- | --- | --- |
| `--uin-accent` | `#007aff` | `#0a84ff` | Primary action color |
| `--uin-accent-fg` | `#ffffff` | `#ffffff` | Text on accent backgrounds |
| `--uin-success` | `#34c759` | `#30d158` | Success state |
| `--uin-warn` | `#ffcc00` | `#ffd60a` | Warning state |
| `--uin-danger` | `#ff453a` | `#ff453a` | Error / destructive |
### Chart tokens
Chart tokens live in `@a3tai/mittsu-charts/tokens/charts.css` and layer on
top of the core tokens. Use semantic chart tokens when the data has meaning,
then fall back to categorical tokens when series are peers.
| Variable | Use |
| --- | --- |
| `--uin-chart-1` through `--uin-chart-8` | Categorical series palette |
| `--uin-chart-critical` | Critical risk, exploited state, urgent exposure |
| `--uin-chart-danger` | High severity, failure, destructive trend |
| `--uin-chart-warning` | Warning, medium risk, attention state |
| `--uin-chart-success` | Healthy, remediated, complete state |
| `--uin-chart-info` | Informational or selected data series |
| `--uin-chart-neutral` | Baselines, archived states, low emphasis |
| `--uin-chart-grid`, `--uin-chart-axis`, `--uin-chart-crosshair` | SVG grid, axis, and pointer-rule chrome |
| `--uin-chart-surface`, `--uin-chart-surface-raised` | Chart frame backgrounds |
### Type — families
| Variable | Default | Used by |
| --- | --- | --- |
| `--uin-font-ui` | system UI sans-serif | Buttons, labels, body text |
| `--uin-font-display` | NY/Iowan/Georgia serif | Headings, large numbers (Stat) |
| `--uin-font-mono` | SF Mono / Menlo | Code, kbd, tabular numbers |
### Spacing
A 4-pixel base scale, named `s-1` through `s-8`:
| Variable | px |
| --- | --- |
| `--uin-s-1` | 4 |
| `--uin-s-2` | 8 |
| `--uin-s-3` | 12 |
| `--uin-s-4` | 16 |
| `--uin-s-5` | 20 |
| `--uin-s-6` | 24 |
| `--uin-s-7` | 32 |
| `--uin-s-8` | 48 |
### Radii
| Variable | px |
| --- | --- |
| `--uin-r-sm` | 4 |
| `--uin-r-md` | 8 |
| `--uin-r-lg` | 12 |
| `--uin-r-xl` | 16 |
### Motion
| Variable | Default |
| --- | --- |
| `--uin-dur-1` | `120ms` |
| `--uin-dur-2` | `200ms` |
| `--uin-dur-3` | `320ms` |
| `--uin-ease-standard` | `cubic-bezier(0.2, 0, 0, 1)` |
| `--uin-ease-emphasized` | `cubic-bezier(0.25, 0.46, 0.45, 0.94)` |
### Focus
| Variable | Default |
| --- | --- |
| `--uin-focus-ring` | 3px ring at 40% accent |
## Theming recipes
### Brand color override (whole app)
```css
:root {
--uin-accent: #ff5722;
--uin-accent-fg: #ffffff;
}
```
Every component picks it up immediately — `Button.primary`, the
`Lamp.running` halo, focus rings, badges, tab indicators, etc.
### Per-region theme
Override at any scope, not just `:root`:
```svelte
```
Components inside that region pick up the indigo accent without
affecting the rest of the app.
### Dark mode
Three modes, listed in order of precedence:
1. **Forced light** — set ``. Ignores system.
2. **Forced dark** — set ``. Ignores system.
3. **Auto** — leave `data-theme` unset. The token file's
`@media (prefers-color-scheme: dark)` block kicks in when the
system is dark.
### Custom material (frosted blur)
Some Wails / Tauri apps want a frosted-glass background:
```css
:root {
--uin-mat-window: color-mix(in srgb, white 35%, transparent);
}
html { backdrop-filter: blur(40px) saturate(1.4); }
```
Components that use `--uin-mat-window` (Topbar, Sidebar) inherit the
frost without component-level edits.
## Adding new tokens
Tokens belong in `tokens.css`. Conventions:
- Always under `--uin-*`
- Add to both `:root` and the dark-mode block (if color-related)
- Document new tokens in this file
- If the token represents a discrete scale step (spacing/radius), keep
the numbering monotonic
When in doubt, prefer adding a new token over a one-off literal in a
component CSS file. Tokens are the contract; literals are the leak.
---
# docs/charts.md
# Charts
`@a3tai/mittsu-charts` is a Svelte 5 chart sub-package built with the
same copy-in conventions as the core UI package.
## Design
- D3 handles visualization math: scales, ticks, line/area paths, pie/radial
arcs, radar geometry, and bar/area stacking.
- Svelte handles markup and interaction state. Components render SVG directly
instead of letting D3 mutate the DOM.
- CSS variables handle theming. Import `tokens/charts.css` after the core
`tokens.css`.
- Each chart renders an SVG plus a screen-reader-only data table.
## LLM design grammar
- Start with the question: trend, comparison, composition, progress, or
capability map.
- Use plain arrays for `data` and plain objects for `series`.
- Choose semantic tokens before categorical tokens for risk, health, and status:
`--uin-chart-critical`, `--uin-chart-danger`, `--uin-chart-warning`,
`--uin-chart-success`, `--uin-chart-info`, and `--uin-chart-neutral`.
- Use categorical tokens (`--uin-chart-1` through `--uin-chart-8`) when series
are peers with no inherent severity order.
- Keep chart titles specific enough for screen readers and generated code. A
good title is `Work burn-down by week`; a weak title is `Chart`.
## Research notes
- D3 is the right underlying engine because it is a low-level toolbox, not a
fixed charting abstraction. We can use only the pieces we need: scales,
ticks, shapes, arcs, and stack layouts.
- Recharts validates the component-composition model for app charts, but it is
React-specific. This package borrows the "data + series config + chart shell"
shape without depending on React.
- shadcn/ui's chart package shows the value of copied chart chrome, color
tokens, tooltips, and legends. This package keeps those ideas but replaces
Recharts with direct D3 math.
- Chart.js is strong for large canvas-rendered datasets, but canvas is a poor
fit for this repo's CSS-token and source-owned SVG styling model.
- Highcharts sets the bar for accessibility: every chart should have a title,
description, keyboard-aware interaction where relevant, and a data-table path.
Primary references:
- [D3](https://d3js.org/what-is-d3)
- [shadcn/ui Chart](https://ui.shadcn.com/docs/components/chart)
- [Recharts](https://recharts.github.io/)
- [Chart.js](https://www.chartjs.org/docs/latest/)
- [Highcharts accessibility](https://www.highcharts.com/docs/accessibility/accessibility-module)
## Local usage
```svelte
```
## Copy-in usage
```bash
npm install d3-array d3-scale d3-shape
npx @a3tai/mittsu add line-chart \
--registry ./packages/charts/registry.json
```
Import the copied CSS:
```css
@import './lib/styles/charts.css';
@import './lib/styles/charts.shared.css';
@import './lib/components/ui/chart-frame/chart-frame.css';
@import './lib/components/ui/chart-tooltip/chart-tooltip.css';
@import './lib/components/ui/line-chart/line-chart.css';
```
## Components
| Component | Purpose |
| --- | --- |
| `ChartFrame` | Shared chart shell, title, description, metrics, legend, and footer |
| `ChartTooltip` | Shared tooltip rows with dot, line, icon, unit, hidden-label, and total variants |
| `AreaChart` | Multi-series area charts with stacked and 100% expanded modes |
| `LineChart` | Multi-series line charts with linear, monotone, and step curves |
| `BarChart` | Grouped, stacked, horizontal, labeled, active, and negative categorical bars |
| `DonutChart` | Pie/donut composition with labels, active slices, center total, and legend |
| `RadarChart` | Polygon/circle radar charts with filled, line-only, dot, and multi-series modes |
| `RadialChart` | Concentric radial progress rings and stacked gauge arcs |
## Screenshot coverage
The local `/charts` page now implements versions of the reference families in
`reference/charts/charts1.png` through `charts7.png`: area, bar, line, pie,
radar, radial, and tooltip variants. The implementation is intentionally API
driven rather than one-off gallery markup, so the same primitives can be copied
into consumer apps.
---
# docs/icons.md
# Icons
Mittsu does not bundle an icon set, and no component type-checks against
one. Anywhere a component accepts an icon — `Button`'s icon slot,
`NavItem`'s leading glyph, an empty state, a toolbar — it renders
whatever SVG you pass, sized to sit next to the text and colored by
`currentColor` so it flips with the theme automatically.
## The supported library: Lucide
We recommend [Lucide](https://lucide.dev) and use it ourselves — the
docs site, the showcase chrome, and the example templates all render
icons with [`@lucide/svelte`](https://www.npmjs.com/package/@lucide/svelte)
(the Svelte 5-native package).
Why Lucide:
- **Same ecosystem as the copy-source model.** Lucide is the default
icon set of the shadcn-style world Mittsu belongs to; agents and
humans alike already know the icon names.
- **Stroke-based.** Lucide draws with strokes, so `strokeWidth={1.75}`
drops it into the same hairline weight as `--uin-line` chrome.
- **`currentColor` by default.** Icons inherit text color, which keeps
dark mode free.
- **Tree-shakeable.** Per-icon deep imports mean you ship only what you
use.
```bash
npm install @lucide/svelte
```
```svelte
```
Prefer the deep import path (`@lucide/svelte/icons/`) over named
imports from the package root — it keeps dev-server startup fast and
guarantees tree-shaking.
## House style
- **Size 14–16** next to 12.5–13px UI text; 12–13 inside pills,
eyebrows, and `Kbd`-adjacent chrome.
- **`strokeWidth={1.75}`.** Lucide's default 2 reads a step too heavy
against 1px hairline borders; 1.75 matches the kit's line weight.
- **Never hard-code icon color.** Let `currentColor` inherit from the
parent (`--uin-fg-mute` for secondary chrome, `--uin-accent` for
links). Setting explicit fills breaks theme flipping.
- **Directional affordances are icons, not ASCII.** `ArrowRight` for
internal navigation, `ArrowUpRight` for external links, `ArrowLeft`
for back links — never `->` or `→` in a label.
- **Accessibility.** Decorative icons take `aria-hidden="true"`.
Icon-only buttons take `aria-label`. An icon never replaces a label
for meaning on its own.
## Bringing a different set
Nothing in the kit depends on Lucide. Phosphor (`phosphor-svelte`),
Tabler, Heroicons, or your own inline `