How to opentype stylistic sets, alternates, and swashes
- Step 1Inspect the font to list its design features — Drop the font on [the OpenType Features Inspector](/font-tools/opentype-features-inspector). In the JSON `features` array, look for rows tagged `ss01`–`ss20` (each labelled `Stylistic set N`), `salt` (`Stylistic alternates`), `swsh` (`Swash`), `cswh` (`Contextual swash`), and `titl` (`Titling`). That's the complete set of design-side tags the font declares.
- Step 2Understand stylistic sets (ss01–ss20) — Up to 20 named slots per font. Each set switches a deliberate group of glyphs — an alternate `a`, a simplified `g`, a different `&`. Modern programming and display fonts use them heavily. The number is a slot, not a meaning: `ss01` is whatever the foundry assigned it.
- Step 3Understand stylistic alternates (salt) — `salt` is a single 'give me the alternate' feature with no slot number. Where `ss01`–`ss20` let the designer expose specific named variations, `salt` is a coarse 'show alternates' switch. Less precise, but useful when a font only exposes alternates through `salt`.
- Step 4Understand swashes (swsh) and titling (titl) — `swsh` adds decorative extended strokes — common in script and display faces; `cswh` is the contextual variant that only swashes in certain positions. `titl` (Titling) substitutes forms designed for large sizes and all-caps headlines. All three are display features — use sparingly and never in body text.
- Step 5Enable them in CSS — in one declaration — Copy the `css_snippet` rows and merge: `font-feature-settings: "ss01" 1, "ss03" 1, "salt" 1;`. A second `font-feature-settings` rule on the same selector overrides the first, so always combine. For swashes there's also `font-variant-alternates: swash(...)` but it requires `@font-feature-values` declarations, so the low-level form is more reliable.
- Step 6Render each set before committing — The Inspector confirms a set exists; it cannot show its effect. Render the live result with [the ligature toggler](/font-tools/ligature-toggler) (which renders feature-applied sample text) or check individual alternate glyphs with [the glyph inspector](/font-tools/glyph-inspector). Document the visual meaning in your design system so the next person doesn't have to re-discover it.
The four design-side feature categories
All four change letter shapes via GSUB substitution. The difference is granularity and intent. Labels are exactly what the Features Inspector prints.
| Tag(s) | Inspector label | What it does | Standardised meaning? |
|---|---|---|---|
ss01–ss20 | Stylistic set 1…20 | Switches a named group of glyphs the foundry chose for that slot | Slot numbers reserved; meaning is font-defined |
salt | Stylistic alternates | Coarse 'show the alternate forms' with no slot | No — font decides which glyphs and which alternate |
swsh / cswh | Swash / Contextual swash | Decorative extended strokes; cswh only in certain positions | Behaviour standard, glyph designs font-specific |
titl | Titling | Forms tuned for large display / all-caps headlines | Behaviour standard, glyph designs font-specific |
cv01–cv99 | (custom feature)* | Single-glyph character variants — finer than a whole set | Slot numbers reserved; meaning font-defined |
How to read the Inspector output for design features
Drop the font, then scan the features array for these tags. Each row you find means that feature is shippable in CSS.
| If you see… | It means… | Next step |
|---|---|---|
ss01 through ss06 (six rows) | The font ships six stylistic sets | Render all six; document what each does |
salt but no ssNN | Alternates exposed only via the catch-all | Use "salt" 1; you can't pick a specific variation |
swsh present | The font has swash capitals/letters | Use for display only; check coverage per letter |
titl present | Dedicated titling forms exist | Use for large all-caps headings |
| None of the above | It's a text-only font with no display alternates | Don't fake it with CSS — the glyphs aren't there |
Cookbook
Recipes for turning a font's stylistic-feature list into working CSS for a brand or display context. The Inspector tells you which tags exist; these show how to enable and verify them. Render the result with the ligature toggler before it reaches a headline.
Enabling two specific stylistic sets
ExampleYou inspected the font, found ss02 changes the 'a' and ss05 changes the '@', and you want both on the brand wordmark. Combine them — never as two rules.
/* From the Inspector's css_snippet rows, merged: */
.wordmark {
font-family: "Brand Display";
font-feature-settings: "ss02" 1, "ss05" 1;
}
/* This would be WRONG — only ss05 survives: */
.wordmark { font-feature-settings: "ss02" 1; }
.wordmark { font-feature-settings: "ss05" 1; }Swash capitals on a headline only
ExampleSwashes (swsh) belong in display sizes. Scope them tightly so body copy and UI never inherit them.
h1.editorial {
font-family: "Script Display";
font-feature-settings: "swsh" 1, "calt" 1;
font-size: clamp(2.5rem, 6vw, 5rem);
}
/* Do NOT apply swsh to body — extended strokes wreck line fit */
body { font-feature-settings: "liga" 1, "kern" 1; }salt as a fallback when there are no named sets
ExampleSome fonts expose alternates only through salt. You lose per-set control but can still flip to the alternate look.
/* Inspector showed `salt` but no ss01–ss20 */
.alt-look { font-feature-settings: "salt" 1; }
/* Pair with stylistic alternates via the higher-level alias
when you have @font-feature-values set up: */
.alt-look { font-variant-alternates: stylistic(alt); }Documenting set meanings in a design system
ExampleBecause ss01 has no universal meaning, the only defence against drift is writing down what each set does for your specific font. Build the doc from the Inspector output plus a render pass.
# Brand Display — stylistic sets (verified 2026) # Source: OpenType Features Inspector + visual render ss01 single-storey a (use for logo) ss02 straight-leg R (off by default) ss03 alternate ampersand & (use in pull-quotes) ss05 rounded @ sign (avoid — too playful) # salt: not present # swsh: present, capitals A C E G L M N Q R only
Character variants (cvNN) for one-glyph tweaks
ExampleWhen you only want to change a single letter, look for cv01–cv99 rather than a whole set. They're labelled '(custom feature)' by the Inspector because cvNN isn't in its label table, but they work the same way in CSS.
/* Inspector lists: { "tag": "cv11", "name": "(custom feature)", ... } */
/* Foundry docs say cv11 = open-tail g */
.body { font-feature-settings: "cv11" 1; }
/* cvNN can take a value to pick among several variants: */
.body { font-feature-settings: "cv11" 2; } /* second variant */Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
ss01 means something different in every font
By designStylistic-set slot numbers (ss01–ss20) are reserved by OpenType, but their meaning is entirely font-defined. ss01 in Inter is not ss01 in Roboto Flex. The Inspector can only print Stylistic set 1 — it cannot know the effect. Always check the foundry's specimen, and never copy an ssNN value between fonts expecting the same result.
Two font-feature-settings rules — the second wins
Gotchafont-feature-settings is not additive. A second declaration on the same selector replaces the first wholesale, so ss01 plus a later tnum rule means you lose ss01. Always merge everything into a single comma-separated declaration. This is the single most common reason a stylistic set 'doesn't work' after it was confirmed present.
Set exists but doesn't cover the letter you want
Partial coverageA stylistic set's tag being present doesn't mean it touches every glyph. swsh might only swash capital A, C, E, G; ss03 might only alter lowercase. The Inspector reports the tag, not which glyphs it substitutes. Render your actual text — if the alternate doesn't appear for a given letter, that letter isn't in the set's substitution rules.
cv01–cv99 show as '(custom feature)'
PreservedCharacter-variant tags cv01–cv99 aren't in the Inspector's built-in label table, so they're reported with the name (custom feature). They are still real, shippable features — the tag and css_snippet are emitted normally. The foundry's docs map each cvNN to a specific glyph; some accept a numeric value ("cv11" 2) to pick among several variants.
salt and ssNN both present
SupportedA font can declare both the catch-all salt and named ss01–ss20. They may overlap or be independent. Enabling salt flips to 'the alternate' broadly; enabling a specific ssNN is surgical. If you enable both you may get unexpected combined substitutions — pick the level of control you want and test the rendered result.
Swashes break line height and fit
Use sparinglySwash glyphs extend well beyond the normal advance width and ascent/descent. Applied to body text or tight UI they collide with neighbouring lines and clip at container edges. The feature being present is an invitation to use it in display contexts only; scope swsh/cswh to large headings and give them generous line-height.
font-variant-alternates needs @font-feature-values
Setup requiredThe high-level font-variant-alternates: styleset(...) / swash(...) syntax only works once you've declared the mapping in an @font-feature-values block. Without it, the alternate silently doesn't apply. The low-level font-feature-settings: "ss01" 1; form (which the Inspector emits) has no such dependency and is the more reliable choice.
Font ships no design features at all
ExpectedA pure text font may have only kern, liga, calt and none of ssNN/salt/swsh/titl. The Inspector simply won't list those rows. You can't add stylistic alternates with CSS — the alternate glyphs don't exist in the file. If you need that personality, you need a different font (or a display cut of the same family).
Frequently asked questions
Are stylistic sets standardised across fonts?
Only the slot numbers are. ss01–ss20 are reserved by the OpenType spec, but what each set does is decided by the type designer. ss01 might be a single-storey a in one font and a different & in another. The Features Inspector can confirm a set exists and label it Stylistic set N; the foundry's specimen is the only authoritative source for the visual effect.
How do I discover what each stylistic set does in my font?
Two-step: (1) run the OpenType Features Inspector to list which ssNN slots are present; (2) render sample text with each set toggled on. Look for cv01–cv99 character-variant features too — they affect single glyphs and are easier to test than full sets. Then write the meanings down in your design system, because nothing in the font tells you.
Can I combine multiple stylistic sets?
Yes — put them in one declaration: font-feature-settings: "ss01" 1, "ss03" 1, "salt" 1;. Stack as many as you like. The critical rule: never write them as separate font-feature-settings rules on the same selector, because the later one replaces the earlier one entirely. Some sets are mutually exclusive by design and toggling both just yields the default.
What's the difference between ss01–ss20 and salt?
ss01–ss20 are 20 distinct, named slots — the designer can expose specific, separable variations and you pick exactly which. salt (stylistic alternates) is a single coarse switch meaning 'use the alternate forms' with no slot number, so you get whatever the font maps to it with no per-variation control. Prefer ssNN when the font offers them; fall back to salt when it doesn't.
What are cv01–cv99 (character variants)?
Character-variant features change a single glyph rather than a coordinated group. cv11 might be 'open-tail g'. They're finer-grained than stylistic sets and often easier to use because you change exactly one letter. The Inspector labels them (custom feature) since cvNN isn't in its name table, but they ship and work normally; some accept a value ("cv11" 2) to choose among variants.
When should I use swashes?
Display contexts only — large editorial headlines, wordmarks, invitations. Swash glyphs (swsh) have extended decorative strokes that overflow the normal box and wreck line fit in body or UI text. Scope them to large headings with generous line-height. cswh (contextual swash) is the safer cousin — it only swashes in appropriate positions (e.g. line-initial or line-final letters).
Why didn't my stylistic set apply even though the Inspector says it's there?
Most often: a second font-feature-settings rule on the same selector overrode the first. Merge all features into one comma-separated declaration. Second possibility: the set doesn't cover the specific letter you're testing — sets often substitute only a subset of glyphs. Third: you used font-variant-alternates without the required @font-feature-values block. The low-level font-feature-settings form avoids that last trap.
Is titl (Titling) the same as small caps or all-caps?
No. titl substitutes glyph forms specifically drawn for large display sizes and all-caps settings — slightly lighter, better-spaced caps that look right at headline scale. It's not small caps (smcp, which makes cap-shaped small letters) and it's not the CSS text-transform: uppercase (which just changes case). Use titl for big all-caps headings when the font provides it.
Do these features add file weight?
They add the alternate glyphs plus the substitution rules in GSUB. A display font with many stylistic sets and swashes is larger than a plain text cut for that reason. If you only ship one weight to the web and never use the sets, you can subset them away with the font subsetter — but check first that you genuinely don't need them, because subsetting glyphs out is irreversible.
Can I see the actual alternate glyph shapes?
The Inspector reports the feature tags, not glyph outlines. To see a specific alternate glyph's outline, bounding box, and metrics, use the glyph inspector, which emits a real SVG path per glyph. To see a set applied to running text, render it with the ligature toggler using sample text.
Do all browsers support stylistic sets?
font-feature-settings is supported in every current browser, and ssNN/salt/swsh work wherever the font ships them. The higher-level font-variant-alternates with @font-feature-values has slightly narrower support and more setup, which is why the low-level form the Inspector emits is the safer cross-browser choice.
Is my font uploaded when I inspect it?
No. The Features Inspector parses the file in your browser with opentype.js and shows a '0 bytes uploaded' badge — the bytes never reach a server. So you can safely inspect a licensed or unreleased display font to plan which stylistic sets to expose in your design system.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.