Skip to content

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.

TokenLightDark
--bg
bg-bg, text-bg, border-bg
#f4f4f5
#0d0d10
--surface
bg-surface, text-surface, border-surface
#ffffff
#131316
--surface-raised
bg-surface-raised, text-surface-raised, border-surface-raised
#ffffff
#18181c
--surface-sunken
bg-surface-sunken, text-surface-sunken, border-surface-sunken
#ececef
#08080a
--surface-idle
bg-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-hover
bg-surface-hover, text-surface-hover, border-surface-hover
#f3f3f5
#202024
--surface-active
bg-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.

TokenLightDark
--border
bg-border, text-border, border-border
rgb(0 0 0 / 0.11)
rgb(255 255 255 / 0.07)
--border-strong
bg-border-strong, text-border-strong, border-border-strong
rgb(0 0 0 / 0.2)
rgb(255 255 255 / 0.13)
--border-soft
bg-border-soft, text-border-soft, border-border-soft
rgb(0 0 0 / 0.06)
rgb(255 255 255 / 0.03)

Text ​

TokenLightDark
--text
bg-text, text-text, border-text
#18181b
#ececf1
--text-muted
bg-text-muted, text-text-muted, border-text-muted
#6f6f78
#9a9aa6
--text-faint
bg-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.

TokenLightDark
--accent
bg-accent, text-accent, border-accent
#155dfc
#155dfc
--accent-rgb
21 93 252
21 93 252
--accent-soft
bg-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-text
bg-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.

TokenLightDark
--status-running
bg-status-running, text-status-running, border-status-running
#2563eb
#60a5fa
--status-needs-you
bg-status-needs-you, text-status-needs-you, border-status-needs-you
#d97706
#fbbf24
--status-idle
bg-status-idle, text-status-idle, border-status-idle
#16a34a
#4ade80
--status-error
bg-status-error, text-status-error, border-status-error
#dc2626
#ef4444
--positive
bg-positive, text-positive, border-positive
#15803d
#4ade80
--positive-text
bg-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.

TokenLightDark
--float-shadow
shadow-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-shadow
shadow-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.

TokenLightDark
--media-handle
bg-media-handle, text-media-handle, border-media-handle
#ffffff
#ffffff
--media-handle-text
bg-media-handle-text, text-media-handle-text, border-media-handle-text
#18181b
#18181b
--media-handle-edge
shadow-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-shadow
shadow-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.

TokenLightDark
--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).

TokenLightDark
--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.

TokenValueSample
--text-xsround(0.875rem, 1px)
round(1.25rem, 1px)
Build history
--text-smround(0.9375rem, 1px)
round(1.375rem, 1px)
Build history
--text-baseround(1rem, 1px)
round(1.5rem, 1px)
Build history
--text-lground(1.125rem, 1px)
round(1.5rem, 1px)
Build history
--text-4xlround(2.25rem, 1px)
round(2.5rem, 1px)
Build history
--text-code13px
20px
Build history
TokenValueSample
--leading-tightround(1.25em, 1px)Build history
--leading-snuground(1.375em, 1px)Build history
--leading-normalround(1.5em, 1px)Build history

Radius ​

TokenValueSample
--radius-xsround(0.125rem, 1px)
--radius-smround(0.25rem, 1px)
--radius-mdround(0.375rem, 1px)
--radius-lground(0.5rem, 1px)
--radius-xlround(0.75rem, 1px)
--radius-2xlround(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.

TokenValue
--spacinground(0.25rem, 1px)
TokenValueSample
--font-sans-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", sans-serifBuild history
--font-monoui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospaceBuild 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.

css
@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:

css
: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.

ClassWhat it draws
.icon-btn, .icon-btn-sm, .icon-btn-xs, .icon-btn-2xsThe square of an icon button and its pressed and disabled states.
.field, .field-smA text field's border, height and focus outline.
.menu-popup, .picker-popup, .menu-itemA menu's surface and a row in it, with its highlighted and disabled states.
.cursor-rowA 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-backdropA dialog's surface and dim, and a dialog that shows only a picture.
.tooltip-positioner, .tooltip-popupA tooltip that slides from one button to the next.
.focus-ring, .focus-ring-withinThe keyboard focus outline on something that is not a button, or on a card around a focused field.
.scroll-fade-x, .scroll-fade-topA fade at an edge that still hides content, switched on by data-fade-start and data-fade-end.
.file-iconA 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.