Design systems and shareable browser support
When we put together a design system, we typically spend a good chunk of that time building components that will live on the web. When we start doing that, there’s an obvious question to ask: what browsers will we support?

Regardless of whether we choose to just support the latest evergreen browsers or to go back many versions, if we want to expose this to code the process is typically the same: we define a browserslist (and / or esbuild) compatibility list. This allows us to specify in code the spread of browsers and versions we want to support, and expose it to various build systems. Feeding the browserslist configuration into something like Babel or autoprefixer will let it calculate whether it needs to add browser-specific workarounds for older systems you’re still targeting. The same goes for esbuild: it’s easily configured for specific target version numbers.
IKEA’s design system — Skapa — does just this. Looking at our browser statistics, we see mostly fairly modern browsers that are quickly updated; the outlier is older versions of Safari on IKEA.com. So we currently have this browserslist configuration for our design system build tooling:
last 5 major versions and last 5 years,
Firefox ESR,
not Safari < 15.4,
not iOS < 15.4,
not op_mini all,
not dead
Let’s deconstruct that. We start by requesting the intersection of the last five major versions and the last five years. For evergreen browsers (those that update quickly and regularly) that gives us around five to seven months worth of support: more than enough time for most systems to automatically upgrade. We also make sure we include the enterprise release of Firefox which has longer support. Then for Safari, we go back to 15.4: that version added a set of features like masking and focus-visible that we want to have native support for. Finally, we disable support for very old browsers that would cost too much to support.
Shipping this data
Having this browser support matrix stored as code is a big benefit for Skapa, as changes to it can now be tracked over time, and it’s stored in one place. But if it’s a big benefit for Skapa, why shouldn’t it also be a big benefit for consuming teams? They are, after all, using Skapa patterns and components to build their own systems, so they might as well standardise on the same browser support if reasonable.
It turns out that this isn’t particularly difficult to do. A typical way to include browserslist is as a field in a project’s package.json file, and this has a hidden super power: instead of directly listing the chosen browser configuration in package.json you can tell browserslist to import it from an NPM package. This means that as well as our components Skapa can ship a package that just exports the browserslist config. The package output then looks a little like this:
module.exports = [
'last 5 major versions and last 5 years',
'Firefox ESR',
'not Safari < 15.4',
'not iOS < 15.4',
'not op_mini all',
'not dead',
];
Once this package has been imported as a dependency, then the compatibility data can be configured in as a key-value pair in package.json, like this:
"browserslist": ["extends @ingka/browserslist-config"]
Given the simplicity of this approach, it was an obvious thing to ship out to Skapa’s consumers. They would then get the benefit of having their system’s browser support automatically updated to match the level the components they’re consuming have.
Compatibility with esbuild
While browserslist is supported by a wide range of different technologies, there’s a major exception: esbuild. Given its widespread and growing popularity — due to the performance benefits it can bring to the table — we need to make sure that our browser compatibility choices are made available to it.
Luckily for us, this is a solved problem: there are a few libraries that will translate a browserslist configuration to something that esbuild can consume. We can keep our browserslist configuration as the source of truth, and expose the automatic translation for esbuild consumers. We chose to do this with a new export called esbuild that’s included the same browserslist-config package.
For an example of how this is used, let’s look at Vite: a commonly used consumer of esbuild. In the standard vite.config.js configuration file, we can consume the browser support data by importing the package’s new export, and attaching it into Vite’s build.target configuration option:
import target from "@ingka/browserslist-config/esbuild";
export default defineConfig({
build: { target }
});
This works in exactly the same way as the previous package.json method, and indeed both can be consumed if you have a mix of esbuild and browserslist tooling (Vite and SASS for example).
Overall benefits
We have already talked about how this process helps product teams align their own browser compatibility with that of the design system’s. There’s another positive side-effect though: the browserslist-config package becomes something that is gradually updated over time, meaning that consuming teams automatically follow the company-wide browser compatibility standard. Historically we’ve found many projects with es2015 or es6 set as the configuration: that’s a static level which leads to steadily increasing bundle sizes as more and more compatibility layers are automatically and silently added into the build. Tracking the design system’s baseline configuration helps to keep bundle sizes small and performance high.
To give specific data for that last point, a piece of JavaScript code that is shipped to the majority of IKEA.com users saw a drop in gzipped file size from 78KB to 34KB — a fall of over 55% — simply by configuring the build system to use the browserslist-config package instead of the previous single target of 'ie': 11.
Another benefit is that this configuration can be shipped as part of an app scaffolding system, meaning that teams starting new projects will automatically have access to the current and future browser support standards without having to lift a finger.
Of course, there’s nothing stopping a team that wants to own their own browser compatibility configuration from managing that themselves. Teams that know they will only be targeting the latest and greatest technologies, or teams that are consuming only simple components and want a longer support schedule, are welcome to configure this themselves. But starting with the default configuration and opting-out later when a need has been discovered is an easy choice.
Hopefully this has inspired you to investigate sharing your browser support matrix in your own design system. It’s a simple technique that leads to compatibility and performance benefits. I’d also love to hear if you’re doing anything similar or if there are ways you see that this could be improved.