How to ship a monochrome fallback for chromatic brand fonts
- Step 1Start from the chromatic source — Your brand font with `COLR`/`CPAL` (or COLRv1, `sbix`, `SVG `) is the source of truth, ideally in your design-system repo. Don't hand-redraw a monochrome version — derive it.
- Step 2Generate the monochrome variant — Run the [Colour Table Remover](/font-tools/colour-table-remover). It drops `COLR`, `CPAL`, `sbix`, `SVG `, `CBDT`, `CBLC`, `EBDT`, `EBLC` and returns `<stem>.monochrome.ttf` — the base outlines only.
- Step 3Re-compress to WOFF2 — The output is uncompressed TTF. Run it through [TTF to WOFF2](/font-tools/ttf-to-woff2) to get `brand-mono.woff2`. Don't skip this — the raw TTF can be larger than the colour WOFF2.
- Step 4Declare two families, not two formats — Because `format()` can't distinguish colour from monochrome WOFF2, declare the colour font as `font-family: Brand` and the monochrome as `font-family: BrandMono` in separate `@font-face` blocks. Don't expect a single `src` list with two WOFF2 entries to choose for you.
- Step 5Switch with @supports or a feature query — Use `@supports (font-variation-settings: normal)` style detection or a colour-font feature query to apply `font-family: Brand` (colour) where supported and `BrandMono` elsewhere. Or, simpler: put the system colour-emoji font / a monochrome face later in the `font-family` stack.
- Step 6Test in a non-supporting engine — Verify in an actual engine that lacks your colour format (e.g. test `sbix` content in Chrome, or `CBDT` in Safari). Confirm it lands on `BrandMono` rather than showing tofu or the wrong file.
Where colour falls back to monochrome
If your chromatic source uses a format an engine can't paint, that engine needs the monochrome fallback.
| Source colour format | Paints in | Needs monochrome fallback in |
|---|---|---|
| COLRv0 | All modern browsers | Very old engines only |
| COLRv1 | Chrome 98+, Firefox 109+, Safari 16.4+ | Older Safari / older Chromium / IE |
| sbix (Apple) | Safari / CoreText | Chrome, Firefox |
| CBDT/CBLC (Google) | Chrome, Firefox | Safari |
| SVG-in-OT | Firefox; partial elsewhere | Chrome, older Safari |
CSS selection strategies (honest version)
format() describes the container, not colour capability — so it can't pick colour-vs-mono between two WOFF2 files.
| Strategy | How it picks | Works? |
|---|---|---|
Two WOFF2 in one src list with format() | Browser uses first supported format | No — both are woff2, can't differentiate colour vs mono |
Two families + @supports | CSS applies colour family where feature is supported, mono elsewhere | Yes — recommended |
Colour family + system fallback in font-family stack | Next family used if colour glyph absent/unsupported | Yes — simplest |
font-feature-settings: 'COLR' | n/a — there is no standard CSS opt-in for COLR | No — not a real control |
Cookbook
Working CSS patterns for serving the derived monochrome fallback, plus the build step that produces it.
Derive the monochrome WOFF2
ExampleOne source font in; one monochrome WOFF2 out, via the remover then WOFF2 compression.
Brand-Colour.woff2 (COLRv1) -> /font-tools/colour-table-remover -> Brand-Colour.monochrome.ttf -> /font-tools/ttf-to-woff2 -> brand-mono.woff2
Two families switched with @supports
ExampleThe recommended pattern: distinct family names, colour applied only where the feature is supported.
@font-face { font-family: "Brand"; src: url(brand-colour.woff2) format("woff2"); }
@font-face { font-family: "BrandMono"; src: url(brand-mono.woff2) format("woff2"); }
.logo { font-family: "BrandMono", sans-serif; } /* safe default */
@supports (font-variation-settings: normal) {
/* proxy feature query; refine to your real colour-support test */
.logo { font-family: "Brand", "BrandMono", sans-serif; }
}Simplest: colour primary, mono in the stack
ExampleLet the font-family stack do the work — if the colour glyph can't render, the next family is used.
@font-face { font-family: "Brand"; src: url(brand-colour.woff2) format("woff2"); }
@font-face { font-family: "BrandMono"; src: url(brand-mono.woff2) format("woff2"); }
.brandmark { font-family: "Brand", "BrandMono", sans-serif; }What NOT to do
ExampleA single src list with two WOFF2 entries does not switch colour vs monochrome — both match format("woff2"), so the browser always takes the first.
/* BROKEN: never falls back to mono */
@font-face {
font-family: "Brand";
src: url(brand-colour.woff2) format("woff2"),
url(brand-mono.woff2) format("woff2"); /* unreachable */
}Subset both before shipping
ExampleIf the brand mark uses a handful of glyphs, subset both the colour and monochrome files so neither ships unused glyphs.
Brand-Colour.woff2 -> font-subsetter (logo glyphs) -> brand-colour.subset.woff2 brand-mono.ttf -> font-subsetter (logo glyphs) -> brand-mono.subset.woff2
Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
Two WOFF2 in one src list never falls back
Doesn't workformat("woff2") matches both the colour and monochrome files, so the browser always loads the first and never reaches the second. Colour-vs-monochrome selection cannot be done with format() hints — use two family names with @supports instead.
Relying on a 'COLR' feature setting
Not a real controlThere is no standard CSS font-feature-settings opt-in for COLR. A browser either renders COLR layers or it doesn't — you can't toggle it. Don't build a fallback strategy around a feature that doesn't exist; use parallel families.
Monochrome output is larger than the colour file
By designThe remover emits uncompressed TTF. Before WOFF2 re-compression, brand-mono.ttf can be heavier than brand-colour.woff2. Always run it through TTF to WOFF2 so the fallback you ship is actually small.
Apple Color Emoji-style minimal outlines
Check the sourceFonts that never expect to render without colour can have sparse outline glyphs. A monochrome fallback derived from such a font may look broken. Inspect the outlines with Glyph Inspector before committing to the fallback; a well-drawn brand font usually has solid outlines.
Safari shows tofu for a CBDT-only emoji font
Renderer gapSafari doesn't paint Google's CBDT/CBLC bitmaps, so a Noto-style colour font fails there. That's exactly the case a monochrome fallback solves — serve BrandMono to Safari via the family-switch pattern.
FOUT/FOIT flashing between colour and mono
ExpectedTwo font files mean two loads. Use font-display: swap (or optional) on both @font-face blocks and preload the one most users will get, so the brand mark doesn't flash. The remover doesn't control loading — that's a CSS/preload concern.
Both files contain the same glyphs — wasted bytes
OptimiseColour and monochrome variants of a logo often need only a handful of glyphs. Subset both with Font Subsetter so you don't ship the full glyph set twice.
Strict validator flags the fallback's head checksum
Validator warningThe remover doesn't recompute head checkSumAdjustment after rebuilding the directory. Browsers don't care, but a CDN or validator might warn. Run a checksum-fixing pass if your pipeline enforces it.
Is a chromatic fallback even worth it?
Often noFor most brands a plain monochrome font that works everywhere is simpler than maintaining a colour + fallback pair. Chromatic brand fonts earn the complexity only where colour glyphs are a defining visual element. Default to monochrome unless colour is core to the identity.
Frequently asked questions
How does the browser pick the colour vs monochrome file?
Not via format() — both files are WOFF2, so format("woff2") matches both and the browser takes the first. The working pattern is two @font-face blocks under different family names (Brand and BrandMono), switched with @supports/feature queries, or a colour family with a monochrome family later in the font-family stack.
Can I use font-feature-settings: 'COLR' to opt in?
No. There is no standard CSS feature setting for COLR. The browser either renders COLR layers or it doesn't; you can't toggle it from CSS. Use parallel family names instead.
Where do I get the monochrome variant?
Derive it from the same source with the Colour Table Remover, then compress with TTF to WOFF2. It strips every colour format at once, so the fallback is guaranteed colour-free.
Why is my monochrome fallback bigger than the colour font?
The remover outputs uncompressed TTF. Re-compress it to WOFF2 and it becomes smaller. The unwrapped SFNT is only an intermediate.
Which browsers actually need the fallback?
Depends on the source format: COLRv1 needs Chrome 98+/Firefox 109+/Safari 16.4+ (older versions need the fallback); sbix only paints in Safari (Chrome/Firefox need the fallback); CBDT/CBLC only in Chrome/Firefox (Safari needs it); OpenType-SVG is mostly Firefox.
Will the fallback have the same glyphs as the colour font?
Yes — it's derived from the same outlines, so character coverage is identical. Only the colour layers are gone. Verify the outlines look good with Glyph Inspector, since some fonts have minimal outlines.
Do I need to redraw a monochrome version by hand?
No. The whole point is to derive it automatically from the chromatic source — one source of truth, no parallel redraw to keep in sync.
How do I avoid a flash between the two fonts?
Set font-display: swap (or optional) on both @font-face blocks and preload the file most of your audience will use. That's a CSS/preload concern, not something the remover handles.
Should I subset both files?
If the brand mark uses few glyphs, yes — subset both colour and monochrome with Font Subsetter so you don't ship the full glyph set twice.
Is this overkill for most brands?
Usually, yes. A plain monochrome brand font works everywhere with zero fallback complexity. The colour + fallback pattern earns its keep only for brands where colour glyphs are central to the identity.
Does generating the fallback upload my font?
No. The Colour Table Remover runs in your browser; the font bytes never leave your machine. That matters for unreleased or licensed brand fonts.
What if the colour source is a .ttc collection?
The remover rejects .ttc collections. Split out the single face you need first, then derive the monochrome fallback from that standalone TTF/OTF.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.