How Hallmark Detects Font Stacks During Its Pre‑Flight Scan

Hallmark detects font stacks by reading CSS custom properties (--font-display, --font-body, --font-outlier) from the root stylesheet, normalizing the computed values into arrays, and caching the results in .hallmark/preflight.json to avoid redundant processing on subsequent runs.

The pre-flight scan in Nutlope/hallmark is the first step in ensuring typographic consistency across a project. Before running audits, the tool programmatically discovers the exact font families defined in your CSS, validates them against the "2+1 rule," and stores this metadata for downstream checks.

CSS Custom Properties as the Source of Truth

Hallmark expects your design system to expose font definitions through three specific CSS variables at the :root level. These CSS custom properties serve as the canonical source for typography detection.

According to [skills/hallmark/references/typography.md](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/typography.md)#L19‑L24, the required variables are:

:root {
  --font-display:  "Fraunces", ui-serif, Georgia, serif;   /* headings, hero */
  --font-body:     "Geist", ui-sans-serif, system-ui, sans;/* prose, UI */
  --font-outlier:  "Geist Mono", ui-monospace, monospace;   /* occasional outlier */
}

These definitions typically live in site/css/tokens.css or site/css/base.css, depending on your project structure.

The Pre‑Flight Detection Process

When Hallmark initiates a scan, it performs the following steps to detect font stacks accurately:

  1. Load stylesheets – The scanner reads the compiled CSS files (e.g., site/css/tokens.css, site/css/base.css).
  2. Create temporary DOM – It instantiates a temporary DOM environment, attaches the stylesheets, and queries the computed values of the three custom properties from :root.
  3. Normalize values – The raw CSS strings are split into arrays, preserving the author's specified fallback order.

The extraction logic functions roughly like this:

// Illustrative implementation from the pre-flight scanner
function getFontStacks() {
  const root = document.querySelector(':root');
  const styles = getComputedStyle(root);
  
  return {
    display: styles.getPropertyValue('--font-display').trim().split(','),
    body:    styles.getPropertyValue('--font-body').trim().split(','),
    outlier: styles.getPropertyValue('--font-outlier').trim().split(',')
  };
}

This process ensures that even complex font stacks with multiple fallbacks are captured correctly.

Caching Strategy and File Watching

To optimize performance, Hallmark caches detected font stacks rather than parsing CSS on every run. The cache is stored at .hallmark/preflight.json and follows an invalidation strategy based on file modification times.

As documented in [skills/hallmark/SKILL.md](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md)#L177‑L179, the tool compares the cache timestamp against source files:

import { statSync } from 'fs';

const preflightMtime = statSync('.hallmark/preflight.json').mtime;
const pkgMtime = statSync('package.json').mtime;
const tailwindMtime = statSync('tailwind.config.js').mtime;

if (pkgMtime <= preflightMtime && tailwindMtime <= preflightMtime) {
  // Safe to reuse cached font stacks
  const cached = JSON.parse(fs.readFileSync('.hallmark/preflight.json'));
} else {
  // Re-scan required
  const stacks = getFontStacks();
  fs.writeFileSync('.hallmark/preflight.json', JSON.stringify(stacks, null, 2));
}

The resulting JSON structure normalizes each stack into a clean array:

{
  "display": ["Fraunces", "ui-serif", "Georgia", "serif"],
  "body":    ["Geist", "ui-sans-serif", "system-ui", "sans"],
  "outlier": ["Geist Mono", "ui-monospace", "monospace"]
}

Tailwind CSS Integration

If your project uses Tailwind CSS, Hallmark merges the fontFamily theme definitions from your tailwind.config.js with the CSS-derived stacks. This ensures that utility-generated fonts (e.g., font-sans, font-serif) are accounted for alongside the custom property definitions, providing complete coverage of all potential font families used in the project.

Validation Against the 2+1 Rule

Once detected, the font stacks feed into Hallmark's pre-flight audit to enforce the "2+1 rule"—a maximum of three distinct font families per page. The audit flags any additional, unapproved font families that appear outside the display, body, and outlier definitions captured during the scan.

Summary

  • Hallmark detects font stacks by parsing three specific CSS custom properties (--font-display, --font-body, --font-outlier) from the :root selector.
  • The scanning process creates a temporary DOM to read computed styles and normalizes values into ordered arrays.
  • Results are cached in .hallmark/preflight.json and only re-scanned when package.json or tailwind.config.js have newer modification timestamps.
  • Tailwind CSS fontFamily configurations are merged with CSS-derived stacks for complete coverage.
  • Detected stacks are validated against the "2+1 rule" to ensure typographic consistency.

Frequently Asked Questions

What CSS files does Hallmark scan to detect font stacks?

Hallmark targets compiled stylesheets typically located at site/css/tokens.css and site/css/base.css. These files must contain the :root definitions for --font-display, --font-body, and --font-outlier for the detection to succeed.

How does Hallmark cache detected font stacks between runs?

The tool writes detected stacks to .hallmark/preflight.json and implements an invalidation check using file modification times. If neither package.json nor tailwind.config.js has been modified since the cache was created, Hallmark reuses the cached data instead of re-parsing the CSS.

Can Hallmark detect font stacks defined only in Tailwind config?

Yes. While Hallmark primarily reads CSS custom properties, it also merges any fontFamily definitions found in your Tailwind configuration. This dual-source approach ensures that both utility-class fonts and CSS variable-based fonts are detected and validated.

What is the "2+1 rule" mentioned in the font stack validation?

The "2+1 rule" is Hallmark's typographic constraint allowing a maximum of three distinct font families per page: one display font, one body font, and one optional outlier (typically monospace). The pre-flight scan uses the detected font stacks to audit the page and warn if additional, unapproved font families appear.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →