Workshop Kit · widget reference

Shipped

font-pair-picker

Six pre-vetted heading + body type pairings, presented as preview cards rendering the same restaurant name + tagline. Every pair is a CSS font-family stack built on system + commonly-installed fallbacks — no remote font fetch — so the rail's sandbox="" iframe shows the actual choice without contradicting the suite's "no fetch" posture. Real Google Fonts come through the L14 generator's @import block at deploy time, not here. Writes fontPair: { id, heading, body } to MuntinContext.

Live demo

Six cards, one selected at any moment. Click or use arrow keys to move the focused card; Enter or Space commits the selection. The chosen pair's id, heading stack, and body stack write to MuntinContext.fontPair.

No config block — six pairs are baked into the widget

Current MuntinContext.fontPair Click a card above to make a selection…

The six pairs

The roster is curated for restaurant use — each pair maps to a recognizable kind of operation. Operators don't have to know typography terminology to pick well; the blurbs tell them what each pair "reads as."

idHeading stackBody stackReads as
editorial-modern Fraunces / Playfair / Georgia / serif Inter / system-ui sans The Method default; the bootcamp itself is set this way.
diner-classic Bebas / Oswald / Impact / Arial Narrow Georgia / serif Breakfast spots, classic American counters, diner-sign feel.
trattoria Playfair / Cormorant / Garamond / serif Lora / EB Garamond / serif Italian-restaurant default; established without trying too hard.
taqueria Anton / Bebas / Impact / sans Inter / system-ui sans Reads street, reads loud — hand-painted-sign style restaurants.
minimal-tasting Inter / system-ui sans Inter / system-ui sans Tasting menus, wine bars — where restraint IS the brand.
corner-store Caveat / Kalam / Comic Sans MS / cursive Inter / system-ui sans Cafés, bakeries, juice bars — "the person behind the counter."

Contract

No data attributes beyond data-widget; no inline JSON config. The widget reads the previously-selected pair id (if any) from MuntinContext.fontPair.id and pre-selects that card.

BehaviorNotes
Reads fontPair.id Pre-selects the matching card on mount. Falls back to no selection if absent (the first card receives keyboard tab focus, but aria-checked="false" until the operator commits).
Writes fontPair { id, heading, body } — the id is the stable slug; heading + body are the CSS family stacks the L14 generator + rail use directly.
No remote font loads Every pair uses system + commonly-installed fallbacks so the rail's sandboxed iframe shows the correct rendering without violating "no fetch." The L14 generator pairs the chosen id with a Google Fonts @import at deploy time.
Bilingual labels Each pair has name_en / name_es and blurb_en / blurb_es. The widget reads deps.locale at mount and renders the matching strings. The sample restaurant name + tagline are also localized ("Jolene's Cafe" → "Café Jolene").
Cross-device sync fontPair is on CONFIG_ALLOWED_KEYS in src/lib/course.js and on the matching ALLOWED array in assets/js/course-config-sync.js, so signed-in operators see the same choice on their second device.

Markup

<section class="course-widget" data-widget="font-pair-picker"></section>

That's it. The six cards render automatically and the selection writes to fontPair on every commit.

Accessibility

Why six, not "build your own"

Typography is the place where operators get stuck or get bad. Six pre-vetted pairs reframes the choice from "make a typography decision" to "pick the one that reads like my place," which is the question they can actually answer. The blurbs ("reads street, reads loud") name the vibe in operator-language. L7 also includes the voice slider for tone — together they cover palette + voice + typography, the three brand-defining decisions the bootcamp needs locked before L8.

Where it ships

The L14 generator reads fontPair.id and emits a Google Fonts @import + CSS variables for --font-display + --font-body in the generated styles.css. The rail uses the same id to render its sandboxed iframe with system fallbacks while the operator is still picking — no deploy needed to preview.

Source

/tools/_shared/workshop/font-pair-picker.js — ~210 LOC. Exports { tag, contextKeys, mount, serialize } per the Workshop Kit widget contract. The six pairs live as a frozen PAIRS constant at the top of the file; the widget is otherwise a thin radio-group renderer with arrow-key navigation and a polite live-region announcer.

← Back to the Workshop Kit