Customization
Tosui is fully customizable via CSS variables. Override any design token to match your brand, and the entire component library adapts automatically.
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";
That's it—Tosui's theme switching handles the rest automatically.
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 appropriate primitive based on 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 |
Rule of thumb: Use primitives for theming, semantic for overrides.
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;
}
{/* Inline: affects only this element and children */}
<Box style={{ '--t-color-primary-default': '#f59e0b' }}>
<Button>Uses amber primary</Button>
</Box>
Note: Scoped and inline overrides use semantic variables (--t-color-*) since they apply to the current 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">
Tosui automatically switches between light and dark values based on this attribute or system preference.
Fonts
Tosui uses system fonts by default. To use custom fonts:
Option 1: Use Tosui's IBM Plex Fonts
import "@tosui/react/styles.css";
import "@tosui/react/fonts.css"; // Adds IBM Plex Sans and Mono
Option 2: 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