# data[].attributes.title
Baseline – which features are safe to  use on the web today?

# data[].attributes.blurb.html
With the constant barrage of new features in CSS and JavaScript, it is quite a challenge to determine whether a feature is safe for use in your projects. It is great to be on the cutting edge of what is possible with the web platform, but we all want our project to just work - and not rely on a specific browser.

# data[].attributes.slidedeck.data[].slides[].desc.html
Hi. I’m Niels. Thank you for having me at this DevFest. It’s been wonderful here in Malta.

# data[].attributes.slidedeck.data[].slides[].desc.html
I am the CTO of Salonhub - and we make Point-Of-Sale and scheduling software for hair salons in The Netherlands.

# data[].attributes.slidedeck.data[].slides[].desc.html
I’ve been a web developer since 1993 and some of you may be familiar with some of my work. Most likely that will be HTML5test which I created in 2010 to promote the then new HTML5 standard and basically push browser vendors into implementing that new standard. Because that was definitely not a given back in the day.

# data[].attributes.slidedeck.data[].slides[].desc.html
The goal was trick browsers into making the web platform more interoperable. And from speaking with people at all of the different browser vendors that definitely helped at the time. Especially as people started comparing scores and started asking questions about better HTML5 support. That helped teams get better funding from the beancounters upstairs.

# data[].attributes.slidedeck.data[].slides[].desc.html
But that is 15 years ago. It hasn’t been updated in 10 years. And it has served it’s purpose.

# data[].attributes.slidedeck.data[].slides[].desc.html
But web standards and interoperability is something very close to my heart. It is something that makes the web a unique place. The web isn’t owned by anybody. Even though it is increasingly difficult to build browsers, it isn’t just one company that determines the direction of the web. We have multiple engines and multiple companies that all have different priorities and goals.

# data[].attributes.slidedeck.data[].slides[].desc.html
One might push more towards privacy and another might push the web towards more capabilities and while those goals are not necessarily opposites, the result is that there is no one direction for the web. And that is great. But it also makes the web a complicated platform to work on. We have problems and issues that other types of platforms do not have. Like feature parity between browsers and interoperability. That said…

# data[].attributes.slidedeck.data[].slides[].desc.html
We are living in the golden age of web development… That is something that…

# data[].attributes.slidedeck.data[].slides[].desc.html
I said, back in 2017, when I did the opening keynote for Fronteers Conference and looked back at 20 years of web design. And I truly believed this at the time. We had finally said goodbye to Internet Explorer and we were living in an age where the web platform was more and more standards driven.

# data[].attributes.slidedeck.data[].slides[].desc.html
It was when grid came to the web platform. I can’t believe this is now 8 years ago. I don’t know about you, but for me, grid has completely changed the way I approach building webpages and web apps.

# data[].attributes.slidedeck.data[].slides[].desc.html
I definitely understand why this became a meme. Especially in the early days CSS was clunky. And it didn’t help that browsers did not implement CSS in an interoperable way. But in 2017 that was already ancient history. And when I looked back at the previous 20 years, back in 2017, it really felt like we were living in the golden age.

# data[].attributes.slidedeck.data[].slides[].desc.html
That does not mean everything was perfect. One of the strengths of the web is it’s backwards compatibility. But at the same time we still feel the burden of bad decisions from 30 years ago. Mistakes are here to stay. Forever. And we’ve made a lot of bad decisions along the way. [ And this particular bad decision can directly be traced to a ten day period back in May 1995 when Brendan Eich wrote the first version of JavaScript, then still named Mocha. And 10 days is not a lot of time, so he took shortcuts. He borrowed heavily from Java, including the Date class. ]

# data[].attributes.slidedeck.data[].slides[].desc.html
And those early years the focus was mostly on experimenting. On trying out new directions for the web platform. As quickly as possible with as many shortcuts as possible. All that matters was gaining as much market share and influence as possible.

# data[].attributes.slidedeck.data[].slides[].desc.html
It is actually quite ironic that Internet Explorer here backed the standards and worked with the W3C. While the cowboys at Netscape just did their own thing. Both <layer> and <div> - in combination with CSS positioning opened new ways to create more creative layouts. And while <div>’s and CSS was supported by Netscape, using positioning was extremely buggy. It was so bad that it would constantly crash the browser. That meant that if you wanted more than table layout, you had to build two websites. One for Internet Explorer and one for Netscape.

# data[].attributes.slidedeck.data[].slides[].desc.html
20 years later it is a completely different world. And grid is perhaps the best example of this. Grid was developed by all browsers at the same time and within a couple of months it was in all browsers. Firefox and Chrome at the beginning of March. Safari a few weeks later. Only Microsoft Edge took longer. Which is again quite ironic, because Grid was actually originally proposed by Microsoft to the W3C in 2012 and there were already experimental versions of Grid in Internet Explorer 10 in 2011. But it was a cooperative effort.

# data[].attributes.slidedeck.data[].slides[].desc.html
And that cooperation is still very much ongoing. Every year the browser vendors decide on a set of features to work on in unison. And the goal is to make those features interoperable by the end of the year. And that interoperability is measured by tests that are written for those features. And to score 100% you have to pass all of the tests. This is what it looks right now - well two weeks ago when I made this slide - with the currently released versions.

# data[].attributes.slidedeck.data[].slides[].desc.html
This doesn’t look that good. Especially for one browser. But when you look at experimental versions you get a different picture.

# data[].attributes.slidedeck.data[].slides[].desc.html
Much much better. The last number is not an average, but it is the percentage of tests working in all browsers.

# data[].attributes.slidedeck.data[].slides[].desc.html
Now, what is being worked in 2025? Quite a bit. And some of it is already done. Some of these have shipped and are now interoperable thanks to Interop 2025.

# data[].attributes.slidedeck.data[].slides[].desc.html
We are living in the golden age of web development.

# data[].attributes.slidedeck.data[].slides[].desc.html
Right now. In 2025. Back in 2017 it felt may felt like. But I was wrong. I could not conceive what would happen to the web platform in the next 8 years. So right now, we are living in the golden age of web development. And I will happily admit I was wrong again in another 8 years time.

# data[].attributes.slidedeck.data[].slides[].desc.html
So much has changed. We can center things in CSS with a single line.

# data[].attributes.slidedeck.data[].slides[].desc.html
Advanced layout techniques like anchoring have arrived. It’s still experimental, but that will change soon. And these are just two new features. There are hundreds. Back in 2017 I pretty much knew all of CSS and the same for JS and HTML. But today…

# data[].attributes.slidedeck.data[].slides[].desc.html
The web platform has grown so much that is difficult to keep track of all the features of the web. It is increasingly difficult to keep up with all the new features. Sometimes at conferences I see things I have never seen before. And not the fancy new features, but just regular things that are used without any explanation. They are used with the expectation that everybody already know and uses that feature.

# data[].attributes.slidedeck.data[].slides[].desc.html
But we do have the same problems as back in 1997 and 2017. Maybe we have even more problems that we had in the past. We still have multiple browsers and even though there is much more interoperability, browser still have their own priorities. Not every browser will support every features at the same time. And browsers still have bugs and compatibility issues.

# data[].attributes.slidedeck.data[].slides[].desc.html
So how do we keep track of that. How do we know what we can use in our projects without breaking things for our users.

# data[].attributes.slidedeck.data[].slides[].desc.html
Of course there are websites like Can I Use. But one of the issues I have with these compatibility tables is that they are based on browser versions. And that is an important part of the story. But on it’s own they are meaningless. You also need to know when versions were released. How old is Firefox 52? Is that old? We are at 144 right now, so it probably is.

# data[].attributes.slidedeck.data[].slides[].desc.html
And mdn has the same issue. Version numbers are good to know and even if we know when a version was released we’re still not seeing the whole picture. One of the other unanswered questions is how quickly users will upgrade to new browsers. I’m not saying these resources are bad. No not at all. They are great. But they are sometimes missing context.

# data[].attributes.slidedeck.data[].slides[].desc.html
And that is something that Baseline is trying to solve. Baseline gives us a simple, opinionated, way to categorise web platform features and give us answers about when you can use what in a number of core browsers. And that what can be a any CSS property, or HTML tag, a JavaScript feature or Web API.

# data[].attributes.slidedeck.data[].slides[].desc.html
At the heart of Baseline is a huge database with information about web platform features and browser compatibility data. Combine that with information on when browser versions were released and put together you have got the context that you need.

# data[].attributes.slidedeck.data[].slides[].desc.html
How baseline works, is that every web platform features falls in one of these three categories. A feature can be Widely available, Newly available or it has Limited availability.

# data[].attributes.slidedeck.data[].slides[].desc.html
And we look at compatibility data for a core set of browsers on multiple platforms. We have two Chromium browsers: Chrome on desktop and on Android and Edge. Firefox, both desktop and Android. And Safari on desktop and iOS. [ Why not Chrome on iOS? Or Firefox on iOS? Because Apple does not allow other rendering engines in the App Store, so those are based on WebKit and use the same rendering engine as Safari. So they are covered by Safari. Why not Opera, Vivaldi or Samsung Internet? Because they use Chromium and can be mapped to a specific version of Chrome. And why not Ladybird and Servo? Because they are still experimental. ]

# data[].attributes.slidedeck.data[].slides[].desc.html
Newly available, means that that feature is available in all core browsers. That means it is interoperable. But that is just the start of it’s journey. When a feature is interoperable, that does not mean it is safe to use. It may be supported by all current browsers, but what percentage of your users are using the latest and greatest. With evergreen browsers uptake can be pretty quick. But probably not quick enough to reliably serve most of your users.

# data[].attributes.slidedeck.data[].slides[].desc.html
A feature is considered Widely available when it has been in all core browsers for at least 30 months. From the moment it becomes Newly available it takes 2.5 years before it becomes Widely available. For most users that should be enough time. That means Widely available is a moving target. What is newly available today, may be widely available tomorrow.

# data[].attributes.slidedeck.data[].slides[].desc.html
If we calculate back from now - November 2025 and subtract 30 months we end up in April of 2023. And that means Chrome 111, Firefox 112 and Safari 16.4 should be considered safe to target for compatibility.

# data[].attributes.slidedeck.data[].slides[].desc.html
Apart from these categories, we can also target a specific year. For example if you have users that are more on the edge, you could decide to use Baseline 2023 instead. That would include all features that have become interoperable up to januari 1st of 2024. Or if you have more users with older devices, you can decide to go for Baseline 2022 or 2021.

# data[].attributes.slidedeck.data[].slides[].desc.html
Does that mean I should only use features that are widely available…

# data[].attributes.slidedeck.data[].slides[].desc.html
Well.. that depends. Baseline uses 30 months because that is relatively safe for most users. But like I said, not every user demographic is the same. Widely available is great. That is a simple choice.

# data[].attributes.slidedeck.data[].slides[].desc.html
But there are more things to consider. There may be a great polyfill that allows you use that new feature in older browsers. Or the feature is used with progressive enhancement, so it will still work in older browsers, it will just work better in newer browsers. So it depends. Newly available does not mean you should not use it. It just means there is no single simple answer.

# data[].attributes.slidedeck.data[].slides[].desc.html
Over the last year you’ve seen these kinds of badges pop up all over.

# data[].attributes.slidedeck.data[].slides[].desc.html
They are on mdn, right at the top. So if you are targeting Widely available, you can tell immediately tell If a web platform feature is safe to use.

# data[].attributes.slidedeck.data[].slides[].desc.html
One other great resource is the Web Platform Status website where you can search for individual features, but you can also get a list of features that belong to Baseline 2025, or 24 or any year you want to know. It is really useful.

# data[].attributes.slidedeck.data[].slides[].desc.html
But you also get these information blocks in your editor. Now you can see it shows the year 2015 – and we’ve already seen grid became interoperable in 2017. So why? Well it actually shows the display properly. Not specifically grid. If we were to hover over a grid property, it would show 2017.

# data[].attributes.slidedeck.data[].slides[].desc.html
But we haven’t talked about Limited availability. That basically is everything else. It’s features that have not become interoperable yet - but will in the future. Maybe tomorrow, maybe next year. But it is also features are limited to just a single browser and will never be interoperable.

# data[].attributes.slidedeck.data[].slides[].desc.html
For example WebUSB, which is Chromium only. So Chrome and Edge. And it will likely never be supported by Firefox or Safari. But don’t let that stop you from using it. I am actually using this in production.

# data[].attributes.slidedeck.data[].slides[].desc.html
We use it to talk to receipt printers and other hardware devices. We just have to tell our users that if you want that kind of integration you’ll have to use Chrome or Edge. Other browsers are supported for everything else. But for certain features you’ll need Chromium. Is this ideal. Well no. It would have been great if it were interoperable. But it’s not. We’ll just going to have to deal with that. And just because Apple decided that they were not going to support it, does not mean we can’t use it on platforms that do support it.

# data[].attributes.slidedeck.data[].slides[].desc.html
Or dns prefetch. This is actually supported in many browsers, including Safari 5 in 2010, Chrome 46 in 2015, Edge 79 in 2020, Firefox 127 in 2024. But it took until iOS 26 for Safari on iOS, which was released in September of 2025

# data[].attributes.slidedeck.data[].slides[].desc.html
So after 15 years of it being introduced in Safari 5, it is now finally available in Safari 26 on iOS making it interoperable and thus Baseline. And in 2.5 years it will be Widely available. Is it useless until that moment? No, of course not. Was it useless the last 15 years… no of course not. It just did not work on Safari on iOS. But that is just one browser and it doesn’t take anything away from the usefulness of the features on the other browsers.

# data[].attributes.slidedeck.data[].slides[].desc.html
Yes, you can. But that does not mean you should. It also does not mean you should avoid these features. It just means that you need to do the hard work and make a decision on your own. So the answer is…

# data[].attributes.slidedeck.data[].slides[].desc.html
It depends. Maybe using a new feature is okay for your users. Maybe it is progressive enhancement so no harm no foul. Maybe there is a polyfill. Maybe you can make a business decision to only support certain features on certain platforms. It depends.

# data[].attributes.slidedeck.data[].slides[].desc.html
One tool that can help you with these decisions is the Google Analytics Baseline Checker. You can basically export your browser usage data from Google Analytics and then see the percentage of your users that can use the different baseline targets.

# data[].attributes.slidedeck.data[].slides[].desc.html
Another tool that really helps is Baseline-lint which can analyse your code base and give you information about which features you product is using what the baseline status is of each web platform feature.

# data[].attributes.slidedeck.data[].slides[].desc.html
But there are so many other new tools popping up that can integrate within your existing ci/cd and build tools like eslint. You may not know that eslint now also supports css and part of @eslint/css is also checking baseline. There is a eslint plugin specifically to enforce baseline targets for javascript. There is a stylelint plugin. Even an mcp server for if you’re into that.

# data[].attributes.slidedeck.data[].slides[].desc.html
And if you are using a bundler or transformer like Babel or PostCSS, than most likely you are also using browserslist to set your compatibility requirements. Before you might have told browerslist to use “last 2 years” or “last 2 versions”. Now you can also say “baseline widely available” or “baseline 2024”.

# data[].attributes.slidedeck.data[].slides[].desc.html
A great resource about Baseline is web.dev/baseline which has all the latest news about tools and practices and published monthly about the new features that made it into baseline.

# data[].attributes.slidedeck.data[].slides[].desc.html
And finally the Baseline website itself.

# data[].attributes.slidedeck.data[].slides[].desc.html
So we now know all about baseline. Let’s take a closer look.

# data[].attributes.slidedeck.data[].slides[].desc.html
We’ve already talked about grid. But one of the things missing in 2017 was subgrid. Do you know what happened to it. Show of hands. Is it baseline?

# data[].attributes.slidedeck.data[].slides[].desc.html
It’s actually Newly available. But it will become widely available in 6 months or so. So if you’re working on a project that launches in a few months, you could already be using it.

# data[].attributes.slidedeck.data[].slides[].desc.html
What about the <dialog> element. And showModalDialog().

# data[].attributes.slidedeck.data[].slides[].desc.html
Yes it is baseline. You can use this. In fact, you should use this.

# data[].attributes.slidedeck.data[].slides[].desc.html
Anchor positioning?

# data[].attributes.slidedeck.data[].slides[].desc.html
No, unfortunately we’ll have to wait a bit longer for anchor positioning. The latest release of Safari added it about a month ago. In this case we’re waiting on Firefox. But we don’t have to wait long, because it is already available in Firefox behind a flag. So hopefully soon it will be Newly available.

# data[].attributes.slidedeck.data[].slides[].desc.html
If we just look at what has become widely available in just the last 2 years it is an enormous list. All of these things are not experimental anymore. These are APIs that are ready for production. You can use this. Things like math functions in CSS. Overflow clip. Fit-content. I love fit-content. But also top-level await…

# data[].attributes.slidedeck.data[].slides[].desc.html
Okay maybe not top-level await…

# data[].attributes.slidedeck.data[].slides[].desc.html
But there are so many small things. But also many big things. Style containment. Cascade layers. Container queries. Container queries. The holy grail of web development. Something that was thought to be impossible to implement for years. And it is Widely available. Right now. But not just CSS. Also things like import maps. StructuredClone(). Streams. IndexedDB.

# data[].attributes.slidedeck.data[].slides[].desc.html
But there is so much more to come. These are things are just around the corner. They are Newly available. So they are already available in all of the core browsers. And within the next 12 months they will become widely available.

# data[].attributes.slidedeck.data[].slides[].desc.html
Things like lazy loading images and iframes. Masks. CSS Nesting. That is a big one. New large gamut color models like oklab and oklch. Things like color-mix() in css. Align content in block layouts. You know, the one that I showed at the beginning. We can finally center elements with a single line of CSS.

# data[].attributes.slidedeck.data[].slides[].desc.html
There are some many things coming to the web platform. And things will only become better. There are already things in Baseline 2025 that I can’t wait to get my hands on to use in production.

# data[].attributes.slidedeck.data[].slides[].desc.html
I want conclude this talk by showing some of my favourites from those two huge lists of new web platform features. And they don’t have to be big features. They can be small, like array at. You can get the value of an array at a specific location. Which isn’t really earth shocking.

# data[].attributes.slidedeck.data[].slides[].desc.html
We can already to that with brackets. So why Array at()? Because it can also do negative numbers.

# data[].attributes.slidedeck.data[].slides[].desc.html
You can say -3 and get the count from the back of the array. That is something that brackets can’t do and previously you had to do a[a.length - 3]. This is just a small little addition that I just love.

# data[].attributes.slidedeck.data[].slides[].desc.html
Another small thing I use all the time is input mode. This is for mobile devices which use different keyboards for different input types. And if you want to input - lets say a numeric one time password, for example 6 digits. You want to force the numeric keyboard, but you don’t want to use input type = number. Because you’ll get the up and down buttons in the field. And you also want to be able to input leading zero’s.

# data[].attributes.slidedeck.data[].slides[].desc.html
This is also one I use all the time. Being able to clone objects and arrays is so handy. Not that it wasn’t possible before. But you had to do it manually or use tricks like JSON.parse(JSON.stringify()).

# data[].attributes.slidedeck.data[].slides[].desc.html
And the other day I was trying to make the little pointing things for a popover. I used to use text for that. Like the unicode triangle that use that as content for an pseudo element. But it is difficult to get right, especially with alignment and different fonts on different platforms.

# data[].attributes.slidedeck.data[].slides[].desc.html
Or you can use thick transparent borders and then give one segment a color.

# data[].attributes.slidedeck.data[].slides[].desc.html
But those are all clunky workarounds. Clip path is so much better. You can customize the triangle. You can make it as pointy as you want. You can slant it to make it more comic book like. You can even use custom SVG paths.

# data[].attributes.slidedeck.data[].slides[].desc.html
Also new is the media query range syntax, which makes media queries so much easier to read.

# data[].attributes.slidedeck.data[].slides[].desc.html
Especially if you want to target certain ranges.

# data[].attributes.slidedeck.data[].slides[].desc.html
And it is not just about the small things. There are big things as well. CSS nesting is coming soon. But unlike some other newly available features, this is something I’d hold of with until it is actually Widely available. Because it will break older browsers. It is not a feature that you can use progressively.

# data[].attributes.slidedeck.data[].slides[].desc.html
One I also use all the time is color-mix to be able to customize colours. For example if you have a base color and you want to make darker or lighter version of it without defining those colours beforehand. This was Newly available when I wrote this talk. But good news, as two weeks ago it is now Widely available.

# data[].attributes.slidedeck.data[].slides[].desc.html
One I also use all the time is color-mix to be able to customize colours. For example if you have a base color and you want to make darker or lighter version of it without defining those colours beforehand. This was Newly available when I wrote this talk. But good news, as two weeks ago it is now Widely available.

# data[].attributes.slidedeck.data[].slides[].desc.html
Finally… I really want to point out web.dev again, especially the Baseline monthly digest that tells you what is happening in Baseline every month. From new tools, to what has become Newly available and Widely available. And with that….

# data[].attributes.slidedeck.data[].slides[].desc.html
Thank you!