Start with operational definitions
CSS defines colors, interpolation spaces, and mixing algorithms, but it does not give the words tint, shade, or tone a special role in the language. Design tools and teams can use those labels differently. Color Atlas therefore treats them as project vocabulary and publishes the exact endpoint, space, weight, and rounding rule alongside the result. That boundary prevents a familiar word from hiding an incompatible calculation.
- Tint: mix the base color toward opaque white #FFFFFF.
- Shade: mix the base color toward opaque black #000000.
- Tone: mix the base color toward an explicitly named neutral; this guide uses #808080.
- Mix amount: the percentage contributed by the white, black, or gray endpoint—not the amount of base retained.
- Result: an opaque six-digit sRGB HEX value after rounding each channel to the nearest integer.
One base color, three reproducible scales
Use Aurora Indigo #4F46E5, whose encoded sRGB channels are 79, 70, and 229. For each channel, calculate result = round(base × (1 − p) + endpoint × p), where p is the endpoint share from 0 to 1. At 20%, the red channel of a tint is round(79 × 0.8 + 255 × 0.2) = 114; the complete tint is #726BEA. The same formula with black or #808080 creates the other two rows.
| Operation | Endpoint | 20% | 40% | 60% | 80% |
|---|---|---|---|---|---|
| Tint | #FFFFFF | #726BEA | #9590EF | #B9B5F5 | #DCDAFA |
| Shade | #000000 | #3F38B7 | #2F2A89 | #201C5C | #100E2E |
| Tone | #808080 | #5952D1 | #635DBD | #6C69A8 | #767494 |
These values describe channel interpolation in encoded sRGB; they do not claim to model paint, reflected light, or human perception. Keeping the scope narrow is useful: a database importer, a build script, and a browser can compare the same integer channels without guessing what “lighter by 20%” meant.
Express the same contract with color-mix()
CSS Color 5 defines color-mix() as a color function that mixes supplied colors in a chosen interpolation space and in specified amounts. State the space even though the current default is Oklab: the table above is intentionally an sRGB-channel calculation, so its matching CSS uses in srgb. With two opaque colors whose percentages total 100%, the first percentage is the base share and the second is the endpoint share.
:root {
--aurora-indigo: #4F46E5;
--aurora-tint-20: #726BEA;
--aurora-shade-40: #2F2A89;
--aurora-tone-40: #635DBD;
}
@supports (color: color-mix(in srgb, black, white)) {
:root {
--aurora-tint-20: color-mix(in srgb, var(--aurora-indigo) 80%, white 20%);
--aurora-shade-40: color-mix(in srgb, var(--aurora-indigo) 60%, black 40%);
--aurora-tone-40: color-mix(in srgb, var(--aurora-indigo) 60%, #808080 40%);
}
}- Write both percentages when the token is documentation, even when CSS could infer one of them.
- Use static HEX custom properties as the fallback, then replace them inside @supports when color-mix() is available.
- Do not confuse a 20% endpoint share with retaining 20% of the base; those produce opposite positions on the scale.
- When alpha is involved, record it explicitly because interpolation uses premultiplied color components and the final opacity can change.
- Inspect the resolved color rather than assuming every interpolation space will serialize to the same HEX value.
The interpolation space is part of the result
Two endpoints and one percentage do not uniquely determine a mixed color until the interpolation space is known. CSS Color 4 explains that spaces suit different goals. Encoded sRGB is useful for legacy-compatible channel interpolation but is neither linear-light nor perceptually uniform. Oklab is designed for more perceptually even spacing. Oklch can preserve chroma and expose hue-path choices. srgb-linear interpolates values that are linear in light intensity.
- Use in srgb when matching an existing RGB-channel formula or a stored legacy scale.
- Consider in oklab when perceptually even steps matter more than matching old HEX arithmetic.
- Consider in oklch when controlling lightness, chroma, and the hue path is part of the palette method.
- Use in srgb-linear only when linear-light interpolation is the intended technical contract.
- Never compare results from two spaces as if a rounding bug were the only possible cause of the difference.
Changing HSL lightness is not the same operation
HSL represents an sRGB color with hue, saturation, and lightness coordinates. Adding twenty percentage points to HSL lightness edits one coordinate; it does not mix the color with twenty percent white in encoded sRGB. The operations can produce visibly and numerically different colors. Likewise, reducing HSL lightness is not automatically the same as an sRGB shade, and lowering saturation is not the same as mixing with a specific neutral gray.
- Say “increase HSL lightness” when that is the actual transform.
- Say “mix 20% white in sRGB” when using channel interpolation.
- Do not label every paler result a tint if it came from opacity over an unknown background.
- Do not label every darker result a shade if it came from a blend mode, overlay, or exposure adjustment.
- Avoid treating equal HSL lightness steps as equal perceived-lightness steps; the CSS specification explicitly notes HSL’s perceptual limitations.
- Convert and store the final resolved color when exact cross-tool reproduction matters.
The naming rule is simple: describe the operation you performed, not only the appearance you expected. A palette can contain HSL lightness variants, Oklab mixes, overlays, and sRGB tints, but its metadata should keep those methods distinguishable.
Every generated step needs a new contrast check
A related color is not automatically suitable for the same text or interface role as its base. Mixing changes relative luminance, and contrast is calculated from the final rendered foreground and background. The 40% examples show the direction clearly. Ratios below are calculated from the unrounded sRGB luminance formula and displayed to two decimals; the recommended text is only the stronger of black and white for normal text.
| Sample | HEX | Black text | White text | AA normal choice |
|---|---|---|---|---|
| Base | #4F46E5 | 3.34:1 | 6.29:1 | White |
| 40% tint | #9590EF | 7.51:1 | 2.79:1 | Black |
| 40% shade | #2F2A89 | 1.81:1 | 11.58:1 | White |
| 40% tone | #635DBD | 3.83:1 | 5.48:1 | White |
Publish palette tokens another team can audit
- Keep one reviewed canonical base color and a stable identifier.
- Name the operation, endpoint color, endpoint percentage, interpolation space, and alpha behavior.
- Generate values from one deterministic implementation and reject duplicate token names.
- Store exact channels or sufficient precision before formatting a human-facing HEX value.
- Calculate contrast for every foreground/background role after resolving the final color.
- Check hover, active, focus, selected, disabled, light-theme, and dark-theme states separately.
- Record the source specification and generator version so a later migration can explain changed outputs.
- Expose copy-ready CSS and the calculation method beside the swatches instead of publishing unexplained color grids.
Color Atlas follows that audit trail. PostgreSQL stores reviewed base records and article data; the SSR page calculates related colors from the declared sRGB value and labels its methodology. That makes a tint or shade useful as a reproducible design input rather than another nearly identical page created only for a keyword.
Primary sources
The factual claims in this article are checked against these published specifications.