How to opentype ligature features: complete reference
- Step 1liga — Standard ligatures — Default on. Fuses `fi`, `fl`, `ffi`, `ffl`. CSS: `font-feature-settings: "liga" 1;` or `font-variant-ligatures: common-ligatures`. Disable for code (`"liga" 0`). Previewable in the Toggler (rows 2/3 vs row 1).
- Step 2calt — Contextual alternates — Default on. Context-aware glyph swaps powering script joins and Arabic shaping. CSS: `font-feature-settings: "calt" 1;` or `font-variant-ligatures: contextual`. Previewed alongside `liga` in the Toggler (on/off together).
- Step 3dlig — Discretionary ligatures — Default off. Decorative `ct`/`st`/`sp` in display faces. CSS: `font-feature-settings: "dlig" 1;` or `font-variant-ligatures: discretionary-ligatures`. Previewable in the Toggler's third row (no-op if the font lacks the glyphs).
- Step 4hlig — Historical ligatures — Default off. Long-`s` forms (`ſt`, `ſi`) for period reproduction. CSS: `font-feature-settings: "hlig" 1;` or `font-variant-ligatures: historical-ligatures`. NOT previewable in the Toggler — verify with [opentype-features-inspector](/font-tools/opentype-features-inspector).
- Step 5rlig — Required ligatures — Default on; do not disable. Script-essential joins for Arabic/Devanagari. The browser keeps these on regardless of CSS. There is no `font-variant-ligatures` keyword for it — it is mandatory by design.
- Step 6Confirm what your font actually has — A reference only tells you what the tags mean. To learn which the font defines, run [opentype-features-inspector](/font-tools/opentype-features-inspector) (JSON list), preview `liga`/`calt`/`dlig` rendering in the [Ligature Toggler](/font-tools/ligature-toggler), and inspect a specific ligature glyph's outline with [glyph-inspector](/font-tools/glyph-inspector).
Ligature-class feature tags at a glance
Default = browser default. 'Toggler preview' marks the tags the Ligature Toggler renders in its three fixed rows; the rest need opentype-features-inspector. Names match the registered-feature map in lib/font/font-processor.ts.
| Tag | Name | Default | Toggler preview | Disable safely? |
|---|---|---|---|---|
liga | Standard ligatures | On | Yes (rows 1 vs 2/3) | Yes — code / tabular / logos |
calt | Contextual alternates | On | Yes (with liga) | Rarely — can break script joins |
dlig | Discretionary ligatures | Off | Yes (row 3) | N/A — opt-in |
hlig | Historical ligatures | Off | No — use inspector | N/A — opt-in, niche |
rlig | Required ligatures | On | No — always on | No — breaks complex scripts |
clig | Contextual ligatures | On | No — use inspector | Rarely — context-dependent |
CSS recipe per tag (two forms)
The low-level font-feature-settings (what the Ligature Toggler emits, ~99% support) and the semantic font-variant-ligatures alias (~97% support). Browser-support figures are approximate, current as of 2026.
| Goal | font-feature-settings | font-variant-ligatures |
|---|---|---|
| Standard ligatures on | "liga" 1 | common-ligatures |
| Standard ligatures off | "liga" 0 | no-common-ligatures |
| Contextual alternates on | "calt" 1 | contextual |
| Contextual alternates off | "calt" 0 | no-contextual |
| Discretionary on | "dlig" 1 | discretionary-ligatures |
| Historical on | "hlig" 1 | historical-ligatures |
| Reset to font default | normal | normal |
Cookbook
Copy-paste CSS for each ligature scenario, with a note on whether you can verify it in the Ligature Toggler or need the inspector first.
Standard ligatures: the default, made explicit
ExampleWhat every browser already does — written out so nothing inherits a surprise. Verify in the Toggler's row 2.
/* equivalent forms */
body { font-feature-settings: "liga" 1, "calt" 1; }
body { font-variant-ligatures: common-ligatures contextual; }Turn everything off for code
Example1:1 character-to-glyph mapping. The Toggler's top row shows exactly this state.
code, pre {
font-feature-settings: "liga" 0, "calt" 0;
/* or: font-variant-ligatures: no-common-ligatures no-contextual; */
}Discretionary ligatures for a display font
ExampleOnly meaningful if the font has dlig glyphs — confirm in the Toggler's third row before shipping.
h1, h2 {
font-feature-settings: "liga" 1, "calt" 1, "dlig" 1;
/* or add: font-variant-ligatures: discretionary-ligatures; */
}Historical ligatures (NOT previewable in the Toggler)
ExampleLong-s forms for facsimile work. The Toggler won't render hlig — run the inspector to confirm the font defines it, then apply by hand.
.facsimile {
font-feature-settings: "hlig" 1;
/* or: font-variant-ligatures: historical-ligatures; */
}
/* Verify presence: opentype-features-inspector -> tags includes "hlig" */Enable dlig without liga (unusual but valid)
ExampleTechnically possible, rarely advisable — most discretionary ligatures are meant to complement standard ones.
.odd { font-feature-settings: "liga" 0, "dlig" 1; }
/* Result: no fi/fl fusion, but ct/st decorative joins on.
Usually produces inconsistent typography. */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.
Enabling dlig on a font without discretionary glyphs
No-opfont-feature-settings: "dlig" 1 on a font that defines no dlig lookups does nothing — it is silently ignored, not an error. The Toggler's third row will match its second. Confirm presence with opentype-features-inspector before relying on it.
Disabling rlig to 'simplify' Arabic
Breaks renderingrlig is required — browsers keep it on regardless of your CSS, and where it can be influenced, turning it off breaks Arabic/Indic shaping. There is no legitimate reason to disable required ligatures for those scripts. The Toggler's 'off' row does not touch rlig precisely for this reason.
Using hlig in modern body text
Looks wrongHistorical ligatures render the long-s (ſ), which reads as a broken f in any post-1800 context and can confuse search and screen readers. hlig is off by default; reserve it for explicit period reproductions.
font-feature-settings replaces instead of merging
GotchaDeclaring font-feature-settings: "dlig" 1 on an element overrides the entire inherited value — it does not add dlig to an existing "liga" 1. Restate every feature you want ("liga" 1, "calt" 1, "dlig" 1). The semantic font-variant-ligatures alias is more forgiving to reason about.
clig vs calt confusion
Clarificationclig (contextual ligatures) and calt (contextual alternates) are distinct registered tags, both typically on by default. clig substitutes ligatures by context; calt substitutes alternate glyphs by context. The Toggler bundles calt with liga in its rows; clig is not separately previewed — check it with the inspector.
font-variant-ligatures: none turns off required ligatures too?
Clarificationfont-variant-ligatures: none disables common, discretionary, historical, and contextual ligatures — but required ligatures (rlig) for complex scripts remain on per the spec. So none will not break Arabic shaping, though it strips Latin fi/fl.
Stylistic sets (ss01–ss20) are not ligature features
Out of scopess01–ss20 and salt are alternate-glyph features, not ligatures, and the Ligature Toggler does not preview them. Enumerate and preview them with opentype-features-inspector, which lists every defined tag with ready-to-paste CSS.
Toggling a feature has no visible effect across all rows
Font lacks the featureIf neither the Toggler nor your page shows any change, the font almost certainly does not define that feature. CSS can only switch lookups the font contains — it cannot synthesise ligatures. Run the inspector to see the real tag set before debugging your CSS further.
Frequently asked questions
What's the difference between liga and clig?
liga is standard ligatures (fi/fl/ffi/ffl), applied generally. clig is contextual ligatures — ligatures that apply only in certain surrounding contexts. Both are typically on by default. The Ligature Toggler previews liga (with calt) but not clig separately; use the inspector to check clig.
Is calt a ligature feature?
calt is contextual alternates — it swaps glyphs based on context rather than fusing two into one — but it is grouped with ligatures because it shares the 'on by default, context-driven' behaviour and is what makes connecting scripts join. The Toggler turns it on/off together with liga.
What's hlig and when do I use it?
hlig is historical ligatures — long-s forms (ſt, ſi) and other archaic combinations, off by default. Use it only for facsimiles of pre-1800 texts. The Ligature Toggler does not preview hlig; confirm the font has it with opentype-features-inspector, then enable it by hand.
What's rlig and can I turn it off?
rlig is required ligatures — script-essential joins (Arabic lam-alef, Devanagari conjuncts) that must always render. It is on by default and browsers keep it on regardless of CSS. Do not disable it; doing so (where possible) produces incorrect text in those scripts.
Which ligature features can the Ligature Toggler preview?
Only liga, calt, and dlig, in three fixed rows (off / standard-on / discretionary-on). For hlig, clig, salt, stylistic sets, or any other tag, enumerate the font's features with opentype-features-inspector and write the CSS yourself.
Should I use font-feature-settings or font-variant-ligatures?
font-variant-ligatures (e.g. common-ligatures, no-common-ligatures, discretionary-ligatures) is semantically clearer with ~97% support. font-feature-settings is lower-level with ~99% support and is what the Ligature Toggler outputs. Use the latter for maximum compatibility or finer control; the former for readability.
What's the browser support for these features?
font-feature-settings has ~99% global support; font-variant-ligatures ~97%. Required ligatures and basic standard ligatures are effectively universal because they're tied to the text rendering engine. For older targets, prefer font-feature-settings.
Can I enable just dlig without liga?
Yes, technically: font-feature-settings: "liga" 0, "dlig" 1;. It's unusual — discretionary ligatures normally complement standard ones, so disabling liga while keeping dlig tends to look inconsistent. Most of the time you want both on or both off.
Does any of this modify the font file?
No. Every feature here is toggled via CSS at render time; the glyphs and lookups stay in the font. The Ligature Toggler outputs CSS and an HTML preview, never a rewritten binary. opentype.js can't reliably write OpenType layout tables anyway, which is why CSS is the safe path.
Why does toggling a feature do nothing for my font?
Because the font doesn't define that feature. CSS can only switch lookups the font contains — it can't synthesise ligatures or alternates. Run opentype-features-inspector to see exactly which tags exist before assuming your CSS is wrong.
Does font-variant-ligatures: none break Arabic?
No. none disables common, discretionary, historical, and contextual ligatures, but required ligatures (rlig) for complex scripts stay on per spec. So Arabic shaping survives; only Latin fi/fl fusing is removed.
Are stylistic sets (ss01) covered by this reference?
No — ss01–ss20 and salt are alternate-glyph features, not ligatures. They're out of scope here and not previewable in the Ligature Toggler. Use opentype-features-inspector to discover and preview them. Once your feature CSS is settled, generate the matching @font-face block with font-face-generator.
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.