Skip to content

A house font set and twelve pairings as shared data - #32

Merged
ecropolis merged 1 commit into
mainfrom
claude/type-data
Sep 29, 2026
Merged

ecropolis merged 1 commit into
mainfrom
claude/type-data

Conversation

@ecropolis

@ecropolis ecropolis commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

src/data/type.ts is now the source for the house font set and the twelve pairings from the type guide (§1). It holds data only, and no font files are committed: a site installs the Fontsource package and self-hosts the Latin subset. The file is framework-free. Its header names the downstream copy (portal src/brand/type.data.ts; the site vendors it for /type/ and /styles/) and carries the pin rule. scripts/check-type.mjs is added to the npm run check chain, and the README gets a "Type" section after "Style boards".

Body SHA-256: ea17c3d410b4bd9c16fa5a86368698094de4ed7bfec64315caced3e18279bfaf

Families (16)

Licences were verified three ways: the package's own LICENSE at 5.3.0 (the licenceUrl, fetched and matched), the package's license field, and api.fontsource.org/v1/fonts/<id>. kb is the Latin upright woff2 as Fontsource 5.3.0 ships it (the wght file from the default index.css for variable families; the listed weights for static ones), measured from the unpacked npm tarballs in KB of 1024 bytes.

family class licence package kb
System UI system none (the device's own) none 0
Inter system OFL-1.1 @fontsource-variable/inter 47.1
Source Sans 3 system OFL-1.1 @fontsource-variable/source-sans-3 28.1
Nunito Sans system OFL-1.1 @fontsource-variable/nunito-sans 30.3
Jost geometric OFL-1.1 @fontsource-variable/jost 26.0
JetBrains Mono mono OFL-1.1 @fontsource-variable/jetbrains-mono 39.5
Source Serif 4 serif OFL-1.1 @fontsource-variable/source-serif-4 49.6
Lora serif OFL-1.1 @fontsource-variable/lora 36.9
Playfair Display didone OFL-1.1 @fontsource-variable/playfair-display 37.5
Fraunces humanist OFL-1.1 @fontsource-variable/fraunces 35.8
EB Garamond humanist OFL-1.1 @fontsource-variable/eb-garamond 43.3
Nunito rounded OFL-1.1 @fontsource-variable/nunito 38.2
Comic Neue hand OFL-1.1 @fontsource/comic-neue (static, 700 only) 18.8
Roboto Slab slab Apache-2.0 @fontsource-variable/roboto-slab 33.4
Archivo heavy OFL-1.1 @fontsource-variable/archivo 34.1
Dancing Script script OFL-1.1 @fontsource-variable/dancing-script 41.7

familyFor(key) returns the first family of a class. Each of the eleven FontKeys resolves, and system resolves to System UI, which is how the boards have always drawn it.

How the metrics were computed. @capsizecss/unpack 4.0.1 measured each web font from the Fontsource woff2 itself: units per em, ascent, descent, line gap and xWidthAvg. The fallback's numbers come from @capsizecss/metrics 4.3.0 (Arial for sans, Times New Roman for serif, Courier New for mono). @capsizecss/core 4.1.3 createFontStack then gave size-adjust and the ascent, descent and line-gap overrides. This is the same method fontaine uses. Values are rounded to two decimals. The metrics are for a fallback @font-face whose src is local() of the first face in fallback, and the check requires that face to be one of those three. Cross-checked against Capsize's published Google Fonts metrics: vertical metrics agree for every family. Average widths differ slightly for a few families, because this PR measures the exact Latin wght file a site loads.

Pairings (12)

Each board is served by at least one pairing. A pairing may list a board only if its heading and body families match the board's classes, plus the label class where the board has one. The check enforces this, so each board's type.note stays true of its pairings.

id heading / body (label) headings: scale · weight · tracking · upper · lh body: size · lh · measure kb serves
system System UI / System UI 1 · 500 · -0.01em · no · 1.15 1rem · 1.55 · 68ch 0 minimal
quiet Inter / Inter 0.95 · 500 · -0.02em · no · 1.15 1rem · 1.6 · 66ch 47.1 minimal
precise Jost / Inter (JetBrains Mono) 1 · 600 · -0.02em · no · 1.1 1rem · 1.6 · 68ch 112.6 high-tech
counsel Source Serif 4 / Source Sans 3 1 · 600 · 0 · no · 1.2 1.0625rem · 1.6 · 66ch 77.7 classic
atelier Playfair Display / Source Serif 4 0.85 · 400 · 0.08em · yes · 1.2 1.0625rem · 1.65 · 64ch 87.1 luxurious
harvest Fraunces / Nunito Sans 1 · 600 · -0.01em · no · 1.15 1.0625rem · 1.6 · 66ch 66.1 natural
old-style EB Garamond / Nunito Sans 1.1 · 500 · 0 · no · 1.15 1.0625rem · 1.6 · 66ch 73.6 natural
friendly Nunito / Nunito 1.05 · 800 · -0.01em · no · 1.1 1.0625rem · 1.6 · 64ch 38.2 playful
crayon Comic Neue / Nunito 1.1 · 700 · 0 · no · 1.1 1.125rem · 1.6 · 60ch 57.0 childlike
diner Roboto Slab / Inter 1 · 700 · 0.02em · yes · 1.1 1rem · 1.6 · 66ch 80.5 retro
loud Archivo / Inter 1.35 · 900 · -0.03em · yes · 0.95 1rem · 1.55 · 64ch 81.2 bold
workshop Lora / Source Sans 3 (Dancing Script) 1 · 600 · 0 · no · 1.2 1.0625rem · 1.6 · 66ch 106.7 handcrafted

All twelve are within the 120 KB budget. The heaviest are precise and workshop, because each loads a third face as its label.

What check-type.mjs holds

  • Every FontKey resolves. The file's written-out FontKey and StyleId unions must equal styles.ts.
  • Ids are unique and slug-shaped, and appear in the same order as TYPE_FAMILY_IDS / TYPE_PAIRING_IDS. There are exactly twelve pairings, and "system" costs 0 KB.
  • Licences are OFL-1.1 or Apache-2.0 only. A variable family comes from @fontsource-variable/* and a static one from @fontsource/*. The licence URL is https and names the package. Each family has 1–4 ascending weights and percent metrics.
  • Pairings reference real families, stay within 120 KB, use a heading weight the heading family lists, and set body text at 400 in the body family.
  • Every family is used by a pairing, and every board is served by one, with the class match above.
  • It prints the SHA-256 of the body.
  • I ran it against seven mutated copies (bad licence, unresolved class, class mismatch, over budget, wrong package scope, bad slug, union drift). Each one failed with the expected message.

Where this departs from the note's shape

  • The System family. A zero-byte "System" pairing needs a family to point at, so TypeFamily.source is "fontsource" | "system". package, licence and licenceUrl are null only for system-ui, and the check enforces that.
  • Additions. metrics.lineGapOverride (always 0%) is added. So are TYPE_BUDGET_KB, TYPE_FAMILY_IDS / TYPE_PAIRING_IDS, and the helpers isFamilyId, familyById, cssFamily ("Inter Variable"), pairingFamilies and pairingKb.
  • Italics. kb counts upright files only. An italic is a separate file that loads only when a page uses it.

Faces excluded, and why

  • Fontshare's own faces (Satoshi, General Sans, Clash Display, Cabinet Grotesk). I read the ITF Free Font License v2.0 (17 Aug 2026). §02 forbids subsetting and format conversion. It also forbids redistributing the files through any repository or public server, and forbids handing them to outside agencies or contractors. We are the agency, and the library is public. Fontshare's OFL faces are allowed in principle, but none was needed.
  • Adobe Fonts. These are licensed for use through Adobe's service and cannot be self-hosted or redistributed.
  • Faces the current FONT_STACKS name (Avenir Next, Futura, Century Gothic, Didot, Bodoni 72, Palatino, Rockwell, Snell Roundhand, Comic Sans MS). These are OS-bundled or commercial, so they cannot be redistributed. Jost, Playfair Display, Fraunces / EB Garamond, Roboto Slab, Dancing Script and Comic Neue stand in for them.

Note

This does not depend on claude/palettes-data (#31) and touches none of its files. Both PRs append to the check chain in package.json and add a README section after "Style boards", so whichever merges second has a trivial conflict in those two spots.

npm run check is green.

🤖 Generated with Claude Code

src/data/type.ts: sixteen families (fifteen Fontsource packages under
OFL-1.1 or Apache-2.0, plus the device's own sans) and twelve pairings,
each mapped to the style boards whose type classes it matches. Data only;
no font files. kb and fallback metrics measured from Fontsource 5.3.0 with
Capsize. scripts/check-type.mjs holds the rules and prints the body hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ecropolis
ecropolis merged commit 6cbc017 into main Sep 29, 2026
2 checks passed
@ecropolis
ecropolis deleted the claude/type-data branch September 29, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant