SymbolFYI

Ligatures in Web Typography: From fi to Modern OpenType Features

Typography on the web has grown far beyond choosing a typeface and setting a font size. Modern fonts ship with hundreds of glyph substitution rules — and ligatures sit at the heart of professional-quality text rendering. Understanding what they are, when to use them, and how to enable them without harming accessibility separates polished editorial typography from the merely functional.

ligature">What Is a Ligature?

A ligature is a single glyph that replaces a sequence of two or more characters. When the characters f and i are set next to each other in most serif fonts, the hook of the f physically collides with the dot of the i. Rather than display an awkward visual clash, typographers long ago designed a combined glyph — fi — that merges the two into a harmonious shape.

The result is invisible to most readers. That invisibility is the point: ligatures exist to eliminate friction in the reading experience, not to draw attention to themselves.

Historical Origins in Metal Type

In the era of movable metal type, a ligature was a single physical slug bearing two (or more) characters. The typesetter would reach for the fi block just as deliberately as reaching for the letter f alone. Common ligatures were cast as standard pieces: fi, fl, ffi, ffl, and the æ (ae) digraph that survives today in words like "aesthetic."

Phototypesetting and early digital fonts broke this tradition. Many fonts shipped with no ligature support at all, and for years web typography simply accepted the collision artifacts. OpenType changed everything.

OpenType and Glyph Substitution

The OpenType font format encodes ligature rules as Glyph Substitution (GSUB) lookup tables. When the rendering engine processes the text "finish," it checks the font's GSUB table and discovers a rule: replace the glyph sequence [f][i] with the single ligature glyph [fi]. This substitution happens silently at render time.

OpenType divides ligatures into several feature tags:

Feature Tag Name Description
liga Standard Ligatures fi, fl, ffi, ffl — enabled by default in most contexts
dlig Discretionary Ligatures ct, st, Th — ornamental, off by default
hlig Historical Ligatures Long-s forms, archaic combinations
calt Contextual Alternates Context-sensitive glyph swaps (not strictly ligatures)
rlig Required Ligatures Ligatures required for correct rendering (Arabic, Devanagari)

Standard Ligatures: The Essential Set

The four standard Latin ligatures cover the most common problematic sequences:

  • fi — f followed by i (the classic)
  • fl — f followed by l
  • ffi — f followed by f followed by i
  • ffl — f followed by f followed by l

Some fonts also include fb, fh, fj, fk, and ft as standard ligatures, though these are less universal. The German sharp S ß (U+00DF) is itself a historical ligature of the long-s and z, now encoded as its own character.

Enabling Standard Ligatures in CSS

In modern browsers, standard ligatures are often on by default for body text when the font supports them. The explicit CSS is:

/* Modern shorthand */
body {
  font-variant-ligatures: common-ligatures;
}

/* Equivalent low-level property */
body {
  font-feature-settings: "liga" 1, "calt" 1;
}

/* Explicitly disable ligatures (for code, data, etc.) */
code, pre, kbd, samp {
  font-variant-ligatures: no-common-ligatures;
}

The font-variant-ligatures property is the preferred high-level interface. Use font-feature-settings only when you need simultaneous control over multiple OpenType features or when targeting the small set of browsers that do not support the variant shorthand.

Discretionary Ligatures

Discretionary ligatures are the ornamental tier — beautiful, but intentional. Common examples in historically-inspired typefaces include:

  • ct — a stylized join of c and t
  • st — a calligraphic swoosh connecting s and t
  • Th — an uppercase T whose crossbar extends into the h
  • sp, si, sk — in some italic faces

These are appropriate for display headings, pull quotes, and editorial design. They are rarely appropriate for body text, where they can read as affectations.

/* Enable discretionary ligatures for display headings */
h1, h2 {
  font-variant-ligatures: common-ligatures discretionary-ligatures;
}

/* Low-level equivalent */
h1, h2 {
  font-feature-settings: "liga" 1, "dlig" 1;
}

unicode-precomposed-ligatures">Unicode Precomposed Ligatures

Unicode encodes several precomposed ligature characters in the Alphabetic Presentation Forms block (U+FB00–U+FB06):

Character Code Point Sequence Represented
U+FB00 ff
U+FB01 fi
U+FB02 fl
U+FB03 ffi
U+FB04 ffl
U+FB05 long s + t
U+FB06 st

These code points exist primarily for round-trip compatibility — they appear in legacy documents and in certain copy-paste scenarios. You should not author content using these characters. Here is why:

  1. Search breaks. A user searching for "office" will not find "office" (U+FB03 in the middle) because the precomposed ligature does not match the character sequence.
  2. Accessibility breaks. Screen readers may pronounce U+FB01 as "fi ligature" rather than the two letters, or skip it entirely.
  3. Copy-paste inconsistency. Some PDF renderers embed precomposed ligatures when the user copies text, producing garbled results in search boxes.
  4. Unicode normalization. NFC and NFD normalization do not decompose these characters — only NFKC/NFKD do. This means many normalization passes leave them intact.

The correct approach is to keep your source text as ordinary ASCII characters (f + i) and let the CSS/font pipeline handle the visual substitution.

Analyze which characters appear in your content with our Character Counter tool — it will flag precomposed ligatures so you can replace them with their canonical sequences.

CSS font-feature-settings: The Full Control Layer

When font-variant-ligatures is not expressive enough, font-feature-settings gives you direct access to any OpenType feature tag by its four-letter code:

.body-text {
  /* Standard ligatures and contextual alternates */
  font-feature-settings: "liga" 1, "calt" 1;
}

.display-heading {
  /* Add discretionary ligatures */
  font-feature-settings: "liga" 1, "calt" 1, "dlig" 1;
}

.data-table {
  /* Disable ligatures, enable tabular numbers and lining figures */
  font-feature-settings: "liga" 0, "calt" 0, "tnum" 1, "lnum" 1;
}

.small-caps-section {
  /* Small caps with standard ligatures */
  font-feature-settings: "liga" 1, "smcp" 1;
}

A note on inheritance: font-feature-settings does not cascade intelligently. If a parent element sets font-feature-settings: "liga" 1, "tnum" 1 and a child sets font-feature-settings: "dlig" 1, the child loses the parent's liga and tnum settings. Always specify every feature you want explicitly on each element, or use CSS custom properties to compose them:

:root {
  --font-features-base: "liga" 1, "calt" 1;
  --font-features-display: "liga" 1, "calt" 1, "dlig" 1;
}

body {
  font-feature-settings: var(--font-features-base);
}

h1 {
  font-feature-settings: var(--font-features-display);
}

Checking Font Support

Not every font ships with ligature glyphs. Before writing CSS to enable ligatures, confirm the font actually contains them. Practical approaches:

  • Browser DevTools → Computed → Rendered Fonts: inspect the glyph count and OpenType features the browser reports for a given element.
  • FontDrop.info or Wakamai Fondue: drag a font file to see every OpenType feature it contains.
  • Variable fonts: many variable fonts expose OpenType features as axes or named instances — check the font's documentation.

If you are loading a web font via @font-face, request the liga subset explicitly if the foundry offers feature-split files. This avoids loading ligature glyphs for contexts (like code blocks) that do not need them.

Accessibility Considerations

Ligatures occupy an interesting accessibility gray zone.

Font-rendered ligatures (CSS approach) are generally safe. Because the underlying text remains f + i, assistive technologies read the characters correctly. Screen readers, search engines, copy-paste, and spell-checkers all see the canonical character sequence.

Precomposed Unicode ligatures are problematic for the reasons outlined above — avoid them in content.

Discretionary ligatures warrant caution in body text. Some dyslexic readers find that unusual glyph shapes increase cognitive load. If your audience includes users with dyslexia, consider applying discretionary ligatures only to headings and using a readable, ligature-free face (or font-variant-ligatures: none) for body copy.

WCAG does not address ligatures directly, but Success Criterion 1.4.8 (Visual Presentation) requires that users be able to override text presentation. If a user's browser stylesheet disables custom fonts, your ligature CSS has no effect — which is exactly the correct behavior.

/* Respects user preference for reduced motion/simplicity */
@media (prefers-reduced-motion: no-preference) {
  /* discretionary ligatures as cosmetic enhancement */
  .display-text {
    font-variant-ligatures: discretionary-ligatures;
  }
}

Practical Recommendations

Context Ligature Setting
Body text (serif) common-ligatures (browser default is usually fine)
Body text (sans-serif) Optional — many sans fonts have minimal ligature benefit
Display headings common-ligatures discretionary-ligatures
Code blocks / monospace no-common-ligatures
Data tables / numbers no-common-ligatures
Logotypes / branding Consider SVG with explicit path control instead

For code fonts, ligature preferences are culturally split. Fonts like Fira Code and Cascadia Code offer programming ligatures (arrows → ->, =>, comparison operators) that many developers enjoy. These are calt-driven contextual alternates rather than traditional ligatures — enable them with font-feature-settings: "calt" 1 if your code font supports them, but let users choose via a toggle if you are building a code-heavy product.

Browser Support

font-variant-ligatures has full support across all modern browsers. font-feature-settings is equally well-supported. The only caveat is that ligature rendering ultimately depends on whether the operating system's text rendering engine honors the OpenType GSUB table — and on macOS, Windows, and Linux with modern HarfBuzz or DirectWrite, it does.


Next in Series: Part 2 covers the full universe of Unicode whitespace characters — non-breaking spaces, thin spaces, zero-width joiners, and the CSS properties that control how they collapse. Whitespace Characters in Web Development: Beyond the Space Bar

Похожие символы

Связанные термины глоссария

Связанные инструменты

Другие руководства