AB#1608258 — Disable font ligatures across all typography classes and themes #298
jason-capsule42
started this conversation in
Post Mortems
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AB#1608258
Executive Summary
Alaska Air Group brand guidelines prohibit the use of "ligatures" — decorative glyphs that fuse adjacent letter pairs (for example, the joined "fl" in "flight search"). Our shared typography styles were letting browsers render these ligatures by default, which put on-screen text out of compliance with the brand standard across every product using the design system.
This change turns ligatures off — completely — for all of the modern typography styles and for the base page text, across every brand theme (Alaska, Alaska Classic, Atmos, and Hawaiian — including the Chronicle Display font). It also replaces an older, weaker rule that only suppressed some ligatures on base text with a single, consistent brand-wide policy. Text now renders with plain, unjoined letters everywhere, bringing the typography back into brand compliance with no other visual change.
Tracking ticket: AB#1608258.
The Problem
Text rendered through the design system's typography styles was displaying ligatures — connected letter combinations that the browser substitutes automatically for certain pairs like "fl" or "fi". Alaska Air Group's brand guidelines forbid ligatures across all brand typefaces, so any surface using these styles was technically out of brand compliance.
There were two gaps. First, an older rule existed that suppressed some ligatures on base page text, but it never reached the newer typography classes that most products actually use. Second, that older rule only turned off the "common" subset of ligatures rather than the full set the brand requires disabled — so even where a rule did apply, it applied the wrong (weaker) policy. The two places that set ligature behavior therefore disagreed with each other, producing inconsistent, non-compliant text rendering.
Root Cause
Ligature behavior was set in different places with different, inconsistent values, and the brand policy lived nowhere as a single source of truth:
font-variant-ligaturesdeclaration, so browsers fell back to their default of rendering common ligatures:generate-body-classesin _body.scss — produces the.body-*classes.generate-fluid-type-classesin _fluid-type.scss — the shared mixin behind the.display-*,.heading-*, and.accent-*classes.html/body/.baseTypeselectors (_base.scss) and the Auro Classic legacy base rule (_auro-classic.scss) did setfont-variant-ligatures, but to the weakerno-common-ligatures. This neither cascaded to the generated classes nor disabled the full ligature set the brand requires.The result was a split policy: base text used
no-common-ligatureswhile the (unset) generated classes rendered whatever the browser defaulted to, with no shared definition of the correct value.The Fix
Introduced a single source of truth for the brand ligature policy and routed every type-setting rule through it:
auro_noLigaturesmixin in _noLigatures.scss that emitsfont-variant-ligatures: none;. This mixin is now the one place the policy is defined.@if/@elsebranches so both branches inherit it:.body-*.display-*,.heading-*,.accent-*no-common-ligaturesand now emitnone, aligning base text with the generated classes on the stronger, brand-correct value.The committed
distbundles were regenerated so the rule appears in the shipped typography classes and in every global theme bundle (Alaska, Alaska Classic, Atmos, Hawaiian, and legacy Auro Classic). A regression test, typeLigatures.spec.js, compiles both real class generators (the CSS-variable and the Alaska static-fallback variants) and asserts every class family emitsfont-variant-ligatures: noneand never a ligature-enabling value. The assertion matches the declaration anywhere within each generated class block rather than at a fixed position, so an unrelated reorder of declarations no longer breaks the guard.Why This Works
font-variant-ligatures: nonedisables the entire ligature set (common, discretionary, historical, and contextual), which is stronger than the legacyno-common-ligaturesand is exactly what the brand standard requires. Defining that value once in theauro_noLigaturesmixin removes the duplication that let base styles and generated classes drift apart, so there is now a single definition that every type rule shares.Because the declaration is emitted directly on the shared, theme-agnostic typography classes — not on any single theme's tokens — it applies regardless of which theme's CSS custom properties are active. That is why one change covers every theme, including the Hawaiian
displaycategory that uses Chronicle Display. Placing the declaration outside the@if/@elsein each mixin guarantees it is present whether a consumer uses the CSS-custom-property build or the static-fallback build.The change is additive and non-breaking: it introduces one new property (and strengthens one existing one from
no-common-ligaturestonone) with no impact on font family, weight, size, line-height, or letter-spacing, so no existing layout or API is affected.Outcome
Ligatures no longer render anywhere in the modern typography system or in base page text, on any brand theme. Text such as "flight search" now displays with separated letters, restoring brand compliance. Base text and generated typography classes now share one consistent policy instead of two disagreeing rules. The change ships in the built stylesheets consumed by all downstream products, and a regression test guards against the rule being dropped in the future. All 72 tests pass and linting is clean.
Ticket Completeness
The ticket ("Disable font ligatures across all typography classes and themes") carried no written description or acceptance criteria in Azure DevOps, so completeness is assessed against the ticket title and the requirements established in the work itself. All were resolved.
Resolved:
.body-*,.display-*,.heading-*, and.accent-*via the sharedauro_noLigaturesmixin (verified in the builtclasses.cssand by the regression test).noneinstead of the weakerno-common-ligatures, matching the generated classes; the policy is defined once inauro_noLigaturesso base text and typography classes can no longer disagree.generate-body-classes(_body.scss) andgenerate-fluid-type-classes(_fluid-type.scss) both route through the mixin.Learnings
noneon generated classes,no-common-ligatureson base rules); consolidating it into theauro_noLigaturesmixin removed the drift and made "the brand ligature policy" a single, greppable definition.no-common-ligaturesonhtml/body/.baseType) gave a false sense of coverage: it neither cascaded to the generated classes nor disabled the full ligature set. When adding a compliance rule, verify it reaches the classes consumers actually apply and uses the correct value everywhere.Iterations That Didn't Work
font-variant-ligatures: none;to the two class generators and left the base rules onno-common-ligatures. This fixed the generated classes but duplicated the value and left base text on the weaker, inconsistent policy. It was superseded by extracting the sharedauro_noLigaturesmixin and routing the base rules through it too, so the whole system is governed by one definition.All reactions