Font Page Preview

A mock browser page that shows what each font-loading strategy actually looks like to the user. Pick block (FOIT), swap (FOUT), optional (FOFT), or no-fallback, and the preview walks a sample heading and paragraph through loadingswaploaded, switching between invisible / fallback / loaded faces at each step. Drives the lesson where the swap-vs-no-swap difference is the punchline.

Preview

Page preview

Pick a strategy and watch the mock page render through the loading timeline.

strategy

FOUT — fallback renders immediately and swaps to the web font. The swap shifts the layout.

font-display: swap

Performance Matters

A 200KB font that arrives 600ms late will push every paragraph down two pixels — small enough to ignore, large enough to fail CLS.

  1. loadingfallback
  2. swaploaded
  3. loadedloaded
Customize
Simulation
swap
1500ms
loading
Display

Installation

npx shadcn@latest add https://craftbits.dev/r/font-page-preview.json

Usage

import { FontPagePreview } from "@craft-bits/core";
 
<FontPagePreview
  title="Page preview"
  pageHeading="Performance Matters"
  pageBody="A 200KB font that arrives 600ms late will push every paragraph down two pixels."
  loadDelayMs={1500}
/>

Controlled mode — drive the strategy and the simulation state from a lesson scrubber:

const [strategy, setStrategy] = useState<FontPagePreviewStrategy>("swap");
const [state, setState] = useState<FontPagePreviewState>("loading");
 
<FontPagePreview
  strategy={strategy}
  onStrategyChange={setStrategy}
  state={state}
  onStateChange={setState}
/>

Anatomy

  • Header. Optional title and description. Both omittable for a chromeless variant.
  • Strategy picker. A <fieldset> wrapping a radiogroup of four chips, one per strategy. Selecting one resets the simulation and replays from loading.
  • Mock page. A tiny browser frame — three dots plus a status tag in the nav — wrapping a heading and a paragraph that re-render under the active strategy + state.
  • Phase strip. Three cells (loading, swap, loaded) showing the rendered phase at each step for the active strategy. The active cell is highlighted.

Understanding the component

  1. Strategy maps state to phase. Each strategy maps (state) to one of three phases: invisible, fallback, loaded. block and no-fallback are invisible while loading; swap and optional show the fallback. On swap, block / swap / no-fallback reveal the loaded face; optional keeps the fallback. On loaded, only optional stays on the fallback for this view.
  2. Simulation loop. Without a controlled state, the component runs a requestAnimationFrame loop that advances loadingswaploaded against the configured loadDelayMs. Picking a new strategy replays the timeline from the start.
  3. Controlled state. When the state prop is provided, the RAF loop is disabled and the preview renders the given state directly. Useful for docs scrubbers, step-driven lesson explainers, and the customize panel above.
  4. Reduced motion. Under prefers-reduced-motion, the simulation loop is skipped — the preview snaps straight to loaded. The mock page still re-renders when the strategy changes; only the timed animation is suppressed.

Variants

// Slow connection — pushes the swap further out so the loading phase is visible.
<FontPagePreview loadDelayMs={3000} defaultStrategy="block" />
 
// Chromeless — drop the strategy picker and the phase strip for an inline visual.
<FontPagePreview hideStrategyPicker hidePhaseStrip />
 
// Controlled state — fix the timeline at the swap moment.
<FontPagePreview strategy="swap" state="swap" />

Props

PropTypeDefaultDescription
titleReactNode'Page preview'Optional heading above the mock page.
descriptionReactNodeOptional sub-headline under the title.
strategyFontPagePreviewStrategyControlled active strategy.
defaultStrategyFontPagePreviewStrategy'swap'Initial strategy in uncontrolled mode.
onStrategyChange(s: FontPagePreviewStrategy) => voidFires when the user picks a new strategy.
stateFontPagePreviewStateControlled simulation state. Disables the internal RAF loop.
onStateChange(s: FontPagePreviewState) => voidFires when the simulation crosses into a new state.
pageHeadingstring'Performance Matters'Heading rendered inside the mock page.
pageBodystringpangram-styleBody paragraph rendered inside the mock page.
loadDelayMsnumber1500Simulated network load delay in milliseconds.
hideStrategyPickerbooleanfalseHide the chip strip.
hidePhaseStripbooleanfalseHide the bottom phase strip.
headingAs'h2' | 'h3' | 'h4''h3'Tag for the title element.
classNamestringMerged onto the root via cn().

FontPagePreviewStrategy is the union 'block' | 'swap' | 'optional' | 'no-fallback'. FontPagePreviewState is 'loading' | 'swap' | 'loaded'.

Accessibility

  • The wrapper is a <section> with data-cb-edu="font-page-preview", plus data-strategy, data-state, and data-phase so consumers can extend tone-specific styling without monkey-patching CSS.
  • The mock page is aria-live="polite" and aria-atomic="true" — phase changes announce as one sentence rather than piecemeal updates.
  • The strategy picker is a real <fieldset> + role="radiogroup" of role="radio" buttons, each with aria-checked. Buttons use native button semantics for keyboard activation; the radios use data-state for styling.
  • A visually-hidden sentence inside the mock page ("Web font rendered." / "Fallback font rendered." / "Text is invisible while the font loads.") conveys the phase without depending on font weight or italic styling.
  • Under prefers-reduced-motion, the simulation loop is skipped — the preview snaps to loaded and re-renders on strategy change without timed animation.

Credits

  • Extracted from: terminal-dreams (src/components/frontend-design/perf-other-assets/ui/FontPagePreview.tsx). The original pulled fontFrame, fontStrategy, and setFontStrategy off an assets-perf-context, rendered a fixed CLS gauge, and exposed five font-display values keyed by a lesson-specific FontStrategy engine. This rewrite drops the context dependency, drops the CSS-Module classnames in favour of the cb-* tokens, and reframes the four behaviours around the FOIT / FOUT / FOFT / no-fallback teaching arc — consumers pick a strategy and the preview walks loadingswaploaded on its own (or under a controlled state prop).