1. Introduction
Baseline is an initiative that provides clearer messaging on which web features are interoperable and safe to use today. Thanks to advances in Baseline tooling, you can now use Baseline directly in your projects as a Browserslist query so that your toolchain can change the output of code based on the Baseline target you choose.
In this codelab, you'll learn how to use Baseline in a sample project, and how to configure it to select a specific Baseline target. You'll also get to observe how the project toolchain's output changes depending on the Baseline target you've selected.
2. Set up the demo on your local machine
First, go to your preferred terminal application, clone the demo repository, and then enter the project directory:
git clone git@github.com:GoogleChromeLabs/baseline-demos.git
cd baseline-demos/tooling/webpack-browserslist-config-baseline
At this point, the demo will already have Baseline integrated, but you'll want to check out a commit that starts you out from scratch:
git checkout 94f12e34
With the repository cloned, the demo can now be spun up. This project uses nvm
to manage Node versioning. If you have a reasonably recent version of Node globally installed, you probably don't need to complete this step—but if you do use nvm
, run the following commands:
nvm install
nvm use
From here, install the project's packages:
npm install
If you want to see the demo, run the following command:
npm start
Then navigate to https://localhost:8080. The demo itself is a list of cards that is filterable using a form field at the top of the page. The app itself uses a mix of features that have reached a Baseline threshold.
When finished with the demo, go to your terminal and press Ctrl+C to stop running the demo at any time.
3. How to integrate Baseline into the project
This demo doesn't specify a Browserslist configuration at the start. Browserslist is a compact querying syntax that tells toolchains what minimum browser versions must be supported. For example, using a query of last 3 years
will specify a wide range of targets. In this demo, we'll use an npm package named browserslist-config-baseline
to specify a Browserslist query that aligns with Baseline targets you can use in your toolchain.
To get started, install browserslist-config-baseline
like so:
npm install browserslist-config-baseline --save-dev
When installed, this package lets you specify an extends
Browserslist query in your project that resolves to a list of Baseline browsers. These Baseline targets can be one of the following:
- Moving targets, which update over time as new browsers are released:
- Baseline Newly available, which aligns interoperable features implemented across the core browser set any time from the present, to 30 months ago.
- Baseline Widely available, which includes interoperable features that have been implemented across the core browser set for 30 or more months ago.
- Fixed targets, which represent browser versions at a fixed point in time. These are expressed as years from 2016 to the current year.
To start, we'll use browserslist-config-baseline
to select the moving Baseline Widely available target for the project. To do this, open package.json
and add the following:
"browserslist": "extends browserslist-config-baseline"
4. Observe changes in code output by selecting different Baseline targets
You just selected Baseline Widely available as a target for the demo project. Next, you'll want to build the project:
npm run build
There's a lot of extra output because the debug
option for @babel/preset-env
is specified as true
in the project's babel.config.js
. First, note the size of the CSS and JavaScript in the bundler stats:
assets by status 213 KiB [emitted]
asset js/home.5f3c5480.js 208 KiB [emitted] [immutable] [minimized] (name: home) 2 related assets
asset css/home.20db50ef.css 3.64 KiB [emitted] [immutable] (name: home) 1 related asset
asset index.html 564 bytes [emitted]
Note here that the JavaScript bundle is 208 KiB, and the CSS is 3.64 KiB. Because this project uses core-js
for JavaScript polyfills and autoprefixer
to apply vendor-specific prefixes for CSS properties that are not yet fully interoperable. Both core-js
and autoprefixer
are affected by browserslist-config-baseline
.
Another thing in the output to pay attention to is how your Browserslist query for Baseline Widely available is translated into a Browserslist query. In your project, that will look something similar to this:
Using targets: {
"chrome": "108",
"edge": "108",
"firefox": "108",
"ios": "16",
"safari": "16"
}
Note the polyfills injected by core-js
in the build output:
The corejs3 polyfill added the following polyfills:
es.iterator.constructor { "chrome":"108", "edge":"108", "firefox":"108", "ios":"16", "safari":"16" }
es.iterator.filter { "chrome":"108", "edge":"108", "firefox":"108", "ios":"16", "safari":"16" }
es.iterator.map { "chrome":"108", "edge":"108", "firefox":"108", "ios":"16", "safari":"16" }
This output can change if you change your Baseline target. Say your application must support a much older set of browsers because of a more strict SLA. If that would be the case for you, you'd likely select a more conservative target. In your package.json
file, change the Browserslist config to reflect the following:
"browserslist": "extends browserslist-config-baseline/2016"
This selects Baseline 2016 as the target, and will be translated to a Browerslist query. You can note the differences in the toolchain output after you re-run the build:
npm run build
First, note the change in file size for the project's JavaScript and CSS in the bundler stats:
assets by status 237 KiB [emitted]
asset js/home.b228612d.js 232 KiB [emitted] [immutable] [minimized] (name: home) 2 related assets
asset css/home.0c3e4fd7.css 3.91 KiB [emitted] [immutable] (name: home) 1 related asset
asset index.html 564 bytes [emitted]
You'll note that the JavaScript bundle has increased in size by nearly 30 KiB. The project's CSS is only slightly larger, owing to autoprefixer
adding more vendor prefixes based on the 2016 Baseline target. Also note the change in the Browserslist query:
Using targets: {
"chrome": "53",
"edge": "14",
"firefox": "49",
"ios": "10",
"safari": "10"
}
Compared to the Baseline Widely available target, these browser versions are much, much earlier—early enough that the version of Edge being targeted in this case is pre-Chromium.
The polyfills of injected by core-js
also will change, which is considerably more than when Baseline Widely available was selected as the target:
The corejs3 polyfill added the following polyfills:
es.array.filter { "edge":"14" }
es.iterator.constructor { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.iterator.filter { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.object.to-string { "edge":"14", "firefox":"49" }
es.array.includes { "firefox":"49" }
es.string.includes { "edge":"14" }
es.array.map { "firefox":"49" }
es.iterator.map { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.symbol { "edge":"14", "firefox":"49" }
es.symbol.description { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.array.iterator { "chrome":"53", "edge":"14", "firefox":"49" }
web.dom-collections.iterator { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.array.push { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.regexp.to-string { "edge":"14" }
es.array.from { "edge":"14", "firefox":"49" }
es.regexp.exec { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
es.regexp.test { "edge":"14" }
es.error.cause { "chrome":"53", "edge":"14", "firefox":"49", "ios":"10", "safari":"10" }
The takeaway here is that your Baseline target can have a significant impact in how your application is transformed by your project's toolchain; The application in this example is very basic, with not a lot of cutting-edge developer experience features in either React or the application itself. For considerably more complex applications, you could expect much different outcomes—possibly even more in the way of added polyfills, transforms, and other sources of additional code to conform to the Baseline target you choose.
5. Targeting downstream browsers with browserslist-config-baseline
To review, the core browser set that Baseline targets includes the following browsers:
- Chrome
- Chrome on Android
- Firefox
- Firefox on Android
- Edge
- Safari on macOS
- Safari on iOS
However, you can target what are known as "downstream browsers". These browsers are those whose engines are derived from a browser in the core browser set—most often Chromium. These include browsers such as Opera, Samsung Internet, and others. You can configure browserslist-config-baseline
to target them in your package.json
file like so:
"browserslist": "extends browserslist-config-baseline/with-downstream"
This targets downstream browsers in accordance with the Baseline Widely available target. To see how this resolves to a Browserslist query, rebuild the project:
npm start
Then note the change in the Browserslist query:
Using targets: {
"android": "108",
"chrome": "108",
"edge": "108",
"firefox": "108",
"ios": "16",
"opera": "94",
"opera_mobile": "80",
"safari": "16",
"samsung": "21"
}
You can also target downstream browsers by year. For example:
"browserslist": "extends browserslist-config-baseline/with-downstream/2016"
With this configuration, your Browserslist query will change accordingly:
Using targets: {
"android": "53",
"chrome": "53",
"edge": "14",
"firefox": "49",
"ios": "10",
"opera": "40",
"opera_mobile": "80",
"safari": "10",
"samsung": "6.2"
}
6. Linters and other tools
browserslist-config-baseline
is a convenient tool for bundlers and other parts of your toolchain, but there's also value in other tools, such as linters which have adopted Baseline targets as a part of their configuration.
A good example of linter support for Baseline includes ESLint, which as a part of its CSS linting, provides a use-baseline
rule using @eslint/css
that lets you target either Baseline Newly, Baseline Widely available, or Baseline years. There's also a similar rule in the community @html-eslint/eslint-plugin
package that lets you do the same for HTML features in your eslint.config.js
file:
export default [
/* Omitted JS linting rules ... */
// Lint CSS files for Baseline:
{
files: ["**/*.css"],
plugins: {
css
},
language: "css/css",
rules: {
"css/no-duplicate-imports": "error",
// Lint CSS files to make sure they are using
// only Baseline Widely available features:
"css/use-baseline": ["warn", {
available: "widely"
}]
},
},
// Lint HTML and JSX files for Baseline:
{
files: ["**/*.html"],
...html.configs["flat/recommended"],
rules: {
// Lint HTML files to make sure they are using
// only Baseline Widely available features:
"@html-eslint/use-baseline": ["warn", {
available: "widely"
}]
}
}
];
There are a few things to note in this configuration:
- Both the HTML and CSS linting packages use a
use-baseline
rule, and it is set to Widely available using theavailable: "widely"
configuration option. - For both linting packages, the log level for linter violations is set to
"warn"
. This can be set to"error"
to drop out with an error code to prevent a build from occurring.
You may have seen the linter output when running npm run build
before, but to see the linter output by itself, you can run the following:
npm run lint
The output you'll see highlights several warnings in the project's CSS:
/var/www/baseline-demos/tooling/webpack-browserslist-config-baseline/src/css/normalize.css
222:3 warning Property 'outline' is not a widely available baseline feature css/use-baseline
/var/www/baseline-demos/tooling/webpack-browserslist-config-baseline/src/css/styles.css
62:3 warning Property 'outline' is not a widely available baseline feature css/use-baseline
81:23 warning Value 'subgrid' of property 'grid-template-rows' is not a widely available baseline feature css/use-baseline
7. Wrapping up
As you can see, using browserslist-config-baseline
in your project does require some understanding of underlying build tools and Browserslist, but the act of placing it into your projects should be possible with a little work. The primary benefit of using it is that you no longer need to think about the browsers you support in terms of version numbers, but rather a Baseline target that does the heavy lifting for you.
Additionally, there is a version of this demo that runs on Rollup, and this Codelab can largely be followed using that demo as well.
Baseline support is also beginning to appear in other bundling tools. Vite, for example, which uses Rollup under the hood, now targets Baseline Widely available browsers by default since version 7.
However you decide to go about it, introducing browserslist-config-baseline
into your project's toolchain— and selecting a Baseline target that works for you—you can target browsers in a simpler and more reliable way.