Font Picker
<esp-font-picker> lets users browse and select from the full Google Fonts catalog by default. Font previews render in the dropdown using locally-served CSS files with base64-embedded font data, so no external requests are made while browsing preview rows.
For email editors, set font-source="web-safe" to offer only email-safe/system CSS font stacks and avoid Google font loading entirely.
How It Works
In the default font-source="google" mode, the picker relies on generated Google Fonts assets.
The font tooling generates three kinds of assets with npm run build-fonts:
font-definitions.json— A compact JSON catalog of every Google Font (family, category, variants, etc.). The component fetches this file once on first render to populate the dropdown.- Individual CSS files — One
<FontFamily>.cssper font, each containing a@font-facerule with the preview weight base64-embedded. These are loaded on demand as the user scrolls through the list and unloaded when they scroll away. font-fallback-profiles.json— Variant-specific metric matches used by host builds to compile first-paint CSS. The picker never fetches this file, and a published site does not serve it to browsers.
All three assets live in the same directory (by default css/fonts/).
Selecting a Google font may still load a full runtime stylesheet from
fonts.googleapis.com so the selected font renders beyond the preview subset.
Use <esp-root google-font-loading="none"> to prevent those runtime Google
Fonts CDN requests while keeping the local preview CSS available.
In font-source="web-safe" mode, the picker uses a built-in list of CSS font stacks and does not fetch font-definitions.json, preview CSS, or Google Fonts stylesheets.
Serving Font Assets
After running npm run build-fonts, copy or serve the css/fonts/ directory from a public path. Then tell <esp-root> where to find it:
<esp-root font-css-root="/css/fonts/">
<!-- your app -->
</esp-root>
The font-css-root attribute defaults to /css/fonts/ and must end with a trailing slash. The font picker constructs CSS URLs by family name (e.g. {font-css-root}Roboto.css), so these files must not be renamed or hashed by your build pipeline.
By default the picker also loads font-definitions.json from the same directory. If your asset pipeline hashes filenames for cache busting, you can point the JSON at a hashed URL separately:
<esp-root
font-css-root="/css/fonts/"
font-definitions-url="/dist/hashed-catalog.json">
</esp-root>
When font-definitions-url is set, the picker fetches from that URL instead of {font-css-root}font-definitions.json.
Basic Usage
<esp-root font-css-root="/css/fonts/">
<esp-form-item label="Body font">
<esp-font-picker></esp-font-picker>
</esp-form-item>
</esp-root>
Category Filtering
Restrict the picker to a specific font category:
<esp-font-picker category="monospace"></esp-font-picker>
Supported categories: serif, sans-serif, display, handwriting, monospace, not-display (everything except display fonts).
Pre-selected Value
<esp-font-picker value="Roboto"></esp-font-picker>
Web-safe Email Fonts
Use font-source="web-safe" when the selected value needs to be a complete CSS font stack that can be written directly into email markup:
<esp-form-item label="Email body font">
<esp-font-picker font-source="web-safe"></esp-font-picker>
</esp-form-item>
Web-safe mode includes stacks such as Arial / Helvetica, Georgia / Times New Roman, Verdana / Geneva, Tahoma / Geneva, Trebuchet MS, Courier New, system sans, system serif, and system monospace. The picker item labels are human-readable, but .value is the complete CSS stack, for example Arial, Helvetica, sans-serif.
Listening for Changes
const picker = document.querySelector("esp-font-picker");
picker.addEventListener("esp-value-changed", (e) => {
console.log("Selected font:", e.detail.family);
});
In the default Google mode, the esp-value-changed event's detail contains the full GoogleFont object for the selected font. In web-safe mode, detail contains the selected web-safe font object with family, stack, category, and kind: "web-safe".
Font & Weight Demo
This demo shows how to pair <esp-font-picker> with a weight picker to set page fonts and weights in real time. Select a font and weight below to see the sample text update.
The quick brown fox jumps over the lazy dog.
Pack my box with five dozen liquor jugs. How vexingly quick daft zebras jump! Sphinx of black quartz, judge my vow.
Keeping Fonts Updated
Google regularly adds new fonts to their catalog. To keep your local assets current:
Manual update:
GOOGLE_FONTS_API_KEY="..." \
npm run build-fonts
GOOGLE_FONTS_API_KEY is required. Keep it in your local environment or a
secret manager; the font builder intentionally does not include a fallback key.
The refresh also updates license/GOOGLE_FONTS_NOTICES.md with source metadata
links and per-family notices. Families without authoritative google/fonts
license metadata are excluded from the generated preview catalog until their
provenance can be resolved.
Automated (GitHub Actions): The repository includes a weekly workflow (.github/workflows/update-fonts.yml) that runs npm run build-fonts every Monday and opens a pull request if new fonts are detected. To use it, add a GOOGLE_FONTS_API_KEY secret to your repository.
Runtime Font Loading
Local preview CSS and runtime font loading are separate. The generated
css/fonts/*.css files are local preview subsets. Runtime Google font loading
is controlled by <esp-root google-font-loading>.
<esp-root google-font-loading="none" font-css-root="/css/fonts/">
<esp-form-item label="Body font">
<esp-font-picker></esp-font-picker>
</esp-form-item>
</esp-root>
google-font-loading="none" prevents Espalier from creating
fonts.googleapis.com stylesheet links for theme fonts or selected
<esp-font-picker> values. This mode is intended for offline deployments,
strict CSP environments, privacy-sensitive screens, and enterprise customers
who self-host approved production fonts. Use font-source="web-safe" when the
picker itself should offer only system/email-safe font stacks.
Static production font plans
Static sites should compile selected fonts before rendering pages instead of using runtime Google stylesheet links. Import the dependency-free compiler and the generated profile catalog in the host build, discover the exact requested faces, resolve only those WOFF2 files, and compile again with the source map:
import {
compileFontPlan,
fontFaceRequestKey,
} from "@taprootio/espalier/shared/font-plan";
import profiles from "@taprootio/espalier/css/fonts/font-fallback-profiles.json"
with { type: "json" };
const discovery = compileFontPlan({
lightTheme,
darkTheme,
catalog: profiles,
});
// The host fetches or copies only discovery.requests, then supplies the
// resolved WOFF2 URLs. This example shows two already-resolved faces.
const plan = compileFontPlan({
lightTheme,
darkTheme,
catalog: profiles,
sources: {
[fontFaceRequestKey("Noto Sans", 400)]: {
url: "/assets/fonts/noto-sans-400.woff2",
},
[fontFaceRequestKey("Inika", 700)]: {
url: "/assets/fonts/inika-700.woff2",
},
},
});
Embed plan.css in the existing inline style block in every page head and set
google-font-loading="none". plan.requests is deduplicated across body,
heading, brand, and monospace slots in both schemes; unused catalog families
and variants never enter the page. plan.missingSources and
plan.missingProfiles must be handled by the host build rather than ignored.
Each synthetic fallback face is sourced entirely from local() names, so it
costs no request — but it only helps where the visitor's machine has one of
those faces. The catalog therefore lists the metric-compatible Liberation face
alongside the Microsoft core name on every base that needs it: Liberation Sans
for the Arial base, Liberation Serif for Times New Roman, and Liberation Mono
for Courier New. That is what keeps serif body text and monospace code from
first painting in an unadjusted generic font, and then reflowing, on a Linux
visitor who has no Microsoft core fonts installed.
The default plan targets the page's esp-root. If one document contains roots
with different themes, compile each pair with a unique id selector such as
rootSelector: "#marketing-root" and put that id on the corresponding root.
The compiler accepts only a single id selector, so selector or style injection
cannot enter the generated head CSS.
The same call supports Google-hosted and self-hosted production output. Supply
selected fonts.gstatic.com WOFF2 URLs for the former, or own-origin/CDN URLs
for the latter. The fallback metrics and effective stacks remain identical.
Self-hosted output contains no Google host. Update font-src CSP from the
chosen URLs, and ensure the page's inline-style nonce, hash, or policy covers
the embedded plan.
Taproot should compute this plan once in its shared site context, insert the
minified CSS into the existing page-head style in page.vto, resolve only the
returned requests, and render <esp-root google-font-loading="none">.
Google picker rows derive the same deterministic fallback aliases and weight normalization without fetching the profile catalog in the browser. When the selected family is part of the site's compiled plan, its picker stack therefore uses the already-declared metric-matched faces; other rows retain their local preview subset.
Configuration Summary
| Attribute / Property | Element | Description |
|---|---|---|
font-css-root |
<esp-root> |
Path to the directory containing font CSS files. Must use stable (unhashed) filenames. Default: /css/fonts/ |
font-definitions-url |
<esp-root> |
Optional URL to font-definitions.json. When set, overrides the default {font-css-root}font-definitions.json path. Useful for cache-busted asset pipelines. |
google-font-loading |
<esp-root> |
Runtime Google Fonts loading policy. auto allows selected Google fonts and theme fonts to load full stylesheets from Google. none prevents those CDN links. |
category |
<esp-font-picker> |
Filter fonts by category (serif, sans-serif, monospace, etc.) |
font-source |
<esp-font-picker> |
Font catalog source. google uses generated Google Fonts assets. web-safe uses built-in email-safe/system font stacks. Default: google |
value |
<esp-font-picker> |
The currently selected Google font family or web-safe CSS font stack |
placeholder |
<esp-font-picker> |
Placeholder text for the input. Default: "Choose a font..." |