Theme
@basmilius/react-ui/theme.css holds the tokens every component draws with. Each color token has a light and a dark value, switched by data-theme="light" or data-theme="dark" on <html>. The tables on this page are read from that file when the site builds, so they list exactly what it holds. Each swatch is drawn inside its own theme, which is why a token such as --accent-soft, mixed from other tokens, shows its real value in both columns.
The theme maps the tokens onto Tailwind utilities (bg-surface, text-text-muted, border-border, shadow-float) and clears Tailwind's default palette with --color-*: initial. A component can reach only these colors, and so can your app, until you add your own.
Ground and surfaces
--bg is the page behind everything. A panel or a card is a --surface, and a popup, a menu or a dialog floats on --surface-raised. --surface-sunken is the recessed track of a segmented control or a field that should not draw a box. Hover and pressed get a step each, --surface-hover lighter than --surface-active, so pressed always outranks hover.
| Token | Light | Dark |
|---|---|---|
--bgbg-bg, text-bg, border-bg | #f4f4f5 | #0d0d10 |
--surfacebg-surface, text-surface, border-surface | #ffffff | #131316 |
--surface-raisedbg-surface-raised, text-surface-raised, border-surface-raised | #ffffff | #18181c |
--surface-sunkenbg-surface-sunken, text-surface-sunken, border-surface-sunken | #ececef | #08080a |
--surface-idlebg-surface-idle, text-surface-idle, border-surface-idle | color-mix(in srgb, var(--surface) 50%, var(--surface-sunken)) | color-mix(in srgb, var(--surface) 50%, var(--surface-sunken)) |
--surface-hoverbg-surface-hover, text-surface-hover, border-surface-hover | #f3f3f5 | #202024 |
--surface-activebg-surface-active, text-surface-active, border-surface-active | #dcdce2 | #28282e |
Borders
Alpha over whatever is behind, never a gray of their own (see Principles). --border-soft is the hairline between rows, --border-strong the outline of a control that has to read as one.
| Token | Light | Dark |
|---|---|---|
--borderbg-border, text-border, border-border | rgb(0 0 0 / 0.11) | rgb(255 255 255 / 0.07) |
--border-strongbg-border-strong, text-border-strong, border-border-strong | rgb(0 0 0 / 0.2) | rgb(255 255 255 / 0.13) |
--border-softbg-border-soft, text-border-soft, border-border-soft | rgb(0 0 0 / 0.06) | rgb(255 255 255 / 0.03) |
Text
| Token | Light | Dark |
|---|---|---|
--textbg-text, text-text, border-text | #18181b | #ececf1 |
--text-mutedbg-text-muted, text-text-muted, border-text-muted | #6f6f78 | #9a9aa6 |
--text-faintbg-text-faint, text-text-faint, border-text-faint | #a1a1aa | #5f5f6b |
Accent
The accent marks the primary button, the focus outline, a picked choice and the text selection. The default is a neutral blue. Your app sets its own, see Your own accent below.
| Token | Light | Dark |
|---|---|---|
--accentbg-accent, text-accent, border-accent | #155dfc | #155dfc |
--accent-rgb | 21 93 252 | 21 93 252 |
--accent-softbg-accent-soft, text-accent-soft, border-accent-soft | color-mix(in srgb, var(--accent) 16%, var(--surface)) | color-mix(in srgb, var(--accent) 16%, var(--surface)) |
--accent-textbg-accent-text, text-accent-text, border-accent-text | #ffffff | #ffffff |
--selection | rgb(var(--accent-rgb) / 0.55) | rgb(var(--accent-rgb) / 0.6) |
Status
Four states something can be in (running, waiting on a person, idle, failed) and a positive fill for a step that went well.
| Token | Light | Dark |
|---|---|---|
--status-runningbg-status-running, text-status-running, border-status-running | #2563eb | #60a5fa |
--status-needs-youbg-status-needs-you, text-status-needs-you, border-status-needs-you | #d97706 | #fbbf24 |
--status-idlebg-status-idle, text-status-idle, border-status-idle | #16a34a | #4ade80 |
--status-errorbg-status-error, text-status-error, border-status-error | #dc2626 | #ef4444 |
--positivebg-positive, text-positive, border-positive | #15803d | #4ade80 |
--positive-textbg-positive-text, text-positive-text, border-positive-text | #ffffff | #052e16 |
Shadows
--float-shadow lifts everything that floats: menus, dialogs, tooltips, toasts. --raised-shadow is the small lift of a switch thumb or a picked segment off its track.
| Token | Light | Dark |
|---|---|---|
--float-shadowshadow-float | 0 1px 2px -1px rgb(0 0 0 / 0.08), 0 3px 6px -2px rgb(0 0 0 / 0.07), 0 8px 16px -8px rgb(0 0 0 / 0.1), 0 16px 28px -14px rgb(0 0 0 / 0.1) | 0 1px 2px -1px rgb(0 0 0 / 0.3), 0 2px 4px rgb(0 0 0 / 0.24), 0 4px 8px rgb(0 0 0 / 0.18), 0 6px 12px rgb(0 0 0 / 0.1) |
--raised-shadowshadow-raised | 0 1px 2px rgb(0 0 0 / 0.05), 0 2px 4px -1px rgb(0 0 0 / 0.05), 0 6px 12px -6px rgb(0 0 0 / 0.08) | 0 1px 2px rgb(0 0 0 / 0.3), 0 2px 4px rgb(0 0 0 / 0.2), 0 4px 8px rgb(0 0 0 / 0.12) |
Media
A control drawn over a picture rather than over the interface, such as the handle of a Wipe. It stays the same in both themes.
| Token | Light | Dark |
|---|---|---|
--media-handlebg-media-handle, text-media-handle, border-media-handle | #ffffff | #ffffff |
--media-handle-textbg-media-handle-text, text-media-handle-text, border-media-handle-text | #18181b | #18181b |
--media-handle-edgeshadow-media-handle-edge | 0 0 0 1px rgb(0 0 0 / 0.3) | 0 0 0 1px rgb(0 0 0 / 0.3) |
--media-handle-shadowshadow-media-handle | 0 2px 12px rgb(0 0 0 / 0.35) | 0 2px 12px rgb(0 0 0 / 0.35) |
File icons
The palette of the @pierre/trees icon set, which FileIcon colors a file type with. A file icon is the one thing the theme does not tint: TypeScript stays blue wherever the file shows up.
| Token | Light | Dark |
|---|---|---|
--file-icon-gray | #84848a | #adadb1 |
--file-icon-red | #d52c36 | #ff6762 |
--file-icon-vermilion | #ff8c5b | #d5512f |
--file-icon-orange | #d47628 | #ffa359 |
--file-icon-yellow | #d5a910 | #ffd452 |
--file-icon-green | #199f43 | #5ecc71 |
--file-icon-teal | #17a5af | #64d1db |
--file-icon-cyan | #1ca1c7 | #68cdf2 |
--file-icon-blue | #1a85d4 | #69b1ff |
--file-icon-indigo | #693acf | #9d6afb |
--file-icon-purple | #a631be | #d568ea |
--file-icon-pink | #d32a61 | #ff678d |
--file-icon-mauve | #594c5b | #79697b |
Layers
Whole numbers in one place, so a new popup cannot guess where it goes. A dialog sits under the menus and selects it can open, and tooltips sit over both. A component reaches the popup step with z-(--z-popup).
| Token | Light | Dark |
|---|---|---|
--z-dialog-backdrop | 80 | 80 |
--z-dialog | 90 | 90 |
--z-popup | 100 | 100 |
--z-tooltip | 110 | 110 |
--z-shortcut-hints | 120 | 120 |
Type scale
The sizes are rem, rounded to whole pixels, against the font size your app sets on <html>. 15px reads well in a desktop app. Code keeps an absolute size.
| Token | Value | Sample |
|---|---|---|
--text-xs | round(0.875rem, 1px)round(1.25rem, 1px) | Build history |
--text-sm | round(0.9375rem, 1px)round(1.375rem, 1px) | Build history |
--text-base | round(1rem, 1px)round(1.5rem, 1px) | Build history |
--text-lg | round(1.125rem, 1px)round(1.5rem, 1px) | Build history |
--text-4xl | round(2.25rem, 1px)round(2.5rem, 1px) | Build history |
--text-code | 13px20px | Build history |
| Token | Value | Sample |
|---|---|---|
--leading-tight | round(1.25em, 1px) | Build history |
--leading-snug | round(1.375em, 1px) | Build history |
--leading-normal | round(1.5em, 1px) | Build history |
Radius
| Token | Value | Sample |
|---|---|---|
--radius-xs | round(0.125rem, 1px) | |
--radius-sm | round(0.25rem, 1px) | |
--radius-md | round(0.375rem, 1px) | |
--radius-lg | round(0.5rem, 1px) | |
--radius-xl | round(0.75rem, 1px) | |
--radius-2xl | round(1rem, 1px) |
Spacing and fonts
The spacing unit is rounded, not each multiple of it, so every step lands on a whole pixel. --font-mono is a variable your app can override on <html> when a person picks a monospace font, and font-mono follows.
| Token | Value |
|---|---|
--spacing | round(0.25rem, 1px) |
| Token | Value | Sample |
|---|---|---|
--font-sans | -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", sans-serif | Build history |
--font-mono | ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace | Build history |
Your own accent
Set the accent in a rule after the theme import, in both forms. The second is the same color as channels, for the tokens that carry the accent with an alpha, such as the text selection.
@import "tailwindcss";
@import "@basmilius/react-ui/theme.css";
:root {
--accent: #7c3aed;
--accent-rgb: 124 58 237;
}--accent-soft is mixed from --accent and follows by itself. Set --accent-text too if white does not read on your accent. To give the dark theme its own accent, repeat the rule under [data-theme="dark"].
Your own colors
Add a token in both themes and map it onto a Tailwind color, the way the theme maps its own:
:root, [data-theme="light"] {
--brand: #0f766e;
}
[data-theme="dark"] {
--brand: #2dd4bf;
}
@theme inline {
--color-brand: var(--brand);
}Now bg-brand, text-brand and border-brand/40 exist. inline makes the utility read the variable where it is used, so the value follows data-theme. A token derived with var() resolves on the element that declares it; declare it in the same rule as the tokens it reads, as the theme does, so it follows a theme change.
Rules the utilities cannot write
The theme also carries a few classes for what a utility cannot express: the states Base UI puts on a menu row, pseudo-elements, keyframes. The components use them, and you can too, for something that has to look like one of them.
| Class | What it draws |
|---|---|
.icon-btn, .icon-btn-sm, .icon-btn-xs, .icon-btn-2xs | The square of an icon button and its pressed and disabled states. |
.field, .field-sm | A text field's border, height and focus outline. |
.menu-popup, .picker-popup, .menu-item | A menu's surface and a row in it, with its highlighted and disabled states. |
.cursor-row | A row of a list the arrow keys walk that is not a Base UI menu, with data-active="true" on the current one. |
.dialog-popup, .dialog-backdrop, .lightbox-frame, .lightbox-backdrop | A dialog's surface and dim, and a dialog that shows only a picture. |
.tooltip-positioner, .tooltip-popup | A tooltip that slides from one button to the next. |
.focus-ring, .focus-ring-within | The keyboard focus outline on something that is not a button, or on a card around a focused field. |
.scroll-fade-x, .scroll-fade-top | A fade at an edge that still hides content, switched on by data-fade-start and data-fade-end. |
.file-icon | A file icon's color by its data-hue. |
Every class sits in Tailwind's components layer, so a utility on the same element still wins.