Skip to Content
Theming

Theming

spacing-ui components are unstyled. You can theme them with any styling solution you already use.

CSS custom properties

Because everything is a plain HTML element, CSS custom properties work out of the box. Define your tokens on :root and use them in your component styles.

:root { --sui-radius: 0.5rem; --sui-bg: #ffffff; --sui-fg: #111827; --sui-accent: #2563eb; } [data-state="open"] { background: var(--sui-bg); }

Every primitive sets data-state, data-orientation, and data-disabled attributes you can hook into.

Tailwind CSS

Attribute selectors compose cleanly with Tailwind variants. Add a plugin (or use @variant) to expose them:

<Dialog.Content className="rounded-md bg-white shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out"> ... </Dialog.Content>

Combine with data-orientation for orientation-aware styling on tabs, radio groups, and separators.

CSS Modules

Pass a className to any part; sub-parts accept className too. State-driven styles use attribute selectors inside the module:

.trigger { padding: 0.5rem 1rem; } .trigger[data-state="open"] { background: var(--sui-accent); }
<Accordion.Trigger className={styles.trigger}>...</Accordion.Trigger>

Design tokens

The docs site ships a small set of CSS custom properties in app/globals.css. They cover surfaces, text, borders, an indigo accent, radii, spacing, elevation shadows, and motion timing:

:root { --sui-color-accent: #4f46e5; --sui-color-accent-soft: #eef2ff; --sui-radius-md: 8px; --sui-shadow-lg: 0 20px 40px rgba(15, 23, 42, 0.18); --sui-duration: 180ms; --sui-ease: cubic-bezier(0.16, 1, 0.3, 1); }

Override any of these on :root, on a parent element, or per component to reskin the entire demo surface.

Animations

Reusable keyframes live alongside the tokens:

  • sui-fade-in — opacity 0 → 1
  • sui-scale-in — opacity + subtle scale, for popovers and dialogs
  • sui-slide-down-fade — for menus and tooltips
  • sui-check-in — for check marks and radio dots

Wire them up with data-state attribute selectors so entry and exit animations follow the component’s own state:

.dialog-content[data-state="open"] { animation: sui-scale-in var(--sui-duration) var(--sui-ease); }
Last updated on