Colors
The complete color token system — semantic, functional, and scale-based.
Overview
Byteion's color system is organized into three layers, each serving a distinct purpose:
Semantic colors describe UI purpose — --background, --foreground, --border, --card, --muted, --popover, --input, --ring. These adapt automatically between light and dark themes. Core and shared components should prefer semantic tokens for every surface and text element.
Functional colors communicate meaning and action — --primary, --secondary, --success, --warning, --danger, --info. Use these for interactive elements, status indicators, and contextual messaging.
Scale colors provide raw 50–950 steps for each hue. They are available for controlled custom states, charts, hover effects, and advanced composition. Scale colors should not replace semantic tokens for standard surfaces and text — use bg-card, not bg-primary-100, for a card surface.
How HSL variables work
Byteion stores color variables as HSL triplets (hue, saturation, lightness). This enables Tailwind's alpha modifier syntax — bg-primary/50, ring-primary/20, and border-primary/30 produce semi-transparent variants of any token. Because the CSS variable stores only the three HSL values, Tailwind can wrap them in hsl() with an alpha channel:
:root {
--primary: 217 91% 60%;
}
/* Tailwind resolves bg-primary/50 as: */
background-color: hsl(217 91% 60% / 0.5);
The Tailwind preset maps each token to hsl(var(--token) / <alpha-value>), so every utility class supports opacity modifiers out of the box.
Semantic colors
Semantic tokens describe purpose, not appearance. They swap values automatically when the theme switches between light and dark modes:
| Token | Purpose | Tailwind class |
|---|---|---|
| --background | Page background | bg-background |
| --foreground | Main text | text-foreground |
| --card | Card and panel surface | bg-card |
| --card-foreground | Text on card surface | text-card-foreground |
| --popover | Floating surface | bg-popover |
| --popover-foreground | Text on floating surface | text-popover-foreground |
| --muted | Subtle background | bg-muted |
| --muted-foreground | Secondary text | text-muted-foreground |
| --border | Borders and dividers | border-border |
| --input | Form field border/background | border-input |
| --ring | Focus ring color | ring-ring |
Functional colors
Functional colors communicate action, status, and intent. Each has a foreground counterpart to ensure readable text on colored backgrounds:
| Token | Purpose | Example usage |
|---|---|---|
| --primary | Main actions, links, active states | bg-primary, text-primary |
| --primary-foreground | Text/icons on primary background | text-primary-foreground |
| --secondary | Secondary actions | bg-secondary |
| --secondary-foreground | Text/icons on secondary background | text-secondary-foreground |
| --success | Positive status | text-success, bg-success |
| --success-foreground | Text on success background | text-success-foreground |
| --warning | Caution state | text-warning, bg-warning |
| --warning-foreground | Text on warning background | text-warning-foreground |
| --danger | Errors, destructive actions | text-danger, bg-danger |
| --danger-foreground | Text on danger background | text-danger-foreground |
| --info | Informational status | text-info, bg-info |
| --info-foreground | Text on info background | text-info-foreground |
Color scales
Each functional color has a multi-step scale (50–950) for hover states, soft backgrounds, chart accents, custom admin dashboards, and advanced composition:
Use scale colors intentionally. For core component surfaces, prefer semantic tokens such as bg-card, text-foreground, and border-border. Scale colors are an escape hatch for cases where semantic tokens do not apply — custom states, data visualisation, and dynamic content.
// packages/tokens/colors.js
module.exports = {
primary: {
50: '#eff6ff', // 50/100: subtle backgrounds
100: '#dbeafe',
// ... 200-400: intermediate steps
500: '#3b82f6', // 500/600: default / action color
600: '#2563eb',
// ... 700-800: hover / active states
900: '#1e3a5f', // 900/950: dark emphasis
950: '#172554',
},
};
All seven scales — primary, secondary, success, warning, danger, info, and neutral — follow the same 11-step pattern. Use them through Tailwind utilities such as bg-primary-50, hover:bg-primary-700, or text-neutral-500.
Dark mode
Adding the .dark class to <html> overrides the semantic color variables. Component class names stay the same — only the CSS variable values change. Functional brand scales (primary, success, etc.) typically remain stable, while surfaces and text invert through the semantic tokens:
.dark {
--background: 240 10% 3.9%; /* Page surface inverts */
--foreground: 0 0% 98%; /* Text inverts */
--border: 240 3.7% 15.9%;
--card: 240 6% 10%;
--primary: 217 91% 60%; /* Brand color stays stable */
}
Correct — semantic tokens adapt to both themes
<div class="rounded-xl border border-border bg-background text-foreground">
Works in light and dark mode.
</div>
Incorrect — hardcoded colors break in dark mode
<div class="rounded-xl border border-gray-200 bg-white text-gray-900">
Stays tied to a light theme.
</div>
Do and Don't
Do
- Use
bg-background,bg-card,text-foreground,text-muted-foreground,border-borderfor surfaces and text - Use
bg-primary,text-primary-foreground,ring-primary/20for primary actions - Use status colors like
text-danger,bg-success, orborder-warningwhen communicating meaning - Use scale colors such as
bg-primary-50orhover:bg-primary-700only for intentional custom states - Test contrast after changing color variables
Don't
- Use
bg-blue-500,text-gray-900,border-gray-200in core or shared components - Hardcode hex values in component classes — use Tailwind token classes
- Use scale colors when a semantic token already exists for the purpose
- Change
--primarywithout checking foreground contrast - Assume a colour works in dark mode without testing
Customization
Change the entire brand by updating --primary and its companion tokens in variables.css. A custom brand needs at minimum the primary hue, its foreground, and the focus ring colour:
:root {
--primary: 270 100% 50%; /* Purple brand */
--primary-foreground: 0 0% 100%; /* White text on purple */
--ring: 270 100% 50%; /* Focus ring matches brand */
}
--primary controls primary backgrounds, buttons, links, and active indicators. --primary-foreground keeps text and icons readable on those backgrounds. --ring aligns focus indicators with the brand colour. Custom themes should be tested in both light and dark mode to ensure adequate contrast.
Developer tips
bg-background, bg-card, and text-foreground before considering a scale colour.
• Use HSL triplets for CSS variables. Only HSL format supports Tailwind alpha modifiers like bg-primary/50.
• Pair foreground tokens with background tokens. When you use bg-primary, use text-primary-foreground for the text — never assume white or black will be readable.
• Test contrast for buttons, alerts, badges, links, and focus rings. A custom primary may look correct in isolation but fail WCAG AA on interactive elements.
• Search for primitive color violations before publishing. Run a project-wide search for bg-blue-, text-gray-, border-gray-, text-white, and bg-zinc- in component files.Related
- Design Principles — Tokens first, semantic usage, and composition
- Theming Guide — Brand themes, dark mode, and custom variables
- Design Tokens — Spacing, radius, shadows, and typography tokens
- Accessibility — Contrast ratios, focus indicators, and colour-blind safe palettes
- Button Component — Primary, secondary, and ghost variants with colour tokens
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced component patterns.
Explore Pro Components