Font Features (OpenType → CSS)
Inspect a font's real OpenType feature inventory and generate CSS to enable chosen features. Never guess what a font supports — always run scripts/otfeat.py list first; only features present in the dump may be offered or enabled.
Requires fontTools (pip install fonttools if missing).
Workflow
Inspect the actual font files in the project:
python3 <skill-dir>/scripts/otfeat.py list path/to/Font-Regular.woff2 [more fonts...]Output lists each font's GSUB/GPOS tags with human descriptions (Monaspace stylistic sets are described precisely; unknown
cvXXare labelled generically).Interview the user via cenno (
mcp__cenno__ask_user, orask_sequencefor several questions). Offer ONLY features found in step 1, grouped sensibly, with a recommendation. Typical question set:- Coding ligatures: which sets? (arrows ss03, colons ss07, comparison ss02...) — for code samples only, usually off for prose
- Numerals: tabular (
tnum) for tables/timers, oldstyle (onum) for prose, slashed zero (zero) - Texture healing (
calt) — for Monaspace, recommend ON everywhere - Caps:
casefor uppercase labels/buttons,smcpif present Useinput: {kind: "choice"}with a text option, mark optional questions low-urgency, and respect a "skip" answer.
Generate CSS deterministically:
python3 <skill-dir>/scripts/otfeat.py css --family "Monaspace Neon" \ --features calt,liga,tnum,case --class prose # cvXX with a value: --features cv01=2High-level
font-variant-*properties are preferred automatically (they cascade and combine better); remaining tags fall back tofont-feature-settings.--no-variant-propsforces the low-level form only.Apply: paste the block into the project stylesheet; scope per element role (e.g.
tnumon tables/stat tiles only, ligature sets oncodeonly). Verify in a browser with real content.
Gotchas
font-feature-settingsis all-or-nothing per declaration — a second declaration on a nested element REPLACES the inherited list; repeat the full list. This is the main reason to preferfont-variant-*.- CSS accepts any tag, browsers silently ignore ones the font lacks — which is why step 1 is mandatory (e.g. Monaspace has NO
tnum/zero/smcp; it's a monospace family and zero is already slashed). - Stylistic-set meanings are font-specific; never reuse Monaspace's ss-descriptions for another family.
- A feature tag being present does not mean it covers the glyphs assumed: dump the actual GSUB substitutions before writing demo copy (e.g. Monaspace's
caseonly raises: ¡ ¿ ‽and combining accents — NOT quotes, brackets, or dashes). - Texture healing needs
caltAND ligatures left enabled;font-variant-ligatures: nonekills it.