Colors
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;
: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.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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-50: oklch(from var(--color--primary) 0.05 c h / a);
--color--primary-100: oklch(from var(--color--primary) 0.1 c h / a);
--color--primary-200: oklch(from var(--color--primary) 0.2 c h / a);
--color--primary-300: oklch(from var(--color--primary) 0.3 c h / a);
--color--primary-400: oklch(from var(--color--primary) 0.4 c h / a);
--color--primary-500: oklch(from var(--color--primary) 0.5 c h / a);
--color--primary-600: oklch(from var(--color--primary) 0.6 c h / a);
--color--primary-700: oklch(from var(--color--primary) 0.7 c h / a);
--color--primary-800: oklch(from var(--color--primary) 0.8 c h / a);
--color--primary-900: oklch(from var(--color--primary) 0.9 c h / a);
--color--primary-950: oklch(from var(--color--primary) 0.95 c h / a);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-light: oklch(from var(--color--primary) 0.75 c h / a);
--color--primary-base: oklch(from var(--color--primary) 0.5 c h / a);
--color--primary-dark: oklch(from var(--color--primary) 0.25 c h / a);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-50: oklch(from var(--color--primary) 0.05 c h / a);
--color--primary-100: oklch(from var(--color--primary) 0.1 c h / a);
--color--primary-150: oklch(from var(--color--primary) 0.15 c h / a);
--color--primary-200: oklch(from var(--color--primary) 0.2 c h / a);
--color--primary-250: oklch(from var(--color--primary) 0.25 c h / a);
--color--primary-300: oklch(from var(--color--primary) 0.3 c h / a);
--color--primary-350: oklch(from var(--color--primary) 0.35 c h / a);
--color--primary-400: oklch(from var(--color--primary) 0.4 c h / a);
--color--primary-450: oklch(from var(--color--primary) 0.45 c h / a);
--color--primary-500: oklch(from var(--color--primary) 0.5 c h / a);
--color--primary-600: oklch(from var(--color--primary) 0.6 c h / a);
--color--primary-700: oklch(from var(--color--primary) 0.7 c h / a);
--color--primary-800: oklch(from var(--color--primary) 0.8 c h / a);
--color--primary-900: oklch(from var(--color--primary) 0.9 c h / a);
--color--primary-950: oklch(from var(--color--primary) 0.95 c h / a);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-shade-50: oklch(
from var(--color--primary) calc(l - 0.05) c h / a
);
--color--primary-shade-100: oklch(
from var(--color--primary) calc(l - 0.1) c h / a
);
--color--primary-shade-150: oklch(
from var(--color--primary) calc(l - 0.15) c h / a
);
--color--primary-shade-200: oklch(
from var(--color--primary) calc(l - 0.2) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-hover: oklch(
from var(--color--primary) calc(l - 0.08) c h / a
);
--color--primary-active: oklch(
from var(--color--primary) calc(l - 0.12) c h / a
);
--color--primary-pressed: oklch(
from var(--color--primary) calc(l - 0.18) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-shade-50: oklch(
from var(--color--primary) calc(l - 0.05) c h / a
);
--color--primary-shade-100: oklch(
from var(--color--primary) calc(l - 0.1) c h / a
);
--color--primary-shade-150: oklch(
from var(--color--primary) calc(l - 0.15) c h / a
);
--color--primary-shade-200: oklch(
from var(--color--primary) calc(l - 0.2) c h / a
);
--color--primary-shade-250: oklch(
from var(--color--primary) calc(l - 0.25) c h / a
);
--color--primary-shade-300: oklch(
from var(--color--primary) calc(l - 0.3) c h / a
);
--color--primary-shade-400: oklch(
from var(--color--primary) calc(l - 0.4) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-tint-50: oklch(
from var(--color--primary) calc(l + 0.05) c h / a
);
--color--primary-tint-100: oklch(
from var(--color--primary) calc(l + 0.1) c h / a
);
--color--primary-tint-150: oklch(
from var(--color--primary) calc(l + 0.15) c h / a
);
--color--primary-tint-200: oklch(
from var(--color--primary) calc(l + 0.2) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-subtle: oklch(
from var(--color--primary) calc(l + 0.25) c h / a
);
--color--primary-light: oklch(
from var(--color--primary) calc(l + 0.35) c h / a
);
--color--primary-pale: oklch(
from var(--color--primary) calc(l + 0.45) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-tint-50: oklch(
from var(--color--primary) calc(l + 0.05) c h / a
);
--color--primary-tint-100: oklch(
from var(--color--primary) calc(l + 0.1) c h / a
);
--color--primary-tint-150: oklch(
from var(--color--primary) calc(l + 0.15) c h / a
);
--color--primary-tint-200: oklch(
from var(--color--primary) calc(l + 0.2) c h / a
);
--color--primary-tint-250: oklch(
from var(--color--primary) calc(l + 0.25) c h / a
);
--color--primary-tint-300: oklch(
from var(--color--primary) calc(l + 0.3) c h / a
);
--color--primary-tint-400: oklch(
from var(--color--primary) calc(l + 0.4) c h / a
);
}
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;
: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 lightness levels */
--color--primary-50: oklch(from var(--color--primary) 0.05 c h / a);
--color--primary-100: oklch(from var(--color--primary) 0.1 c h / a);
--color--primary-200: oklch(from var(--color--primary) 0.2 c h / a);
--color--primary-300: oklch(from var(--color--primary) 0.3 c h / a);
--color--primary-400: oklch(from var(--color--primary) 0.4 c h / a);
--color--primary-500: oklch(from var(--color--primary) 0.5 c h / a);
--color--primary-600: oklch(from var(--color--primary) 0.6 c h / a);
--color--primary-700: oklch(from var(--color--primary) 0.7 c h / a);
--color--primary-800: oklch(from var(--color--primary) 0.8 c h / a);
--color--primary-900: oklch(from var(--color--primary) 0.9 c h / a);
--color--primary-950: oklch(from var(--color--primary) 0.95 c h / a);
/* Primary shades */
--color--primary-shade-50: oklch(
from var(--color--primary) calc(l - 0.05) c h / a
);
--color--primary-shade-100: oklch(
from var(--color--primary) calc(l - 0.1) c h / a
);
--color--primary-shade-150: oklch(
from var(--color--primary) calc(l - 0.15) c h / a
);
--color--primary-shade-200: oklch(
from var(--color--primary) calc(l - 0.2) c h / a
);
/* Primary tints */
--color--primary-tint-50: oklch(
from var(--color--primary) calc(l + 0.05) c h / a
);
--color--primary-tint-100: oklch(
from var(--color--primary) calc(l + 0.1) c h / a
);
--color--primary-tint-150: oklch(
from var(--color--primary) calc(l + 0.15) c h / a
);
--color--primary-tint-200: oklch(
from var(--color--primary) calc(l + 0.2) c h / a
);
/* Other color variants */
--color--secondary-shade-100: oklch(
from var(--color--secondary) calc(l - 0.1) c h / a
);
--color--success-tint-100: oklch(
from var(--color--success) calc(l + 0.1) c h / a
);
--color--danger-shade-100: oklch(
from var(--color--danger) calc(l - 0.1) c h / a
);
}
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;
:root {
--color--primary: oklch(0.5749 0.233917 259.9541 / 1);
--color--primary-100: oklch(from var(--color--primary) 0.1 c h / a);
--color--primary-500: oklch(from var(--color--primary) 0.5 c h / a);
--color--primary-900: oklch(from var(--color--primary) 0.9 c h / a);
}
[data-theme="dark"] {
--color--primary: oklch(0.6999 0.166 259.9541 / 1);
}
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 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
useColor
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.useColor()
can be in any valid CSS color format (hex, rgb, hsl, etc.). The composable will handle the conversion to oklch when applying the relative color transformations.{ 100: 10, 500: 50, 900: 90 }
for a simple three-tone system.useColorShade()
and useColorTint()
on the same base color to create a complete range of darker and lighter variants.defaultColorLightnessValues
, 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
to update all derived variants automatically.Breakpoints
Create and manage responsive breakpoint design tokens with CSS variables for consistent media query handling and adaptive layouts across your application.
Fluid Design 🚧
Styleframe variables are the foundation of your design system. They let you define design tokens such as colors, spacing, typography, and more.