{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/hnrss-org/2026-10-21-the-future-of-css/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "For the past few years, all of the major browser vendors (Google, Apple, Microsoft, and Mozilla, along with Igalia and Bocoup) have been working together on [Interop](https://github.com/web-platform-tests/interop/blob/main/README.md), a project to improve universal browser support on web technologies. It’s been a *remarkably* successful effort; when a focus area gets added to Interop, it often winds up reaching near-100% support across all major browsers within a year. ✨\n\nCSS Subgrid is a good example. Subgrid is a super-powerful extension to CSS Grid which enables [entirely new layouts](https://www.joshwcomeau.com/css/subgrid/). It was implemented in Firefox back in 2019, but there wasn’t much movement with other browsers for years. It got added to Interop 2023, and by the end of that year, it was widely available across all modern browsers:\n\n[![A graph showing CSS Subgrid going from only 12% of tests passing in January 2023 to 91% of tests passing by the end of the year. Safari went from 72% to 100%, Chrome/Edge went from 12% to 100%, and Firefox went from 63% to 91%](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fsubgrid-graph.png&w=1200&q=75)](https://wpt.fyi/interop-2023?feature=interop-2022-subgrid)\n\nThe Y axis shows the percentage of tests related to subgrid passing in each browser. The black “Interop” line shows tests passing in *all* browsers.\n\nAs we reach the end of 2026, Interop 2027 is currently being planned, and the team at Mozilla has created a [ranking system](https://interop-rank.fxdx.dev/) for us to upvote our most-requested features. They plan on using the ranking data to decide what to include for next year’s Interop!\n\nIn this blog post, I want to dig into the top 10 features I voted for, to offer a bit of a preview of what CSS might look like within the next year. There will also be some little tips and tricks you can start using today!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#one-css-functions-1)1\\. CSS functions\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nOne of the most exciting ideas is the [proposal to add functions to CSS](https://github.com/web-platform-tests/interop/issues/1357):\n\n```\n/* Define a function: */\n@function --gradient-to-transparent(--color, --direction: bottom) {\n  result: linear-gradient(\n    to var(--direction),\n    var(--color),\n    transparent;\n  );\n}\n\n/* Invoke it: */\n.elem {\n  background: --gradient-to-transparent(red);\n}\n\n/* …which produces: */\n.elem {\n  background: linear-gradient(\n    to bottom,\n    red,\n    transparent;\n  );\n}\n```\n\nIf you’re familiar with JavaScript, this should feel pretty familiar. We define new functions with `@function` and specify the returned value with `result`.\n\nThere’s *so* much cool stuff we can do with this. Personally, the use case I’m most interested in is the ability to convert between polar and cartesian coordinates, a concept we use over and over in my [Whimsical Animations course](https://whimsy.joshwcomeau.com/):\n\n```\n@function --translate-polar(--angle, --distance) {\n  result: translate(\n    calc(cos(var(--angle)) * var(--distance)),\n    calc(sin(var(--angle)) * var(--distance))\n  );\n}\n\n/* Move this element 100px at a 45° angle: */\n.particle {\n  transform: --translate-polar(45deg, 100px);\n}\n```\n\nThis feels pretty “science-fiction” to me, but it’s already implemented in Chromium browsers, and if it gets added to Interop 2027, I think there’s very good odds that it could become widely available sooner rather than later!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#two-bevels-and-squircles-with-corner-shape-2)2\\. Bevels and squircles with `corner-shape`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn CSS, we can use the `border-radius` property to round off the edges of our DOM nodes to create rounded rectangles. This property effectively adds a quarter-circle to each corner, connecting the 4 straight sides.\n\nThe `corner-shape` property is an exploration of *alternative ways* to connect the 4 sides of our DOM nodes. It provides a handful of new options:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .box {    border-radius: 16px;\n    &.squircle {      corner-shape: squircle;    }    &.bevel {      corner-shape: bevel;    }    &.notch {      corner-shape: notch;    }    &.scoop {      corner-shape: scoop;    }  }</style>\n<div class=\"wrapper\">  <div class=\"box\">Round (default)</div>  <div class=\"box squircle\">Squircle</div>  <div class=\"box bevel\">Bevel</div>  <div class=\"box notch\">Notch</div>  <div class=\"box scoop\">Scoop</div></div>\n```\n\nResult\n\n![5 boxes each with different corner shapes: round, squircle, bevel, notch, and scoop. Round is the default, squircle is a slightly smoother and more angled circular corner, bevel is a 45-degree diagonal corner, not is a square cutout corner, and scoop is a circular cutout corner](https://www.joshwcomeau.com/images/interop-2027/corner-shape-options.png)\n\nWhen I first heard about this property, I was most excited about `squircle`. If you’re not familiar, squircles are an alternative algorithm for generating rounded corners using superellipses. Apple has been making use of them for years because they produce smoother, gentler edges.\n\nThat said, after messing around with `corner-shape` for a while, I find myself using `corner-shape: bevel` a ton. It’s great for creating video game / sci-fi UIs. Here’s a little preview of a side-project I’ve been working on, with *tons* of `corner-shape: bevel`:\n\n![Screenshot of a monochromatic UI with tons of layered gray boxes. Each box has a 45-degree beveled corner.](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fbeveled-corners.png&w=750&q=75)\n\nWe’ve technically been able to do stuff like this with `clip-path` or masks, but unlike those features, `corner-shape` actually *changes the shape of the border-box*. This means that our borders, outlines, and box-shadows will correctly follow the altered corner shapes!\n\nAnd even though this property isn’t available in Firefox/Safari (as I write this in October 2026), I think it’s reasonable to start using it today. It’s a perfect progressive enhancement!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#three-infinite-possibilities-with-border-shape-3)3\\. Infinite possibilities with `border-shape`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn addition to `corner-shape`, there’s a separate proposal for [border-shape](https://github.com/web-platform-tests/interop/issues/1371), which is essentially a lower-level version that allows us to *draw the border ourselves.*\n\nI covered this property in depth in a [recent newsletter issue](https://www.joshwcomeau.com/email/2026-09-14-new-css-features/), so I won’t repeat myself too much here. To summarize, `border-shape` lets us create whatever border shape we want using straight lines, arcs, and Bézier curves.\n\nI used this property recently to draw this card with a big circular scoop at the top:\n\n![Screenshot of a portrait-oriented green card with a floating gold coin. The card has squircle-style corners and a big circular cutout along the top, tracing the bottom edge of the coin](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fhegemon-card.png&w=1080&q=75)\n\nLike `corner-shape`, this property changes the element’s actual border box, so borders/outlines/shadows will trace it correctly. I drew the shape I wanted and then added a white border and black outline to achieve the effect shown above!\n\nWe can even animate between differnt border shapes, like I do in this clip:\n\nYou can learn a bit more about this property in my [newsletter issue on the subject](https://www.joshwcomeau.com/email/2026-09-14-new-css-features/).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#four-anchored-container-queries-4)4\\. Anchored Container Queries\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nI recently published a [blog post on Anchor Positioning](https://www.joshwcomeau.com/css/anchor-positioning/), one of the most exciting modern CSS features (which was actually prioritized in Interop 2026, and is now widely available!).\n\nThe Anchor Positioning API allows us to anchor one element to another. For example, we might anchor a floating tooltip element to its trigger, so that no matter where that trigger is on the page, the tooltip will always be connected to it.\n\nIf you’ve ever tried to solve this sort of problem yourself, you know it’s surprisingly tricky. For example, we might want our tooltip to float above the anchor, but what if the anchor is near the top of the viewport?\n\nThis is a tooltip displaying additional information about this button.\n\nThe tooltip fits comfortably.\n\nThis is a tooltip displaying additional information about this button.\n\nThe tooltip’s getting cut off!\n\nThe original Anchor Positioning API gave us *most* of the tools needed to solve this problem easily in CSS. It allows us to specify *fallback* positions; if the target element doesn’t fit above the anchor, it can flip automatically to the bottom.\n\n**The only thing missing was the ability to tell whether or not the target had flipped.** We had no way to know, in CSS, whether the target element is currently sitting above or below the anchor, whether it’s using the default or fallback position. For tooltips, this is a problem because we typically want to add a little protrusion that points towards the anchor.\n\nThe “Level 2” version of the Anchor Positioning API includes a new type of container query that allows us to check whether the target element is using a fallback position or not. You can learn more about it in my [Anchor Positioning blog post](https://www.joshwcomeau.com/css/anchor-positioning/#flipped-carets-with-anchored-container-queries-3), or check out the [Interop 2027 proposal](https://github.com/web-platform-tests/interop/issues/1363).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#five-css-inherit-function-5)5\\. CSS inherit() function\n\nSo honestly, I had never heard of [this proposal](https://github.com/web-platform-tests/interop/issues/1453), and as far as I can tell, it isn’t implemented in any browsers. It’s not even listed on caniuse, so I can’t display one of those little widgets!\n\nAnd yet, the moment I saw it, it became something I needed to have. 😂\n\nAs you might know, the `inherit` keyword in CSS allows us to pass a value down from the parent, even if that property isn’t typically inherited:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  main {    border: 3px dashed red;  }  p {    border: inherit;  }</style>\n<main>  <p>    This paragraph inherits the dashed red border!  </p></main>\n```\n\nResult\n\nThis is a useful little trick to reuse values, but I frequently wind up discovering that it doesn’t *quite* work. For example, suppose we have nested border radiuses (or is it radii?):\n\nCode Playground\n\nCode editor:\n\n```\n<style>  main {    border: 2px solid red;    border-radius: 16px;  }  p {    border: inherit;    border-radius: inherit;  }</style>\n<main>  <p>    This paragraph inherits the dashed red border!  </p></main>\n```\n\nResult\n\nIn order for nested border radii to look correct, we need to reduce the amount of rounding on the inner radius. So I don’t *exactly* want to use the inherited value; I want to transform it.\n\nThat's where the proposed `inherit()` function comes in. It will pluck the inherited value and allow us to modify it using `calc()`:\n\n```\np {\n  border-radius: calc(inherit() - 4px);\n}\n```\n\nGiven how preliminary this feature is, I’d be a bit surprised if it makes the cut for Interop 2027. But I hope we do get this feature in the future! It seems super useful. 😄\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn my [blog post on scroll-driven animations](https://www.joshwcomeau.com/animation/scroll-driven-animations/), we explore how we can scrub through a keyframe animation by scrolling. For example, we can use it to fade an element in as it traverses the viewport:\n\nScroll-driven animations were [part of Interop 2026](https://wpt.fyi/interop-2026?feature=interop-2026-scroll-driven-animations), and it’s been successfully implemented across all browsers. In addition to Scroll-*driven* animations, however, there’s a separate proposal for [scroll-*triggered* animations](https://github.com/web-platform-tests/interop/issues/1433).\n\nWith scroll-triggered animations, the goal is to trigger a keyframe animation when the user scrolls to a specific point, like a proximity sensor going off. At that point, the animation runs normally over a specified duration.\n\nWe can currently implement scroll-triggered animations using the `IntersectionObserver` API in JavaScript, but it’s kind of a pain. It’ll be super nice when we can use CSS for both scroll-driven and scroll-triggered animations, since they each have their own use cases.\n\nYou can learn more about scroll-triggered animations in this [wonderful article by Bramus](https://developer.chrome.com/blog/scroll-triggered-animations).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#seven-css-reading-flow-and-reading-order-7)7\\. CSS reading-flow and reading-order\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nI’m currently working on a which involves a dynamic grid-based layout. Items are arranged very differently on desktop vs. mobile:\n\n![A blurry image showing a list of things on the left, a big red button in the middle, and some extra panels on the right](https://www.joshwcomeau.com/images/interop-2027/btn-sounds-desktop.png)\n\nDesktop Layout\n\n![A blurry image showing a single narrow column with a big red button, then a list of things, then some extra config panels](https://www.joshwcomeau.com/images/interop-2027/btn-sounds-mobile.png)\n\nMobile Layout\n\nThis is one of the great things about responsive CSS, but it leads to an unfortunate side effect: tab order is always based on the *DOM order,* not the visual order. So, if someone uses assistive technologies to navigate between elements, that navigation order will always be the same, even as the layout changes dramatically depending on screen size.\n\nOn desktop, the first visual item is that list of Redacted on the left, but on mobile, the first item is that big red button. Whichever element comes first in the DOM will be the first item to receive focus, which will invariably be wrong for a percentage of our users.\n\nThankfully, the new `reading-flow` property grants us a solution to this problem, and there’s a [proposal for Interop 2027](https://github.com/web-platform-tests/interop/issues/1349).\n\nThe basic idea is that we can specify that the tab order should follow one of several visual orders:\n\n```\n.grid {\n  display: grid;\n  reading-flow: grid-rows;\n}\n```\n\nThere are values for both CSS Grid and Flexbox, and if you need even more control, the `reading-order` property allows us to create a custom order, sorta like `tabIndex` in HTML.\n\nThis property has been championed by one of my personal heroes, Rachel Andrew. You can read more about it in [her blog post on *reading-flow*](https://developer.chrome.com/blog/reading-flow).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#eight-keyword-size-animations-with-interpolate-size-and-calc-size-8)8\\. Keyword size animations with `interpolate-size` and `calc-size()`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nA long-standing pain point on the web has been the inability to animate between keyword values like `auto`. The `interpolate-size` property solves this problem, and there’s an [Interop 2027 proposal](https://github.com/web-platform-tests/interop/issues/1373) for it, alongside the related `calc-size()` function.\n\nThe most common use case for this feature is to animate between `height: 0` and `height: auto`, for creating collapsible accordion-type UIs:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  html {    interpolate-size: allow-keywords;  }    @supports (    interpolate-size: allow-keywords  ) {    details {      overflow: hidden;        &::details-content {        height: 0;        transition:          height 300ms,          content-visibility 300ms;        transition-behavior: allow-discrete;      }      &:open::details-content {        height: auto;      }    }  }</style>\n<details>  <summary>Click me to show/hide!</summary>  <p>This is the hidden content which animates as it is shown/hidden. 😄</p></details>\n```\n\nResult\n\nThe `interpolate-size` property is not yet supported in your browser. Here’s what it looks like in supported browsers:\n\nThis is admittedly kind of a complex example, requiring other niche-but-well-supported CSS like `transition-behavior`. But the key thing is that `interpolate-size: allow-keywords` makes this sort of transition possible.\n\nThis proposal has been kicking around for a while now, and it’s been implemented in Chrome since 2024, so hopefully it makes it into Interop 2027!\n\nAnd even though it isn’t yet available in all browsers, we can start using it today as a progressive enhancement. Just be sure to gate the `transition` behind a `@supports` query, since otherwise a brief delay will be added to unsupported browsers.\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#nine-css-gap-decorations-9)9\\. CSS gap decorations\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nOne of the things that makes CSS Grid layout unique is that the grid children are positioned using rows/columns defined entirely in CSS. In every other layout mode, elements are positioned using other elements! In Table layout, for example, every grid cell has an associated `<td>` or `<th>` element.\n\nThere are a lot of upsides to CSS Grid, but one of the downsides is that it’s not as easy to add borders between grid cells. If a grid cell is empty, there’s no element to slap a `border: 1px solid` on!\n\nThe [gap decorations proposal](https://github.com/web-platform-tests/interop/issues/1374) introduces a new set of CSS properties that will allow us to use a border-like syntax on the grid itself. Check this out:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .grid {    display: grid;    grid-template-columns: repeat(3, 1fr);    grid-template-rows: repeat(3, 1fr);    gap: 1px;    border: 1px solid red;    /* 👇 The new feature 👇 */    rule: 1px solid red;  }</style>\n<div class=\"grid\">  <div class=\"child\">1</div>  <div class=\"child\">2</div></div>\n```\n\nResult\n\nThe `rule` property is not yet supported in your browser. Here’s what it looks like in supported browsers:\n\n![A 3-by-3 grid with red borders between each row and column. The first two cells have a peach-colored element, the rest are empty.](https://www.joshwcomeau.com/images/interop-2027/row-rule.png)\n\nLike `border`, the `rule` property is a shorthand property that allows us to specify the width, color, and style (dashed/dotted/solid) of the line. We can also use `row-rule` and `column-rule` if we only want to draw lines between each row or column.\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nWhen working with `position: sticky`, have you ever wished there was a way to know whether the element was currently “stuck” or not? For example, maybe you’ve wanted to change the background color of a sticky header when it becomes stuck to the top of the viewport.\n\nThis [proposal](https://github.com/web-platform-tests/interop/issues/1364) is for the implementation of a new type of container query, one that matches when a `position: sticky` element switches from its typical in-flow position to being anchored to the viewport. Check it out:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  header {    position: sticky;    top: 0;    container-type: scroll-state;  }  @container scroll-state(stuck: top) {    .header-content {      background: var(--gray-300);      border-bottom:        1px solid var(--gray-500);    }  }</style>\n<header>  <div class=\"header-content\">    Sticky header example  </div></header><p>  👇 Scroll down 👇</p>\n```\n\nResult\n\nHere’s what this looks like, if this feature isn’t yet supported in your browser.\n\nInterestingly, this new API isn’t *just* for sticky positioning; we can use it to query all sorts of scroll-related state. For example, we can tell whether a scroll container is scrolled to the top/bottom, which can be useful for showing those scroll-indicating gradients:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .container {    height: 100%;    container-type: scroll-state size;    overflow: auto;        &::before {      top: 0;      background: linear-gradient(        to bottom,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: top) {        opacity: 1;      }    }    &::after {      bottom: 0;      background: linear-gradient(        to top,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: bottom) {        opacity: 1;      }    }    /* Shared styles */    &::before, &::after {      content: '';      position: absolute;      left: 0;      right: 0;      height: 50px;      pointer-events: none;      transition: opacity 300ms;    }  }</style>\n<div class=\"container\">  <ul>    <li>One</li>    <li>Two</li>    <li>Three</li>    <li>Four</li>    <li>Five</li>    <li>Six</li>    <li>Seven</li>    <li>Eight</li>    <li>Nine</li>    <li>Ten</li>    <li>Eleven</li>    <li>Twelve</li>    <li>Thirteen</li>    <li>Fourteen</li>    <li>Fifteen</li>    <li>Sixteen</li>    <li>Seventeen</li>    <li>Eighteen</li>    <li>Nineteen</li>    <li>Twenty</li>    <li>Twenty One</li>    <li>Twenty Two</li>    <li>Twenty Three</li>    <li>Twenty Four</li>    <li>Twenty Five</li>  </ul></div>\n```\n\nResult\n\nIf there's the ability to scroll up, `scrollable: top` matches, and so we reveal the pseudo-element floating over the top of the content. When scrolled all the way up, `scrollable: top` no longer applies, and so the gradient is hidden.\n\nHere’s what this looks like in supported browsers:\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#conclusion-11)Conclusion\n\nAs we head into Interop’s 7th year, it's undeniable that the project has been a smashing success for the web. I’m so glad they keep building on this success!\n\nI know it’s kind of a weird time right now for web developers, and many of us are wondering whether we even need to care about this kind of stuff anymore. If we’re delegating all of our work to LLMs, do we still need to keep up with the latest-and-greatest CSS features?\n\n**Personally, I think it’s more important than ever.** We can’t expect LLMs to use modern CSS features when so much of their training data is years old. Most of the features I’ve shared in this post can be used today as a progressive enhancement, and if history is any guide, many of them will soon be widely available.\n\nIf we want to build the best possible user experiences, we’ll need to guide our agents to take advantage of modern web features, which requires us to know about them! This is something I dug into in [a newsletter issue](https://www.joshwcomeau.com/email/wham-launch-005-elephant-2-p/) earlier this year: deep technical expertise remains *incredibly* worthwhile in the agentic era.\n\nFinally, I’d strongly encourage you to [rank your own top choices](https://interop-rank.fxdx.dev/)! There’s a ton of great proposals in this list, including some exciting JavaScript APIs like Temporal which didn’t make sense to include in this blog post.\n\n### Last updated on\n",
  "authors": [
    "soheilpro"
  ],
  "captured_at": "2026-10-21T09:34:18.891023861+00:00",
  "category": "news",
  "content_html": "<p>For the past few years, all of the major browser vendors (Google, Apple, Microsoft, and Mozilla, along with Igalia and Bocoup) have been working together on <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/blob/main/README.md\">Interop</a>, a project to improve universal browser support on web technologies. It’s been a <em>remarkably</em> successful effort; when a focus area gets added to Interop, it often winds up reaching near-100% support across all major browsers within a year. ✨</p>\n<p>CSS Subgrid is a good example. Subgrid is a super-powerful extension to CSS Grid which enables <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/subgrid/\">entirely new layouts</a>. It was implemented in Firefox back in 2019, but there wasn’t much movement with other browsers for years. It got added to Interop 2023, and by the end of that year, it was widely available across all modern browsers:</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://wpt.fyi/interop-2023?feature=interop-2022-subgrid\"><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fsubgrid-graph.png&amp;w=1200&amp;q=75\" alt=\"A graph showing CSS Subgrid going from only 12% of tests passing in January 2023 to 91% of tests passing by the end of the year. Safari went from 72% to 100%, Chrome/Edge went from 12% to 100%, and Firefox went from 63% to 91%\" loading=\"eager\" decoding=\"async\" fetchpriority=\"high\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></a></p>\n<p>The Y axis shows the percentage of tests related to subgrid passing in each browser. The black “Interop” line shows tests passing in <em>all</em> browsers.</p>\n<p>As we reach the end of 2026, Interop 2027 is currently being planned, and the team at Mozilla has created a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://interop-rank.fxdx.dev/\">ranking system</a> for us to upvote our most-requested features. They plan on using the ranking data to decide what to include for next year’s Interop!</p>\n<p>In this blog post, I want to dig into the top 10 features I voted for, to offer a bit of a preview of what CSS might look like within the next year. There will also be some little tips and tricks you can start using today!</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#one-css-functions-1\">Link to this heading</a>1. CSS functions</h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>One of the most exciting ideas is the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1357\">proposal to add functions to CSS</a>:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-comment syntax-block syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">/*</span> Define a function: <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">*/</span></span>\n@<span class=\"syntax-meta syntax-selector syntax-css\">function --<span class=\"syntax-entity syntax-name syntax-tag syntax-custom syntax-css\">gradient-to-transparent</span>(--color, --direction: bottom</span>) <span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\">result</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\"><span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-gradient syntax-css\">linear-gradient</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span>\n    <span class=\"syntax-keyword syntax-other syntax-css\">to</span> var(--direction</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span>,\n    <span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-var syntax-css\">var</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-custom-property syntax-css\">--</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-name syntax-css\">color</span></span></span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span>,\n    <span class=\"syntax-support syntax-constant syntax-color syntax-w3c-special-color-keyword syntax-css\">transparent</span></span><span class=\"syntax-punctuation syntax-terminator syntax-rule syntax-css\">;</span>\n  );\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n\n<span class=\"syntax-comment syntax-block syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">/*</span> Invoke it: <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">*/</span></span>\n<span class=\"syntax-meta syntax-selector syntax-css\"><span class=\"syntax-entity syntax-other syntax-attribute-name syntax-class syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-entity syntax-css\">.</span>elem</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-property-name syntax-css\">background</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\">--<span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">gradient</span>-<span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">to</span>-<span class=\"syntax-support syntax-constant syntax-color syntax-w3c-special-color-keyword syntax-css\">transparent</span>(<span class=\"syntax-support syntax-constant syntax-color syntax-w3c-standard-color-name syntax-css\">red</span></span>);\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n\n<span class=\"syntax-comment syntax-block syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">/*</span> …which produces: <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">*/</span></span>\n<span class=\"syntax-meta syntax-selector syntax-css\"><span class=\"syntax-entity syntax-other syntax-attribute-name syntax-class syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-entity syntax-css\">.</span>elem</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-property-name syntax-css\">background</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\"><span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-gradient syntax-css\">linear-gradient</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span>\n    <span class=\"syntax-keyword syntax-other syntax-css\">to</span> <span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">bottom</span><span class=\"syntax-punctuation syntax-separator syntax-css\">,</span>\n    <span class=\"syntax-support syntax-constant syntax-color syntax-w3c-standard-color-name syntax-css\">red</span><span class=\"syntax-punctuation syntax-separator syntax-css\">,</span>\n    <span class=\"syntax-support syntax-constant syntax-color syntax-w3c-special-color-keyword syntax-css\">transparent</span>;\n  </span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span></span><span class=\"syntax-punctuation syntax-terminator syntax-rule syntax-css\">;</span>\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n</span></span></code></pre>\n<p>If you’re familiar with JavaScript, this should feel pretty familiar. We define new functions with <code>@function</code> and specify the returned value with <code>result</code>.</p>\n<p>There’s <em>so</em> much cool stuff we can do with this. Personally, the use case I’m most interested in is the ability to convert between polar and cartesian coordinates, a concept we use over and over in my <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://whimsy.joshwcomeau.com/\">Whimsical Animations course</a>:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\">@<span class=\"syntax-meta syntax-selector syntax-css\">function --<span class=\"syntax-entity syntax-name syntax-tag syntax-custom syntax-css\">translate-polar</span>(--angle, --distance</span>) <span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\">result</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\"><span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-transform syntax-css\">translate</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span>\n    calc(cos(<span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-var syntax-css\">var</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-custom-property syntax-css\">--</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-name syntax-css\">angle</span></span></span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span></span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span> * <span class=\"syntax-meta syntax-function-call syntax-css\"><span class=\"syntax-support syntax-function syntax-var syntax-css\">var</span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-begin syntax-css\">(</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-custom-property syntax-css\">--</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-name syntax-css\">distance</span></span></span><span class=\"syntax-meta syntax-group syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-group syntax-end syntax-css\">)</span></span></span></span>),\n    <span class=\"syntax-meta syntax-property-name syntax-css\">calc</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">sin</span>(<span class=\"syntax-meta syntax-property-name syntax-css\">var</span>(<span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-custom-property syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-custom-property syntax-css\">--</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-name syntax-css\">angle</span></span></span>)) * <span class=\"syntax-meta syntax-property-name syntax-css\">var</span>(<span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-custom-property syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-custom-property syntax-css\">--</span><span class=\"syntax-support syntax-type syntax-custom-property syntax-name syntax-css\">distance</span></span></span>))\n  );\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n\n<span class=\"syntax-comment syntax-block syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">/*</span> Move this element 100px at a 45° angle: <span class=\"syntax-punctuation syntax-definition syntax-comment syntax-css\">*/</span></span>\n<span class=\"syntax-meta syntax-selector syntax-css\"><span class=\"syntax-entity syntax-other syntax-attribute-name syntax-class syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-entity syntax-css\">.</span>particle</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-property-name syntax-css\">transform</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\">--translate-polar(<span class=\"syntax-constant syntax-numeric syntax-css\">45<span class=\"syntax-keyword syntax-other syntax-unit syntax-css\">deg</span></span>, <span class=\"syntax-constant syntax-numeric syntax-css\">100<span class=\"syntax-keyword syntax-other syntax-unit syntax-css\">px</span></span></span>);\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n</span></span></code></pre>\n<p>This feels pretty “science-fiction” to me, but it’s already implemented in Chromium browsers, and if it gets added to Interop 2027, I think there’s very good odds that it could become widely available sooner rather than later!</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#two-bevels-and-squircles-with-corner-shape-2\">Link to this heading</a>2. Bevels and squircles with <code>corner-shape</code></h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>In CSS, we can use the <code>border-radius</code> property to round off the edges of our DOM nodes to create rounded rectangles. This property effectively adds a quarter-circle to each corner, connecting the 4 straight sides.</p>\n<p>The <code>corner-shape</code> property is an exploration of <em>alternative ways</em> to connect the 4 sides of our DOM nodes. It provides a handful of new options:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  .box {    border-radius: 16px;\n    &amp;.squircle {      corner-shape: squircle;    }    &amp;.bevel {      corner-shape: bevel;    }    &amp;.notch {      corner-shape: notch;    }    &amp;.scoop {      corner-shape: scoop;    }  }&lt;/style&gt;\n&lt;div class=&quot;wrapper&quot;&gt;  &lt;div class=&quot;box&quot;&gt;Round (default)&lt;/div&gt;  &lt;div class=&quot;box squircle&quot;&gt;Squircle&lt;/div&gt;  &lt;div class=&quot;box bevel&quot;&gt;Bevel&lt;/div&gt;  &lt;div class=&quot;box notch&quot;&gt;Notch&lt;/div&gt;  &lt;div class=&quot;box scoop&quot;&gt;Scoop&lt;/div&gt;&lt;/div&gt;\n</span></code></pre>\n<p>Result</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/images/interop-2027/corner-shape-options.png\" alt=\"5 boxes each with different corner shapes: round, squircle, bevel, notch, and scoop. Round is the default, squircle is a slightly smoother and more angled circular corner, bevel is a 45-degree diagonal corner, not is a square cutout corner, and scoop is a circular cutout corner\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>When I first heard about this property, I was most excited about <code>squircle</code>. If you’re not familiar, squircles are an alternative algorithm for generating rounded corners using superellipses. Apple has been making use of them for years because they produce smoother, gentler edges.</p>\n<p>That said, after messing around with <code>corner-shape</code> for a while, I find myself using <code>corner-shape: bevel</code> a ton. It’s great for creating video game / sci-fi UIs. Here’s a little preview of a side-project I’ve been working on, with <em>tons</em> of <code>corner-shape: bevel</code>:</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fbeveled-corners.png&amp;w=750&amp;q=75\" alt=\"Screenshot of a monochromatic UI with tons of layered gray boxes. Each box has a 45-degree beveled corner.\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>We’ve technically been able to do stuff like this with <code>clip-path</code> or masks, but unlike those features, <code>corner-shape</code> actually <em>changes the shape of the border-box</em>. This means that our borders, outlines, and box-shadows will correctly follow the altered corner shapes!</p>\n<p>And even though this property isn’t available in Firefox/Safari (as I write this in October 2026), I think it’s reasonable to start using it today. It’s a perfect progressive enhancement!</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#three-infinite-possibilities-with-border-shape-3\">Link to this heading</a>3. Infinite possibilities with <code>border-shape</code></h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>In addition to <code>corner-shape</code>, there’s a separate proposal for <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1371\">border-shape</a>, which is essentially a lower-level version that allows us to <em>draw the border ourselves.</em></p>\n<p>I covered this property in depth in a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/email/2026-09-14-new-css-features/\">recent newsletter issue</a>, so I won’t repeat myself too much here. To summarize, <code>border-shape</code> lets us create whatever border shape we want using straight lines, arcs, and Bézier curves.</p>\n<p>I used this property recently to draw this card with a big circular scoop at the top:</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fhegemon-card.png&amp;w=1080&amp;q=75\" alt=\"Screenshot of a portrait-oriented green card with a floating gold coin. The card has squircle-style corners and a big circular cutout along the top, tracing the bottom edge of the coin\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>Like <code>corner-shape</code>, this property changes the element’s actual border box, so borders/outlines/shadows will trace it correctly. I drew the shape I wanted and then added a white border and black outline to achieve the effect shown above!</p>\n<p>We can even animate between differnt border shapes, like I do in this clip:</p>\n<p>You can learn a bit more about this property in my <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/email/2026-09-14-new-css-features/\">newsletter issue on the subject</a>.</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#four-anchored-container-queries-4\">Link to this heading</a>4. Anchored Container Queries</h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>I recently published a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/anchor-positioning/\">blog post on Anchor Positioning</a>, one of the most exciting modern CSS features (which was actually prioritized in Interop 2026, and is now widely available!).</p>\n<p>The Anchor Positioning API allows us to anchor one element to another. For example, we might anchor a floating tooltip element to its trigger, so that no matter where that trigger is on the page, the tooltip will always be connected to it.</p>\n<p>If you’ve ever tried to solve this sort of problem yourself, you know it’s surprisingly tricky. For example, we might want our tooltip to float above the anchor, but what if the anchor is near the top of the viewport?</p>\n<p>This is a tooltip displaying additional information about this button.</p>\n<p>The tooltip fits comfortably.</p>\n<p>This is a tooltip displaying additional information about this button.</p>\n<p>The tooltip’s getting cut off!</p>\n<p>The original Anchor Positioning API gave us <em>most</em> of the tools needed to solve this problem easily in CSS. It allows us to specify <em>fallback</em> positions; if the target element doesn’t fit above the anchor, it can flip automatically to the bottom.</p>\n<p><strong>The only thing missing was the ability to tell whether or not the target had flipped.</strong> We had no way to know, in CSS, whether the target element is currently sitting above or below the anchor, whether it’s using the default or fallback position. For tooltips, this is a problem because we typically want to add a little protrusion that points towards the anchor.</p>\n<p>The “Level 2” version of the Anchor Positioning API includes a new type of container query that allows us to check whether the target element is using a fallback position or not. You can learn more about it in my <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/anchor-positioning/#flipped-carets-with-anchored-container-queries-3\">Anchor Positioning blog post</a>, or check out the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1363\">Interop 2027 proposal</a>.</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#five-css-inherit-function-5\">Link to this heading</a>5. CSS inherit() function</h2>\n<p>So honestly, I had never heard of <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1453\">this proposal</a>, and as far as I can tell, it isn’t implemented in any browsers. It’s not even listed on caniuse, so I can’t display one of those little widgets!</p>\n<p>And yet, the moment I saw it, it became something I needed to have. 😂</p>\n<p>As you might know, the <code>inherit</code> keyword in CSS allows us to pass a value down from the parent, even if that property isn’t typically inherited:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  main {    border: 3px dashed red;  }  p {    border: inherit;  }&lt;/style&gt;\n&lt;main&gt;  &lt;p&gt;    This paragraph inherits the dashed red border!  &lt;/p&gt;&lt;/main&gt;\n</span></code></pre>\n<p>Result</p>\n<p>This is a useful little trick to reuse values, but I frequently wind up discovering that it doesn’t <em>quite</em> work. For example, suppose we have nested border radiuses (or is it radii?):</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  main {    border: 2px solid red;    border-radius: 16px;  }  p {    border: inherit;    border-radius: inherit;  }&lt;/style&gt;\n&lt;main&gt;  &lt;p&gt;    This paragraph inherits the dashed red border!  &lt;/p&gt;&lt;/main&gt;\n</span></code></pre>\n<p>Result</p>\n<p>In order for nested border radii to look correct, we need to reduce the amount of rounding on the inner radius. So I don’t <em>exactly</em> want to use the inherited value; I want to transform it.</p>\n<p>That's where the proposed <code>inherit()</code> function comes in. It will pluck the inherited value and allow us to modify it using <code>calc()</code>:</p>\n<pre><code><span class=\"code-snippet\">p {\n  border-radius: calc(inherit() - 4px);\n}\n</span></code></pre>\n<p>Given how preliminary this feature is, I’d be a bit surprised if it makes the cut for Interop 2027. But I hope we do get this feature in the future! It seems super useful. 😄</p>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>In my <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/animation/scroll-driven-animations/\">blog post on scroll-driven animations</a>, we explore how we can scrub through a keyframe animation by scrolling. For example, we can use it to fade an element in as it traverses the viewport:</p>\n<p>Scroll-driven animations were <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://wpt.fyi/interop-2026?feature=interop-2026-scroll-driven-animations\">part of Interop 2026</a>, and it’s been successfully implemented across all browsers. In addition to Scroll-<em>driven</em> animations, however, there’s a separate proposal for <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1433\">scroll-<em>triggered</em> animations</a>.</p>\n<p>With scroll-triggered animations, the goal is to trigger a keyframe animation when the user scrolls to a specific point, like a proximity sensor going off. At that point, the animation runs normally over a specified duration.</p>\n<p>We can currently implement scroll-triggered animations using the <code>IntersectionObserver</code> API in JavaScript, but it’s kind of a pain. It’ll be super nice when we can use CSS for both scroll-driven and scroll-triggered animations, since they each have their own use cases.</p>\n<p>You can learn more about scroll-triggered animations in this <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://developer.chrome.com/blog/scroll-triggered-animations\">wonderful article by Bramus</a>.</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#seven-css-reading-flow-and-reading-order-7\">Link to this heading</a>7. CSS reading-flow and reading-order</h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>I’m currently working on a which involves a dynamic grid-based layout. Items are arranged very differently on desktop vs. mobile:</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/images/interop-2027/btn-sounds-desktop.png\" alt=\"A blurry image showing a list of things on the left, a big red button in the middle, and some extra panels on the right\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>Desktop Layout</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/images/interop-2027/btn-sounds-mobile.png\" alt=\"A blurry image showing a single narrow column with a big red button, then a list of things, then some extra config panels\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>Mobile Layout</p>\n<p>This is one of the great things about responsive CSS, but it leads to an unfortunate side effect: tab order is always based on the <em>DOM order,</em> not the visual order. So, if someone uses assistive technologies to navigate between elements, that navigation order will always be the same, even as the layout changes dramatically depending on screen size.</p>\n<p>On desktop, the first visual item is that list of Redacted on the left, but on mobile, the first item is that big red button. Whichever element comes first in the DOM will be the first item to receive focus, which will invariably be wrong for a percentage of our users.</p>\n<p>Thankfully, the new <code>reading-flow</code> property grants us a solution to this problem, and there’s a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1349\">proposal for Interop 2027</a>.</p>\n<p>The basic idea is that we can specify that the tab order should follow one of several visual orders:</p>\n<pre><code><span class=\"code-snippet\" data-language=\"CSS\"><span class=\"syntax-source syntax-css\"><span class=\"syntax-meta syntax-selector syntax-css\"><span class=\"syntax-entity syntax-other syntax-attribute-name syntax-class syntax-css\"><span class=\"syntax-punctuation syntax-definition syntax-entity syntax-css\">.</span>grid</span> </span><span class=\"syntax-meta syntax-property-list syntax-css\"><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">{</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\"><span class=\"syntax-support syntax-type syntax-property-name syntax-css\">display</span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\"><span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">grid</span></span><span class=\"syntax-punctuation syntax-terminator syntax-rule syntax-css\">;</span>\n  <span class=\"syntax-meta syntax-property-name syntax-css\">reading-flow</span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-css\">:</span><span class=\"syntax-meta syntax-property-value syntax-css\"> </span><span class=\"syntax-meta syntax-property-value syntax-css\"><span class=\"syntax-support syntax-constant syntax-property-value syntax-css\">grid</span>-rows</span><span class=\"syntax-punctuation syntax-terminator syntax-rule syntax-css\">;</span>\n</span><span class=\"syntax-punctuation syntax-section syntax-property-list syntax-css\">}</span>\n</span></span></code></pre>\n<p>There are values for both CSS Grid and Flexbox, and if you need even more control, the <code>reading-order</code> property allows us to create a custom order, sorta like <code>tabIndex</code> in HTML.</p>\n<p>This property has been championed by one of my personal heroes, Rachel Andrew. You can read more about it in <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://developer.chrome.com/blog/reading-flow\">her blog post on <em>reading-flow</em></a>.</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#eight-keyword-size-animations-with-interpolate-size-and-calc-size-8\">Link to this heading</a>8. Keyword size animations with <code>interpolate-size</code> and <code>calc-size()</code></h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>A long-standing pain point on the web has been the inability to animate between keyword values like <code>auto</code>. The <code>interpolate-size</code> property solves this problem, and there’s an <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1373\">Interop 2027 proposal</a> for it, alongside the related <code>calc-size()</code> function.</p>\n<p>The most common use case for this feature is to animate between <code>height: 0</code> and <code>height: auto</code>, for creating collapsible accordion-type UIs:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  html {    interpolate-size: allow-keywords;  }    @supports (    interpolate-size: allow-keywords  ) {    details {      overflow: hidden;        &amp;::details-content {        height: 0;        transition:          height 300ms,          content-visibility 300ms;        transition-behavior: allow-discrete;      }      &amp;:open::details-content {        height: auto;      }    }  }&lt;/style&gt;\n&lt;details&gt;  &lt;summary&gt;Click me to show/hide!&lt;/summary&gt;  &lt;p&gt;This is the hidden content which animates as it is shown/hidden. 😄&lt;/p&gt;&lt;/details&gt;\n</span></code></pre>\n<p>Result</p>\n<p>The <code>interpolate-size</code> property is not yet supported in your browser. Here’s what it looks like in supported browsers:</p>\n<p>This is admittedly kind of a complex example, requiring other niche-but-well-supported CSS like <code>transition-behavior</code>. But the key thing is that <code>interpolate-size: allow-keywords</code> makes this sort of transition possible.</p>\n<p>This proposal has been kicking around for a while now, and it’s been implemented in Chrome since 2024, so hopefully it makes it into Interop 2027!</p>\n<p>And even though it isn’t yet available in all browsers, we can start using it today as a progressive enhancement. Just be sure to gate the <code>transition</code> behind a <code>@supports</code> query, since otherwise a brief delay will be added to unsupported browsers.</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#nine-css-gap-decorations-9\">Link to this heading</a>9. CSS gap decorations</h2>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>One of the things that makes CSS Grid layout unique is that the grid children are positioned using rows/columns defined entirely in CSS. In every other layout mode, elements are positioned using other elements! In Table layout, for example, every grid cell has an associated <code>&lt;td&gt;</code> or <code>&lt;th&gt;</code> element.</p>\n<p>There are a lot of upsides to CSS Grid, but one of the downsides is that it’s not as easy to add borders between grid cells. If a grid cell is empty, there’s no element to slap a <code>border: 1px solid</code> on!</p>\n<p>The <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1374\">gap decorations proposal</a> introduces a new set of CSS properties that will allow us to use a border-like syntax on the grid itself. Check this out:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  .grid {    display: grid;    grid-template-columns: repeat(3, 1fr);    grid-template-rows: repeat(3, 1fr);    gap: 1px;    border: 1px solid red;    /* 👇 The new feature 👇 */    rule: 1px solid red;  }&lt;/style&gt;\n&lt;div class=&quot;grid&quot;&gt;  &lt;div class=&quot;child&quot;&gt;1&lt;/div&gt;  &lt;div class=&quot;child&quot;&gt;2&lt;/div&gt;&lt;/div&gt;\n</span></code></pre>\n<p>Result</p>\n<p>The <code>rule</code> property is not yet supported in your browser. Here’s what it looks like in supported browsers:</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://www.joshwcomeau.com/images/interop-2027/row-rule.png\" alt=\"A 3-by-3 grid with red borders between each row and column. The first two cells have a peach-colored element, the rest are empty.\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>Like <code>border</code>, the <code>rule</code> property is a shorthand property that allows us to specify the width, color, and style (dashed/dotted/solid) of the line. We can also use <code>row-rule</code> and <code>column-rule</code> if we only want to draw lines between each row or column.</p>\n<p>Current Browser Support</p>\n<p>Supported in Chrome 155 and Edge 154</p>\n<p>Not supported in Firefox 157</p>\n<p>Not supported in Safari 27</p>\n<p>When working with <code>position: sticky</code>, have you ever wished there was a way to know whether the element was currently “stuck” or not? For example, maybe you’ve wanted to change the background color of a sticky header when it becomes stuck to the top of the viewport.</p>\n<p>This <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/web-platform-tests/interop/issues/1364\">proposal</a> is for the implementation of a new type of container query, one that matches when a <code>position: sticky</code> element switches from its typical in-flow position to being anchored to the viewport. Check it out:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  header {    position: sticky;    top: 0;    container-type: scroll-state;  }  @container scroll-state(stuck: top) {    .header-content {      background: var(--gray-300);      border-bottom:        1px solid var(--gray-500);    }  }&lt;/style&gt;\n&lt;header&gt;  &lt;div class=&quot;header-content&quot;&gt;    Sticky header example  &lt;/div&gt;&lt;/header&gt;&lt;p&gt;  👇 Scroll down 👇&lt;/p&gt;\n</span></code></pre>\n<p>Result</p>\n<p>Here’s what this looks like, if this feature isn’t yet supported in your browser.</p>\n<p>Interestingly, this new API isn’t <em>just</em> for sticky positioning; we can use it to query all sorts of scroll-related state. For example, we can tell whether a scroll container is scrolled to the top/bottom, which can be useful for showing those scroll-indicating gradients:</p>\n<p>Code Playground</p>\n<p>Code editor:</p>\n<pre><code><span class=\"code-snippet\">&lt;style&gt;  .container {    height: 100%;    container-type: scroll-state size;    overflow: auto;        &amp;::before {      top: 0;      background: linear-gradient(        to bottom,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: top) {        opacity: 1;      }    }    &amp;::after {      bottom: 0;      background: linear-gradient(        to top,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: bottom) {        opacity: 1;      }    }    /* Shared styles */    &amp;::before, &amp;::after {      content: &#39;&#39;;      position: absolute;      left: 0;      right: 0;      height: 50px;      pointer-events: none;      transition: opacity 300ms;    }  }&lt;/style&gt;\n&lt;div class=&quot;container&quot;&gt;  &lt;ul&gt;    &lt;li&gt;One&lt;/li&gt;    &lt;li&gt;Two&lt;/li&gt;    &lt;li&gt;Three&lt;/li&gt;    &lt;li&gt;Four&lt;/li&gt;    &lt;li&gt;Five&lt;/li&gt;    &lt;li&gt;Six&lt;/li&gt;    &lt;li&gt;Seven&lt;/li&gt;    &lt;li&gt;Eight&lt;/li&gt;    &lt;li&gt;Nine&lt;/li&gt;    &lt;li&gt;Ten&lt;/li&gt;    &lt;li&gt;Eleven&lt;/li&gt;    &lt;li&gt;Twelve&lt;/li&gt;    &lt;li&gt;Thirteen&lt;/li&gt;    &lt;li&gt;Fourteen&lt;/li&gt;    &lt;li&gt;Fifteen&lt;/li&gt;    &lt;li&gt;Sixteen&lt;/li&gt;    &lt;li&gt;Seventeen&lt;/li&gt;    &lt;li&gt;Eighteen&lt;/li&gt;    &lt;li&gt;Nineteen&lt;/li&gt;    &lt;li&gt;Twenty&lt;/li&gt;    &lt;li&gt;Twenty One&lt;/li&gt;    &lt;li&gt;Twenty Two&lt;/li&gt;    &lt;li&gt;Twenty Three&lt;/li&gt;    &lt;li&gt;Twenty Four&lt;/li&gt;    &lt;li&gt;Twenty Five&lt;/li&gt;  &lt;/ul&gt;&lt;/div&gt;\n</span></code></pre>\n<p>Result</p>\n<p>If there's the ability to scroll up, <code>scrollable: top</code> matches, and so we reveal the pseudo-element floating over the top of the content. When scrolled all the way up, <code>scrollable: top</code> no longer applies, and so the gradient is hidden.</p>\n<p>Here’s what this looks like in supported browsers:</p>\n<h2><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/css/interop-2027/#conclusion-11\">Link to this heading</a>Conclusion</h2>\n<p>As we head into Interop’s 7th year, it's undeniable that the project has been a smashing success for the web. I’m so glad they keep building on this success!</p>\n<p>I know it’s kind of a weird time right now for web developers, and many of us are wondering whether we even need to care about this kind of stuff anymore. If we’re delegating all of our work to LLMs, do we still need to keep up with the latest-and-greatest CSS features?</p>\n<p><strong>Personally, I think it’s more important than ever.</strong> We can’t expect LLMs to use modern CSS features when so much of their training data is years old. Most of the features I’ve shared in this post can be used today as a progressive enhancement, and if history is any guide, many of them will soon be widely available.</p>\n<p>If we want to build the best possible user experiences, we’ll need to guide our agents to take advantage of modern web features, which requires us to know about them! This is something I dug into in <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.joshwcomeau.com/email/wham-launch-005-elephant-2-p/\">a newsletter issue</a> earlier this year: deep technical expertise remains <em>incredibly</em> worthwhile in the agentic era.</p>\n<p>Finally, I’d strongly encourage you to <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://interop-rank.fxdx.dev/\">rank your own top choices</a>! There’s a ton of great proposals in this list, including some exciting JavaScript APIs like Temporal which didn’t make sense to include in this blog post.</p>\n<h3>Last updated on</h3>\n",
  "content_markdown": "For the past few years, all of the major browser vendors (Google, Apple, Microsoft, and Mozilla, along with Igalia and Bocoup) have been working together on [Interop](https://github.com/web-platform-tests/interop/blob/main/README.md), a project to improve universal browser support on web technologies. It’s been a *remarkably* successful effort; when a focus area gets added to Interop, it often winds up reaching near-100% support across all major browsers within a year. ✨\n\nCSS Subgrid is a good example. Subgrid is a super-powerful extension to CSS Grid which enables [entirely new layouts](https://www.joshwcomeau.com/css/subgrid/). It was implemented in Firefox back in 2019, but there wasn’t much movement with other browsers for years. It got added to Interop 2023, and by the end of that year, it was widely available across all modern browsers:\n\n[![A graph showing CSS Subgrid going from only 12% of tests passing in January 2023 to 91% of tests passing by the end of the year. Safari went from 72% to 100%, Chrome/Edge went from 12% to 100%, and Firefox went from 63% to 91%](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fsubgrid-graph.png&w=1200&q=75)](https://wpt.fyi/interop-2023?feature=interop-2022-subgrid)\n\nThe Y axis shows the percentage of tests related to subgrid passing in each browser. The black “Interop” line shows tests passing in *all* browsers.\n\nAs we reach the end of 2026, Interop 2027 is currently being planned, and the team at Mozilla has created a [ranking system](https://interop-rank.fxdx.dev/) for us to upvote our most-requested features. They plan on using the ranking data to decide what to include for next year’s Interop!\n\nIn this blog post, I want to dig into the top 10 features I voted for, to offer a bit of a preview of what CSS might look like within the next year. There will also be some little tips and tricks you can start using today!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#one-css-functions-1)1\\. CSS functions\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nOne of the most exciting ideas is the [proposal to add functions to CSS](https://github.com/web-platform-tests/interop/issues/1357):\n\n```\n/* Define a function: */\n@function --gradient-to-transparent(--color, --direction: bottom) {\n  result: linear-gradient(\n    to var(--direction),\n    var(--color),\n    transparent;\n  );\n}\n\n/* Invoke it: */\n.elem {\n  background: --gradient-to-transparent(red);\n}\n\n/* …which produces: */\n.elem {\n  background: linear-gradient(\n    to bottom,\n    red,\n    transparent;\n  );\n}\n```\n\nIf you’re familiar with JavaScript, this should feel pretty familiar. We define new functions with `@function` and specify the returned value with `result`.\n\nThere’s *so* much cool stuff we can do with this. Personally, the use case I’m most interested in is the ability to convert between polar and cartesian coordinates, a concept we use over and over in my [Whimsical Animations course](https://whimsy.joshwcomeau.com/):\n\n```\n@function --translate-polar(--angle, --distance) {\n  result: translate(\n    calc(cos(var(--angle)) * var(--distance)),\n    calc(sin(var(--angle)) * var(--distance))\n  );\n}\n\n/* Move this element 100px at a 45° angle: */\n.particle {\n  transform: --translate-polar(45deg, 100px);\n}\n```\n\nThis feels pretty “science-fiction” to me, but it’s already implemented in Chromium browsers, and if it gets added to Interop 2027, I think there’s very good odds that it could become widely available sooner rather than later!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#two-bevels-and-squircles-with-corner-shape-2)2\\. Bevels and squircles with `corner-shape`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn CSS, we can use the `border-radius` property to round off the edges of our DOM nodes to create rounded rectangles. This property effectively adds a quarter-circle to each corner, connecting the 4 straight sides.\n\nThe `corner-shape` property is an exploration of *alternative ways* to connect the 4 sides of our DOM nodes. It provides a handful of new options:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .box {    border-radius: 16px;\n    &.squircle {      corner-shape: squircle;    }    &.bevel {      corner-shape: bevel;    }    &.notch {      corner-shape: notch;    }    &.scoop {      corner-shape: scoop;    }  }</style>\n<div class=\"wrapper\">  <div class=\"box\">Round (default)</div>  <div class=\"box squircle\">Squircle</div>  <div class=\"box bevel\">Bevel</div>  <div class=\"box notch\">Notch</div>  <div class=\"box scoop\">Scoop</div></div>\n```\n\nResult\n\n![5 boxes each with different corner shapes: round, squircle, bevel, notch, and scoop. Round is the default, squircle is a slightly smoother and more angled circular corner, bevel is a 45-degree diagonal corner, not is a square cutout corner, and scoop is a circular cutout corner](https://www.joshwcomeau.com/images/interop-2027/corner-shape-options.png)\n\nWhen I first heard about this property, I was most excited about `squircle`. If you’re not familiar, squircles are an alternative algorithm for generating rounded corners using superellipses. Apple has been making use of them for years because they produce smoother, gentler edges.\n\nThat said, after messing around with `corner-shape` for a while, I find myself using `corner-shape: bevel` a ton. It’s great for creating video game / sci-fi UIs. Here’s a little preview of a side-project I’ve been working on, with *tons* of `corner-shape: bevel`:\n\n![Screenshot of a monochromatic UI with tons of layered gray boxes. Each box has a 45-degree beveled corner.](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fbeveled-corners.png&w=750&q=75)\n\nWe’ve technically been able to do stuff like this with `clip-path` or masks, but unlike those features, `corner-shape` actually *changes the shape of the border-box*. This means that our borders, outlines, and box-shadows will correctly follow the altered corner shapes!\n\nAnd even though this property isn’t available in Firefox/Safari (as I write this in October 2026), I think it’s reasonable to start using it today. It’s a perfect progressive enhancement!\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#three-infinite-possibilities-with-border-shape-3)3\\. Infinite possibilities with `border-shape`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn addition to `corner-shape`, there’s a separate proposal for [border-shape](https://github.com/web-platform-tests/interop/issues/1371), which is essentially a lower-level version that allows us to *draw the border ourselves.*\n\nI covered this property in depth in a [recent newsletter issue](https://www.joshwcomeau.com/email/2026-09-14-new-css-features/), so I won’t repeat myself too much here. To summarize, `border-shape` lets us create whatever border shape we want using straight lines, arcs, and Bézier curves.\n\nI used this property recently to draw this card with a big circular scoop at the top:\n\n![Screenshot of a portrait-oriented green card with a floating gold coin. The card has squircle-style corners and a big circular cutout along the top, tracing the bottom edge of the coin](https://www.joshwcomeau.com/_next/image/?url=%2Fimages%2Finterop-2027%2Fhegemon-card.png&w=1080&q=75)\n\nLike `corner-shape`, this property changes the element’s actual border box, so borders/outlines/shadows will trace it correctly. I drew the shape I wanted and then added a white border and black outline to achieve the effect shown above!\n\nWe can even animate between differnt border shapes, like I do in this clip:\n\nYou can learn a bit more about this property in my [newsletter issue on the subject](https://www.joshwcomeau.com/email/2026-09-14-new-css-features/).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#four-anchored-container-queries-4)4\\. Anchored Container Queries\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nI recently published a [blog post on Anchor Positioning](https://www.joshwcomeau.com/css/anchor-positioning/), one of the most exciting modern CSS features (which was actually prioritized in Interop 2026, and is now widely available!).\n\nThe Anchor Positioning API allows us to anchor one element to another. For example, we might anchor a floating tooltip element to its trigger, so that no matter where that trigger is on the page, the tooltip will always be connected to it.\n\nIf you’ve ever tried to solve this sort of problem yourself, you know it’s surprisingly tricky. For example, we might want our tooltip to float above the anchor, but what if the anchor is near the top of the viewport?\n\nThis is a tooltip displaying additional information about this button.\n\nThe tooltip fits comfortably.\n\nThis is a tooltip displaying additional information about this button.\n\nThe tooltip’s getting cut off!\n\nThe original Anchor Positioning API gave us *most* of the tools needed to solve this problem easily in CSS. It allows us to specify *fallback* positions; if the target element doesn’t fit above the anchor, it can flip automatically to the bottom.\n\n**The only thing missing was the ability to tell whether or not the target had flipped.** We had no way to know, in CSS, whether the target element is currently sitting above or below the anchor, whether it’s using the default or fallback position. For tooltips, this is a problem because we typically want to add a little protrusion that points towards the anchor.\n\nThe “Level 2” version of the Anchor Positioning API includes a new type of container query that allows us to check whether the target element is using a fallback position or not. You can learn more about it in my [Anchor Positioning blog post](https://www.joshwcomeau.com/css/anchor-positioning/#flipped-carets-with-anchored-container-queries-3), or check out the [Interop 2027 proposal](https://github.com/web-platform-tests/interop/issues/1363).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#five-css-inherit-function-5)5\\. CSS inherit() function\n\nSo honestly, I had never heard of [this proposal](https://github.com/web-platform-tests/interop/issues/1453), and as far as I can tell, it isn’t implemented in any browsers. It’s not even listed on caniuse, so I can’t display one of those little widgets!\n\nAnd yet, the moment I saw it, it became something I needed to have. 😂\n\nAs you might know, the `inherit` keyword in CSS allows us to pass a value down from the parent, even if that property isn’t typically inherited:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  main {    border: 3px dashed red;  }  p {    border: inherit;  }</style>\n<main>  <p>    This paragraph inherits the dashed red border!  </p></main>\n```\n\nResult\n\nThis is a useful little trick to reuse values, but I frequently wind up discovering that it doesn’t *quite* work. For example, suppose we have nested border radiuses (or is it radii?):\n\nCode Playground\n\nCode editor:\n\n```\n<style>  main {    border: 2px solid red;    border-radius: 16px;  }  p {    border: inherit;    border-radius: inherit;  }</style>\n<main>  <p>    This paragraph inherits the dashed red border!  </p></main>\n```\n\nResult\n\nIn order for nested border radii to look correct, we need to reduce the amount of rounding on the inner radius. So I don’t *exactly* want to use the inherited value; I want to transform it.\n\nThat's where the proposed `inherit()` function comes in. It will pluck the inherited value and allow us to modify it using `calc()`:\n\n```\np {\n  border-radius: calc(inherit() - 4px);\n}\n```\n\nGiven how preliminary this feature is, I’d be a bit surprised if it makes the cut for Interop 2027. But I hope we do get this feature in the future! It seems super useful. 😄\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nIn my [blog post on scroll-driven animations](https://www.joshwcomeau.com/animation/scroll-driven-animations/), we explore how we can scrub through a keyframe animation by scrolling. For example, we can use it to fade an element in as it traverses the viewport:\n\nScroll-driven animations were [part of Interop 2026](https://wpt.fyi/interop-2026?feature=interop-2026-scroll-driven-animations), and it’s been successfully implemented across all browsers. In addition to Scroll-*driven* animations, however, there’s a separate proposal for [scroll-*triggered* animations](https://github.com/web-platform-tests/interop/issues/1433).\n\nWith scroll-triggered animations, the goal is to trigger a keyframe animation when the user scrolls to a specific point, like a proximity sensor going off. At that point, the animation runs normally over a specified duration.\n\nWe can currently implement scroll-triggered animations using the `IntersectionObserver` API in JavaScript, but it’s kind of a pain. It’ll be super nice when we can use CSS for both scroll-driven and scroll-triggered animations, since they each have their own use cases.\n\nYou can learn more about scroll-triggered animations in this [wonderful article by Bramus](https://developer.chrome.com/blog/scroll-triggered-animations).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#seven-css-reading-flow-and-reading-order-7)7\\. CSS reading-flow and reading-order\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nI’m currently working on a which involves a dynamic grid-based layout. Items are arranged very differently on desktop vs. mobile:\n\n![A blurry image showing a list of things on the left, a big red button in the middle, and some extra panels on the right](https://www.joshwcomeau.com/images/interop-2027/btn-sounds-desktop.png)\n\nDesktop Layout\n\n![A blurry image showing a single narrow column with a big red button, then a list of things, then some extra config panels](https://www.joshwcomeau.com/images/interop-2027/btn-sounds-mobile.png)\n\nMobile Layout\n\nThis is one of the great things about responsive CSS, but it leads to an unfortunate side effect: tab order is always based on the *DOM order,* not the visual order. So, if someone uses assistive technologies to navigate between elements, that navigation order will always be the same, even as the layout changes dramatically depending on screen size.\n\nOn desktop, the first visual item is that list of Redacted on the left, but on mobile, the first item is that big red button. Whichever element comes first in the DOM will be the first item to receive focus, which will invariably be wrong for a percentage of our users.\n\nThankfully, the new `reading-flow` property grants us a solution to this problem, and there’s a [proposal for Interop 2027](https://github.com/web-platform-tests/interop/issues/1349).\n\nThe basic idea is that we can specify that the tab order should follow one of several visual orders:\n\n```\n.grid {\n  display: grid;\n  reading-flow: grid-rows;\n}\n```\n\nThere are values for both CSS Grid and Flexbox, and if you need even more control, the `reading-order` property allows us to create a custom order, sorta like `tabIndex` in HTML.\n\nThis property has been championed by one of my personal heroes, Rachel Andrew. You can read more about it in [her blog post on *reading-flow*](https://developer.chrome.com/blog/reading-flow).\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#eight-keyword-size-animations-with-interpolate-size-and-calc-size-8)8\\. Keyword size animations with `interpolate-size` and `calc-size()`\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nA long-standing pain point on the web has been the inability to animate between keyword values like `auto`. The `interpolate-size` property solves this problem, and there’s an [Interop 2027 proposal](https://github.com/web-platform-tests/interop/issues/1373) for it, alongside the related `calc-size()` function.\n\nThe most common use case for this feature is to animate between `height: 0` and `height: auto`, for creating collapsible accordion-type UIs:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  html {    interpolate-size: allow-keywords;  }    @supports (    interpolate-size: allow-keywords  ) {    details {      overflow: hidden;        &::details-content {        height: 0;        transition:          height 300ms,          content-visibility 300ms;        transition-behavior: allow-discrete;      }      &:open::details-content {        height: auto;      }    }  }</style>\n<details>  <summary>Click me to show/hide!</summary>  <p>This is the hidden content which animates as it is shown/hidden. 😄</p></details>\n```\n\nResult\n\nThe `interpolate-size` property is not yet supported in your browser. Here’s what it looks like in supported browsers:\n\nThis is admittedly kind of a complex example, requiring other niche-but-well-supported CSS like `transition-behavior`. But the key thing is that `interpolate-size: allow-keywords` makes this sort of transition possible.\n\nThis proposal has been kicking around for a while now, and it’s been implemented in Chrome since 2024, so hopefully it makes it into Interop 2027!\n\nAnd even though it isn’t yet available in all browsers, we can start using it today as a progressive enhancement. Just be sure to gate the `transition` behind a `@supports` query, since otherwise a brief delay will be added to unsupported browsers.\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#nine-css-gap-decorations-9)9\\. CSS gap decorations\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nOne of the things that makes CSS Grid layout unique is that the grid children are positioned using rows/columns defined entirely in CSS. In every other layout mode, elements are positioned using other elements! In Table layout, for example, every grid cell has an associated `<td>` or `<th>` element.\n\nThere are a lot of upsides to CSS Grid, but one of the downsides is that it’s not as easy to add borders between grid cells. If a grid cell is empty, there’s no element to slap a `border: 1px solid` on!\n\nThe [gap decorations proposal](https://github.com/web-platform-tests/interop/issues/1374) introduces a new set of CSS properties that will allow us to use a border-like syntax on the grid itself. Check this out:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .grid {    display: grid;    grid-template-columns: repeat(3, 1fr);    grid-template-rows: repeat(3, 1fr);    gap: 1px;    border: 1px solid red;    /* 👇 The new feature 👇 */    rule: 1px solid red;  }</style>\n<div class=\"grid\">  <div class=\"child\">1</div>  <div class=\"child\">2</div></div>\n```\n\nResult\n\nThe `rule` property is not yet supported in your browser. Here’s what it looks like in supported browsers:\n\n![A 3-by-3 grid with red borders between each row and column. The first two cells have a peach-colored element, the rest are empty.](https://www.joshwcomeau.com/images/interop-2027/row-rule.png)\n\nLike `border`, the `rule` property is a shorthand property that allows us to specify the width, color, and style (dashed/dotted/solid) of the line. We can also use `row-rule` and `column-rule` if we only want to draw lines between each row or column.\n\nCurrent Browser Support\n\nSupported in Chrome 155 and Edge 154\n\nNot supported in Firefox 157\n\nNot supported in Safari 27\n\nWhen working with `position: sticky`, have you ever wished there was a way to know whether the element was currently “stuck” or not? For example, maybe you’ve wanted to change the background color of a sticky header when it becomes stuck to the top of the viewport.\n\nThis [proposal](https://github.com/web-platform-tests/interop/issues/1364) is for the implementation of a new type of container query, one that matches when a `position: sticky` element switches from its typical in-flow position to being anchored to the viewport. Check it out:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  header {    position: sticky;    top: 0;    container-type: scroll-state;  }  @container scroll-state(stuck: top) {    .header-content {      background: var(--gray-300);      border-bottom:        1px solid var(--gray-500);    }  }</style>\n<header>  <div class=\"header-content\">    Sticky header example  </div></header><p>  👇 Scroll down 👇</p>\n```\n\nResult\n\nHere’s what this looks like, if this feature isn’t yet supported in your browser.\n\nInterestingly, this new API isn’t *just* for sticky positioning; we can use it to query all sorts of scroll-related state. For example, we can tell whether a scroll container is scrolled to the top/bottom, which can be useful for showing those scroll-indicating gradients:\n\nCode Playground\n\nCode editor:\n\n```\n<style>  .container {    height: 100%;    container-type: scroll-state size;    overflow: auto;        &::before {      top: 0;      background: linear-gradient(        to bottom,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: top) {        opacity: 1;      }    }    &::after {      bottom: 0;      background: linear-gradient(        to top,        black,        transparent      );      opacity: 0;            @container scroll-state(scrollable: bottom) {        opacity: 1;      }    }    /* Shared styles */    &::before, &::after {      content: '';      position: absolute;      left: 0;      right: 0;      height: 50px;      pointer-events: none;      transition: opacity 300ms;    }  }</style>\n<div class=\"container\">  <ul>    <li>One</li>    <li>Two</li>    <li>Three</li>    <li>Four</li>    <li>Five</li>    <li>Six</li>    <li>Seven</li>    <li>Eight</li>    <li>Nine</li>    <li>Ten</li>    <li>Eleven</li>    <li>Twelve</li>    <li>Thirteen</li>    <li>Fourteen</li>    <li>Fifteen</li>    <li>Sixteen</li>    <li>Seventeen</li>    <li>Eighteen</li>    <li>Nineteen</li>    <li>Twenty</li>    <li>Twenty One</li>    <li>Twenty Two</li>    <li>Twenty Three</li>    <li>Twenty Four</li>    <li>Twenty Five</li>  </ul></div>\n```\n\nResult\n\nIf there's the ability to scroll up, `scrollable: top` matches, and so we reveal the pseudo-element floating over the top of the content. When scrolled all the way up, `scrollable: top` no longer applies, and so the gradient is hidden.\n\nHere’s what this looks like in supported browsers:\n\n## [Link to this heading](https://www.joshwcomeau.com/css/interop-2027/#conclusion-11)Conclusion\n\nAs we head into Interop’s 7th year, it's undeniable that the project has been a smashing success for the web. I’m so glad they keep building on this success!\n\nI know it’s kind of a weird time right now for web developers, and many of us are wondering whether we even need to care about this kind of stuff anymore. If we’re delegating all of our work to LLMs, do we still need to keep up with the latest-and-greatest CSS features?\n\n**Personally, I think it’s more important than ever.** We can’t expect LLMs to use modern CSS features when so much of their training data is years old. Most of the features I’ve shared in this post can be used today as a progressive enhancement, and if history is any guide, many of them will soon be widely available.\n\nIf we want to build the best possible user experiences, we’ll need to guide our agents to take advantage of modern web features, which requires us to know about them! This is something I dug into in [a newsletter issue](https://www.joshwcomeau.com/email/wham-launch-005-elephant-2-p/) earlier this year: deep technical expertise remains *incredibly* worthwhile in the agentic era.\n\nFinally, I’d strongly encourage you to [rank your own top choices](https://interop-rank.fxdx.dev/)! There’s a ton of great proposals in this list, including some exciting JavaScript APIs like Temporal which didn’t make sense to include in this blog post.\n\n### Last updated on\n",
  "dateCreated": "2026-10-21T09:34:18.891023861+00:00",
  "date_created": "2026-10-21T01:47:28+00:00",
  "date_modified": "2026-10-21T01:47:28+00:00",
  "date_published": "2026-10-21T01:47:28+00:00",
  "description": "For the past few years, all of the major browser vendors (Google, Apple, Microsoft, and Mozilla, along with Igalia and Bocoup) have been working together on Interop, a project to improve universal browser support on web technologies. It’s…",
  "external_url": "https://www.joshwcomeau.com/css/interop-2027/",
  "git": {
    "commit": "33e7967b937ef6329f2d9194d87eba92105b7ced",
    "path": "items/hnrss-org/2026/10/2026-10-21-the-future-of-css.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/33e7967b937ef6329f2d9194d87eba92105b7ced/items/hnrss-org/2026/10/2026-10-21-the-future-of-css.md"
  },
  "id": "urn:aggr:item:87f997193c5812fb915b7901d4210c7062d84c45",
  "isBasedOn": {
    "@id": "https://www.joshwcomeau.com/css/interop-2027/"
  },
  "mainEntity": {
    "@id": "https://www.joshwcomeau.com/css/interop-2027/",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/hnrss-org/2026-10-21-the-future-of-css/#webpage"
    },
    "author": [
      {
        "@type": "Person",
        "name": "soheilpro"
      }
    ],
    "dateModified": "2026-10-21T01:47:28+00:00",
    "datePublished": "2026-10-21T01:47:28+00:00",
    "headline": "The Future of CSS",
    "image": {
      "@type": "ImageObject",
      "caption": null,
      "height": 90,
      "url": "https://www.joshwcomeau.com/images/og-interop-2027.png",
      "width": 160
    },
    "timeRequired": "PT15M",
    "url": "https://www.joshwcomeau.com/css/interop-2027/",
    "wordCount": 3201
  },
  "name": "Archived snapshot: The Future of CSS",
  "preview": {
    "alt": null,
    "color": null,
    "height": 90,
    "placeholder": {
      "data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
      "hash": ""
    },
    "url": "https://www.joshwcomeau.com/images/og-interop-2027.png",
    "width": 160
  },
  "reading_minutes": 15,
  "replicated_at": null,
  "source": "hnrss-org",
  "source_name": "Hacker News: Front Page",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-21T09:34:18.891023861+00:00",
  "title": "The Future of CSS",
  "url": "https://aggr.aymericbeaumet.com/items/hnrss-org/2026-10-21-the-future-of-css/",
  "word_count": 3201
}