The previous article covered how to create a custom font with a chosen SVG image. While icon fonts are convenient for visual presentation, they can create accessibility challenges. For example, if the font isn’t loaded — whether due to browser settings or temporary network glitch — users are left with an unappealing missing character symbols. Additionally, screens may announce a cryptic symbol name when reading the web page, confusing users.
This article describes a method of addressing those issues through the use of ligatures. It’s building on the previous tutorial and assumes the custom icon font has already been created.
What are ligatures?
Ligatures originated as a solution to the physical constraints of movable type. In traditional typesetting, the hood of a lowercase ‘f’ often collided with neighbouring characters, such as the tittle of an ‘i’ or the ascender of an ‘l’. This led to awkward visual spacing or even physical damage to the metal type. To resolve this, European foundries cast problematic sequences of characters as a single ligature.1Comparison of an ‘ffi’ sequence set using EB Garamond font as individual characters (left) and as a single ligature (right).
While ligatures are often a stylistic choice in Latin scripts, they are a necessity in certain other writing systems. For example the cursive nature of the Arabic alphabet requires characters to transition seamlessly into one another; without specialized combined forms, the script would appear broken or illegible.2
In digital typography, those necessities have evolved into Glyph Substitution (GSUB) tables of the OpenType format. Rendering engines dynamically swap standard character sequences for unified glyphs as configured by the substitution tables. And because the feature is required by some languages, all engines implement it.
Using ligatures in icon fonts
There’s no prescribed list of ligatures. Font designers have the freedom to define any sequence of characters as a ligature. A proportional serif font might need entries for ‘fi’ and ‘fl’ ligatures, but a monospace font could do without any ligatures.
The idea is to define a substitution which replaces meaningful text with a corresponding icon. For example, mapping ‘Bluesky’ into the Bluesky logo. With such an entry in the substitution table, writing the name of the service renders its logo. In other words, <span class=my-icons translate=no>Bluesky</span> gets rendered as: Bluesky.
- If the web font is loaded correctly, you’ll see a Bluesky logo.
- Otherwise, you’ll see ‘Bluesky’ text, a very reasonable fallback.
- If you’re using a screen reader, you’ll hear ‘Bluesky’.
- If you select the logo, copy and then paste in a text editor, you’ll get ‘Bluesky’ text.
This technique can be adapted for any icon and in some situations simplifies accessibility considerations.
Adding ligatures to icon font
This section continues from the custom icon font created in the previous article. To begin, open the custom font in FontForge.
First, we need to define all the glyphs that are part of the ligature. In our case those are the letters which constitute the word ‘Bluesky’, but to keep things simpler we can define glyphs for all printable ASCII characters. This may sound daunting, but those glyphs will never be rendered and can be blank.
- Make sure that the space character is defined in the font (as suggested in the previous article).
- In the main FontForge window, right click on the space character and select ‘Copy Reference’.
- Select all the printable ASCII characters. Click and drag starting from the exclamation mark (!) until the tilde character (~).
- Right click on one of the selected boxes and pick ‘Paste’.
This will define blank glyphs for all of the characters. It doesn’t make for a font which can be used to write text, but the next step is to map combinations of those letters to the icon we care about.
- Locate the U+1F98B character via ‘View’ → ‘Goto’ (Ctrl+Shift+>).
- Open the ‘Element’ → ‘Glyph Info…’ (Ctrl+I) dialogue and navigate to the ‘Ligatures’ section.
- Click on ‘<New Ligature>’ (truncated to ‘<New L’) in the ‘Subtable’ column and then ‘New Lookup Subtable…’.
- In the dialogue that pops up, select ‘rlig Required Ligatures’ from the ‘<New>’ drop down.3 Click ‘Ok’ to confirm and close the dialogue window.
- A ‘Please name the subtable’ dialogue will pop up. Accept the suggested name by clicking ‘Ok’.
- That brings us back to the Glyph Info dialogue. Now, in the ‘Source Glyph Names’ column enter ‘B l u e s k y’ (with spaces between each letter). The value is a space-separated list of glyph names. For Latin letters, the glyph names are the same as the letters, hence we can spell the word separating each character with a space. Keep in mind that case matters.
- Finally, click ‘Ok’ to close the Glyph Info dialogue.
And that’s it. Generate a WOFF2 file via ‘File’ → ‘Generate Fonts…’ (Ctrl+Shift+G) and it can now be used on a web site:
<style>
@font-face {
font-family: MyIcons;
font-style: normal;
font-weight: 400;
font-display: swap;
/* File path is relative to the CSS file
or absolute to website’s root. */
src: url(/path/to/MyIcons.woff2) format('woff2');
}
.my-icons {
font-family: MyIcons;
}
</style>
<button class=my-icons translate=no>Bluesky</button>Because substitution of the logo is done by the text rendering engine, all other technologies see the ‘Bluesky’ text. Screen readers will read ‘Bluesky’ and if the font doesn’t load, ‘Bluesky’ text will be displayed.
Downsides of using ligatures
Even though the ligature approach addresses some accessibility and fallback issues, it’s not without downsides. If the website is available in multiple languages, all the translated strings must be included in the font. Even worse, the output of machine translation (such as offered by web browsers) is outside of the webmaster’s control so it’s not even possible to enumerate strings that would need to be handled.
In some cases this can be solved with a translate=no attribute like so:
<a href="…">Share on <span class=my-icons translate=no>Bluesky</span></a>
However, while many proper nouns remain unchanged in different languages, it’s improper to use translate=no on other text; furthermore, even proper nouns may need to be declined in some languages. For example, if the font maps the word ‘Settings’ to a gear icon, it would be inappropriate to use <button class=my-icons translate=no>Settings</button> HTML code. A screen reader user who uses machine translation wouldn’t expect the common word to remain unchanged in English.
This is particularly troublesome because we’ve defined all the ASCII characters to be blank. Any text which doesn’t have a corresponding ligature in the font will be rendered as an empty space as demonstrated in the table below:
| Expected | |
|---|
| Actual | Bluesky |
|---|
| No web font | Bluesky |
|---|
| Translated | Błękitne niebo |
|---|
| No ligatures | Bluesky |
|---|
- The top row shows the reference image.
- The second row, how the current browser renders the text ‘Bluesky’ when the custom font is used. It should look exactly like the reference image.
- The third row simulates the custom font not being loaded. It should simply read ‘Bluesky’ in browser’s default serif font.
- The fourth row simulates the text translated into Polish ‘Błękitne niebo’. Only ‘ł’ and ‘ę’ should be visible since they aren’t included in the custom font.
- The last row simulates behaviour if the ligatures aren’t applied at all.
One way to alleviate this issue is by basing the icon font on an existing free software font, grabbing the Latin letters from it. (pyftsubset from the fontTools package can be used to create a font subset). This will at least avoid the catastrophic problem of text being blank.
Conclusion
Using ligatures in a custom icon font is a way to help preserve meaning when the font doesn’t load or a screen reader parses the page. However, this approach doesn’t handle translations seamlessly and in the worst case may lead to ‘invisible’ text.
Ligatures are best suited for brand names that never need translation. If used for this purpose, it’s important to remember to use translate=no attribute on the element with the image. In other cases, using aria-hidden=true and a sr-only CSS class, or a CSS content attribute with alternative text — as described in the previous article — may be a better option.
For completeness, it’s worth noting another approach is to use SVG images directly; either by embedding them in an HTML5 document or using them as a CSS background image. Both approaches are universally supported [1, 2] and in certain situations may be preferred over an icon font.1 Robert Bringhurst. 2004. Harmony & Counterpoint. In The Elements of Typographic Style (3rd ed.). Hartley & Marks, Vancouver, Canada. ISBN 0-88179-206-3. ↩
2 Tom Scott speculates that improper assumption about Arabic ligatures was the cause of ‘Effective Power’ iOS bug that could crash a phone. ↩
3 OpenType includes various subtable names. rlig defines ligatures that are always used by the rendering engine. Other examples include liga which defines standard ligatures such as ‘fi’ (usually enabled by default) and dlig which defines discretionary ligatures such as ‘st’ (usually disabled by default). In CSS, which ligature tables are enabled is controlled by the font-variant-ligatures property. ↩