Tagged “fonts”
Modern Font Stacks
System font stack CSS organized by typeface classification for every modern OS. The fastest fonts available. No downloading, no layout shifts, no flashes — just instant renders.
This is a great resource for when you want a particular style of font (workhorse sans-serif, grotesque, monospace, display slab serif etc) and to favour a system font rather than a custom font to get performance and simplicity benefits allied to having many weights and characters natively available.
To grab a stack, just copy the font-family
declaration from its card then paste that into your CSS.
To easily preview how custom text of your choice would look in each stack, use the handy form at the top of the page.
Aside from its utility I also love this page as a learning resource. For each font category, the font and weight your browsing context is currently using is highlighted in blue with a solid underline, while those available but not currently in use and those unavailable are also separately highlighted. This gives you even more information than Firefox’s font panel or the WhatFont extension for Chrome.
Notes:
- if you notice support for a font when you wouldn’t expect it (because your OS doesn’t include it), it’s worth remembering that you may have previously installed it locally. This was the case for me with Inter in the Neo-Grotesque stack. You can click the little “info” icon links beside each stack’s title for detailed info on that stack including which OSs use which font
- the declaration
font-family: system-ui, sans-serif
as a means of serving the Operating System’s default sans-serif seemed suspiciously simple given the complicated iterations I’ve seen previously. However after some digging I confirmed that Firefox added support forsystem-ui
a few years ago so previous, verbose alternatives are no longer necessary.
How I subset web fonts
On my personal website I currently use three web fonts from the Source Sans 3 group: regular, italic and semibold. I self-host my fonts because that’s a good practice. Additionally I use a variety of special characters to add some typographic life to the text.
When self-hosting it’s important from a performance perspective to minimise the weight of the font files your visitors must download. To achieve this I subset my fonts so as to include only the characters my pages use but no more. Here’s how I do it.
Note: to follow these steps, you’ll need to install glyphhanger. The Github page includes installation and usage guidelines however there are a few common installation pitfalls so if you’re on a Mac and run into trouble I recommend checking Sara Soueidan’s How I set up Glyphhanger on macOS to get you back on track.
For the purposes of this walkthrough I’ll assume you have a directory in your application named fonts
.
Start by deleting any existing custom font files from your application’s fonts
directory.
Run your site locally in an incognito browser window. For my Eleventy-based site, I run npm run serve
which serves the site at http://localhost:8080
.
Visually check your locally-running site to ensure that now you’ve deleted your web fonts it’s no longer serving them and is instead rendering text using system fonts.
Visit the source page for your custom fonts—in my case the Github repository for Source Sans 3. Download in .ttf
format the latest versions of the fonts you need and place them in your fonts
directory. For me these are:
- Regular,
- Italic; and
- Semibold.
You’ll notice the large file sizes of these .ttf
files. For example Source Sans 3’s Regular.ttf
font is 299 kb.
At the command line, cd
into your fonts
directory.
Now we’re going to run glyphhanger on one font at a time. This fantastic tool will intelligenty crawl your website to check which glyphs are currently in use for the specified weight then include those in a subset file which it outputs in .ttf
, .woff
and .woff2
formats. I use glyphhanger’s spider
option so that it spiders multiple pages (rather than just one) at a time, meaning that it is more likely to catch all the special characters I’m using.
glyphhanger http://localhost:8080/posts/ --subset=SourceSans3-Regular.ttf --spider-limit=0
If all went well you should see output like this:
U+20-23,U+25-2A,U+2C-5B,U+5D,U+5F,U+61-7D,U+A9,U+B7,U+BB,U+D7,U+E9,U+F6,U+200B,U+200E,U+2013,U+2014,U+2018,U+2019,U+201C,U+201D,U+2026,U+2122,U+2190,U+2192,U+2615,U+FE0F
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.ttf (was 292.24 KB, now 46.99 KB)
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.zopfli.woff (was 292.24 KB, now 22.14 KB)
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.woff2 (was 292.24 KB, now 17.77 KB)
The .woff2
subset file has reduced the file size from 299 kb to 17.77 kb which is pretty impressive!
Update your CSS to point at the new woff2
and woff
subset files for your font. My updated CSS looks like this:
@font-face {
font-family: Source Sans Pro;
src: url(/fonts/sans/SourceSans3-Regular-subset.woff2) format("woff2"),
url(/fonts/sans/SourceSans3-Regular-subset.zopfli.woff) format("woff");
font-weight: 400;
font-display: swap;
}
Check your locally running application to ensure that the relevant text (body copy in this case) is now being served using the web font rather than fallback font, and that special characters are also being served using the web font.
I’ll usually crack open the Fonts panel in Firefox’s DevTools and check that, amongst other things, my pagination links which use the rightward pointing arrow character (→ or unicode U+2192
) are rendering it using Source Sans Pro and not sticking out like a sore thumb by using Helvetica due to the glyph not being present in the subset.
Delete the .ttf
file you started with and any .ttf
subsets generated, because you won’t serve files in that format to your website visitors.
Repeat the glyphhanger subsetting and CSS updating process for any other weights (italic, semibold) or custom fonts you want to subset.
One last handy tip: if there’s a weight for which I don’t need a fancy character set (for example the Semibold I use for headings), I might just grab default latin charset woff
and woff2
files from the Google Webfonts Helper. The files tend to be small and well-optimised and this can save a little time. (This is only possible if the font is available from Google Fonts which is true in the case of Source Sans 3.)
Font Match
A font pairing app that helps you match fonts – useful for pairing a webfont with a suitable fallback. You can place the fonts on top of each other, side by side, or in the same line. You can adjust your fallback font’s size and position to get a great match.
Font style matcher
If you’re using a web font, you're bound to see a flash of unstyled text (or FOUC), between the initial render of your websafe font and the webfont that you’ve chosen. This usually results in a jarring shift in layout, due to sizing discrepancies between the two fonts. To minimize this discrepancy, you can try to match the fallback font and the intended webfont’s x-heights and widths. This tool helps you do exactly that.
How to optimise performance when using Google-hosted fonts (on CSS Wizardry)
A combination of asynchronously loading CSS, asynchronously loading font files, opting into FOFT, fast-fetching asynchronous CSS files, and warming up external domains makes for an experience several seconds faster than the baseline.
Harry Roberts suggests that, while self-hosting your web fonts is likely to be the overall best solution to performance and availability problems, we’re able to design some fairly resilient measures to help mitigate a lot of these issues when using Google Fonts.
Harry then kindly provides a code snippet that we can use in the <head>
of our document to apply these measures.
Webfont loading strategies
When it comes to webfonts, if you want to serve an accessible and high performance experience across device types it’s not as straightforward as just specifying your fonts in CSS then hoping for the best.
We likely have goals such as the following:
- avoid Flash of Invisible Text (FOIT). Flash of Unstyled Text (FOUT) is preferable.
- provide a good experience for users on slow connections.
- while favouring FOUT over FOIT, take care to minimise jarring reflows.
Browser support is also a factor. For example font-display: swap
seems like a great option however as Chris Ferdinandi pointed out in Why use the vanilla JS fontfaceset.load method instead of CSS font-display: swap it doesn’t have comprehensive mobile support. Since mobile users are regularly on slower connections they are likely to be hit hard by load times incurred due to custom fonts, therefore a solution which doesn’t cater to them is less attractive.
I tend to go for FOUT with a class—an approach that is reliable and good enough for most use cases. The idea is to use JavaScript to detect when a font has loaded successfully, and only then add a fonts-loaded
class to the HTML element causing CSS scoped to that class to take effect. To date I’ve handled the detection using Bram Stein’s Font Face Observer to benefit from greater browser support (it handles IE) than I’d get using the native FontFaceSet.load()
however I expect to gravitate toward the native API solution soon.
I’ve also experimented with pre-loading fonts in the <head>
in combination with font-display: swap
on this, my personal website. This was inspired by the font-loading approach Zach Leatherman took with CSS-Tricks. However due to the fonts I’m using right now I’d currently be preloading too heavy a load for slow connections (thus temporarily blocking initial page render) so I’d need to go further and introduce two-stage loading into that mix.
I’ve also found that your choice of font and font host can really influence (constrain) the flexibility and options you have for font features and loading.
font-display: swap
can only be applied on a self-hosted font or one hosted on Google Fonts (via a special querystring parameter on the font URL). So this CSS-only approach is not available with (for example) fonts hosted on Adobe Fonts or fonts.com.link rel=preload
is only available for self-hosted fonts because you need to be able to rely on the URL not changing, and you can’t rely on that for externally hosted fonts.- You might want to use the opentype features of a font but are they available in your font hosting/loading solution? Some of the fonts hosted on Google Fonts have nice OpenType features but by default these features are pruned out.
- Getting access to the source
.ttf
file is beneficial as it allows you to subset the font, including special features and characters but also cutting out any your website does not require. Google Fonts have an open license which means we don’t need to lean on their hosting but can instead download fonts in.ttf
, subset them (I use Glyphhanger) and self-host them.
This is all a work in progress and I’m still working this stuff out.
Definitive web font @font-face syntax
These days, whenever I’m about to use a web font on a new site I generally find myself running a google search for the latest “definitive @font-face
syntax” that covers all modern browser/device needs.
For a long time I headed straight for Paul Irish’s Bulletproof @font-face Syntax but I noted a few years back that he’d stopped updating it.
When buying web fonts from type foundries such as Fontsmith the foundries do tend to provide their own guidelines. However, I’m not convinced that these are sufficiently cross-platform compatible.
Recently I’ve been reading Flexible Typesetting by Tim Brown and in it he recommends Webfont Handbook by Bram Stein. That’s now next on my reading list, however in the meantime I found an excerpt on A List Apart which specifically covers the best modern @font-face
syntax.
Based on Stein’s advice, here’s what I’m now using.
@font-face {
font-family: Elena;
src: url(elena.woff2) format("woff2"),
url(elena.woff) format("woff"),
url(elena.otf) format("opentype");
}
Soon, when all browsers support the woff
format we’ll be able to reduce this to simply:
@font-face {
font-family: Elena;
src: url(elena.woff2) format("woff2"),
url(elena.woff) format("woff");
}
See all tags.