feat(typography): bake IBM Plex Sans + Source Serif 4 into every tool
System-default font stack ('-apple-system, BlinkMacSystemFont, Segoe UI,
…') is the textbook generic admin-tool look. The tools have a real point
of view (engineering documents, traceability, immutability); the
typography should reflect that.
Picks:
--font → IBM Plex Sans (400 + 600). UI body text. Distinctive
engineering sans with tabular nums and proper figures.
--font-display → Source Serif 4 (600). Headings, page titles,
.app-header__title. Reads as "document" not "UI label."
--font-mono → unchanged. Platform mono fonts are already excellent
and engineering tools rarely benefit from a custom mono.
Wiring:
- Raw .woff2 files live in shared/fonts/ (~60 KB total, latin subset,
SIL OFL 1.1 — both families)
- shared/fonts.css is base64-inlined data URIs for those three fonts
(~80 KB after b64 overhead). Generated once from the snippet in
shared/fonts/README.md.
- Every tool's build.sh prepends shared/fonts.css before shared/base.css
so @font-face is parsed before any rule references the family names.
- Headings (h1-h6) and .app-header__title now use var(--font-display);
.app-header__title bumped 17→18px and letter-spacing reset since the
serif doesn't need the original sans-text tightening.
- table/code/.tabular-nums get font-variant-numeric: tabular-nums so
tracking-number columns align vertically.
"Ship the record player with the record": zero CDN dependency at render
time. Tools render identically offline and online. Per-tool dist sizes
grew by ~80 KB.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8be6c4d98b
commit
6260aa4860
15 changed files with 187 additions and 13 deletions
|
|
@ -19,6 +19,7 @@ trap cleanup EXIT
|
|||
|
||||
# CSS files to concatenate in order
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ trap cleanup EXIT
|
|||
# is bundled because the markdown plugin uses Toast UI inside the
|
||||
# preview pane (.md files render as a full editor).
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ trap cleanup EXIT
|
|||
|
||||
# CSS files to concatenate in order
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ cleanup() { rm -f "$css_temp" "$js_raw" "$js_temp"; }
|
|||
trap cleanup EXIT
|
||||
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ cleanup() { rm -f "$css_temp" "$js_raw" "$js_temp"; }
|
|||
trap cleanup EXIT
|
||||
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ trap cleanup EXIT
|
|||
# CSS files to concatenate in order
|
||||
concat_files \
|
||||
"css/tailwind-utils.css" \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -35,9 +35,17 @@
|
|||
/* Shape */
|
||||
--radius: 4px;
|
||||
|
||||
/* Typography */
|
||||
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
/* Typography. --font-display covers headings (Source Serif 4 — a refined
|
||||
transitional serif that reads as "engineering / document / serious"
|
||||
without being academic). --font is body UI text (IBM Plex Sans —
|
||||
distinctive engineering sans, with proper figures and tabular nums).
|
||||
Both are base64-inlined via shared/fonts.css; system fallbacks kick in
|
||||
when fonts.css isn't loaded (e.g. unbuilt component preview). --font-mono
|
||||
stays as a system stack; engineering tools rarely benefit from a custom
|
||||
mono and platform mono fonts are already excellent. */
|
||||
--font-display: 'Source Serif 4', ui-serif, Charter, 'Iowan Old Style', Georgia, serif;
|
||||
--font: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* ── Dark mode tokens ─────────────────────────────────────────────────────── */
|
||||
|
|
@ -103,8 +111,19 @@ html, body {
|
|||
|
||||
/* ── Typography ───────────────────────────────────────────────────────────── */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
/* Source Serif 4 has subtle optical sizing; let the browser opt in
|
||||
where supported (modern Chromium/Firefox). */
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
/* Tracking numbers and other engineering identifiers should align in
|
||||
columns when stacked vertically. Apply tabular figures wherever we
|
||||
render structured numeric data. */
|
||||
table, .tabular-nums, code {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
@ -292,12 +311,14 @@ a:hover {
|
|||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Tool name inside the header */
|
||||
/* Tool name inside the header. Renders in the display serif so the
|
||||
tool's identity reads as a document title, not a UI label. */
|
||||
.app-header__title {
|
||||
font-size: 17px;
|
||||
font-family: var(--font-display);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
letter-spacing: 0.01em;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
|
|||
30
shared/fonts.css
Normal file
30
shared/fonts.css
Normal file
File diff suppressed because one or more lines are too long
63
shared/fonts/README.md
Normal file
63
shared/fonts/README.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# shared/fonts/
|
||||
|
||||
Source `.woff2` files for the typography baked into every tool. These are
|
||||
the *raw* font bytes; the actual `@font-face` declarations live in
|
||||
`shared/fonts.css` as base64 data URIs so single-file HTML tools render
|
||||
identically offline (`file://`) and online with no CDN dependency.
|
||||
|
||||
## Files
|
||||
|
||||
- `ibm-plex-sans-400.woff2` — UI body text (regular weight)
|
||||
- `ibm-plex-sans-600.woff2` — UI emphasis (semibold)
|
||||
- `source-serif-4-600.woff2` — display/headings (semibold)
|
||||
|
||||
Latin subsets only. ~60 KB total raw; ~80 KB once base64-inlined.
|
||||
|
||||
## Regenerating `shared/fonts.css`
|
||||
|
||||
Run from the repo root:
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
import base64, pathlib
|
||||
fonts = [
|
||||
('IBM Plex Sans', 400, 'ibm-plex-sans-400.woff2'),
|
||||
('IBM Plex Sans', 600, 'ibm-plex-sans-600.woff2'),
|
||||
('Source Serif 4', 600, 'source-serif-4-600.woff2'),
|
||||
]
|
||||
out = pathlib.Path('shared/fonts.css')
|
||||
lines = ['/* shared/fonts.css — base64-inlined woff2. Generated by',
|
||||
' * shared/fonts/README.md instructions; do NOT edit by hand. */', '']
|
||||
for family, weight, fn in fonts:
|
||||
b64 = base64.b64encode((pathlib.Path('shared/fonts') / fn).read_bytes()).decode('ascii')
|
||||
lines += ['@font-face {',
|
||||
f" font-family: '{family}';",
|
||||
' font-style: normal',
|
||||
f' font-weight: {weight};',
|
||||
' font-display: swap;',
|
||||
f" src: url(data:font/woff2;base64,{b64}) format('woff2');",
|
||||
'}', '']
|
||||
out.write_text('\n'.join(lines))
|
||||
PY
|
||||
```
|
||||
|
||||
## Adding or swapping a font
|
||||
|
||||
1. Download the new `.woff2` into this directory (latin subset only — keep
|
||||
the bundle small).
|
||||
2. Update the `fonts` list in the snippet above to include the new family
|
||||
+ weight + filename.
|
||||
3. Re-run the regen snippet.
|
||||
4. Update `--font` or `--font-display` tokens in `shared/base.css`.
|
||||
5. `./build` and verify every tool's `dist/*.html` still includes
|
||||
`@font-face` (three by default).
|
||||
|
||||
## Sourcing
|
||||
|
||||
Originally downloaded from the `@fontsource` npm packages via jsdelivr:
|
||||
|
||||
- IBM Plex Sans 400/600: `@fontsource/ibm-plex-sans@5.0.18`
|
||||
- Source Serif 4 600: `@fontsource/source-serif-4@5.0.5`
|
||||
|
||||
Both licenses are SIL Open Font License 1.1. Fontsource bundles only the
|
||||
latin subset by default, which is exactly what we want.
|
||||
BIN
shared/fonts/ibm-plex-sans-400.woff2
Normal file
BIN
shared/fonts/ibm-plex-sans-400.woff2
Normal file
Binary file not shown.
BIN
shared/fonts/ibm-plex-sans-600.woff2
Normal file
BIN
shared/fonts/ibm-plex-sans-600.woff2
Normal file
Binary file not shown.
BIN
shared/fonts/source-serif-4-600.woff2
Normal file
BIN
shared/fonts/source-serif-4-600.woff2
Normal file
Binary file not shown.
|
|
@ -18,6 +18,7 @@ cleanup() { rm -f "$css_temp" "$js_raw" "$js_temp"; }
|
|||
trap cleanup EXIT
|
||||
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ trap cleanup EXIT
|
|||
|
||||
# CSS files to concatenate in order
|
||||
concat_files \
|
||||
"../shared/fonts.css" \
|
||||
"../shared/base.css" \
|
||||
"../shared/toast.css" \
|
||||
"../shared/nav.css" \
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue