Theming

Components read color, type, radius, density, motion, and elevation from CSS variables. Change a token to restyle every component, or style one instance with classes and data-ui selectors.

Where tokens live

Package installs use @sivir-ui/svelte/ui.css. CLI installs use src/lib/sivir/ui.css. Both define the same tokens.

Studio

The Studio builds a theme visually. Start from a built-in preset, then adjust brand color, neutral temperature, radius, density, motion, fonts, header size, per-role font weights, per-mode foundation colors, and chrome flags. The Tokens tab overrides individual theme variables, separately for light and dark mode where they differ.

Export the theme as theme.css for your app, or as JSON to load it again later. The Studio keeps your draft in local storage between visits.

Publish a draft to share it on the themes page, where anyone can preview it and install it by slug. The browser that publishes a theme keeps its edit key, so only that browser can publish updates or unpublish it. Open any theme in the Studio with /studio?theme=<slug> to start a new draft from it.

Override tokens

Set values in your app CSS after importing Sivir’s sheet. Light defaults go in @theme. Dark values go under .dark.

@theme {
  --color-primary: #155eef;
  --color-background: #fcfcfd;
  --color-foreground: #101828;
  --radius-lg: 0.55rem;
  --font-sans: 'DM Sans', sans-serif;
}

.dark {
  --color-background: #0d1118;
  --color-foreground: #f5f7fb;
  --color-primary: #7aa2ff;
}

Useful public tokens

  • Color: --color-background, --color-card, --color-panel, --color-secondary, --color-foreground, --color-foreground-muted, --color-primary, --color-on-primary, --color-button-foreground, --color-border, --color-input, --color-ring
  • Type: --font-sans, --font-mono, --font-header, --font-size-header, and role weights like --font-weight-body, --font-weight-label, --font-weight-button
  • Radius and density: --radius-sm, --radius-md, --radius-lg, --radius-xl, and the base spacing unit --sivir-space-unit
  • Control height: --size-control-sm, --size-control-md, --size-control-lg, and button-only overrides --size-button-sm, --size-button-md, --size-button-lg that fall back to the matching control height
  • Motion: --motion-duration-hover, --motion-duration-menu, --motion-duration-panel, --motion-duration-sheet
  • Elevation: --elevation-1, --elevation-float, --elevation-control, --elevation-modal

Built-in presets

Six presets ship with Sivir: default, magic, profitable, raven, clawd, and inspiration. Preview them live on the themes page, where you can copy each preset’s CSS or JSON.

With the CLI, install a preset into theme.css:

bunx @sivir-ui/svelte add theme raven

Import it after ui.css so it wins:

/* src/routes/layout.css */
@import '../lib/sivir/ui.css';
@import '../lib/sivir/theme.css';

sivir list prints the built-in theme slugs. sivir add theme also installs community themes published from the Studio; pass the slug shown on the themes page.

Dark mode

Toggle a .dark class on <html>. Sivir reads the class but does not set it.

Theme JSON

Theme JSON (version 4) stores a theme as data. The Studio, the CLI, and the theme registry all read this format. Per-mode surface colors go in foundation.light and foundation.dark: base, border, background, secondary, foreground, foregroundMuted, onPrimary, and buttonForeground. typography sets headerSize, headerWeight, and roleWeights for body, label, button, badge, and description. tokens.shared, tokens.light, and tokens.dark override raw variables, such as a per-mode --color-primary. chrome holds the flags. shadows: false removes every shadow; surfaceShadows, controlShadows, and dialogShadows turn off one group. travelingHighlight: false keeps the item fill and drops the slide. fancySwap: false makes icon and label swaps a plain crossfade. menuPaneling: false gives menus a plain 1px border with no inset frame. surfacePaneling: false makes modals, sheets, popovers, cards, and code blocks one continuous container. primaryStroke adds an inset stroke to primary buttons, and interactiveCursor is 'default' or 'pointer'. triggerDistance sets the gap in pixels, from 0 to 16, between a trigger and the menu or popover it opens (default 8). Setting motion: "none" disables every animation, including dialogs, menus, and the traveling highlight.

class prop

Every primitive accepts class. Use Tailwind utilities or your own classes for one-off tweaks.

<Button class="w-full rounded-2xl">Continue</Button>

data-ui selectors

Components render data-ui (and often data-variant / data-size). Use them to style every instance of a component without editing its source.

[data-ui='button'][data-variant='primary'] {
  border-radius: 999px;
}

[data-ui='badge'][data-variant='secondary'] {
  text-transform: uppercase;
}

Edit the source

With the CLI path, files live under src/lib/sivir/components/<name>/. Edit them to change behavior or markup that tokens and classes cannot reach.

# after: bunx @sivir-ui/svelte add button
src/lib/sivir/components/button/
├── button.svelte
├── index.ts
└── variants.ts

Next

Components