Color System
Maple's color system uses the OKLCH color space to resolve, manipulate, and generate colors entirely at runtime—no build step, no predefined palettes.
Color Resolution
When you write a color utility like c-primary or bgc-accent, Maple resolves the value through a multi-layer CSS variable fallback chain inside the OKLCH color space. This is the generated CSS for a single color utility:
.c-primary {
color: oklch(
from var(--c-primary, var(--color-primary, var(--primary, primary)))
calc(l * var(--c-primary-l-scale, var(--primary-l-scale, var(--c-l-scale, var(--l-scale, 1)))))
calc(c * var(--c-primary-c-scale, var(--primary-c-scale, var(--c-c-scale, var(--c-scale, 1)))))
calc(h + var(--c-primary-h-rotate, var(--primary-h-rotate, var(--c-h-rotate, var(--h-rotate, 0))))) /
alpha
);
}This single rule encodes the complete resolution, manipulation, and fallback logic. Let's break it down.
Variable Fallback Chain
The base color value is resolved through a four-step fallback chain. To make the rule produce a valid color, at least one of the following must be defined:
- Property-Specific:
--c-primary. The property shorthand (cforcolor) is combined with the value name. For example,bgc-primaryfirst looks for--bgc-primary. - Type-Specific:
--color-primary. The first fallback uses the property type. All color properties (color, background-color, border-color, etc.) share the--color-*namespace. - Value-Specific:
--primary. The second fallback is the bare value name as a CSS variable. - Literal:
primary. The final fallback is the raw string. If it's a valid CSS named color (red, cornflowerblue, silver, etc.), it works out of the box. Invalid names are silently ignored.
Example: Defining a primary color
<!-- Define a single base color -->
<div class="--primary=oklch(0.55_0.25_260)">
<!-- All of these resolve through the fallback chain -->
<div class="c-primary">Text uses --primary</div>
<div class="bgc-primary">Background uses --primary</div>
<div class="brc-primary">Border uses --primary</div>
<!-- Override just the background color -->
<div class="--bgc-primary=oklch(0.9_0.05_260) bgc-primary">
Background uses --bgc-primary, ignoring --primary
</div>
</div>
Named CSS Colors
Because the literal fallback accepts any CSS named color, classes like c-coral, bgc-teal, and brc-slateblue work without defining any variables. They flow through the same OKLCH pipeline and gain automatic lightness, chroma, and hue controls—see the native palette for ready to use colors.
<div>
<div class="bgc-coral">coral</div>
<div class="bgc-teal">teal</div>
<div class="bgc-slateblue">slateblue</div>
</div>
Tones
You can generate a wide range of tones from any base color by appending a numeric suffix -{number} to the color name. The tone range spans from 50 (lightest) to 950 (darkest). The calculation is adaptive—it factors in the base color's actual lightness to ensure visually consistent results across different colors:
- Low tones (e.g. 100–200): Always produce light shades, regardless of the base color's lightness.
- High tones (e.g. 800–900): Always produce dark shades, regardless of the base color's lightness.
c-blue-100Light shade — high lightness.
c-blue-500 Normalized toward mid-lightness.
Not the same as c-blue. See Adaptive Tone Normalization section below.
c-blue-900Dark shade — low lightness.
Even though the example below illustrates stepped shades, any number between 50 and 950 is valid—c-primary-51 and c-primary-949 both work. The mathematics is continuous, not stepped.
<div class="--primary=oklch(0.55_0.25_260)">
<div class="bgc-primary-50"></div>
<div class="bgc-primary-100"></div>
<div class="bgc-primary-200"></div>
<div class="bgc-primary-300"></div>
<div class="bgc-primary-400"></div>
<div class="bgc-primary-500"></div>
<div class="bgc-primary-600"></div>
<div class="bgc-primary-700"></div>
<div class="bgc-primary-800"></div>
<div class="bgc-primary-900"></div>
<div class="bgc-primary-950"></div>
</div>
Adaptive Tone Normalization
The tone formula compensates for the base color's actual lightness. A dark base color receives a stronger lightening push at low tones, while a light base color receives less. This means c-red-200 and c-skyblue-200 will land in a similar lightness range even though their base lightness values are very different.
As a direct result of this normalization, the 500 tone represents a mathematically aligned midpoint, rather than your exact starting color. For example, c-navy (no tone) uses the raw base color as-is, while c-navy-500 applies the adaptive formula—they are not equivalent.
<div class="c-white">
<div class="bgc-navy">Raw base color</div>
<div class="bgc-navy-500">Adaptive mid-tone</div>
</div>
Transparency
Append /{number} to set the alpha channel. Values range from 0 (fully transparent) to 100 (fully opaque):
<!-- 50% opacity -->
<div class="bgc-primary/50">Semi-transparent background</div>
<!-- 20% opacity -->
<div class="c-primary/20">Faded text</div>
<!-- Combined: tone 232 at 68% opacity -->
<div class="bgc-primary-232/68">Light tone, partial transparency</div>
<div class="--primary=oklch(0.55_0.25_260)">
<div class="bgc-primary-500/20"></div>
<div class="bgc-primary-500/40"></div>
<div class="bgc-primary-500/60"></div>
<div class="bgc-primary-500/80"></div>
<div class="bgc-primary-500/100"></div>
</div>
Pro Tip
Color Manipulation
Beyond tones and transparency, Maple gives you fine-grained control over lightness, chroma, and hue via CSS variables. Each variable follows its own fallback chain for maximum flexibility:
--l-scale1--l-shift1--c-scale1--h-rotate0Try the Color Playground
Manipulation Fallback Chain
Each manipulation variable follows a four-level fallback chain. To override a value, you can define the following variables (using --l-scale and c-primary as an example):
- Property + Value:
--c-primary-l-scale. The most specific level. It only affectsc-primary. Other color properties using theprimaryname (likebgc-primary) are not affected. - Value-Specific:
--primary-l-scale. Affects all properties using theprimarycolor name. - Property-Specific:
--c-l-scale. Affects textcolorproperty, regardless of color name. - Global:
--l-scale. The final fallback. Affects all color properties across all color names.
Example: Scoped manipulation
<!-- Global lightness scaling -->
<div class="
--l-scale=1.2
--primary=oklch(0.55_0.25_260)
--secondary=oklch(0.8_0.05_240)
">
<!-- Affects all colors in this scope -->
<div class="bgc-secondary c-primary">
Primary and secondary are both 20% lighter
</div>
<!-- Override for just primary color's lightness -->
<div class="--primary-l-scale=0.8">
<div class="bgc-secondary c-primary">
Primary is now 20% darker,
Secondary is still 20% lighter
</div>
</div>
</div>
The same four-level structure applies identically to --l-shift, --c-scale, and --h-rotate.
Global Curve Controls
When tone variations are used (e.g., c-primary-200), two additional global variables control the behavior of the tone curve:
--l-edge-shift0.5--c-curve0.5 Fallback Reference
The list below serves as a complete reference for every fallback chain. Each category expands into the four resolution levels, from most specific (1) to global (4):
- Base Color
--c-primary(Property + Value)--color-primary(Type + Specific)--primary(Global)primary(Value as Literal string)
- Lightness Scale
--c-primary-l-scale(Property + Value)--primary-l-scale(Value Specific)--c-l-scale(Property Specific)--l-scale(Global)
- Lightness Shift
--c-primary-l-shift(Property + Value)--primary-l-shift(Value Specific)--c-l-shift(Property Specific)--l-shift(Global)
- Chroma Scale
--c-primary-c-scale(Property + Value)--primary-c-scale(Value Specific)--c-c-scale(Property Specific)--c-scale(Global)
- Hue Rotate
--c-primary-h-rotate(Property + Value)--primary-h-rotate(Value Specific)--c-h-rotate(Property Specific)--h-rotate(Global)
Related Properties
Refer to the underlying CSS properties for granular color control.