IKEA’s web fonts
For the longest time, if you mentioned IKEA to a designer they’d say the word Futura. We’d used the Futura font since only a few years after the company was founded — the first use I can find is in the 1954 catalogue — and it had become pretty synonymous with our branding.
This all changed however in 2010. As Futura was only a default installed font on Macs, our digital branding was lagging behind our physical work, defaulting to Arial or whatever generic sans-serif font was around on other systems. What we needed was something that would be automatically available on every computer and device we might appear on, and also serve the physical space. Enter Verdana!

Verdana was designed for Microsoft as one of their web fonts: fonts designed to look good on low resolution (at least, compared to print) displays. It was given away as part of their “core fonts for the web” project and due to a favourable redistribution licence quickly arrived as a default install on most devices. This meant that on web we could simply start our font stacks with Verdana
, pick a couple of reasonable fallbacks, and get IKEA branding effectively for free. Everyone wins.
Or at least that was the plan, but there turned out to be a problem that developed over time. While Verdana was broadly available over our users’ devices, it only had support for latin, greek and cyrillic writing systems. This was in a time when IKEA was going through fairly rapid expansion, adding non-latin markets like Thailand in 2011 and South Korea in 2014, and we also needed to support the existing Arabic-speaking markets. In the end we settled on Verdana for markets it supported and whatever was the system default sans serif for others. Not the best branding, and it meant that while the idea behind Verdana was sound, the implementation was lacking. So how could we fix this?
Noto IKEA
What was needed was a new typeface that both accomplished the goal of the change to Verdana — a font that worked well across digital platforms — and was also one that would work in any language we chose to offer our services. Luckily, there is an easy choice for such a font: Noto Sans. Noto was designed by Google to be a font for the world, with the ambitious goal of encoding every character in every language into a modern typeface. The name comes from “no tofu”, where “tofu” is the name given to the white squares that pop up when a character is missing from a font.
While Noto Sans met 99% of our needs, as with any design work there’s always a little tweaking to make it perfect. We chose to amend a few characters — including the capital “I” that’s obviously going to see a lot of use at IKEA — and created our own variant that we called Noto IKEA.
So now the big question for the web: how do we deploy this so that we get all the benefits we’ve talked about already, but without the performance costs traditionally associated with shipping custom fonts to our users? First up, we decided to defer to the experts. Google had a long history of shipping fonts, so using them for the majority of our font usage felt like the fastest option, especially when you look at the work they’ve done in automatically splitting large east Asian fonts into easily downloadable chunks.
Google obviously weren‘t going to be able to serve Noto IKEA, but luckily we’d only changed a few characters. This lead to the next step of the plan: a browser evaluates what font should be used on a character-by-character basis, not across whole elements. If we built a small font that contained just the amended characters from Noto Sans then we could have a font stack that looked like this:
Noto IKEA, Noto Sans, …, sans-serif;
We could then base64-encode the tiny font with just the changed Noto IKEA characters and ship that as part of our design system’s base styles.
Problems with our initial approach
While this approach was fine to start with, it wasn’t long before we ran into problems. First, we’d been hoping that our users would benefit from already having a cached copy of Noto Sans from other sites, speeding up our initial page loads. This was wishful thinking: for privacy reasons, Safari had cached resources on a per-domain basis since 2013, and Mozilla and Google adopted this approach at the end of 2020. So there was no benefit to using a central domain there.
Secondly, while our thin Noto IKEA layer was very small, it was integrated into our CSS which is a blocking request; this meant that the site’s loading speed was being slowed down. Another bad experience for our users was that they might see both the usual flash of unstyled text and also a flash of Noto Sans before the Noto IKEA layer finished loading. We also ran into problems if Google Fonts was down or was unreachable: the occasional letter would show in Noto IKEA and the rest in whatever fallback system font was available.
Thirdly, it was becoming obvious that Google Fonts represented a potential legal issue, with several countries investigating whether non-consensual usage was a problem under GDPR, and Austria in particular ruling against it.
And finally, it just generally felt like a fragile solution. It worked, but the number of ways it could go wrong and the unexpected outcomes for our users meant that we were not happy with it. So, time to investigate a new approach.
Moving to self-hosting
Self-hosting the fonts was an obvious choice: for our core markets it removed a DNS request, and meant that we could start tuning the fonts to our specific needs. We wanted this to be a turnkey solution for our internal teams where possible, so we deliberately kept it simple and standards aligned.
We obviously also wanted the highest performance from the font system as possible. The fastest thing is usually the smallest thing, so we knew we needed as much caching as possible, along with subsetting, chunking and feature removal.
The easiest option if the team could work with it was to pull in a font setup fragment to their page with a technology like ESI. These are available at a language-specific URL like https://www.ikea.com/global/assets/fonts/en/font-fragment.html, and provide everything necessary for the page: a cache-busted CSS link, references to chunked fonts with appropriate unicode ranges, and the font applied into the page along with a font stack variable. The only thing the team needs to do is adjust the language code in the URL to match the language of the page they’re building.
If the team wasn’t using ESI or equivalent we also provided the same CSS without the cache-busting hash in the URL: they’d lose some of the other performance work we were planning but anyone can easily add a CSS link to their system.
For the fonts themselves, we chose to standardise only on WOFF2 (a modern font format that uses Brotli internally for minimum file sizes), and to chunk into small easily downloaded pieces based on Google’s unicode ranges for Noto Sans. This meant we could reuse the considerable work they’ve put in to understanding which characters are most used, without using their hosting.
Additionally, we chose to remove as many font features as possible. Font features are typically things like alternative styles like swashes, or small caps. We ended up keeping slashed zeros, tabular numbers, and some features around diacritic positioning, but removing the rest to save on download time. Wakamai Fondue was invaluable in helping to work out how the changes we were making affected the font.
For pages that use only latin characters and a handful of diacritics this ended up with three small files for users to download for a full IKEA font experience: a ~2KB CSS file with the font definitions, a ~12KB normal weight font and a ~12KB bold weight font. At IKEA we generally don’t use italics, but they’re provided if necessary.
The project setup
The font project is owned by the design system team at IKEA, but working with fonts is quite a specialist skill and we wanted to cast a wide net, so from the start it has been an innerSource project that anyone internally can contribute to.
The subsetting system is built on top of subset-font, which uses a WebAssembly build of Harfbuzz to provide subsetting functionality in a Node environment. Eventually we’d like to provide feature stripping through the same system, but as this is something we only do occasionally when we receive font updates we currently do this with the Python library fonttools.
Because there is a risk of breaking user interfaces with a failed deploy, we obviously need testing. We started off with unit tests and a set of demo pages for manual checks, but we found the most value from the demo pages when we hooked them up to Playwright for visual regression testing. This is run on a macOS runner via GitHub actions to make sure that we’re able to accurately test font rendering in as close a WebKit as we can get to Apple’s default.
What can we try with a solid platform?
Experimenting with variable fonts
One of the first things we wanted to test was variable fonts. Variable fonts are fonts that have fluid instead of fixed variations. For example, if we think about a font that has a normal and an italic variant, the variable font would allow you to have any degree of character lean between the normal and italic types.
This had a couple of major perceived potential benefits. The first is that designers get a lot more flexibility in how they use the font, ranging from new intermediate states through to micro-optimisations like being able to tweak optical sizes for dark mode. The second is a potential for decreased font sizes, by shipping a single small font that generates its own states.
We didn’t have a variable cut of Noto IKEA available, but luckily Noto Sans ships with variable support from Google. This allowed us to test an approach, before sorting out an updated cut of Noto IKEA.
Unfortunately, the tests highlighted a misconception in our understanding of how variable fonts work. We’d assumed that there would be a single built-in fixed point on any variable axis, and everything else would be extrapolated from that. It turns out that the variable axes will only interpolate between fixed points, so for our testing with bold we’d still need both the normal and bold versions, along with the data on how to interpolate between them. Although we dropped to a single file, we saw no overall file size improvements.
When it came to the design improvements, it also became apparent that for our current typography system there wasn’t at the time any perceived need for intermediate font states. Both of these together rather put a damper on our reasons to use variables fonts.
We did however find one big win in the variable font evaluation process. In markets like China, Korea and Japan, there are tens of thousands of characters that could potentially be used. We obviously don’t want to ship 5–10MB font files on first load, so we rely on chunking the fonts into small pieces. That means that we find a balance between the size of the font chunks and the size of the CSS that references them.
Given that there aren’t any IKEA-specific changes to Noto Sans in CJK markets, we currently load Noto Sans directly from Google there. If we look at the font CSS for Japan for example you can see that it’s 62KB transferred. It’s a little hard to understand, but the chunks’ unicode ranges are duplicated twice, as both normal and bold weights need to be individually specified. Luckily Google provides a solution for this. By switching the semicolon in the URL (400;700
) to two dots (400..700
) Google will ship the variable font version. This doesn’t save any font size, but because the font weights come in a single file the CSS can use a single reference per chunk with the variable font space-separated notation (font-weight: 400 700;
). This halves the CSS size to 31.5KB transferred, which should help us to get the actual font to the screen much more quickly in network-constrained environments.
Other potential future improvements
Obviously we’re not stopping here. Further improvements we’d like to investigate include:
- Shipping CJK font chunks ourselves to remove a DNS lookup in those markets.
- Building font files that are more specific to particular languages where needed. For example, we ship þ and Þ to every market even though they’re only really used in Icelandic.
- Removing characters that definitely aren’t used (for example, we ship a reversed semicolon ⁏ to everyone, and Wikipedia says is “only used in old writing systems”).
- Checking our fallback stack to see if we can better align metrics with font overrides in the event that Noto IKEA is slow to load.
As Ingvar Kamprad was fond of saying, “most things remain undone” and there are plenty more undone font improvements to come!