Theming
One file themes every Rabtx component. It uses shadcn/ui's token names, so a shadcn theme and the Rabtx theme are interchangeable.
The one file
Everything visual comes from packages/rabtx/src/styles/theme.css. It is laid out like a shadcn
theme:
:rootholds the light values and.darkthe dark ones, asbackground/foregroundpairs:--primaryis a surface,--primary-foregroundis the text and icons on it.@theme inlinemaps every token into Tailwind, sobg-primary,text-muted-foreground,border-inputandrounded-lgwork and stay live when a variable changes.--radiusis the one corner radius. Tailwind'srounded-smtorounded-4xland the Rabtx radius tiers are derived from it.
Components read nothing else: no colors, radii or shadows are written into a component. Change a value in this file and the web app, these docs and every component installed from the registry follow.
Customising
The whole theme. Edit theme.css. To swap in a theme from the
shadcn themes page or tweakcn, paste its :root and .dark
blocks over the shadcn tokens; the Rabtx tokens below keep working.
Part of a page. Set tokens on any ancestor. Everything inside follows.
<section className="[--primary:var(--success)] [--radius:0.25rem]">
<Button>Approve</Button>
</section>One component. Pass Tailwind classes. Components put their own styles in the components
layer, so your utilities win: <Button className="rounded-md">.
A subtree only. Add the class rabtx to apply the theme to one element and its children; it
follows a .dark ancestor, or pin it with rabtx light or rabtx dark. The web app and this site
use that to embed Rabtx next to their own themes.
In a shadcn project. Components from the registry read your existing shadcn theme. Add the Rabtx
theme on top with npx shadcn@latest add @rabtx/theme; without it, the Rabtx-only tokens (status
tones, field depth and the rest) are derived from your shadcn colors.
Why there are no per-component variables
Earlier versions had a layer of component variables (--rx-btn-primary-bg, --rx-select-panel
and so on) on top of the tokens. They are gone: every component reads the semantic tokens directly,
which is what makes one file restyle everything and lets a shadcn theme drop straight in. To change
one component, set a semantic token on it or an ancestor, or pass classes. Each component page lists
the tokens it reads under Theming.
Modes
Set on any ancestor, never as a component prop.
| Mode | Values | Effect |
|---|---|---|
class="dark" | present or not | The .dark values |
data-depth | floating (default), flat | flat drops the raised edges, sheens and shadows |
data-radius | sharp, default, round | Overrides the radius tiers |
data-motion | on (default), off | off stops every transition; reduced motion in the OS does too |
data-size | sm, md (default), lg | Resizes the default control. An explicit size on one component still wins. |
--motion-ms | a number of milliseconds | One duration for every spring. The curves stay. |
The UI switch at the top of these pages sets theme, radius, primary color, size and --motion-ms
on one canvas. Depth stays floating there.
Tokens
Read from theme.css when this page is built, with live swatches in light and dark.
Base
| Token | Light | Dark |
|---|---|---|
| --radius | 0.5rem | same |
The control font (buttons, selects)
| Token | Light | Dark |
|---|---|---|
| --font-sans | "SF Pro Text", "SF Pro", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, sans-serif | same |
shadcn semantic colors
| Token | Light | Dark |
|---|---|---|
| --background | #ffffff | #161616 |
| --foreground | #292929 | #ebebeb |
| --card | #ffffff | #212121 |
| --card-foreground | #292929 | #ebebeb |
| --popover | #ffffff | #212121 |
| --popover-foreground | #292929 | #ebebeb |
| --primary | #2d7cf6 | #2d7cf6 |
| --primary-foreground | #ffffff | #ffffff |
| --secondary | #f5f5f5 | #1d1d1d |
| --secondary-foreground | #292929 | #ebebeb |
| --muted | #f2f2f2 | #303030 |
| --muted-foreground | #5d5d5d | #a3a3a3 |
| --accent | rgb(41 41 41 / 0.06) | rgb(235 235 235 / 0.06) |
| --accent-foreground | #292929 | #ebebeb |
| --destructive | #e5484d | #f0686d |
| --border | #f2f2f2 | #303030 |
| --input | #e3e3e3 | #3d3d3d |
| --ring | #2d7cf6 | #6aa4fa |
| --chart-1 | #2d7cf6 | #4a8ff7 |
| --chart-2 | #30a46c | #30a46c |
| --chart-3 | #f0803c | #f0803c |
| --chart-4 | #8e5cf0 | #8e5cf0 |
| --chart-5 | #e5484d | #f0686d |
| --sidebar | #ffffff | #161616 |
| --sidebar-foreground | #292929 | #ebebeb |
| --sidebar-primary | #2d7cf6 | #2d7cf6 |
| --sidebar-primary-foreground | #ffffff | #ffffff |
| --sidebar-accent | rgb(41 41 41 / 0.06) | rgb(235 235 235 / 0.06) |
| --sidebar-accent-foreground | #292929 | #ebebeb |
| --sidebar-border | #f2f2f2 | #303030 |
| --sidebar-ring | #2d7cf6 | #6aa4fa |
Rabtx
| Token | Light | Dark |
|---|---|---|
| --subtle-foreground | #9e9e9e | #7f7f7f |
| --disabled-foreground | #a3a3a3 | #5f5f5f |
Rabtx
| Token | Light | Dark |
|---|---|---|
| --success | #30a46c | #30a46c |
| --warning | #f0803c | #f0803c |
| --violet | #8e5cf0 | #8e5cf0 |
| --tint-ink | #000000 | #ffffff |
Rabtx depth
| Token | Light | Dark |
|---|---|---|
| --field | #f5f5f5 | #1b1b1b |
| --field-disabled | #fafafa | #161616 |
| --field-edge-top | rgb(0 0 0 / 0.14) | rgb(0 0 0 / 0.55) |
| --field-edge-bottom | rgb(0 0 0 / 0.05) | rgb(255 255 255 / 0.07) |
| --field-shade | rgb(0 0 0 / 0.04) | rgb(0 0 0 / 0.25) |
Rabtx depth
| Token | Light | Dark |
|---|---|---|
| --surface-ring | rgb(0 0 0 / 0.1) | rgb(0 0 0 / 0.6) |
| --surface-edge-top | #ffffff | rgb(255 255 255 / 0.14) |
| --surface-edge-bottom | rgb(0 0 0 / 0.08) | rgb(0 0 0 / 0.4) |
| --surface-sheen | rgb(255 255 255 / 0.5) | rgb(255 255 255 / 0.05) |
| --surface-shade | rgb(0 0 0 / 0.03) | rgb(0 0 0 / 0.16) |
Rabtx shadows.
| Token | Light | Dark |
|---|---|---|
| --shadow-popover | 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1) | same |
| --shadow-raised | 0 8px 22px rgb(0 0 0 / 0.16) | same |
Rabtx radius tiers, all derived from --radius, so Round never over-rounds a box and clips text
| Token | Light | Dark |
|---|---|---|
| --radius-button | 999px | same |
| --radius-item | var(--radius) | same |
| --radius-control | var(--radius) | same |
| --radius-box | calc(var(--radius) * 1.5) | same |
| --radius-badge | calc(var(--radius) * 0.5) | same |
| --radius-check | calc(var(--radius) * 0.5) | same |
Rabtx motion
| Token | Light | Dark |
|---|---|---|
| --motion-ease | cubic-bezier(0.25, 1, 0.5, 1) | same |
Springs (generated from src/motion/springs.ts; each also has a -duration)
| Token | Light | Dark |
|---|---|---|
| --spring-press | linear(…), 279ms | same |
| --spring-swap | linear(…), 432ms | same |
| --spring-check | linear(…), 306ms | same |
| --spring-label | linear(…), 244ms | same |
| --spring-magnetic | linear(…), 300ms | same |
| --spring-unfold | linear(…), 249ms | same |
| --spring-tabs | linear(…), 672ms | same |
| --spring-panel | linear(…), 520ms | same |
| --spring-toast | linear(…), 315ms | same |
| --spring-switch | linear(…), 527ms | same |
| --spring-glide | linear(…), 424ms | same |
| --spring-expand | linear(…), 891ms | same |