“Ligature” in Mobile Development — Basics and Application of Character Connections

Author: IT Sectr Published: 2026-07-24 Reading time: 9 min

Ligature is a graphic combination of two or more characters into a single typographic symbol to improve readability and text aesthetics. Classic examples include the pairs fi, fl, ff, ffi — where the protruding element of the letter f merges with the adjacent character, preventing a visual conflict. In modern mobile and web development, ligatures are controlled at the font level through OpenType features and are supported on iOS, Android, and in browsers. According to MDN Web Docs, the CSS property font-variant-ligatures allows developers to enable and disable various types of ligatures: standard, discretionary (decorative), and contextual.

Key Takeaways

  • Ligature — a combination of several characters into one graphic element to improve readability
  • Standard ligatures (fi, fl, ff) are enabled by default in professional fonts
  • Discretionary ligatures (ct, st, sp) are decorative and enabled optionally
  • On iOS, controlled via UIFontDescriptor with featureSettings for OpenType features
  • In programming fonts, ligatures combine operators: !=, >=, ->, =>

What Is a Ligature in Typography

Ligature is a typographic technique where two or more characters are replaced by a single specially designed glyph. The main purpose of a ligature is to eliminate visual conflicts between adjacent characters and improve text perception. In the pair fi, for example, the upper protrusion of the letter f collides with the dot of the letter i — the ligature merges them into a single elegant symbol.

Ligatures are divided into mandatory (standard) and optional (decorative). Standard ligatures are pairs found in most fonts: fi, fl, ff, ffi, ffl. They are considered necessary for quality typography and are enabled in a font by default. According to the OpenType Specification, standard ligatures are coded with the ‘liga’ tag and must be present in any professional font.

Visual effect: without a ligature, the pair fi looks like two separate characters with an undesirable collision. With a ligature, it looks like one harmonious symbol. The difference is especially noticeable in large sizes (headings, logos) and in text with many repeated pairs (for example, in German with häufigen Buchstabenkombinationen).

Types of Ligatures: Standard, Discretionary, Contextual

OpenType fonts support several types of ligatures, each with its own tag and purpose. Standard (tag ‘liga’) are mandatory ligatures enabled by default in a font. They improve readability and should not be disabled without good reason. They include the pairs fi, fl, ff, ffi, ffl and others specific to the typeface.

Discretionary ligatures (tag ‘dlig’) are decorative ligatures that are optionally enabled at the discretion of the designer. They are not required for readability and are used for text styling: ct, st, sp, Th, Qu and others. Discretionary ligatures are often found in fonts with a historical or calligraphic character — for example, in Garamond or Adobe Caslon. Caution: an excess of discretionary ligatures impairs readability, especially for users with dyslexia.

Contextual ligatures (tag ‘clig’) are ligatures that depend on the character’s surrounding context. They are applied only under certain conditions: if the character is at the start or end of a word, after a specific punctuation mark, and so on. Contextual ligatures are an advanced OpenType feature not supported by all fonts. Historical ligatures (tag ‘hlig’) are outdated ligatures that mimic the style of old printed books (long s, &). They are rarely used, only in historical or stylized texts.

Ligature TypeOpenType TagExamplesDefault
Standardligafi, fl, ff, ffiEnabled
Discretionarydligct, st, sp, ThDisabled
ContextualcligDepends on positionEnabled
Historicalhliglong s, ct (historical)Disabled

Rare types: there are also ligatures for mathematical typesetting (tag ‘dlig’ for mathematical symbols) and initial ligatures (drop caps), but they are not used in interface typography and are only supported by specialized fonts.

History of Ligatures: From Metal Type to Digital Fonts

Ligatures appeared long before digital typography — back in the era of metal type in the 15th century. The first printer Johannes Gutenberg used ligatures to save space in the lines of his 42-line Bible. Each type piece was a physical metal block, and combining two characters into one piece saved lead and simplified typesetting. The standard ligatures fi, fl, ffi, ffl are a legacy of that era.

In phototypesetting (20th century), ligatures lost their practical function but remained as an aesthetic element of quality typography. Font designers continued to include ligatures in their typefaces as they became a mark of professionalism and attention to detail. In the era of digital fonts (PostScript, TrueType), ligatures were implemented as separate glyphs with programmatic substitution.

OpenType (1996) was a revolution: it introduced the GSUB mechanism (Glyph Substitution Table), which automatically replaces a sequence of characters with a ligature without user intervention. GSUB supports contextual substitutions, conditions, and fallback variants. This allowed fonts to have hundreds of ligatures that can be enabled/disabled via tags. Modern fonts — SF Pro, Roboto, Inter — support OpenType ligatures on all platforms: iOS, Android, macOS, Windows, web.

Implementing Ligatures on iOS via UIFontDescriptor

On iOS, ligatures can be controlled through UIFontDescriptor using the featureSettings attribute. This attribute accepts an array of dictionaries, each describing one OpenType feature. Controlling ligatures through a descriptor is the only way to manage individual types of ligatures without affecting other OpenType features of the font.

swift
let descriptor = UIFontDescriptor.preferredFontDescriptor(
    withTextStyle: .body
)

// Enable discretionary ligatures
let ligatureDescriptor = descriptor.addingAttributes([
    .featureSettings: [
        [
            UIFontDescriptor.FeatureIdentifier: kLigaturesType,
            UIFontDescriptor.TypeIdentifier: kCommonLigaturesOnSelector
        ]
    ]
])

let font = UIFont(
    descriptor: ligatureDescriptor,
    size: 17
)

NSAttributedString also supports the ligature attribute: NSNumber. A value of 0 disables all ligatures, 1 enables standard (default), 2 enables all (including discretionary). However, the ligature attribute in NSAttributedString does not provide fine-grained control over ligature types — only global enable/disable. For selective control, use UIFontDescriptor with featureSettings.

SwiftUI does not provide a direct modifier for ligatures. To control ligatures in SwiftUI, create a UIFont with the desired settings via UIFontDescriptor and use it through Font(descriptor:size:). Alternatively, use AttributedString (iOS 15+) with the .ligature attribute for NSMutableAttributedString in AppKit/UIKit views.

swift
// SwiftUI with custom ligatures via AttributedString
var attributedText: AttributedString {
    var text = AttributedString(
        "Effective typography with ligatures"
    )

    // Enable all ligatures for whole text
    text.ligature = .all

    return text
}

var body: some View {
    Text(attributedText)
}

Controlling Ligatures in CSS and Android

On the web, ligatures are controlled through the CSS property font-variant-ligatures, which accepts keywords: common-ligatures (standard enabled), no-common-ligatures (standard disabled), discretionary-ligatures, no-discretionary-ligatures, contextual, no-contextual. The property is supported by all modern browsers since 2015. By default, browsers enable standard and contextual ligatures.

css
/* Disable all ligatures for monospace */
code, pre {
    font-variant-ligatures: none;
}

/* Enable discretionary ligatures for headings */
.title-fancy {
    font-variant-ligatures:
        common-ligatures
        discretionary-ligatures
        contextual;
}

Android does not have a direct API for controlling ligatures. On Android, ligatures are controlled at the font level and by the Minikin rendering engine (Android 10+). If the font contains an OpenType GSUB table, ligatures are applied automatically. To disable ligatures on Android, custom Typefaces created via Typeface.Builder or text preprocessing (character replacement before display) are used. Limitation: on Android below 10, ligatures may not work correctly with some TTF fonts.

Low-level control: on all platforms, you can use CSS @font-face with the font-feature-settings parameter for OpenType features. This method provides access to any OpenType tags, including ligatures. Example: font-feature-settings: ‘liga’ 1, ‘dlig’ 1. However, font-feature-settings is a low-level syntax, and MDN recommends using font-variant-ligatures as a higher-level alternative.

Ligatures in Programming Fonts

A special class of ligatures — programming ligatures — combine operators and code symbols into more readable glyphs. Programming fonts such as Fira Code, JetBrains Mono, Cascadia Code include discretionary ligatures for common operators: != (becomes ≠), >= (becomes ≥), -> (becomes an arrow), => (becomes a fat arrow), === (becomes triple equals).

Programming ligatures are a controversial topic in the developer community. Proponents argue that ligatures speed up code reading because operators are perceived as single concepts. A study (Kera et al., PLATEAU 2020) showed that ligatures do not affect code reading speed, but 67% of developers subjectively like them. Opponents of ligatures note that they distort code: the != symbol in the editor displays as ≠, but in the text file it is stored as two characters, which can be confusing during collaboration.

Technical implementation: programming ligatures are discretionary OpenType ligatures (tag ‘dlig’). They are disabled by default and enabled in the code editor through font settings. Developers using such fonts must configure the editor to enable discretionary ligatures. In terminals (iTerm2, Windows Terminal), ligatures are also supported through font settings. Compatibility: not all editors and terminals support OpenType ligatures. Before choosing a font, check compatibility with your toolchain.

kotlin
// Example: operator ligatures in JetBrains Mono
val isEqual = a != b  // != rendered as ≠
val arrow = x -> x + 1  // -> rendered as →
val range = 1 .. 10  // .. rendered as range

Popular fonts with programming ligatures: Fira Code (the first mass-market font with ligatures, 2015), JetBrains Mono (optimized for code reading, 2020), Cascadia Code (from Microsoft, 2019), Iosevka (customizable font with a modular ligature system). Each font has its own set of ligatures — from 50 to 150+ substitutions. Recommendation: start with Fira Code or JetBrains Mono — they have the best support in editors and IDEs.

Frequently Asked Questions

How to disable ligatures on iOS?

Set attribute ligature = 0 in NSAttributedString or use UIFontDescriptor with kCommonLigaturesOffSelector. In SwiftUI, create an AttributedString with .ligature = .disabled.

How is a ligature different from kerning?

Ligature replaces two characters with one new glyph, changing their shape. Kerning adjusts the space between characters without changing their shape. Ligature is a graphic connection; kerning is a spatial adjustment.

Does Android support ligatures?

Yes, starting from Android 10 (API 29). Minikin rendering fully supports OpenType GSUB tables. On earlier versions, support depends on the device manufacturer and the font engine version.

Should I use ligatures in an app interface?

For body text, standard ligatures (fi, fl, ff) are sufficient — they improve readability and are invisible to the user. Use discretionary ligatures only in headings or decorative text, as they reduce readability in long texts.

Why don’t ligatures display in all code editors?

The editor must support the OpenType ‘dlig’ tag and be able to render discretionary ligatures. VS Code, IntelliJ IDEA, Sublime Text support them. Older terminals and editors (nano, vim without GUI) do not. Check your editor settings: enable font-ligatures.

Summary

  • Ligature — a combination of several characters into one graphic element for aesthetics and readability
  • Standard ligatures (fi, fl, ff) are enabled by default in professional fonts
  • Discretionary ligatures (ct, st, sp) are decorative, enabled via CSS font-variant-ligatures
  • On iOS, controlled via UIFontDescriptor with featureSettings or the ligature attribute
  • On the web, controlled via font-variant-ligatures or font-feature-settings (‘liga’, ‘dlig’)
  • Programming ligatures (Fira Code, JetBrains Mono) combine operators: !=, >=, ->
  • For interface typography, use only standard ligatures — discretionary ligatures reduce readability

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also