Bevel

Get started

Theming

Dark mode, palettes and materials: change the look without forking a component.

Dark mode

Bevel follows the shadcn convention: a .dark class on html. In the dark, the light dims rather than inverting. Highlights get weaker, and paper stays paper, just less lit. With next-themes, set attribute="class" and it works.

For a theme switch that feels like the lights going down, the theme-transition item runs the change through a View Transition:

Terminal
npx shadcn@latest add @bevel/theme-transition
theme-toggle.tsx
import { transitionTheme } from "@/lib/theme-transition"

onClick={() => transitionTheme(next, () => setTheme(next))}

Change a palette

Each component names its colours once, as variables in its stylesheet. Override them in your own CSS, globally or under any selector, and the component follows. Nothing to fork.

globals.css
/* Brand red for every alarm lamp */
:root {
  --annunciator-red: oklch(0.6 0.22 15);
}

/* A green-steel Kardex tray, only in the billing area */
.billing [data-slot="visible-file"] {
  --visible-file-tray: oklch(0.5 0.04 160);
}

Change the whole look

Foundation tokens change everything at once. A flatter, softer set needs only a few lines:

globals.css
:root {
  --bevel-shade: oklch(0 0 0 / 0.2);        /* softer shadows */
  --bevel-radius-md: 0.875rem;               /* rounder objects */
  --bevel-duration-move: 240ms;              /* snappier motion */
}

One-off changes

Every part accepts className, merged last, so yours wins. Parts carry data-slot and their state as data-* attributes, so you can style any part in any state from outside:

page.tsx
<AnnunciatorTile className="min-h-20 text-sm" ... />
globals.css
[data-slot="annunciator-tile"][data-state="new"] {
  outline: 2px solid var(--annunciator-red);
}
Polymorphism uses Base UI's render prop, where a part supports it. There is no asChild.
NextAccessibility