Styling multi-color fonts with custom color palettes
Color fonts store several color layers per glyph directly in the font file, from multi-color icon sets to emoji fonts. @font-palette-values lets you override that factory palette through CSS and match it to your own theme, with no SVG sprites and no separate icon file per color variant.
Table of Contents
- 1. What color fonts are: COLRv1, SVG-in-OpenType, sbix and CBDT at a glance
- 2. @font-palette-values syntax: font-family, base-palette and override-colors
- 3. Defining custom palettes: override-colors and finding the color indices
- 4. Applying the font-palette property: linking it to @font-palette-values
- 5. A practical recipe: a multi-color icon font with two theme palettes
- 6. Browser support: which engines support COLRv1 and how to check
- 7. Fallback strategy for single-color fonts: how the regular color property applies
- 8. Performance considerations: color font file size compared to SVG icons
- 9. Combining with prefers-color-scheme for automatic palette switching
- 10. Summary
- 11. FAQ
1. What color fonts are: COLRv1, SVG-in-OpenType, sbix and CBDT at a glance
A regular typeface stores every glyph as a single-color outline, its actual color only decided later through the color property in CSS. Color fonts break that principle: they store several stacked color layers per glyph directly in the font file, so a single emoji character or icon glyph consists of multiple colored layers the browser composites at render time, with no extra image files and no SVG sprites required.
Several competing formats exist within the OpenType standard for this multi-color rendering: COLRv0 stores simple, flat color layers with a fixed palette, COLRv1 extends that with gradients, transparency and nested shapes. SVG-in-OpenType embeds a full SVG document per glyph, sbix and CBDT instead store ready-made bitmap graphics per glyph and size. For CSS-side color customization only COLRv1 matters, because it is the only format that defines a palette the browser can actually change.
2. @font-palette-values syntax: font-family, base-palette and override-colors
The @font-palette-values rule defines a named palette set, referenced with an arbitrary name starting with two dashes, similar to a custom property. Inside the block, font-family sets which typeface the palette applies to, base-palette picks one of the palettes already predefined inside the font file as a starting point, usually through an index like 0 or 1 that the respective foundry documents.
override-colors then selectively overrides individual color indices of that base palette with custom color values, while every index not mentioned keeps its original color from the base-palette. That combination lets you adjust just the accent color of an icon set without redefining the entire palette, which noticeably cuts effort for fonts with many color layers per glyph.
@font-palette-values --brand-icons {
font-family: "Color Icons";
base-palette: 0;
override-colors:
0 #4a1d96,
1 #7c3aed,
2 #c4b5fd;
}
3. Defining custom palettes: override-colors and finding the color indices
Every color layer inside a COLRv1 glyph is internally assigned a numeric index defined by the font file itself, and that index can differ between color fonts. Without documentation from the foundry, the most reliable way to find that index is a font inspection tool that lists a font file's embedded palettes in a table and maps every color to its index.
When setting override-colors, every line consists of an index and a color value separated by a space, and multiple overrides are separated by commas. If an index is given that does not exist in the font file at all, the browser ignores just that single entry without affecting the rest of the palette, which makes debugging individual color assignments noticeably easier than a complete rule failure would.
/* Only adjust two of five color layers,
the rest of the palette stays at font default. */
@font-palette-values --muted-icons {
font-family: "Color Icons";
base-palette: 0;
override-colors:
2 #94a3b8,
4 #475569;
}
4. Applying the font-palette property: linking it to @font-palette-values
Defining the palette is only half the work, it gets activated through the font-palette property on the actual text element, referencing the same name used in the @font-palette-values rule. Without that assignment, an element stays on the font file's base-palette, no matter how many @font-palette-values rules exist in the stylesheet.
Several distinct @font-palette-values blocks can be defined in parallel for the same typeface, for example one for light mode and one for dark mode, and assigned through different selectors depending on context. That makes font-palette a standalone, deliberately controllable theming mechanism, independent of the regular color property, which no longer has any effect on the embedded color layers of a color font anyway.
.icon {
font-family: "Color Icons";
font-palette: --brand-icons;
}
.icon--muted {
font-palette: --muted-icons;
}
5. A practical recipe: a multi-color icon font with two theme palettes
For an icon set with two theme variants, define two @font-palette-values blocks with an identical base-palette but different override-colors values, one tuned to the brand colors for light mode, one to correspondingly adjusted, high-contrast tones for dark mode. Both blocks can be maintained fully independently from the rest of the page's color scheme, since they only affect the icon glyphs.
Assigning the matching palette then happens through a single selector switch, for example tied to a theme class on the html element, recoloring every icon on the page at once without touching each icon instance individually. This pattern scales notably better than separate SVG files per color variant, because only a single font file needs to load, regardless of how many themes exist.
@font-palette-values --icons-light {
font-family: "Color Icons";
base-palette: 0;
override-colors: 0 #4a1d96, 1 #7c3aed;
}
@font-palette-values --icons-dark {
font-family: "Color Icons";
base-palette: 0;
override-colors: 0 #c4b5fd, 1 #ede9fe;
}
.icon {
font-family: "Color Icons";
font-palette: --icons-light;
}
html.theme-dark .icon {
font-palette: --icons-dark;
}
6. Browser support: which engines support COLRv1 and how to check
COLRv1 and its associated CSS properties font-palette and @font-palette-values are by now implemented across all major rendering engines, though the maturity of the implementation, particularly around complex gradients and nested shapes within a glyph, can still differ between engines. Before shipping to production, a visual test across all relevant target browsers pays off, especially if the typeface uses gradients rather than plain flat colors.
For feature detection in code, @supports font-palette: --test-name works well, checking whether the browser understands the font-palette property's syntax at all. It matters to distinguish syntax support from actual COLRv1 rendering: a browser can accept the property syntactically while still failing to fully render the embedded color layer if the font file uses advanced COLRv1 features like gradients that browser has not fully implemented yet.
7. Fallback strategy for single-color fonts: how the regular color property applies
If font-palette is applied to a typeface that contains no COLRv1 color layers at all, or the browser does not support the feature, the renderer ignores the property entirely and falls back to regular, single-color rendering. In that case the normal color property takes effect again as usual, so an icon font with color-font benefits does not become invisible when support is missing, it simply renders single-color in the color defined through color.
This fault-tolerant behavior allows using font-palette in production without maintaining a separate single-color fallback font, as long as the typeface in use defines a sensible one-color fallback through its own metadata, which most commercial color fonts do by default. It still matters to set color explicitly, so the single-color fallback rendering matches the rest of the page's color scheme instead of following the foundry's default color.
8. Performance considerations: color font file size compared to SVG icons
A COLRv1 font file with several hundred multi-color glyphs can be larger than a comparable SVG sprite holding only single-color icons, because extra color layer tables, and additional metadata for gradients, need to be stored per glyph. Once a page needs many different icons across several color variants, though, that size comparison often flips, because a single color font file replaces multiple SVG files with redundant outlines per color variant.
An additional performance advantage of color fonts over multiple SVG variants is caching behavior: the font file loads once and gets cached by the browser like any other web font, while every additional color variant with SVG sprites would in the worst case require its own file, or a larger, combined sprite, to load. For icon-heavy interfaces with several themes, this caching effect is often the deciding advantage.
9. Combining with prefers-color-scheme for automatic palette switching
font-palette can be assigned directly inside a prefers-color-scheme media query, so icons automatically pick up the matching palette for light or dark mode without JavaScript needing to control the switch. Once the media query matches, the browser swaps the icon glyphs' color assignment just as automatically as any other CSS property, because font-palette is technically a completely normal, cascade-capable CSS property.
For pages that additionally offer a manual theme switcher, both mechanisms combine: prefers-color-scheme provides the default based on system settings, an explicit theme class on the html element overrides that on manual user choice. That way the icon color palette stays consistent in every case with the rest of the page's appearance, governed either by prefers-color-scheme or the color-scheme property.
.icon {
font-family: "Color Icons";
font-palette: --icons-light;
}
@media (prefers-color-scheme: dark) {
.icon {
font-palette: --icons-dark;
}
}
/* Manual user choice wins over the
system setting */
html.theme-dark .icon {
font-palette: --icons-dark;
}
| Format | Multi-color | Palette customizable | Typical use |
|---|---|---|---|
| COLRv1 | Yes, with gradients | Yes, via @font-palette-values | Icon sets, emoji, logos |
| COLRv0 | Yes, flat | No, fixed factory palette | Older multi-color icon fonts |
| SVG-in-OpenType | Yes, full SVG features | No, only via SVG's own mechanisms | Complex illustrative glyphs |
| sbix / CBDT | Yes, as bitmap | No | Ready-made bitmap emoji |
| Regular fonts | No, single-color | Via color property | Classic icon fonts |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
@font-palette-values: The Essentials at a Glance
Core idea
COLRv1 font files store several color layers per glyph, @font-palette-values selectively overrides that factory palette through override-colors.
Application
font-palette on the text element links it to the name from @font-palette-values, several palettes can be defined in parallel for different themes.
Fallback
If support or COLRv1 data in the typeface is missing, the regular color property applies automatically, no visible error results.
Theme coupling
font-palette can be set inside prefers-color-scheme media queries, automating the palette switch between light and dark mode.