This is my running checklist of modern CSS features—something I scan to decide which techniques to experiment with next. It's grouped by purpose (layout, selectors, color, typography, animation, forms, responsive, performance, functions & math), and each entry lists what it's for, an example, and browser support, plus whether I've already tried it.
The last section (K) is HTML rather than CSS. It lives in the same checklist because it's doing the same job: turning things that used to need a pile of JavaScript into one declarative line—and several entries are two halves of the same feature (appearance: base-select and <selectedcontent>, anchor positioning and popover, reading-flow and focusgroup), so splitting them apart would make the list harder to use, not easier.
Tried it
- ✅ = I've experimented with it
- ⬜ = Not yet
Support status (as of October 2026; 🟠 / 🔴 move fast—re-check caniuse / MDN before shipping)
- 🟢 Stable — broadly available across major browsers, safe for production
- 🟡 Newer — supported in most browsers; add a fallback
- 🟠 Experimental — only some browsers / behind a flag / just landed in Chrome
- 🔴 Proposed — still at the proposal stage, not usable in browsers yet
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
grid-template-rows: masonry | Masonry (waterfall) layout without JS. WebKit's alternative, display: grid-lanes, now ships in Safari (Safari 27.2 Beta renames flow-tolerance to fit-tolerance); the two approaches are still converging | grid-template-rows: masonry; | 🟠 | ✅ |
subgrid | Lets a child grid inherit the parent's tracks and gaps, so alignment snaps to the parent's lines; great for aligning elements across cards | grid-template-columns: subgrid; | 🟢 | ✅ |
| Container Queries | Responsive design based on the container's size (not the screen)—the key to true componentization | container-type: inline-size; + @container (width > 400px){} | 🟢 | ✅ |
| Anchor Positioning | Anchor one element's position to another (tooltip / popover / menu), replacing a lot of JS math | anchor-name: --a; + top: anchor(--a bottom); | 🟠 | ✅ |
@position-try | Fallback for anchor positioning: auto-flip when there isn't enough room | position-try-fallbacks: flip-block; | 🟠 | ✅ |
reading-flow / reading-order | When flex/grid visual order is reordered, fix keyboard and screen-reader reading order (an accessibility must) | reading-flow: flex-visual; / reading-order: 1; | 🟠 | ✅ |
Gap Decorations (row-rule / column-rule / rule) | Draw and style separators directly in grid/flex gaps—no more divider elements; plus fine-grain rule-inset/rule-overlap/rule-visibility-items (Chrome/Edge 149+; not in other browsers yet) | column-rule: 1px solid; row-rule: 1px solid; or shorthand rule: 1px solid; | 🟠 | ✅ |
margin-inline | Logical property; set both inline-axis margins at once (often auto to center) | margin-inline: auto; | 🟢 | ✅ |
| Two-value display | Write outer × inner display separately: display: block flex etc.; flex = block flex, inline-flex = inline flex. Clarifies display's two layers; single-value shorthands still fine day-to-day | display: inline flex; | 🟡 | ✅ |
stretch sizing keyword | Make a box fill its container's available space (margins included), replacing vendor-specific values like -webkit-fill-available (see my post testing it and checking my site) | width: stretch; | 🟡 | ✅ |
| Single-axis scroll containers | Scroll in one direction and clip in the other, so position: sticky can be constrained by different scroll containers per axis (e.g. a sticky table header and first column); Chrome 156 Beta | overflow: scroll clip; | 🟠 | ⬜ |
safe / unsafe overflow alignment | When centered content doesn't fit, safe falls back to start alignment so content isn't pushed somewhere you can't see or scroll to; Safari 27.2 Beta adds pairing with normal and fixed-position support (see my post testing it with zoom and keyboard focus) | align-items: safe center; | 🟡 | ✅ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
:has() | The "parent selector" / select by descendant or sibling state—a logic powerhouse | .card:has(img){} | 🟢 | ✅ |
::part() | Style a specific element inside a Web Component from the outside (needs Shadow DOM knowledge) | my-el::part(label){} | 🟢 | ✅ |
:is() / :where() | Group selectors; :where() has 0 specificity (lower than inline), :is() takes its highest-specificity member | :is(h1,h2) :where(.x){} | 🟢 | ✅ |
nth-of syntax | :nth-child(An+B of S)—count the nth item within a filtered subset | :nth-child(2 of .active){} | 🟢 | ✅ |
:placeholder-shown | Style only while the placeholder is still showing (floating-label effect) | input:placeholder-shown{} | 🟢 | ✅ |
:in-range / :out-of-range | Style form values inside/outside their min/max range | input:out-of-range{} | 🟢 | ✅ |
:user-valid / :user-invalid | Show validation state only after the user has interacted—no angry red on load | input:user-invalid{} | 🟡 | ✅ |
sibling-index() / sibling-count() | Get an element's index and total among siblings; enables staggered animation, dynamic widths (Baseline 2026) | --i: sibling-index(); | 🟡 | ✅ |
:heading | Select all headings at once; supported from Safari 27, not in other browsers yet | :heading{} | 🟠 | ⬜ |
| Carousel selectors | ::scroll-marker / scroll-marker-group / :target-current for native carousels and scrollspy; Chrome 154 adds links (default) and tabs modes to scroll-marker-group, changing the markers' focus order and ARIA semantics | :target-current{} | 🟠 | ✅ |
Media state pseudo-classes (:playing / :paused / :muted, etc.) | Style <video>/<audio> directly by playing, paused, muted or buffering state, so custom players no longer need JS to toggle classes (Safari first, added in Chrome 156 Beta, not in Firefox yet) | video:paused + .play-btn{} | 🟠 | ⬜ |
image-animation / :animated-image | Control whether animated images such as GIF and APNG play, and select animated images; animated GIFs have always been hard to square with WCAG 2.2.2 Pause, Stop, Hide, so this is high-value for accessibility (Chrome 156 Beta) | img:animated-image { image-animation: paused; } | 🟠 | ⬜ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
New color functions (oklch-first) | hwb/lab/lch/color() for wider gamuts; I've settled on oklch, which supports the oklch(from …) relative syntax | color: oklch(0.7 0.15 200 / 50%); | 🟢 | ✅ |
color-mix() | Mix two or more colors (best done in oklch space) for hover variants and transparent overlays; mixing three or more is currently Firefox 150 and Safari 27 | color-mix(in oklch, red 40%, blue); | 🟢 | ✅ |
contrast-color() | Automatically picks a readable foreground for a background (Baseline 2026). It only returns black or white; in my Chromium 153 tests it uses the WCAG 2 formula to pick the higher-contrast one, which mathematically is always at least 4.58:1, so AA is guaranteed but AAA isn't, and it can't pick brand colors | color: contrast-color(var(--bg)); | 🟡 | ✅ |
accent-color | One line to re-theme checkbox/radio/range and other system controls | accent-color: rebeccapurple; | 🟢 | ✅ |
light-dark() | Give a property both a light and dark value at once; with color-scheme it skips the media query; it also accepts images (supported in all three major browsers) | color: light-dark(#000, #fff); | 🟢 | ✅ |
font-palette / @font-palette-values | Custom palettes for color (COLR) fonts | @font-palette-values --p{...} + font-palette: --p; | 🟡 | ✅ |
| Gradient interpolation color space | linear-gradient(in oklch …) makes gradient transitions smoother and less muddy | background: linear-gradient(in oklch, #4ba3f7, #9d2398); | 🟢 | ✅ |
alpha() | Relative color function that changes only the opacity of an existing color, without rewriting the whole color value (Baseline 2026: Chrome 151, Firefox 155, Safari 27) (see my post testing every color format and contrast) | color: alpha(from var(--brand) / 50%); | 🟡 | ✅ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
text-wrap: balance / pretty | balance evens out heading line widths; pretty avoids orphan words in body text (pretty is newer) | text-wrap: balance; | 🟡 | ✅ |
text-decoration-skip-ink | Break the underline around descenders (g, y) for a cleaner look; Chrome 155 Beta adds text-decoration-skip-spaces to control whether the underline skips spaces | text-decoration-skip-ink: auto; | 🟢 | ✅ |
text-decoration-inset | Shorten or extend decorations such as underlines; positive values inset, negative values outset, for underline animations without extra elements (Chrome 154) | text-decoration-inset: 0.5em; | 🟠 | ⬜ |
text-combine-upright | Lay numbers/Latin horizontally within vertical text ("tate-chu-yoko"); common in CJK long-form | text-combine-upright: all; | 🟡 | ✅ |
text-emphasis | Emphasis dots/circles that track the character center; common CJK annotation | text-emphasis: dot; | 🟢 | ✅ |
ruby-align | Alignment of ruby annotations (bopomofo / pinyin, etc.) | ruby-align: center; | 🟡 | ✅ |
paint-order | Control the paint order of text fill vs. stroke (for outlined text) | paint-order: stroke fill; | 🟢 | ✅ |
box-decoration-break: clone | On line/page breaks, apply border/radius/background to each fragment; especially nice for link focus | box-decoration-break: clone; | 🟡 | ✅ |
text-box-trim | Trim the extra space above/below a font for precise vertical alignment (Chrome and Safari; not in Firefox yet) | text-box: trim-both cap alphabetic; | 🟡 | ✅ |
line-clamp (-webkit-line-clamp) | Show "…" after N lines; the new standard line-clamp is gradually replacing the webkit form | -webkit-line-clamp: 3; (needs -webkit-box) | 🟢 | ✅ |
hanging-punctuation | Hang leading/trailing punctuation outside the edge for tidier alignment (Safari-first) | hanging-punctuation: first last; | 🟠 | ⬜ |
font-variant-numeric: tabular-nums | Monospaced digits—essential for timers/prices/percentages that jitter (font must support it) | font-variant-numeric: tabular-nums; | 🟢 | ✅ |
lh unit | Set margin/spacing in line-height units so they scale with font size | margin-bottom: 1.5lh; | 🟡 | ✅ |
margin-trim | Trim margins of the first/last children in a container, avoiding :first/:last-child resets (Safari-first; added in Chrome 155 Beta) | margin-trim: block; | 🟠 | ⬜ |
text-fit | Auto-scales font size so text exactly fills its container's width—responsive headlines without manual math or JS (new in Chrome 150; always add a percentage limit, or zooming makes the text smaller—see my tests) | h1 { text-fit: grow per-line-all 200%; } | 🟠 | ✅ |
text-autospace | Automatically adds proper spacing between CJK text and Latin letters or numbers, so you don't have to type the spaces by hand; Safari 27 adds the insert value | text-autospace: normal; | 🟡 | ✅ |
font-width | The new canonical name for font-stretch, selecting condensed or expanded faces; the old name stays as an alias (Baseline 2026) | font-width: 75%; | 🟡 | ⬜ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
| Scroll-driven animation (formerly "@scroll-timeline") | Drive animation by scroll progress instead of time; the spec is now animation-timeline: scroll()/view() | animation-timeline: scroll(); | 🟠 | ✅ |
@view-transition / transition-behavior | Transitions for page/state changes; allow-discrete lets discrete properties like display transition | transition-behavior: allow-discrete; | 🟡 | ✅ |
@starting-style | Define an element's first-appearance start styles; enables display:none → shown entry animations | @starting-style{ opacity:0; } | 🟡 | ✅ |
backface-visibility | Whether the back face shows during a 3D flip (card-flip effect) | backface-visibility: hidden; | 🟢 | ✅ |
prefers-reduced-motion | Detect the reduced-motion preference, an accessibility must; pairs with prefers-color-scheme for light/dark | @media (prefers-reduced-motion: reduce){} | 🟢 | ✅ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
field-sizing: content | Let input/textarea grow automatically to fit their content (Baseline 2026) | field-sizing: content; | 🟡 | ✅ |
Customizable <select> (appearance: base-select) | Fully style the native dropdown; options can hold icons/HTML. Pair it with ::picker(select) and ::picker-icon to style the popup, and <selectedcontent> to clone the chosen option. You finally don't have to rebuild a select out of divs just to style it — and rebuilding it is a classic source of accessibility disasters (Chrome 135 and Safari 27; not in Firefox yet) | select{ appearance: base-select; } | 🟡 | ✅ |
<selectlist> (formerly <selectmenu>) | A highly customizable dropdown component; renamed and folded into the "customizable select" track | <selectlist>…</selectlist> | 🟠 | ✅ |
::backdrop | Style the layer behind dialog.showModal() or fullscreen | dialog::backdrop{} | 🟢 | ✅ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
| Media query range syntax | Write breakpoints with <=/>=, skipping the min-width + 320.01px pain | @media (width >= 320px){} | 🟢 | ✅ |
prefers-color-scheme | Detect dark/light theme preference | @media (prefers-color-scheme: dark){} | 🟢 | ✅ |
prefers-contrast | Detect a request for more/less contrast | @media (prefers-contrast: more){} | 🟡 | ✅ |
forced-colors | Adjust for high-contrast / forced-colors mode (Windows High Contrast) | @media (forced-colors: active){} | 🟢 | ✅ |
resolution media | Switch by output device pixel density (e.g. Retina) | @media (resolution >= 2dppx){} | 🟢 | ✅ |
Style Queries @container style() | Style by a container's custom-property value (e.g. theme); great for component variants | @container style(--theme: dark){} | 🟡 | ✅ |
| Scroll-State Queries | Style by a container's scroll state (e.g. stuck, snapped); can detect whether a sticky element is stuck | @container scroll-state(stuck: top){} | 🟠 | ✅ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
Cascade Layers @layer | Manage priority with layers to tame specificity in large projects | @layer base, components, utilities; | 🟢 | ✅ |
revert-rule | Roll a property back to the value it would have if the current rule didn't exist; the same family as revert (back to browser defaults) and revert-layer (back to the previous @layer) (Baseline 2026) (see my post comparing it with revert and revert-layer) | color: revert-rule; | 🟡 | ✅ |
@scope | Scope styles to a block with an optional lower bound, similar to CSS Modules scoping | @scope (.card) to (.content){} | 🟡 | ✅ |
contain | Declare inner layout/style independence to isolate reflow and boost performance | contain: layout style; | 🟢 | ✅ |
contain-intrinsic-size | With content-visibility: auto, give an estimated size first to avoid scroll jumps; switches to auto once cached | contain-intrinsic-size: auto 100px; | 🟢 | ✅ |
scrollbar-gutter: stable | Reserve the scrollbar gutter so content doesn't shift when the scrollbar appears | scrollbar-gutter: stable; | 🟡 | ✅ |
scrollbar-color / scrollbar-width | Customize scrollbar color and thickness | scrollbar-color: #888 #eee; | 🟡 | ✅ |
@supports | Feature detection for progressive enhancement and fallbacks | @supports (display: grid){} | 🟢 | ✅ |
at-rule() | Test inside @supports whether an at-rule such as @scope or @position-try is supported, instead of guessing indirectly (Firefox 157+) | @supports at-rule(@scope){} | 🟠 | ⬜ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
round() / mod() / rem() | Rounding and remainders in CSS, to align to a grid/rhythm | width: round(down, 15.5px, 4px); | 🟡 | ✅ |
Trig functions sin/cos/tan | Circular layouts, waveforms, angle math | width: calc(sin(30deg) * 100px); | 🟢 | ✅ |
calc-size() / interpolate-size | Animate transitions to keywords like auto/min-content, e.g. an accordion expanding to an unknown height; combined with round(), it can also make a height an exact multiple of a background pattern so the pattern isn't cut off (height: calc-size(auto, round(up, size, 48px))) | interpolate-size: allow-keywords; | 🟠 | ✅ |
random() | Native random values in CSS (scatter, jitter effects) | rotate: random(-5deg, 5deg); | 🟠 | ✅ |
if() | Conditional logic inside a property value (without else, the property falls back to its initial value and a focus outline can disappear entirely; see this post) | display: if(style(--open: 1): block; else: none); | 🟠 | ✅ |
@function | Custom CSS functions / a mixin-like concept, with parameters and a return value (see this post for using one to guarantee a minimum target size) | @function --double(--x){ result: calc(var(--x)*2); } | 🟠 | ✅ |
progress() | Returns where a value sits between two others as a 0–1 ratio, for styles that change continuously with size (Baseline 2026; Firefox 155 and Safari 27 also support no-clamp, letting the result go outside 0–1) (applied to this site's fluid h1 and h2 sizes so headings also reach 2× at 200% zoom) | opacity: progress(100cqw, 300px, 900px); | 🟡 | ✅ |
attr() in any property | attr() used to work only in content; now it can read an HTML attribute with a type and use it as the value of any property, such as a width or color (Chrome first, added in Firefox 155; see my post on reading ARIA attributes with it) | width: attr(data-w type(<length>)); | 🟠 | ✅ |
symbols() | Define list markers directly in CSS without a separate @counter-style (Chrome 155 Beta) | list-style: symbols(cyclic "◆" "◇"); | 🟠 | ⬜ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
| Native Nesting | Sass/SCSS-style nesting without a preprocessor | .card{ & .title{} } | 🟢 | ✅ |
<img loading="lazy"> | Lazy-load images to improve first-paint performance | <img loading="lazy"> | 🟢 | ✅ |
overscroll-behavior: contain | Stop a scrolled-to-the-end block from scrolling the outer container (common for modals/sidebars) | overscroll-behavior: contain; | 🟢 | ✅ |
object-view-box | Crop a region of an image directly in CSS, no separate cropped file needed | object-view-box: inset(10% 10% 10% 10%); | 🟠 | ✅ |
Responsive iframes (frame-sizing) | An iframe sizes its height to the embedded content, with no more postMessage height plumbing; the embedded page opts in with <meta name="responsive-embedded-sizing"> (Chrome 154) | iframe { frame-sizing: content-height; } | 🟠 | ⬜ |
scroll-snap-type: pair | A new scroll snapping mode (Chrome 156 Beta); I'll test the details once it ships to stable | scroll-snap-type: pair; | 🟠 | ⬜ |
JPEG XL (image/jxl) | A newer image format that compresses better than JPEG and can losslessly convert existing JPEGs; Safari supports it, and Chrome 155 Beta adds decoding | <source type="image/jxl" srcset="a.jxl"> | 🟠 | ⬜ |
| Individual transform properties | translate/rotate/scale as their own properties, easier to animate separately | rotate: 45deg; scale: 1.2; | 🟢 | ✅ |
border-image | Complex borders from a sliced image, avoiding traditional slicing | border-image: url(frame.png) 30 round; | 🟢 | ✅ |
background-clip: border-area | Clips the background to the area painted by the border strokes (respecting border-width/border-style, ignoring border-color transparency), so gradient borders work natively instead of via border-image workarounds (new in Chrome 150) (tested: forced colors mode drops the gradient, but the browser turns the transparent border into the text color, so the border shape stays visible) | border: 3px solid transparent; background: linear-gradient(45deg, red, blue); background-clip: border-area; | 🟠 | ✅ |
corner-shape | Corner shapes beyond border-radius (squircle / notched, etc.), used together with border-radius; Chrome 156 Beta adds the corner shorthand (tested with an app icon, coupon, step arrows and cut-corner button; see this post) | corner-shape: squircle; border-radius: 30px; | 🟠 | ✅ |
Font smoothing (-webkit-font-smoothing) | The key to non-harsh text in dark mode: on macOS, light text on a dark background looks bold and glowing due to subpixel antialiasing; grayscale antialiasing makes it thinner and gentler. Non-standard, works only on certain platforms | -webkit-font-smoothing: antialiased; (+ -moz-osx-font-smoothing: grayscale;) | 🟡 | ✅ |
@when / @else | CSS if/else conditional blocks; still a proposal, unusable in any browser | @when supports(...){} @else{} | 🔴 | ✅ |
Scroll anchoring (overflow-anchor) | When content loads or is inserted above the viewport, the browser keeps your reading position steady instead of jumping; on by default, opt out per element with none. Those jumps are especially disorienting for people with cognitive or motor disabilities (with Safari 27, all three engines support it) | overflow-anchor: none; | 🟡 | ✅ |
Strictly speaking this section isn't CSS, but it points the same way modern CSS does: taking things that used to need a pile of JavaScript and making them declarative — and most of them affect accessibility quality directly, so they belong in the same checklist.
| Element | Main use & when to use | Example | Support | Tried |
|---|
<geolocation> | A declarative button for location requests: the browser owns the permission flow and — crucially — offers a recovery path for users who previously denied access (which used to be a dead end). Degrades to a <span> where unsupported, so it progressively enhances (tested: the browser only compares the button's own text and background colors and disables it below 3:1, but background images, blur and a removed focus outline all get through) | <geolocation onlocation="fn(event)"> | 🟠 | ✅ |
<usermedia> | Declarative camera/microphone access that hands the page a MediaStream directly, with no getUserMedia() call of your own, and lets people who blocked access recover right on the page. Video-only <camera> and audio-only <microphone> variants shipped in Chrome 153 (in my tests, lang="zh-Hant-TW" shows Simplified Chinese, there's no state once it's on, and keyboard users who press too quickly get a silent failure—see my post) | <usermedia> | 🟠 | ✅ |
<install> | An install button for PWAs whose label and appearance the browser controls (users who click "Install" aren't surprised by what happens next). Point installurl elsewhere to install apps from other origins — enough to build a catalogue page | <install installurl="…" manifestid="…"> | 🟠 | ⬜ |
<model> | Embed an interactive 3D model (USDZ) as easily as an <img>; supply multiple formats via <source> and custom lighting via environmentmap | <model src="a.usdz"> | 🟠 | ⬜ |
| Property / Feature | Main use & when to use | Example | Support | Tried |
|---|
focusgroup | Declaratively gives composite widgets (toolbars, tab lists, menus) arrow-key navigation, a guaranteed tab stop, and last-focused memory — exactly the WAI-ARIA keyboard pattern you previously had to hand-roll with roving tabindex (new in Chrome 150; it needs a behavior type, a bare focusgroup does nothing, see my test post) | <div focusgroup="toolbar">…</div> | 🟠 | ✅ |
Invoker Commands (command / commandfor) | Control a popover/dialog with declarative HTML buttons, no scripting. Landed stable: show-modal, close, request-close, toggle-popover, show-popover, hide-popover (Baseline 2025; more coming — media controls, copy text, etc.) | <button command="show-modal" commandfor="dlg">Open</button> | 🟡 | ✅ |
| Interest Invokers | The same command mechanism, but triggered by "showing interest" (hover/focus) rather than a click — enough to build a native tooltip with no JS | <button interestfor="tip"> | 🟠 | ✅ |
hidden="until-found" | Content stays collapsed but is still reachable by in-page search, and expands automatically when matched. The right way to collapse long-form sections and FAQs without making their content unfindable | <div hidden="until-found"> | 🟡 | ✅ |
popover="hint" | A popover category meant for tooltips: it keeps light-dismiss, but only closes other hints instead of tearing down your main popover with it | <div popover="hint"> | 🟡 | ✅ |
sizes="auto" | Stop hand-computing sizes for responsive images and let the browser work it out; pairs with srcset and loading="lazy" (now in Chrome, Firefox and Safari 27) (see my post testing it and fixing my portfolio) | <img srcset="…" sizes="auto"> | 🟡 | ✅ |
| Declarative Shadow DOM | Build a shadow root with no JavaScript, so SSR output can carry it | <template shadowrootmode="open"> | 🟢 | ✅ |
Element.setHTML() | A safe innerHTML: inserts markup while stripping <script>, event attributes and other dangerous markup (Sanitizer API); the counterpart to the setHTMLUnsafe() mentioned in my Declarative Shadow DOM post. Firefox 157 now sanitizes while parsing; check caniuse for other browsers (in my tests the default config strips every aria-* and role; see this post) | el.setHTML(userInput); | 🟠 | ✅ |
shadowrootreferencetarget | Lets a <label> or aria-* outside the shadow root point at the real input inside it — the long-standing labelling problem for web components (see my post testing it, with a UI Kit example) | <template shadowrootmode="open" shadowrootreferencetarget="real-input"> | 🟠 | ✅ |
<h1> nested sizing fix | UA stylesheet change: an <h1> inside a <section> is no longer shrunk automatically, so heading hierarchy finally renders predictably (see my post testing it and fixing my site's headings) | — | 🟢 | ✅ |
ariaNotify() | Ask the screen reader to announce a message directly (e.g. "Added to cart"), replacing the old trick of hiding an aria-live region and stuffing text into it. It's a DOM API rather than an HTML attribute, but what it replaces is exactly that HTML-attribute pattern (Chrome 141 and Firefox 150 onward; sources disagree on Safari, so test it yourself) | document.ariaNotify("Added to cart"); | 🟡 | ✅ |
| Feature | Main use | Support | Tried |
|---|
| Declarative Partial Updates | Streaming partial HTML updates that inject content without a navigation; a native <include> element may follow | 🔴 | ⬜ |
<persistentwidget> | Embedded content that survives same-origin navigations (a player that doesn't stop, for instance) | 🔴 | ⬜ |
HTML-in-Canvas (layoutsubtree) | Render interactive HTML onto a canvas | 🔴 | ⬜ |
Everything that used to be listed here is now checked off. These are the unchecked items I want to do next: both need Chrome 156 stable (in the 153 I tested, the clip in overflow: scroll clip still turns into hidden), and the animated-image one actually has the highest accessibility value.
- Single-axis scroll containers (
overflow: scroll clip)—a data table with both the header row and first column sticky; can it be scrolled with the keyboard? image-animation / :animated-image—lets animated GIFs be paused, mapping directly to WCAG 2.2.2.
Note: Most items marked 🟠 / 🔴 only landed in Chrome in 2025–2026 or are still proposals, and support changes every quarter. Before shipping to production, treat caniuse.com and MDN as the source of truth, and remember to add an @supports fallback for new features.