Gradient System & Tokens
Tokenized gradient stops and presets so gradients stay on-brand and consistent.
Visual DesignIntermediate
Why
Hand-picked hex pairs for every gradient drift off-brand, clash with the palette, and can't adapt to dark mode. A tokenized gradient system (stops + presets) keeps them consistent and themeable.
Google's research shows users form a first impression in 50ms, and 75% of judgments about website credibility are based on visual aesthetics. Consistent spacing scales (8px grid) reduce cognitive load measurably.
When
Any app using 3+ gradients (hero backgrounds, buttons, badges). One-off marketing gradients can stay inline; product gradients should be tokens.
How
1. Stops — define color stops as tokens: --grad-brand-1: var(--color-brand-500), --grad-brand-2: var(--color-brand-700)
2. Presets — compose stops into named gradients: --grad-brand: linear-gradient(135deg, var(--grad-brand-1), var(--grad-brand-2))
3. Angle as token — --grad-angle-hero: 135deg; reuse across hero variants
4. Dark mode — redefine stops in [data-theme="dark"]; the preset inherits automatically
5. Radial/conic — separate presets for radial (spotlight) and conic (color wheel); don't force one type
6. Mesh gradients — multi-stop linear approximations; true mesh gradients are heavy (SVG/filter)
7. Text on gradient — always check contrast; gradient backgrounds need a solid fallback for high-contrast mode
Example
:root {
/* Stops — reference the color tokens, not raw hex */
--grad-brand-1: var(--color-brand-500);
--grad-brand-2: var(--color-brand-700);
--grad-surface-1: var(--color-gray-50);
--grad-surface-2: var(--color-gray-200);
--grad-angle: 135deg;
/* Presets — compose stops into named gradients */
--grad-brand: linear-gradient(var(--grad-angle), var(--grad-brand-1), var(--grad-brand-2));
--grad-surface: linear-gradient(180deg, var(--grad-surface-1), var(--grad-surface-2));
--grad-spotlight: radial-gradient(circle at 30% 20%, var(--color-brand-300), transparent 60%);
}
[data-theme="dark"] {
--grad-surface-1: var(--color-gray-800); /* preset inherits */
--grad-surface-2: var(--color-gray-900);
}
/* Usage */
.hero { background: var(--grad-brand); }
.card { background: var(--grad-surface); }
.badge { background: var(--grad-spotlight); }
/* High-contrast fallback */
@media (prefers-contrast: more) {
.hero { background: var(--color-brand-600); } /* solid, no gradient */
}
Common Problems
- ⚠Raw hex in every gradient — drifts off-brand, can't adapt to dark mode without re-declaring
- ⚠No high-contrast fallback — gradient text becomes unreadable in forced-colors mode
- ⚠Too many stops — 5-stop 'mesh' gradients are heavy to render and look muddy
Best Practices
- ✓Tokenize stops (reference color tokens) + presets (compose stops) — themeable, consistent
- ✓Provide a solid fallback for prefers-contrast: more — gradients can fail in forced-colors
- ✓Keep 2–3 stops — more looks muddy; use a radial or SVG mesh for true multi-color effects