Colors Design Tokens
Overview
The color composables help you create comprehensive color systems with minimal code. They generate color variables with automatic variants using pre-computed, gamut-aware oklch values for perceptually uniform color manipulation.
Why use color composables?
Color composables help you:
- Create color systems quickly: Define your base colors and automatically generate tints, shades, and levels.
- Maintain perceptual uniformity: Use oklch color space for consistent, predictable color variations.
- Enable flexible theming: Use the theme API to re-compute all derived variants for each theme's base colors.
- Reduce manual work: No need to manually calculate and define every color variant.
useColorDesignTokens
The useColorDesignTokens() function creates a set of color variables from a simple object of color definitions.
import { styleframe } from "styleframe";
import { useColorDesignTokens } from "@styleframe/theme";
const s = styleframe();
const {
colorPrimary,
colorSecondary,
colorInfo,
colorSuccess,
colorWarning,
colorDanger,
} = useColorDesignTokens(s, {
primary: "#006cff",
secondary: "#6c757d",
info: "#17a2b8",
success: "#28a745",
warning: "#ffc107",
danger: "#dc3545",
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--secondary: oklch(0.5575 0.0165 244.89 / 1);
--color--info: oklch(0.6552 0.1105 212.17 / 1);
--color--success: oklch(0.6401 0.1751 146.74 / 1);
--color--warning: oklch(0.8442 0.172159 84.9338 / 1);
--color--danger: oklch(0.5915 0.202 21.24 / 1);
}
Each key in the object becomes a color variable with the prefix color--, and the export name is automatically converted to camelCase (e.g., primary → colorPrimary).
primary, secondary, info, success, warning, and danger to create a consistent color system across your application.useColorLevelDesignTokens
The useColorLevelDesignTokens() function creates a set of color variants at specific OKLCH lightness targets. Each level maps to an absolute lightness value (0–1), and chroma is automatically scaled to stay within the sRGB gamut.
import { styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorLevelDesignTokens,
colorLevelValues,
} from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Use default level values
// 50: 0.97, -> near white
// 100: 0.93, -> very light
// 200: 0.85, -> light
// 300: 0.75, -> light-mid
// 400: 0.65, -> mid-light
// 500: 0.55, -> mid-tone
// 600: 0.45, -> mid-dark
// 700: 0.35, -> dark
// 800: 0.25, -> very dark
// 900: 0.17, -> near black
// 950: 0.12, -> darkest
const {
colorPrimary50,
colorPrimary100,
colorPrimary200,
colorPrimary300,
colorPrimary400,
colorPrimary500,
colorPrimary600,
colorPrimary700,
colorPrimary800,
colorPrimary900,
colorPrimary950,
} = useColorLevelDesignTokens(s, colorPrimary);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-50: oklch(0.97 0.0142 259.95 / 1);
--color--primary-100: oklch(0.93 0.0336 259.95 / 1);
--color--primary-200: oklch(0.85 0.0743 259.95 / 1);
--color--primary-300: oklch(0.75 0.1287 259.95 / 1);
--color--primary-400: oklch(0.65 0.1873 259.95 / 1);
--color--primary-500: oklch(0.55 0.2238 259.95 / 1);
--color--primary-600: oklch(0.45 0.1831 259.95 / 1);
--color--primary-700: oklch(0.35 0.1424 259.95 / 1);
--color--primary-800: oklch(0.25 0.1018 259.95 / 1);
--color--primary-900: oklch(0.17 0.0693 259.95 / 1);
--color--primary-950: oklch(0.12 0.0492 259.95 / 1);
}
50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 to create a balanced color palette with predictable steps.Creating Custom Level Variables
You can define your own custom level scale that fits your design needs:
import { styleframe } from "styleframe";
import { useColorDesignTokens, useColorLevelDesignTokens } from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Custom three-tone system
const { colorPrimaryLight, colorPrimaryBase, colorPrimaryDark } =
useColorLevelDesignTokens(s, colorPrimary, {
light: 0.75, // L = 0.75
base: 0.55, // L = 0.55
dark: 0.25, // L = 0.25
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-light: oklch(0.75 0.1287 259.95 / 1);
--color--primary-base: oklch(0.55 0.2238 259.95 / 1);
--color--primary-dark: oklch(0.25 0.1018 259.95 / 1);
}
Extending the Default Level Values
You can add custom levels to the default scale:
import { styleframe } from "styleframe";
import { useColorDesignTokens, useColorLevelDesignTokens, colorLevelValues } from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Extend with additional fine-grained levels
const colorPrimaryLevels = useColorLevelDesignTokens(s, colorPrimary, {
...colorLevelValues,
150: 0.80, // Add 150 level
250: 0.70, // Add 250 level
350: 0.60, // Add 350 level
450: 0.50, // Add 450 level
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-50: oklch(0.97 0.0142 259.95 / 1);
--color--primary-100: oklch(0.93 0.0336 259.95 / 1);
--color--primary-150: oklch(0.8 0.101 259.95 / 1);
--color--primary-200: oklch(0.85 0.0743 259.95 / 1);
--color--primary-250: oklch(0.7 0.1575 259.95 / 1);
--color--primary-300: oklch(0.75 0.1287 259.95 / 1);
--color--primary-350: oklch(0.6 0.2181 259.95 / 1);
--color--primary-400: oklch(0.65 0.1873 259.95 / 1);
--color--primary-450: oklch(0.5 0.2035 259.95 / 1);
--color--primary-500: oklch(0.55 0.2238 259.95 / 1);
--color--primary-600: oklch(0.45 0.1831 259.95 / 1);
--color--primary-700: oklch(0.35 0.1424 259.95 / 1);
--color--primary-800: oklch(0.25 0.1018 259.95 / 1);
--color--primary-900: oklch(0.17 0.0693 259.95 / 1);
--color--primary-950: oklch(0.12 0.0492 259.95 / 1);
}
useColorShadeDesignTokens
The useColorShadeDesignTokens() function creates darker variants of a color by subtracting from the base color's lightness, with gamut-aware chroma scaling.
import { styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorShadeDesignTokens,
} from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Use default shade values
// 50: 5, -> 5% darker
// 100: 10, -> 10% darker
// 150: 15, -> 15% darker
// 200: 20, -> 20% darker
const {
colorPrimaryShade50,
colorPrimaryShade100,
colorPrimaryShade150,
colorPrimaryShade200,
} = useColorShadeDesignTokens(s, colorPrimary);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-shade-50: oklch(0.5249 0.2136 259.95 / 1);
--color--primary-shade-100: oklch(0.4749 0.1932 259.95 / 1);
--color--primary-shade-150: oklch(0.4249 0.1729 259.95 / 1);
--color--primary-shade-200: oklch(0.3749 0.1526 259.95 / 1);
}
Creating Custom Shade Variables
Define your own shade scale for precise control over darker variants:
import { styleframe } from "styleframe";
import { useColorDesignTokens, useColorShadeDesignTokens } from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Custom shade system for hover and active states
const { colorPrimaryHover, colorPrimaryActive, colorPrimaryPressed } =
useColorShadeDesignTokens(s, colorPrimary, {
hover: 8, // 8% darker for hover
active: 12, // 12% darker for active
pressed: 18, // 18% darker for pressed
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-hover: oklch(0.4949 0.2014 259.95 / 1);
--color--primary-active: oklch(0.4549 0.1851 259.95 / 1);
--color--primary-pressed: oklch(0.3949 0.1607 259.95 / 1);
}
Extending the Default Shade Values
Add more shade levels to the default set:
import { styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorShadeDesignTokens,
defaultColorShadeValues,
} from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Extend with additional shade levels
const colorPrimaryShades = useColorShadeDesignTokens(s, colorPrimary, {
...defaultColorShadeValues,
250: 25, // Add 250 level (25% darker)
300: 30, // Add 300 level (30% darker)
400: 40, // Add 400 level (40% darker)
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-shade-50: oklch(0.5249 0.2136 259.95 / 1);
--color--primary-shade-100: oklch(0.4749 0.1932 259.95 / 1);
--color--primary-shade-150: oklch(0.4249 0.1729 259.95 / 1);
--color--primary-shade-200: oklch(0.3749 0.1526 259.95 / 1);
--color--primary-shade-250: oklch(0.3249 0.1322 259.95 / 1);
--color--primary-shade-300: oklch(0.2749 0.1119 259.95 / 1);
--color--primary-shade-400: oklch(0.1749 0.0713 259.95 / 1);
}
useColorTintDesignTokens
The useColorTintDesignTokens() function creates lighter variants of a color by adding to the base color's lightness, with gamut-aware chroma scaling.
import { styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorTintDesignTokens,
defaultColorTintValues,
} from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Use default tint values
// 50: 5, -> 5% lighter
// 100: 10, -> 10% lighter
// 150: 15, -> 15% lighter
// 200: 20, -> 20% lighter
const {
colorPrimaryTint50,
colorPrimaryTint100,
colorPrimaryTint150,
colorPrimaryTint200,
} = useColorTintDesignTokens(s, colorPrimary, defaultColorTintValues);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-tint-50: oklch(0.6249 0.2026 259.95 / 1);
--color--primary-tint-100: oklch(0.6749 0.1723 259.95 / 1);
--color--primary-tint-150: oklch(0.7249 0.143 259.95 / 1);
--color--primary-tint-200: oklch(0.7749 0.1148 259.95 / 1);
}
Creating Custom Tint Variables
Create your own tint scale for specific design needs:
import { styleframe } from "styleframe";
import { useColorDesignTokens, useColorTintDesignTokens } from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Custom tint system for background variations
const { colorPrimarySubtle, colorPrimaryLight, colorPrimaryPale } =
useColorTintDesignTokens(s, colorPrimary, {
subtle: 25, // 25% lighter
light: 35, // 35% lighter
pale: 45, // 45% lighter
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-subtle: oklch(0.8249 0.0876 259.95 / 1);
--color--primary-light: oklch(0.9249 0.0361 259.95 / 1);
--color--primary-pale: oklch(1 0 259.95 / 1);
}
Extending the Default Tint Values
Add more tint levels to the default set:
import { styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorTintDesignTokens,
defaultColorTintValues,
} from "@styleframe/theme";
const s = styleframe();
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Extend with additional tint levels
const colorPrimaryTints = useColorTintDesignTokens(s, colorPrimary, {
...defaultColorTintValues,
250: 25, // Add 250 level (25% lighter)
300: 30, // Add 300 level (30% lighter)
400: 40, // Add 400 level (40% lighter)
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-tint-50: oklch(0.6249 0.2026 259.95 / 1);
--color--primary-tint-100: oklch(0.6749 0.1723 259.95 / 1);
--color--primary-tint-150: oklch(0.7249 0.143 259.95 / 1);
--color--primary-tint-200: oklch(0.7749 0.1148 259.95 / 1);
--color--primary-tint-250: oklch(0.8249 0.0876 259.95 / 1);
--color--primary-tint-300: oklch(0.8749 0.0614 259.95 / 1);
--color--primary-tint-400: oklch(0.9749 0.0119 259.95 / 1);
}
Examples
Complete Color System
Here's a complete example showing how to build a comprehensive color system:
import { styleframe } from "styleframe";
import type { Styleframe } from "styleframe";
import {
useColorDesignTokens,
useColorLevelDesignTokens,
useColorShadeDesignTokens,
useColorTintDesignTokens,
colorLevelValues,
defaultColorShadeValues,
defaultColorTintValues,
} from "@styleframe/theme";
const s = styleframe();
// Base colors
const { colorPrimary, colorSecondary, colorSuccess, colorDanger } = useColorDesignTokens(
s,
{
primary: "#006cff",
secondary: "#6c757d",
success: "#28a745",
danger: "#dc3545",
} as const,
);
// Create full scales for primary color
const colorPrimaryLevels = useColorLevelDesignTokens(
s,
colorPrimary,
colorLevelValues,
);
const colorPrimaryShades = useColorShadeDesignTokens(
s,
colorPrimary,
defaultColorShadeValues,
);
const colorPrimaryTints = useColorTintDesignTokens(s, colorPrimary, defaultColorTintValues);
// Create limited variants for secondary colors
const { colorSecondaryShade100 } = useColorShadeDesignTokens(s, colorSecondary, {
100: 10,
} as const);
const { colorSuccessTint100 } = useColorTintDesignTokens(s, colorSuccess, {
100: 10,
} as const);
const { colorDangerShade100 } = useColorShadeDesignTokens(s, colorDanger, {
100: 10,
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--secondary: oklch(0.5575 0.0165 244.89 / 1);
--color--success: oklch(0.6401 0.1751 146.74 / 1);
--color--danger: oklch(0.5915 0.202 21.24 / 1);
/* Primary levels */
--color--primary-50: oklch(0.97 0.0142 259.95 / 1);
--color--primary-100: oklch(0.93 0.0336 259.95 / 1);
--color--primary-200: oklch(0.85 0.0743 259.95 / 1);
--color--primary-300: oklch(0.75 0.1287 259.95 / 1);
--color--primary-400: oklch(0.65 0.1873 259.95 / 1);
--color--primary-500: oklch(0.55 0.2238 259.95 / 1);
--color--primary-600: oklch(0.45 0.1831 259.95 / 1);
--color--primary-700: oklch(0.35 0.1424 259.95 / 1);
--color--primary-800: oklch(0.25 0.1018 259.95 / 1);
--color--primary-900: oklch(0.17 0.0693 259.95 / 1);
--color--primary-950: oklch(0.12 0.0492 259.95 / 1);
/* Primary shades */
--color--primary-shade-50: oklch(0.5249 0.2136 259.95 / 1);
--color--primary-shade-100: oklch(0.4749 0.1932 259.95 / 1);
--color--primary-shade-150: oklch(0.4249 0.1729 259.95 / 1);
--color--primary-shade-200: oklch(0.3749 0.1526 259.95 / 1);
/* Primary tints */
--color--primary-tint-50: oklch(0.6249 0.2026 259.95 / 1);
--color--primary-tint-100: oklch(0.6749 0.1723 259.95 / 1);
--color--primary-tint-150: oklch(0.7249 0.143 259.95 / 1);
--color--primary-tint-200: oklch(0.7749 0.1148 259.95 / 1);
/* Other color variants */
--color--secondary-shade-100: oklch(0.4575 0.0135 244.89 / 1);
--color--success-tint-100: oklch(0.7401 0.2024 146.74 / 1);
--color--danger-shade-100: oklch(0.4915 0.1679 21.24 / 1);
}
Theme-Aware Color System
Create color variables that adapt to different themes. Since variants are pre-computed at build time, the theme API re-computes all derived variants for each theme's base colors:
import { styleframe } from "styleframe";
import { useColorDesignTokens, useColorLevelDesignTokens } from "@styleframe/theme";
const s = styleframe();
const { theme } = s;
// Light theme colors
const { colorPrimary } = useColorDesignTokens(s, {
primary: "#006cff",
} as const);
// Dark theme colors
theme('dark', ({ variable }) => {
variable(colorPrimary, "#4da3ff");
})
// Create level variants that work with both themes
const colorPrimaryLevels = useColorLevelDesignTokens(s, colorPrimary, {
100: 0.93,
500: 0.55,
900: 0.17,
} as const);
export default s;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-100: oklch(0.93 0.0336 259.95 / 1);
--color--primary-500: oklch(0.55 0.2238 259.95 / 1);
--color--primary-900: oklch(0.17 0.0693 259.95 / 1);
}
[data-theme="dark"] {
--color--primary: oklch(0.7047 0.1587 252.35 / 1);
--color--primary-100: oklch(0.93 0.0345 252.35 / 1);
--color--primary-500: oklch(0.55 0.1662 252.35 / 1);
--color--primary-900: oklch(0.17 0.0515 252.35 / 1);
}
Best Practices
- Start with base colors using
useColorDesignTokens()before creating variants. - Choose the right approach for variants: Use levels for absolute control, and shades/tints for relative adjustments.
- Be consistent with naming: If you use
50, 100, 200for shades, use the same scale for tints. - Test color contrast: Ensure your color variants meet WCAG accessibility guidelines for text and UI elements.
- Document your color system: Explain when to use each variant and what they represent in your design language.
- Use semantic color names: Names like
primary,success,warningare more meaningful thanblue,green,yellow. - Leverage OKLCH benefits: Take advantage of the perceptually uniform color space for consistent color variations.
as const to ensure the object is treated as a constant type. This helps TypeScript infer the return type of the composables and provides better type safety.FAQ
useColorDesignTokens composable is designed to be called multiple times. Variables are created using the default flag, which ensures that the color is only created once and reused across calls.useColorDesignTokens() can be in any valid CSS color format (hex, rgb, hsl, etc.). The composable will handle the conversion to oklch for pre-computed color generation.{ 100: 0.93, 500: 0.55, 900: 0.17 } for a simple three-tone system.useColorShadeDesignTokens() and useColorTintDesignTokens() on the same base color to create a complete range of darker and lighter variants.colorLevelValues, defaultColorShadeValues, defaultColorTintValues) that you can import and use directly. These provide common scales that work well for most design systems. You can use them as-is or extend them with additional levels.--color--primary via CSS or JavaScript at runtime will not automatically update derived variants like --color--primary-100. To support multiple color schemes, use the theme API (e.g., theme('dark', ...)) which re-computes all variants for each theme's base colors at build time.Breakpoints
Create and manage responsive breakpoint design tokens with CSS variables for consistent media query handling and adaptive layouts across your application.
Duration
Create and manage duration design tokens with CSS variables for consistent animation and transition timing across your application.