Install
$ agentstack add skill-jpoindexter-typography-skills-variable-fonts ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Variable fonts
One file containing a continuous design space instead of discrete instances. Powerful and frequently misused — the most common error is reaching for font-variation-settings when a standard CSS property already maps to the axis.
1. When to invoke
- Choosing between a variable font and a set of statics.
- Using weight, width, optical size, slant, or italic axes.
- Axes not responding, or rendering incorrectly.
- Animating or transitioning a typographic axis.
- Auditing whether a variable font is actually paying for itself.
2. Required context
- Which axes the font exposes, and their ranges (read the font, don't
assume — wght 100–900 is common but not universal).
- How many instances you'd otherwise ship.
- Real byte comparison: variable file vs. the statics it replaces.
- Whether you need continuous values or only named instances.
- Whether the design needs optical sizing.
3. Invariant principles
- **Use standard CSS properties, not
font-variation-settings, wherever one
exists.** Registered axes map to real properties:
| Axis | Standard property | |---|---| | wght | font-weight | | wdth | font-stretch (or font-width) | | opsz | font-optical-sizing | | ital | font-style: italic | | slnt | font-style: oblique |
font-variation-settings is a low-level override. It does not inherit predictably per-axis — setting it replaces the whole list, so a child that sets one axis loses every other. It also bypasses the cascade's understanding of weight, breaking things like font-synthesis control and browser bolding logic. Reserve it for custom (unregistered) axes only.
- Declare the axis range in
@font-face.font-weight: 100 900on the
@font-face rule tells the browser the file covers that span; omit it and the browser may treat the font as a single weight and synthesize faux bold.
- A variable font is not automatically smaller. It carries the whole design
space. Compare real bytes.
- Only ship the axes you use. Instancing/subsetting can strip unused axes.
4. Context-dependent heuristics
Variable vs. static — the break-even.
| Instances needed | Usually | |---|---| | 1–2 | Static wins | | 3 | Roughly even — measure | | 4+ | Variable usually wins | | Continuous values or animation | Variable, necessarily |
Measure, don't assume. A variable file replacing two weights often costs bytes.
Optical size (opsz) is the underused one. It adjusts the design for the size it's rendered at — more contrast and tighter spacing at display sizes, sturdier forms and looser spacing at text sizes. This is what metal type did by cutting each size separately. Enable with font-optical-sizing: auto (browsers apply it from font-size automatically when the axis exists). Turn it off only deliberately.
Weight is now continuous. You can use 450 or 530. Resist it in a design system — the value of a scale is exclusion. Pick your named steps and tokenize them.
Grade (GRAD) — the axis most people don't know they need. Grade changes apparent weight without changing advance widths, so text gets visually heavier or lighter while occupying exactly the same space. Nothing reflows.
Two uses:
- Dark mode. Light-on-dark text appears optically bloated — the light strokes
bleed into the dark ground. Drop the grade slightly in dark mode and the text reads at the same visual weight as its light-mode counterpart. (In print the relationship inverts.) This is the correct fix for "our dark mode looks heavier", far better than switching to a lighter weight, which would reflow the line.
- Interaction states. Because nothing reflows, grade is safe for hover and
active states in a way font-weight is not.
GRAD is a custom axis (Roboto Flex exposes it), so it is one of the legitimate cases for font-variation-settings — there is no standard property for it.
Parametric axes go further still, exposing thin-stroke, thick-stroke, counter width, and similar low-level parameters. Powerful for fine-tuning a face to a specific context; easy to produce something the type designer would not recognise. Use sparingly and compare against the designed instances. Custom axes are arbitrary — a type designer can expose almost any parameter of the design this way, down to contrast, x-height, serif shape, or the length of a single glyph's tail (Rutter cites a Q's tail as a real example); the four-character tag is whatever the foundry chose, so read the specimen rather than guessing at what a custom axis controls.
Multiplexed fonts solve the same reflow problem a different way: every weight occupies identical horizontal space by design, so switching weight never changes width. See ui-component-typography.
Animation. Axes are animatable, which enables genuinely new effects. Constrain it: animating wght reflows text if the width changes, and any motion must respect prefers-reduced-motion.
@media (prefers-reduced-motion: no-preference) {
.headline { transition: font-variation-settings 200ms ease; }
}
Fallbacks. Variable font support is universal in current browsers, but the file still has to load. The metric-matched-fallback technique in font-loading-and-performance applies unchanged.
5. Failure patterns
| Pattern | Cause | Fix | |---|---|---| | Faux bold despite a variable font | @font-face didn't declare the range | font-weight: 100 900 in @font-face | | Child element loses width/optical settings | font-variation-settings replaces the whole list | Use standard properties | | Variable font larger than the statics it replaced | Replaced too few instances | Measure; consider statics | | Axis has no effect | Axis not present, or outside its range | Inspect the font's actual axes | | Text reflows during a weight animation | Weight change alters advance widths | Animate a non-reflowing property, or reserve space | | Display text looks flabby at 72px | opsz unused or disabled | font-optical-sizing: auto | | Design drifts to arbitrary weights | Continuous axis, no tokens | Tokenize named steps | | Dark mode text looks heavier than light mode | Optical bloat of light-on-dark | Lower GRAD, not wght — no reflow | | Hover state shifts the layout | font-weight changes advance widths | GRAD, or a multiplexed face | | Parametric tuning produced something odd | Low-level axes pushed past the design space | Compare against the designed instances | | Motion sickness complaints | Axis animation, no reduced-motion guard | Wrap in the media query |
6. Evaluation procedure
- Inspect the font's actual axes and ranges (browser devtools,
fonttools, or
the foundry spec). Don't assume.
- Count the instances the design genuinely uses.
- Measure both: variable file vs. sum of the statics, after subsetting.
- Confirm
@font-facedeclares each axis range. - Confirm standard properties are used for registered axes;
font-variation-settings
only for custom axes.
- Check inheritance: set an axis on a parent, verify a child that changes one axis
retains the rest.
- If
opszexists, verifyfont-optical-sizing: autoand compare display vs. text
rendering.
- Confirm no faux bold/italic is being synthesized.
- If animating, verify
prefers-reduced-motionis respected and no reflow occurs.
7. Output format
Font:
Axes available:
Axes used: — via
@font-face ranges declared:
Instances replaced: — variable KB vs statics KB →
Optical sizing: — because
Tokenized weights:
Faux synthesis:
Reduced-motion respected:
8. Examples
Design system shipping Inter at 400/500/600/700 as four static files.
> Four instances is past the break-even, and the variable file measures smaller > than the four statics after subsetting, so switch. Declare > font-weight: 100 900 in @font-face so the browser knows the span and stops > synthesizing. Use font-weight — not font-variation-settings — so the cascade > and font-synthesis keep working. Tokenize exactly the four existing steps > despite the axis now being continuous; the scale's value is that 530 is > unavailable.
Headline set in a family with an opsz axis, looking weak at 64px.
> The optical size axis isn't engaged, so the display setting is using text-optimised > forms — too little contrast and too much spacing for 64px. font-optical-sizing: > auto lets the browser drive opsz from the rendered size; the headline picks up > the higher-contrast, tighter-spaced cut it was designed to have. No markup change, > no extra bytes.
9. Counterexamples
- ❌
font-variation-settings: "wght" 700whenfont-weight: 700exists. — Breaks
inheritance and cascade behavior.
- ❌ "Variable fonts are always smaller." — They carry the full design space; two
weights are often cheaper as statics.
- ❌ Omitting
font-weight: 100 900from@font-face. — Invites faux bold. - ❌ "We have a continuous weight axis, so let designers pick any value." — Destroys
the system's constraint.
- ❌ Animating
wghton body text without checking reflow or reduced-motion. - ❌ "It has an
opszaxis so it handles sizes automatically." — Only if optical
sizing is enabled and not overridden.
10. Source citations
- Current standards take precedence here — variable fonts postdate most of the
corpus. MDN — variable fonts guide, font-variation-settings, font-optical-sizing. web.dev — optimize web fonts.
- Stocks, Universal Principles of Typography (2024) — the most current book in the
corpus on variable fonts. §29 multiple widths; §30 masters and interpolation (weight and width masters combined by vector addition); §31 multiplexed typefaces; §84–85 variable fonts and axes; §86 grade — "grade, unlike weight (except in multiplexed fonts), doesn't affect the overall width of the glyphs", and "on screens, light-on-dark text will appear slightly bloated, so, if possible, switch to a lighter grade when designing for Dark Mode. The opposite is true in print"; §87 parametric axes; §88 the inheritance problem; §27 avoid faux styles.
- Brown, Flexible Typesetting — designing typographic systems that adapt rather
than fixed instances; the conceptual case for continuous ranges.
- Rutter, Web Typography — OpenType foundations that variable fonts extend;
custom axes as arbitrary designer-defined parameters (contrast, x-height, serif shape, individual glyph details) reachable only through font-variation-settings. His browser-support and format-string details (format('woff-variations'), multiple repeated @font-face rules for fallback, an early-2018 viability estimate) are from 2017 and superseded — variable font support is now universal; don't carry those specifics forward.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jpoindexter
- Source: jpoindexter/typography-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.