# 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](https://img.shields.io/badge/status-pre--release-orange)](#status) [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE) [![Svelte 5](https://img.shields.io/badge/svelte-5-FF3E00)](https://svelte.dev) [![Components](https://img.shields.io/badge/components-18%20shipped%20%E2%80%A2%20140%2B%20roadmap-success)](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 `` drop into the same slots — anything that renders an SVG at text size and strokes/fills with `currentColor` fits. If you swap sets, keep the optical weight close to a 1.5–1.75 stroke at 16px so mixed surfaces stay coherent. --- # docs/architecture.md # Architecture How the pieces fit together. Useful when you're contributing, debugging, or wondering whether the constraint you just hit is intentional. ## High-level ``` ┌────────────────────────┐ │ Consumer project │ │ (Svelte 5 / SvelteKit)│ └─────────────▲──────────┘ │ copies files │ ┌──────────────────┐ reads ┌──┴───────────────────────┐ │ components.json ├──────────────▶│ @a3tai/mittsu CLI │ │ (consumer-side) │ │ (Node, zero-dep) │ └──────────────────┘ └──────────────▲───────────┘ │ fetches │ ┌───────────────────────┴───────────┐ │ registry.json │ │ (HTTPS or local path) │ └─────────────▲─────────────────────┘ │ enumerates │ ┌──────────────┴────────────────────┐ │ packages/svelte5/components// │ │ └── .svelte + .css │ └────────────────────────────────────┘ ``` ## Repository The repo is an npm workspace with three packages and one app: | Path | Role | | --- | --- | | `packages/cli/` | The `@a3tai/mittsu` CLI npm package | | `packages/svelte5/` | Component sources for Svelte 5 | | `apps/web/` | SvelteKit showcase + future docs site | | `docs/` | Source markdown docs (this folder) | | `.claude/skills/` | Repo-aware AI workflows | Future framework packages (`packages/react`, `packages/vue`, `packages/svelte5-mobile`) will sit alongside `packages/svelte5/`, each shipping their own `registry.json`. ## Registry The registry is a single JSON manifest that enumerates every component and what files belong to it. The CLI reads it, fetches component files, and writes them into the consumer's project. ```json { "name": "@a3tai/mittsu-svelte5", "version": "0.0.1", "framework": "svelte5", "tokens": { "files": ["tokens/tokens.css"] }, "lib": { "files": ["lib/cn.ts"] }, "components": { "search-input": { "name": "search-input", "description": "Pill search field with magnifier + ⌘K hint + clear button.", "files": [ "components/search-input/search-input.svelte", "components/search-input/search-input.css" ], "dependencies": ["lib/cn", "input", "kbd"] } } } ``` `tokens` and `lib` are special — they ship to fixed locations configured in `components.json`. Everything under `components` is a component the user can `add`. A registry is **the only source of truth** for what's installable. The CLI doesn't enumerate the filesystem. ## CLI `packages/cli/` is intentionally simple: - `bin/mittsu.js` — shebang entry; defers to `src/index.js`. - `src/index.js` — picks a command handler based on argv. - `src/args.js` — hand-rolled argv parser (no commander/yargs to keep the install hop empty). - `src/registry.js` — fetch + parse registry, fetch component files via fetch() or fs.readFile(). - `src/config.js` — read/write `components.json`, resolve paths. - `src/fs-utils.js` — atomic-ish writes + the import rewriter. - `src/commands/{init,add,list,help}.js` — one file per command. The CLI assumes Node ≥ 18 (for native `fetch`). ### Import rewriting When `add` copies a file, it rewrites a small set of import specifiers so the file Just Works in the consumer's project layout. Specifically, `from '../../lib/'` (the source-tree path) becomes a relative path to `/`. Sibling component imports (e.g. `from '../input/input.svelte'`) are left alone, because the consumer keeps the same `/.svelte` shape we use in-tree. CSS files don't go through the rewriter — they have no JS imports. ## Component conventions Every component lives in `packages/svelte5/components//` and has exactly two files: - `.svelte` — markup + `

MyComponent

``` This also serves as the manual-test loop while you iterate. ## See also - [Tokens reference](tokens.md) — every CSS variable - [Architecture](architecture.md) — how the registry / CLI / rewriter fit together - [Contributing](contributing.md) — PR conventions - The `components` Claude skill at `.claude/skills/components/SKILL.md` — scaffolds new components from a single prompt. --- # docs/deployment.md # Deployment The Mittsu showcase (`apps/web`) deploys to **Cloudflare Pages**. The project is named `mittsu`; the live domain is **mittsu.dev**. Local development still uses `npm --workspace apps/web run dev`. Nothing in this doc affects that — these notes only cover the deploy path. ## How it works ``` git push origin main │ ▼ GitHub Action (.github/workflows/deploy.yml) │ · npm ci │ · type-check both packages │ · build apps/web with adapter-static (→ apps/web/build/) │ · wrangler pages deploy build --project-name=mittsu ▼ Cloudflare Pages project "mittsu" → mittsu.dev ``` The GitHub Action reads two repo secrets: | Secret | Where to find it | | --- | --- | | `CLOUDFLARE_API_TOKEN` | Cloudflare dashboard → My Profile → API Tokens → Create token. Use template **"Edit Cloudflare Workers"** (or scope a custom token to: Account · Cloudflare Pages: Edit). | | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare dashboard → any zone or Workers/Pages page → right sidebar shows Account ID. | Set both at: **GitHub → repo → Settings → Secrets and variables → Actions → New repository secret.** ## First deploy (one-time) 1. Create the Cloudflare API token + grab the account ID (see table above). 2. Add both as GitHub repo secrets. 3. Push to `main`. The Action will: - Auto-create a Pages project named `mittsu` on first run. - Deploy the build output. - Print the preview URL in the job log (e.g. `https://mittsu.pages.dev`). 4. **Bind `mittsu.dev` to the project**: Cloudflare dashboard → Pages → `mittsu` → Custom domains → Set up a custom domain → enter `mittsu.dev`. Cloudflare will create the DNS record automatically (since the domain is registered through Cloudflare). Allow ~30s for cert provisioning. 5. (Optional) Add `www.mittsu.dev` as a redirect in the same panel. After this, every push to `main` deploys automatically. PRs / non-`main` branches get a preview URL but don't update the production domain. ## Local environment (1Password + direnv) If you want to run `wrangler` locally (e.g. to deploy from your laptop or to use `wrangler dev` for local testing of the deployed build), the same two env vars need to be available in your shell. The repo uses **direnv + 1Password CLI** for this. No secrets are ever written to disk; `direnv` resolves them at shell-load time. ### One-time setup ```sh # Install brew install direnv 1password-cli # Hook direnv into your shell (zsh shown — bash/fish similar) echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc exec zsh # Sign in to 1Password CLI eval "$(op signin)" ``` ### Per-repo setup ```sh # Copy the template and edit the op:// paths to match your vault layout cp .envrc.example .envrc # Allow direnv to load this file (it asks once, then remembers) direnv allow ``` The default `.envrc.example` assumes a 1Password item at: - **Vault**: `Private` - **Item**: `Cloudflare — Mittsu` - **Fields**: `api-token`, `account-id` Adjust the `op://` paths inside `.envrc` to match wherever you actually stored the credentials. `.envrc` is **gitignored**. Only `.envrc.example` is committed. ### Verifying ```sh cd /path/to/mittsu echo $CLOUDFLARE_ACCOUNT_ID # should print your account id echo $CLOUDFLARE_API_TOKEN | head -c 4 # should print 4 chars and a newline ``` If the second one prints nothing, check `op account get` works. ## Manual deploy (from your laptop) After the env vars are loaded: ```sh npm --workspace apps/web run build cd apps/web npx wrangler pages deploy build --project-name=mittsu ``` The first deploy creates the project; subsequent ones push new versions. ## Troubleshooting - **`Authentication error`** — your API token doesn't have Pages: Edit scope, or `CLOUDFLARE_ACCOUNT_ID` is wrong. The token must include the account in its scope. - **`Project mittsu does not exist`** — first deploy auto-creates it on Cloudflare's side. If it errors instead, create the project manually in the Pages dashboard with the same name. - **mittsu.dev shows the default Cloudflare placeholder** — DNS hasn't propagated yet, or the custom domain binding step is incomplete. Cloudflare's "Custom domains" tab in the Pages project shows the current state. - **direnv didn't load `.envrc`** — run `direnv allow` once after creating it, and check `direnv status` for errors. ## What's NOT in here yet - Preview deploys for branches / PRs — works automatically with the `--branch=...` flag we already pass. No extra config needed. - Edge functions / SSR — the showcase is pure static. If we ever add edge functions, switch the SvelteKit adapter from `@sveltejs/adapter-static` to `@sveltejs/adapter-cloudflare` and add a `main` entry to `wrangler.toml`. - Build caching — Cloudflare Pages caches automatically; no config. --- # docs/contributing.md # Contributing Welcome. Here's how to land code without surprises. ## Setup ```bash git clone cd mittsu npm install ``` The install wires the workspaces (`packages/*` + `apps/*`) so the showcase can resolve `@a3tai/mittsu-svelte5` as a workspace package. ## Workflows ### Run the showcase ```bash npm --workspace apps/web run dev ``` ### Smoke-test the CLI against the local registry ```bash node packages/cli/bin/mittsu.js list \ --registry ./packages/svelte5/registry.json node packages/cli/bin/mittsu.js add button \ --target /tmp/mittsu-smoke \ --registry ./packages/svelte5/registry.json ``` ### Type-check everything ```bash npm run check ``` ## Adding a component See [`docs/component-authoring.md`](component-authoring.md) for the full convention guide. Quick recap: 1. Create `packages/svelte5/components//{.svelte, .css}`. 2. Use only `--uin-*` tokens for color / spacing / radii / duration. 3. Class names: `.uin-`, `.uin--`. 4. Add an entry to `packages/svelte5/registry.json`. 5. Add a showcase block to `apps/web/src/routes/components/+page.svelte`. 6. Smoke-test the CLI against your new component. The `components` Claude skill scaffolds steps 1–4 from a single prompt. ## PR conventions - **One component per PR** when adding to the registry. Refactors spanning multiple components are fine. - **Title format**: imperative, lowercase, no trailing period. Examples: `add segmented-control`, `fix focus ring on input`, `tokens: split material colors out of accent block`. - **Body**: a short why + a screenshot or screencast for any visible change. Showcase URL is fine when the change is in there. - **Don't bump versions** in your PR. The release workflow (or a maintainer) handles that. ## Review checklist If you're reviewing (or self-reviewing), walk this list: - **Tokens, not literals.** `grep -nE '#[0-9a-f]{3,6}|rgba?\(' packages/svelte5/components//` should return nothing except inside the design-tokens file. - **No `