Svelte components for building user interfaces — borrowed behavior, original look.
smui is a pnpm monorepo housing a design system and a Svelte 5 component library. It stands on the shoulders of the best primitives and design languages in the ecosystem — bits-ui headless primitives, Material Design 3 (Expressive), SVAR, shadcn-svelte, and Tailwind CSS — and unifies them under a single, original visual identity.
It ships a CSS-only theme package, ~50 independently versioned component packages under the @wimwian-org/* npm scope, and a SvelteKit playground for developing and demoing every token, tint, and component. Everything is built on Svelte 5 runes, Tailwind CSS v4 (@theme + @utility), and an oklch design-token system with first-class light/dark theming.
smui is built on a simple idea: inherit behavior, not appearance.
bits-ui, Material Design 3 (Expressive), SVAR, and shadcn-svelte — each component is inspired by a parent rather than reinvented.@wimwian-org/theme library is the single source of truth for the color palette and typography standards. Every component then exposes its own common token language that maps onto those generic theme tokens.bits-ui headless primitives, Material Design 3 (Expressive) motion and shape, SVAR's data-dense widgets, and shadcn-svelte's composition patterns — then dress them in a single, unique visual identity.@wimwian-org/theme defines the generic palette and typography; each component exposes a common token language that maps onto those theme tokens. Retheme once, restyle everything.--L / --D) — no JS theme switch, no flash.@wimwian-org/* package with its own version and changelog, depending only on @wimwian-org/theme. Consumers install only what they use.$state, $derived, $props, snippets) throughout — no legacy stores or export let.smui/
├── apps/
│ └── playground/ (@wimwian-org/playground) — SvelteKit dev sandbox, NOT published
├── packages/
│ └── theme/ (@wimwian-org/theme) — CSS design tokens + Tailwind v4 theme, published
├── components/ — ~50 bit components, each a published @wimwian-org/* package
├── .claude/ — project guidance docs (see Documentation Map)
├── bin/wt — git-flow + worktree helper
└── scripts/ — build/metadata/release utilities
Dependency direction: apps/ → components/ → packages/. No reverse dependencies. A component depends only on @wimwian-org/theme (peer) and bits-ui; it never reaches back into the playground.
Requires Node + pnpm (^11.4.0 — the repo auto-downloads it via devEngines if missing).
# Install all workspace dependencies
pnpm install
# Run the playground dev sandbox
pnpm --filter @wimwian-org/playground dev
# Type-check, lint, format
pnpm check
pnpm lint
pnpm format
pnpm add @wimwian-org/button @wimwian-org/theme
/* app.css — import the theme once at the root */
@import '@wimwian-org/theme';
<script>
import { Button } from '@wimwian-org/button';
</script>
<Button class="primary">Click me</Button>
| Package | Path | Published | Description |
|---|---|---|---|
@wimwian-org/theme |
packages/theme |
✅ | CSS-only design tokens, oklch palettes, Tailwind v4 theme. |
@wimwian-org/<bit> |
components/<bit> |
✅ | One package per component, wrapping a bits-ui primitive. |
@wimwian-org/playground |
apps/playground |
❌ | SvelteKit sandbox — demos every token and component. Dev-only. |
@wimwian-org/themeA pure-CSS package (no Svelte, no JS). Consumers import one of:
@import '@wimwian-org/theme'; /* full bundle */
@import '@wimwian-org/theme/tokens'; /* --L/--D toggle, ground/content scales, elevation */
@import '@wimwian-org/theme/tints'; /* .primary, .error, … tint @utility classes */
@import '@wimwian-org/theme/typography'; /* heading-*, body-*, code-*, elevation-* utilities */
Each component follows the same layout (example: @wimwian-org/button):
components/button/
├── package.json — @wimwian-org/button, deps: bits-ui; peer: @wimwian-org/theme, svelte, tailwindcss
├── README.md — per-component usage
├── CHANGELOG.md — changesets-generated
├── tsconfig.json
└── src/
├── index.ts — public entry
├── Button.svelte — the component (wraps bits-ui)
├── button.variants.ts — tailwind-variants config
├── button.css — semantic styles
├── types.ts — prop types
└── *.test.ts — Vitest specs
~50 components live in components/, each published as @wimwian-org/<name>:
accordion · alert-dialog · aspect-ratio · avatar · badge · breadcrumb · button · calendar · card · carousel · checkbox · collapsible · combobox · command · data-grid · date-field · date-picker · date-range-field · date-range-picker · dialog · drawer · dropdown-menu · input · item · kbd · label · link-preview · menubar · meter · navigation-menu · pagination · popover · progress · radio-group · range-calendar · rating-group · resizable · scroll-area · select · separator · sheet · slider · sonner · spinner · switch · tabs · theme-toggle · time-field · time-range-field · toggle · tooltip
Two slash commands scaffold new components end to end:
/create-bit — wrap a bits-ui primitive into a new @wimwian-org/* package (variants, tests, demo route, git-flow branch)./create-shadcn — fetch a shadcn-svelte component and adapt it to smui's tokens and conventions.Full reference: .claude/styling.md.
mono (21-step neutral) plus primary, secondary, error, warning, success (11 steps each). All oklch.--L / --D space-toggle keyed off html[data-theme="light|dark"]. No JavaScript needed to switch themes.--color-g-* mirrors mono and flips in dark mode.--color-c-* defaults to ground; tint utilities retint it per element..primary, .secondary, .error, .warning, .success retint --color-c-* and --surface-*.--page-* / --canvas-* / --surface-* (three layers, each with bg/fg/border/shadow).Example classes: bg-primary-500, text-error-700, bg-c-600, text-c-0, border-mono-200.
Any new token or component must meet AA contrast (4.5:1) in both light and dark.
Commands are workspace-scoped — run from the repo root, or target a workspace with --filter.
| Command | Does |
|---|---|
pnpm format |
Prettier write (all workspaces) |
pnpm lint |
ESLint (all workspaces) |
pnpm check |
svelte-check across the workspace |
pnpm commit |
Commitizen prompt — conventional commit message |
pnpm changeset |
Author a changeset by hand |
pnpm test |
Vitest (node mode) |
pnpm test:browser |
Vitest (browser mode, Playwright) |
pnpm test:all |
Both node and browser test suites |
pnpm coverage |
Full coverage run + regenerate playground status JSON |
pnpm --filter @wimwian-org/playground dev |
Vite dev server |
pnpm --filter @wimwian-org/playground build |
Static build |
Conventions live in .claude/ — code style (karpathy.md), Svelte 5 patterns (svelte.md), CSS authoring (css-authoring.md), variants (variants.md), and the headless-wrapping pattern (bits.md).
See .claude/testing.md.
pnpm test) — fast unit tests for pure logic, variants, and .svelte.ts rune harnesses.pnpm test:browser) — Playwright-backed tests for components whose behaviour only compiles/runs in the browser (PascalCase .test.ts specs).pnpm coverage runs both suites with v8 coverage and regenerates the per-bit JSON the playground homepage reads.Full model: .claude/gitflow.md. We use git flow with protected long-lived branches.
| Branch | Cut from | Merges into | Notes |
|---|---|---|---|
master |
— | — | Production. Tagged. Protected. |
dev |
master |
— | Integration. Protected. |
feature/<slug> |
dev |
dev |
Standard development work. |
release/<version> |
dev |
master + dev |
Where changeset version runs. |
hotfix/<version> |
master |
master + dev |
Emergency patch on production. |
master or dev. Both are protected (admins included).master only advances by fast-forward from dev (pnpm push:master or git push origin dev:master) — never via PR merge.feat: / fix: commit on a feature branch auto-attaches a changeset (post-commit hook).bin/wt feature <slug> cuts a branch into a sibling ../smui.worktrees/<slug> directory so multiple sessions never collide on HEAD.pnpm check — types passpnpm lint — no errorspnpm test:all — both suites passpnpm --filter @wimwian-org/playground build — playground builds cleanDeveloped with Claude Code. Guidance lives in CLAUDE.md:
| Doc | Covers |
|---|---|
| CLAUDE.md | Start here — doc index + commands |
| .claude/karpathy.md | Behavioral guidelines — minimal, deliberate change |
| .claude/process.md | Plan-before-execute discipline |
| .claude/gitflow.md | Branching model + worktree workflow |
| .claude/svelte.md | SvelteKit + Svelte 5 runes, snippets, routing |
| .claude/styling.md | Design tokens, oklch palettes, --L/--D theming |
| .claude/css-authoring.md | Inline-utility budget, semantic naming, data-slot |
| .claude/bits.md | bits-ui headless wrapping pattern |
| .claude/component.md | Component standards, prop design, a11y |
| .claude/variants.md | tailwind-variants config |
| .claude/testing.md | Vitest + Playwright browser mode |
| .claude/tooling.md | ESLint, Prettier, lefthook, commitlint, changesets |
| .claude/distribution.md | Package publishing and release workflows |
Contributing? Read CLAUDE.md first — it defines the conventions every change in this repo follows.
@theme + @utility, oklchsmui stands on two kinds of shoulders: code it depends on, and design languages it draws inspiration from. The distinction is deliberate — behavior is borrowed, but the look and feel is entirely smui's own.
Projects smui builds on directly:
@wimwian-org/theme's @theme blocks, @utility classes, and the entire design-token cascade.Design languages and libraries smui learns from but does not depend on — referenced for anatomy, behaviour, and feel, then re-expressed on smui's own tokens:
MIT © Anand Panchapakesan