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:

  1. 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.
  2. Individual CSS files — One <FontFamily>.css per font, each containing a @font-face rule with the preview weight base64-embedded. These are loaded on demand as the user scrolls through the list and unloaded when they scroll away.
  3. 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..."
Components API Guides Getting started Styling Espalier Browser support GitHub npm package Taproot I/O