Customization
Tosui exposes its design tokens as CSS custom properties. Override a token after importing the library styles to change every component that uses it.
Quick start
Customize your brand colors for both light and dark modes in one place:
/* custom-theme.css */
:root {
/* Light mode primary - teal */
--t-light-primary-default: #0d9488;
--t-light-primary-emphasis: #0f766e;
--t-light-primary-subtle: #ccfbf1;
/* Dark mode primary - teal */
--t-dark-primary-default: #2dd4bf;
--t-dark-primary-emphasis: #5eead4;
--t-dark-primary-subtle: #134e4a;
}
Import after Tosui's styles:
import "@tosui/react/styles.css";
import "./custom-theme.css";
Tosui maps these primitive values to the active light or dark theme.
How theming works
Tosui uses a two-tier variable system for colors:
- Primitive variables (
--t-light-*,--t-dark-*) hold the actual color values for each mode - Semantic variables (
--t-color-*) reference the matching primitive for the active theme
For example, --t-color-primary-default automatically resolves to --t-light-primary-default in light mode and --t-dark-primary-default in dark mode.
When to use which
| Use case | Variable type | Example |
|---|---|---|
| Theme customization (both modes) | Primitives | --t-light-primary-default, --t-dark-primary-default |
| Scoped overrides | Semantic | --t-color-primary-default |
| Single element styling | Semantic | --t-color-primary-default |
| Single-mode-only changes | Semantic | --t-color-primary-default |
Use primitive variables to define a theme. Use semantic variables for a scoped override in the active theme.
Customizing colors
Brand colors
Primary and accent colors each have three variants:
| Variant | Purpose |
|---|---|
-default | Standard state (buttons, links) |
-emphasis | Hover/active states |
-subtle | Backgrounds, badges |
:root {
/* Primary brand color */
--t-light-primary-default: #8b5cf6;
--t-light-primary-emphasis: #7c3aed;
--t-light-primary-subtle: #ede9fe;
--t-dark-primary-default: #a78bfa;
--t-dark-primary-emphasis: #c4b5fd;
--t-dark-primary-subtle: #4c1d95;
/* Accent color */
--t-light-accent-default: #f59e0b;
--t-light-accent-emphasis: #d97706;
--t-light-accent-subtle: #fef3c7;
--t-dark-accent-default: #fbbf24;
--t-dark-accent-emphasis: #fcd34d;
--t-dark-accent-subtle: #78350f;
}
Feedback colors
Success, warning, error, and info follow the same -default, -emphasis, -subtle pattern:
:root {
/* Example: custom error colors */
--t-light-error-default: #dc2626;
--t-light-error-emphasis: #b91c1c;
--t-light-error-subtle: #fee2e2;
--t-dark-error-default: #f87171;
--t-dark-error-emphasis: #fca5a5;
--t-dark-error-subtle: #7f1d1d;
}
Surface colors
These control text, borders, and backgrounds:
| Variable | Purpose |
|---|---|
--t-{mode}-foreground | Primary text |
--t-{mode}-foreground-muted | Secondary text |
--t-{mode}-foreground-subtle | Tertiary text |
--t-{mode}-border | Standard borders |
--t-{mode}-border-muted | Subtle borders |
--t-{mode}-background | Page background |
--t-{mode}-surface | Card/panel backgrounds |
Replace {mode} with light or dark.
Other tokens
Spacing
| Variable | Default | Description |
|---|---|---|
--t-spacing-unit | 4px | Base unit for all spacing (multipliers 0-32) |
Changing this scales all spacing proportionally. With 5px, p={4} becomes 20px instead of 16px.
Typography
| Variable | Description |
|---|---|
--t-font-family-heading | Headings (default: system sans-serif) |
--t-font-family-body | Body text (default: system sans-serif) |
--t-font-family-mono | Code (default: system monospace) |
--t-font-size-xs through --t-font-size-5xl | Font sizes (12px to 48px) |
--t-font-weight-normal through --t-font-weight-bold | Font weights (400 to 700) |
--t-line-height-tight through --t-line-height-relaxed | Line heights (1.25 to 1.75) |
Borders & shadows
| Variable | Description |
|---|---|
--t-radius-none through --t-radius-full | Border radii (0px to 9999px) |
--t-border-width-thin through --t-border-width-thick | Border widths (1px to 4px) |
--t-shadow-sm through --t-shadow-lg | Box shadows |
Transitions
| Variable | Default | Description |
|---|---|---|
--t-transition-fast | 150ms | Quick transitions |
--t-transition-normal | 250ms | Standard transitions |
--t-transition-slow | 350ms | Slow transitions |
--t-transition-easing | ease-in-out | Easing function |
Override scopes
CSS variables cascade, so you can override at any level:
/* Global: affects entire app */
:root {
--t-light-primary-default: #dc2626;
}
/* Scoped: affects only this subtree */
.marketing-section {
--t-color-primary-default: #059669;
}
Scoped overrides use semantic variables (--t-color-*) because they apply to the active theme only.
Dark mode
Control the color scheme with the data-theme attribute:
<!-- Follow system preference (default) -->
<html>
<!-- Or explicitly: -->
<html data-theme="auto">
<!-- Force light mode -->
<html data-theme="light">
<!-- Force dark mode -->
<html data-theme="dark">
Without an explicit value, Tosui follows the system color scheme. Set data-theme to force a mode.
Fonts
Tosui uses system fonts by default. To use custom fonts:
Use Tosui's IBM Plex fonts
import "@tosui/react/styles.css";
import "@tosui/react/fonts.css"; // Adds IBM Plex Sans and Mono
Use your own fonts
/* custom-fonts.css */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
--t-font-family-heading: 'Inter', sans-serif;
--t-font-family-body: 'Inter', sans-serif;
}
import "@tosui/react/styles.css";
import "./custom-fonts.css";
Token reference
For the complete list of tokens and their default values, see the source:
packages/react/src/styles/styles.css