smui Svelte Themes

Smui

Svelte 5 UI component library and design system built on bits-ui, shadcn-svelte, and Tailwind CSS v4; inspired by Material Design 3 (Expressive) and SVAR.

smui

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.

Project Goals

smui is built on a simple idea: inherit behavior, not appearance.

  • Stand on proven foundations. Interactive behavior, accessibility, and structure are borrowed from bits-ui, Material Design 3 (Expressive), SVAR, and shadcn-svelte — each component is inspired by a parent rather than reinvented.
  • One theme, two layers of tokens. The @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.
  • A look and feel that's entirely our own. Because each component speaks the shared token language, retheming the whole system happens in one place — and the result looks like smui, not like any of the libraries it draws from. The behavior is familiar; the visual identity is unique.

Table of Contents


Why smui

  • Behavior borrowed, look original. Components inherit interaction, accessibility, and structure from established sources — 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.
  • Two-layer token language. @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.
  • Token-driven theming. A single oklch palette set drives the whole system. Light/dark is a CSS space-toggle (--L / --D) — no JS theme switch, no flash.
  • Independently published. Each component is its own @wimwian-org/* package with its own version and changelog, depending only on @wimwian-org/theme. Consumers install only what they use.
  • Svelte 5 native. Runes ($state, $derived, $props, snippets) throughout — no legacy stores or export let.

Workspace Layout

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.


Quick Start

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

Consuming a component (downstream)

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>

Packages

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/theme

A 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 */

Component package anatomy

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

Components

~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

Adding a component

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.

Design Tokens

Full reference: .claude/styling.md.

  • Palettesmono (21-step neutral) plus primary, secondary, error, warning, success (11 steps each). All oklch.
  • Theming--L / --D space-toggle keyed off html[data-theme="light|dark"]. No JavaScript needed to switch themes.
  • Ground scale--color-g-* mirrors mono and flips in dark mode.
  • Content scale--color-c-* defaults to ground; tint utilities retint it per element.
  • Tint utilities.primary, .secondary, .error, .warning, .success retint --color-c-* and --surface-*.
  • Elevation--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.


Development

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).


Testing

See .claude/testing.md.

  • Node mode (pnpm test) — fast unit tests for pure logic, variants, and .svelte.ts rune harnesses.
  • Browser mode (pnpm test:browser) — Playwright-backed tests for components whose behaviour only compiles/runs in the browser (PascalCase .test.ts specs).
  • Coveragepnpm coverage runs both suites with v8 coverage and regenerates the per-bit JSON the playground homepage reads.

Branching & Releases

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.
  • Never commit directly to 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.
  • Each feat: / fix: commit on a feature branch auto-attaches a changeset (post-commit hook).
  • Parallel work uses git worktrees — bin/wt feature <slug> cuts a branch into a sibling ../smui.worktrees/<slug> directory so multiple sessions never collide on HEAD.

Before shipping

  • pnpm check — types pass
  • pnpm lint — no errors
  • pnpm test:all — both suites pass
  • pnpm --filter @wimwian-org/playground build — playground builds clean
  • AA contrast (4.5:1) in light and dark for any new token/component
  • All commits conventional; a changeset exists for any consumer-visible change

Documentation Map

Developed 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.


Tech Stack


Acknowledgements

smui 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.

Dependencies

Projects smui builds on directly:

  • bits-ui (Hunter Johnston) — the headless Svelte primitives every interactive component wraps. Accessibility, focus management, and keyboard behaviour are bits-ui's.
  • shadcn-svelte (Huntabyte) — the Svelte port of shadcn/ui. Its components and composition patterns are the starting point smui adapts onto its own tokens and conventions.
  • Tailwind CSS (Tailwind Labs) — the utility-first CSS framework powering @wimwian-org/theme's @theme blocks, @utility classes, and the entire design-token cascade.

Inspirations

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:

  • Material Design 3 (Expressive) (Google) — the design language informing smui's motion, shape, and interaction semantics, reinterpreted through smui's own token system rather than adopted wholesale.
  • SVAR — Svelte widgets for data-dense surfaces (grids, schedulers, and the like) that inspire smui's more complex, data-driven components.
  • SMUI (Hunter Perrin) — Svelte Material UI, an early comprehensive Svelte component library and a benchmark for a full-featured Svelte UI library. smui shares its name but is an independent project on its own design-token system.

License

MIT © Anand Panchapakesan

Top categories

Loading Svelte Themes