Design Tokens

Colors

Create and manage color design tokens with automatic, easily configurable variants, tints, and shades using the oklch color space.

Overview

The color composables help you create comprehensive color systems with minimal code. They generate color variables with automatic variants using modern CSS relative color syntax and the oklch color space 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 lightness levels.
  • Maintain perceptual uniformity: Use oklch color space for consistent, predictable color variations.
  • Enable flexible theming: Override base colors to instantly update all derived variants.
  • Reduce manual work: No need to manually calculate and define every color variant.

useColor

The useColor() function creates a set of color variables from a simple object of color definitions.

import { styleframe } from "styleframe";
import { useColor } from "@styleframe/theme";

const s = styleframe();

const {
    colorPrimary,
    colorSecondary,
    colorInfo,
    colorSuccess,
    colorWarning,
    colorDanger,
} = useColor(s, {
    primary: "#006cff",
    secondary: "#6c757d",
    info: "#17a2b8",
    success: "#28a745",
    warning: "#ffc107",
    danger: "#dc3545",
} as const);

export default s;

Each key in the object becomes a color variable with the prefix color--, and the export name is automatically converted to camelCase (e.g., primarycolorPrimary).

Pro tip: Use semantic names like primary, secondary, info, success, warning, and danger to create a consistent color system across your application.

useColorLightness

The useColorLightness() function creates a set of color variants with different lightness values using oklch's lightness channel.

import { styleframe } from "styleframe";
import {
    useColor,
    useColorLightness,
    defaultColorLightnessValues,
} from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Use default lightness values
// 50: 5,   -> 5% lightness
// 100: 10, -> 10% lightness
// 200: 20, -> 20% lightness
// 300: 30, -> 30% lightness
// 400: 40, -> 40% lightness
// 500: 50, -> 50% lightness
// 600: 60, -> 60% lightness
// 700: 70, -> 70% lightness
// 800: 80, -> 80% lightness
// 900: 90, -> 90% lightness
// 950: 95, -> 95% lightness
const {
    colorPrimary50,
    colorPrimary100,
    colorPrimary200,
    colorPrimary300,
    colorPrimary400,
    colorPrimary500,
    colorPrimary600,
    colorPrimary700,
    colorPrimary800,
    colorPrimary900,
    colorPrimary950,
} = useColorLightness(s, colorPrimary);

export default s;
Common lightness scale: Many design systems use levels like 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 to create a balanced color palette with predictable steps.

Creating Custom Lightness Variables

You can define your own custom lightness scale that fits your design needs:

import { styleframe } from "styleframe";
import { useColor, useColorLightness } from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Custom three-tone system
const { colorPrimaryLight, colorPrimaryBase, colorPrimaryDark } =
    useColorLightness(s, colorPrimary, {
        light: 75, // 75% lightness
        base: 50, // 50% lightness
        dark: 25, // 25% lightness
    } as const);

export default s;

Extending the Default Lightness Values

You can add custom levels to the default scale:

import { styleframe } from "styleframe";
import { useColor, useColorLightness, defaultColorLightnessValues } from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Extend with additional fine-grained levels
const colorPrimaryLevels = useColorLightness(s, colorPrimary, {
    ...defaultColorLightnessValues,
    150: 15, // Add 150 level
    250: 25, // Add 250 level
    350: 35, // Add 350 level
    450: 45, // Add 450 level
} as const);

export default s;

useColorShade

The useColorShade() function creates darker variants of a color by subtracting from the lightness channel.

import { styleframe } from "styleframe";
import {
    useColor,
    useColorShade,
} from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(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,
} = useColorShade(s, colorPrimary);

export default s;

Creating Custom Shade Variables

Define your own shade scale for precise control over darker variants:

import { styleframe } from "styleframe";
import { useColor, useColorShade } from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Custom shade system for hover and active states
const { colorPrimaryHover, colorPrimaryActive, colorPrimaryPressed } =
    useColorShade(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;

Extending the Default Shade Values

Add more shade levels to the default set:

import { styleframe } from "styleframe";
import {
    useColor,
    useColorShade,
    defaultColorShadeValues,
} from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Extend with additional shade levels
const colorPrimaryShades = useColorShade(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;
Naming convention: The level names don't have to follow any specific pattern. Use whatever scale makes sense for your design system.

useColorTint

The useColorTint() function creates lighter variants of a color by adding to the lightness channel.

import { styleframe } from "styleframe";
import {
    useColor,
    useColorTint,
    defaultColorTintValues,
} from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(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,
} = useColorTint(s, colorPrimary, defaultColorTintValues);

export default s;

Creating Custom Tint Variables

Create your own tint scale for specific design needs:

import { styleframe } from "styleframe";
import { useColor, useColorTint } from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Custom tint system for background variations
const { colorPrimarySubtle, colorPrimaryLight, colorPrimaryPale } =
    useColorTint(s, colorPrimary, {
        subtle: 25, // 25% lighter
        light: 35, // 35% lighter
        pale: 45, // 45% lighter
    } as const);

export default s;

Extending the Default Tint Values

Add more tint levels to the default set:

import { styleframe } from "styleframe";
import {
    useColor,
    useColorTint,
    defaultColorTintValues,
} from "@styleframe/theme";

const s = styleframe();

const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Extend with additional tint levels
const colorPrimaryTints = useColorTint(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;

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 {
    useColor,
    useColorLightness,
    useColorShade,
    useColorTint,
    defaultColorLightnessValues,
    defaultColorShadeValues,
    defaultColorTintValues,
} from "@styleframe/theme";

const s = styleframe();

// Base colors
const { colorPrimary, colorSecondary, colorSuccess, colorDanger } = useColor(
    s,
    {
        primary: "#006cff",
        secondary: "#6c757d",
        success: "#28a745",
        danger: "#dc3545",
    } as const,
);

// Create full scales for primary color
const colorPrimaryLevels = useColorLightness(
    s,
    colorPrimary,
    defaultColorLightnessValues,
);
const colorPrimaryShades = useColorShade(
    s,
    colorPrimary,
    defaultColorShadeValues,
);
const colorPrimaryTints = useColorTint(s, colorPrimary, defaultColorTintValues);

// Create limited variants for secondary colors
const { colorSecondaryShade100 } = useColorShade(s, colorSecondary, {
    100: 10,
} as const);

const { colorSuccessTint100 } = useColorTint(s, colorSuccess, {
    100: 10,
} as const);

const { colorDangerShade100 } = useColorShade(s, colorDanger, {
    100: 10,
} as const);

export default s;

Theme-Aware Color System

Create color variables that adapt to different themes:

import { styleframe } from "styleframe";
import { useColor, useColorLightness } from "@styleframe/theme";

const s = styleframe();
const { theme } = s;

// Light theme colors
const { colorPrimary } = useColor(s, {
    primary: "#006cff",
} as const);

// Dark theme colors
theme('dark', ({ variable }) => {
    variable(colorPrimary, "#4da3ff");
})

// Create lightness variants that work with both themes
const colorPrimaryLevels = useColorLightness(s, colorPrimary, {
    100: 10,
    500: 50,
    900: 90,
} as const);

export default s;

Best Practices

  • Start with base colors using useColor() before creating variants.
  • Choose the right approach for variants: Use lightness levels for absolute control, and shades/tints for relative adjustments.
  • Be consistent with naming: If you use 50, 100, 200 for 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, warning are more meaningful than blue, green, yellow.
  • Leverage OKLCH benefits: Take advantage of the perceptually uniform color space for consistent color variations.
Good to know: We must use 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