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:

TokenPurposeTailwind class
--backgroundPage backgroundbg-background
--foregroundMain texttext-foreground
--cardCard and panel surfacebg-card
--card-foregroundText on card surfacetext-card-foreground
--popoverFloating surfacebg-popover
--popover-foregroundText on floating surfacetext-popover-foreground
--mutedSubtle backgroundbg-muted
--muted-foregroundSecondary texttext-muted-foreground
--borderBorders and dividersborder-border
--inputForm field border/backgroundborder-input
--ringFocus ring colorring-ring

Functional colors

Functional colors communicate action, status, and intent. Each has a foreground counterpart to ensure readable text on colored backgrounds:

TokenPurposeExample usage
--primaryMain actions, links, active statesbg-primary, text-primary
--primary-foregroundText/icons on primary backgroundtext-primary-foreground
--secondarySecondary actionsbg-secondary
--secondary-foregroundText/icons on secondary backgroundtext-secondary-foreground
--successPositive statustext-success, bg-success
--success-foregroundText on success backgroundtext-success-foreground
--warningCaution statetext-warning, bg-warning
--warning-foregroundText on warning backgroundtext-warning-foreground
--dangerErrors, destructive actionstext-danger, bg-danger
--danger-foregroundText on danger backgroundtext-danger-foreground
--infoInformational statustext-info, bg-info
--info-foregroundText on info backgroundtext-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-border for surfaces and text
  • Use bg-primary, text-primary-foreground, ring-primary/20 for primary actions
  • Use status colors like text-danger, bg-success, or border-warning when communicating meaning
  • Use scale colors such as bg-primary-50 or hover:bg-primary-700 only for intentional custom states
  • Test contrast after changing color variables

Don't

  • Use bg-blue-500, text-gray-900, border-gray-200 in 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 --primary without 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

• Use semantic tokens first. Reach for 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.
  • 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