Theming

Change the look of every component with CSS variables.

Components use semantic color tokens such as background, foreground and primary. Tailwind maps each token to a utility, so bg-primary reads var(--primary). To change the look of your app, change the token values. You do not need to rewrite component classes.

<div class="bg-background text-foreground">Hello</div>

Tokens come in pairs

Each surface token has a -foreground token for the text and icons on that surface. For example, primary pairs with primary-foreground:

--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
<div class="bg-primary text-primary-foreground">Hello</div>

The token list

TokenUsed for
background, foregroundThe page and its text
card, card-foregroundCards
popover, popover-foregroundPopovers, menus and dialogs
primary, primary-foregroundPrimary buttons and strong accents
secondary, secondary-foregroundSecondary buttons
muted, muted-foregroundSubtle backgrounds and secondary text
accent, accent-foregroundHover and active states
destructiveDestructive actions and errors
border, input, ringBorders, form controls and focus rings
chart-1 to chart-5Charts
sidebar and its pairsSidebars
radiusThe base corner radius. rounded-sm to rounded-4xl derive from it

Default theme

Add the tokens to your global stylesheet. The .dark block overrides them for dark mode.

src/app.css
:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.145 0 0);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  --secondary: oklch(0.97 0 0);
  --secondary-foreground: oklch(0.205 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --accent: oklch(0.97 0 0);
  --accent-foreground: oklch(0.205 0 0);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0 0);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.708 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --card: oklch(0.205 0 0);
  --card-foreground: oklch(0.985 0 0);
  --popover: oklch(0.205 0 0);
  --popover-foreground: oklch(0.985 0 0);
  --primary: oklch(0.922 0 0);
  --primary-foreground: oklch(0.205 0 0);
  --secondary: oklch(0.269 0 0);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.269 0 0);
  --muted-foreground: oklch(0.708 0 0);
  --accent: oklch(0.269 0 0);
  --accent-foreground: oklch(0.985 0 0);
  --destructive: oklch(0.704 0.191 22.216);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.556 0 0);
}

Base colors

The @sveltery/components/themes.css stylesheet also ships the shadcn/ui base colors as classes, from theme-neutral to theme-zinc. Add one to an element to recolor everything inside it.

<div class="theme-blue">
  <Button>Blue</Button>
</div>

Styling open triggers

Upstream shadcn/ui class strings use data-open: on trigger elements, such as a sidebar menu button that opens a dropdown menu. Base UI sets data-open on popups, panels and accordion items, not on most triggers, so those classes never apply here. This is a deliberate fix of an upstream mismatch. Use the attribute the trigger really sets.

TriggerAttributeExample class
Menu, popover, dialog, tooltip, navigation menudata-popup-opendata-popup-open:bg-accent
Collapsible and accordion triggersdata-panel-opendata-panel-open:rotate-90
Any trigger with aria-expandedaria-expandedaria-expanded:bg-muted

Popups and panels, such as DropdownMenuContent, CollapsibleContent and the collapsible root, do set data-open and data-closed. A group-data-open/collapsible:rotate-90 class on a chevron works when group/collapsible sits on the collapsible root.