Harmony
Generate harmonious color schemes by rotating hue in OKLCH space. All rotations preserve the source color's lightness and chroma.
Harmony Type
Generated Colors
generateHarmony(
{ hue: 15, saturation: 72, lightness: 45 },
"complementary"
);
// => [
"#008bb7"
]Tonal Palette
8 role-based UI colors generated from the base hue. Hover to expand.
generateTonalPalette(15);
// => [
primary: "#db6371",
onPrimary: "#fdf6f7",
primaryContainer: "#271c1c",
secondary: "#dd999d",
surface: "#191515",
onSurface: "#e7e3e3",
accent: "#d3983c",
muted: "#7c6e6e"
]import { generateHarmony, complementary, analogous } from "colorscope/harmony";Functions
generateHarmony
Generate harmony colors by type. Dispatches to the specific generator.
function generateHarmony(input: HarmonyInput, type: HarmonyType): HarmonyColor[]| Param | Type | Description |
|---|---|---|
input | HarmonyInput | Source color |
type | HarmonyType | Harmony scheme to generate |
Returns: Array of HarmonyColor objects (does not include the input color).
const input = { hue: 0, saturation: 100, lightness: 50 }; // red
const harmony = generateHarmony(input, "complementary");
// [{ oklab: {...}, oklch: {...}, hex: "#00b3b3" }]complementary
Generate the complementary color (180° rotation). Returns 1 color.
function complementary(input: HarmonyInput): [HarmonyColor]analogous
Generate analogous colors (±angle rotation). Returns 2 colors adjacent on the wheel.
function analogous(input: HarmonyInput, angle?: number): [HarmonyColor, HarmonyColor]| Param | Type | Default | Description |
|---|---|---|---|
input | HarmonyInput | — | Source color |
angle | number | 30 | Rotation angle in degrees |
triadic
Generate triadic colors (±120° rotation). Returns 2 colors forming an equilateral triangle.
function triadic(input: HarmonyInput): [HarmonyColor, HarmonyColor]splitComplementary
Generate split-complementary colors (180° ± angle). Returns 2 colors flanking the complement.
function splitComplementary(
input: HarmonyInput,
angle?: number
): [HarmonyColor, HarmonyColor]| Param | Type | Default | Description |
|---|---|---|---|
input | HarmonyInput | — | Source color |
angle | number | 30 | Split angle from complement |
tetradic
Generate tetradic colors (90° apart). Returns 3 colors forming a rectangle with the source.
function tetradic(input: HarmonyInput): [HarmonyColor, HarmonyColor, HarmonyColor]generateTonalPalette
Generate a tonal palette from a single base hue. Unlike classical harmony (which rotates hue at constant lightness/chroma), a tonal palette assigns each color a UI role with appropriate lightness and chroma — producing palettes that work in real interfaces.
function generateTonalPalette(baseHue: number, baseChroma?: number): TonalColor[]| Param | Type | Default | Description |
|---|---|---|---|
baseHue | number | — | Hue angle in degrees (0-360) |
baseChroma | number | 0.15 | Maximum chroma for the primary role |
Returns: 8 role-based TonalColor objects.
import { generateTonalPalette } from "colorscope/harmony";
const palette = generateTonalPalette(220); // blue-based palette
// [
// { role: "primary", hex: "#5b8fd4", ... },
// { role: "onPrimary", hex: "#f5f7fa", ... },
// { role: "primaryContainer", hex: "#1a2a3d", ... },
// { role: "secondary", hex: "#8ba8c9", ... },
// { role: "surface", hex: "#1c2028", ... },
// { role: "onSurface", hex: "#e5e8ec", ... },
// { role: "accent", hex: "#c9a55b", ... },
// { role: "muted", hex: "#6b7078", ... },
// ]The 8 roles:
- primary — Main brand/accent color
- onPrimary — Text/icons on primary surfaces
- primaryContainer — Darker container for primary content
- secondary — Supporting color at lower chroma
- surface — Dark background surface
- onSurface — Readable text color for surfaces
- accent — Complementary hue (60° offset) for visual interest
- muted — Low-chroma supporting tone for borders/dividers
Types
HarmonyType
type HarmonyType =
| "complementary"
| "analogous"
| "triadic"
| "split-complementary"
| "tetradic"HarmonyInput
interface HarmonyInput {
hue: number; // HSL hue (0-360)
saturation: number; // HSL saturation (0-100)
lightness: number; // HSL lightness (0-100)
oklab?: OKLab; // Optional pre-computed OKLab (used if provided)
}QuantizedColor satisfies this interface, so you can pass extracted colors directly.
HarmonyColor
interface HarmonyColor {
oklab: OKLab; // OKLab values
oklch: OKLCH; // OKLCH values (cylindrical)
hex: string; // Hex color string
}TonalRole
type TonalRole =
| "primary"
| "onPrimary"
| "primaryContainer"
| "secondary"
| "surface"
| "onSurface"
| "accent"
| "muted"TonalColor
interface TonalColor {
role: TonalRole; // The UI role this color fulfills
oklch: OKLCH; // OKLCH representation (L/C tuned per role)
oklab: OKLab; // OKLab representation
hex: string; // sRGB hex string, gamut-clamped
}