zudo-panel-designer docs
GitHub repository

Type to search...

to open search from anywhere

Fonts

Text layers are rendered as physical silkscreen printing, not a monitor — so the font list in the text inspector isn't "whatever's installed," it's a small curated set chosen for legibility at silkscreen sizes and printing tolerances. fonts.ts is the single source of that list.

Self-hosted, no CDN

Every curated font is a pinned @fontsource/* package, statically imported at the top of fonts.ts:

import '@fontsource/inter';
import '@fontsource/oswald';
import '@fontsource/bebas-neue';
import '@fontsource/orbitron';
import '@fontsource/rajdhani';
import '@fontsource/audiowide';
import '@fontsource/share-tech-mono';
import '@fontsource/archivo-black';
import '@fontsource/monoton';
import '@fontsource/press-start-2p';

These are OFL-licensed font files bundled into the app build — there is no runtime request to fonts.googleapis.com or any other font CDN. That matters both for offline use and because a panel design tool shouldn't depend on a third-party network call just to render text.

The curated list

CURATED_FONTS is the array the text inspector's font <select> renders from:

FamilyCharacter
InterNeutral, highly legible UI sans — the default general-purpose choice.
OswaldCondensed sans; the default font (DEFAULT_FONT_FAMILY) for new text layers.
Bebas NeueAll-caps display condensed — bold panel labels.
OrbitronGeometric, technical/sci-fi character.
RajdhaniGeometric sans with a technical/industrial feel.
AudiowideRounded, bold, futuristic display face.
Share Tech MonoMonospace — good for numeric/technical labeling.
Archivo BlackVery heavy weight sans — maximum legibility at small silkscreen sizes.
MonotonBold outline-style display face.
Press Start 2PPixel/8-bit style display face.

The common thread is bold, geometric, or monospace faces that stay legible when printed small and thin by a silkscreen process — a delicate serif or a light-weight sans would blur or drop out entirely at typical panel label sizes.

Loading a face on demand

Only the definitions (@font-face CSS) load eagerly with the module; the actual font file bytes load lazily, on first use, via ensureFont(family):

export function ensureFont(family: string): Promise<void>

ensureFont is idempotent per family — it kicks off document.fonts.load() once, caches the in-flight promise, and resolves once the face is actually usable. It's called from two places:

  • The text tool, immediately after placing a new text layer.

  • The text inspector, whenever the user changes the selected layer's font.

Both callers chain .then(() => ctx.requestRepaint()), so the canvas repaints with the real glyphs as soon as they're ready — until then, the renderer's ctx.font = ... draws with the browser's fallback face, which is a normal, harmless transient state rather than an error.

ensureFont never throws or hangs a caller: if a family fails to load, or the runtime has no FontFaceSet API at all (e.g. jsdom's default test environment), the promise still resolves — it's just never marked "ready," so the fallback face keeps rendering indefinitely instead of the call blocking anything.

Note

A layer's fontFamily isn't validated against CURATED_FONTS — a hand-edited JSON import, or the demo document's generic sans-serif, is preserved and shown as a synthesized extra option in the inspector's <select> rather than being silently swapped out. See Inspectors → Text.