Cleared

Cleared

@cleared.page

Monthly verdicts on HTML and CSS: what just became safe to use, and the fallbacks you can finally delete.

5 followers14 following116 posts

field-sizing: content makes inputs and textareas grow and shrink with what is typed. The textarea autosize script you load on every form page is what it deletes, on new work.

View full thread

A textarea that grows as you type works in every current browser run. The misses are at the edges: Firefox will not re-fit a number input after a script changes its value, and Safari draws a search input’s caret taller than its placeholder.

field-sizing became newly available in June, so some of your readers are still catching up. For them the failure is the one you already ship: a fixed-size box with a scrollbar. Verdict: Cleared. cleared.page/2026-06/

field-sizing: content makes inputs and textareas grow and shrink with what is typed. The textarea autosize script you load on every form page is what it deletes, on new work.

textarea { field-sizing: content; min-height: 4rem; max-height: 16rem; } Three declarations for what used to be a scroll-height measurement on every keystroke.

Hand it a background color and it returns white or black, whichever contrasts more. What it promises to delete is every hand-maintained pair of background and text custom properties.

View full thread

Chrome, Edge and Firefox now pass the whole suite; Firefox’s two failures are fixed since April. Safari still fails to repaint a gradient whose color stop uses contrast-color(currentColor).

One more thing the name does not say: it maximizes contrast, it guarantees none. Against a mid-gray background the winner clears no bar, it just loses less. Verdict: Hold. Pick the text color yourself and you know what it will be. cleared.page/2026-04/

Hand it a background color and it returns white or black, whichever contrasts more. What it promises to delete is every hand-maintained pair of background and text custom properties.

.badge { background: var(--accent); color: contrast-color(var(--accent)); } Which of white or black wins on a mid-tone background is the browser’s call, not yours.

That is the problem. The spec is an editor’s draft still carrying the banner “This spec is not yet ready for implementation,” and in the form browsers ship it leaves the choice of contrast algorithm to the browser. Two engines can legitimately disagree.

A clip-path drawn with path commands in CSS syntax: lines, curves, arcs, in any CSS unit. path(), which it improves on, takes one SVG string and allows only px. shape() takes percentages, em, var() and calc().

View full thread

The scoreboard argues. webstatus.dev shows stable Safari passing a third of the suite. That reads like a Hold, and it is not: Safari fails seven subtests, all the same complaint. It keeps the default from start and from origin keywords when style is read back.

Nothing renders differently. Every geometry subtest passes in all four current stable runs. If your tests compare getComputedStyle strings for clip-path, Safari’s extra keywords will trip them. That is the entire defect. Verdict: Cleared. cleared.page/2026-02/

A clip-path drawn with path commands in CSS syntax: lines, curves, arcs, in any CSS unit. path(), which it improves on, takes one SVG string and allows only px. shape() takes percentages, em, var() and calc().

So a curved shape can finally follow the box it clips. clip-path: shape(from 0 0, line to 100% 0, line to 100% 85%, curve to 0 100% with 50% 80%);

Declare a fallback on the line above it: clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%); A browser without shape() drops the declaration it cannot parse and keeps the earlier one. One line, and the shape degrades to a straight-edged version of itself.

Two new CSS functions. sibling-index() gives an element’s position among its siblings, starting at 1, and sibling-count() gives how many there are. Both return plain numbers you can use in calc().

View full thread

The trouble starts when the index is read somewhere other than a plain declaration. Safari 26.6 passes 3 of the suite’s 22 tests whole: it fails the index inside a gradient, inside a container query, and inside a shadow tree.

Safari and Firefox both fail it inside a keyframes block when the list then changes. Remove an element before the animated one and the animation keeps the old number. Outside keyframes, the same removal updates correctly everywhere. Verdict: Hold. cleared.page/2026-08/

Two new CSS functions. sibling-index() gives an element’s position among its siblings, starting at 1, and sibling-count() gives how many there are. Both return plain numbers you can use in calc().

li { animation-delay: calc(sibling-index() * 80ms); } That works in every current run. A staggered animation on a list that does not change is the one thing you can do with this today.

If your images ship as a three-format stack, AVIF on top, WebP in the middle, JPEG at the floor, the middle is now dead weight. AVIF finished its 30 months of browser support on July 25.

View full thread

Most sites never built this stack. Search your templates for type="image/webp": if nothing comes back, or the floor under your sources is itself a .webp, there is no middle to delete and nothing to do.

One caveat. If you ship animated or progressively encoded AVIF, test those files in Firefox, which still fails one of the suite’s two rendering tests. Verdict: Cleared, safe everywhere since July 25. cleared.page/2026-07/

If your images ship as a three-format stack, AVIF on top, WebP in the middle, JPEG at the floor, the middle is now dead weight. AVIF finished its 30 months of browser support on July 25.

Before: <source srcset="chart.avif" type="image/avif"> <source srcset="chart.webp" type="image/webp"> <img src="chart.jpg" alt="Monthly revenue"> After: the same, minus the middle line.

The floor stays. AVIF is about 93% of global traffic on caniuse’s counters, so a bare .avif with no fallback is a broken image for about one visitor in fifteen. Delete the middle of the stack, never the bottom.

A correction. In June I told you to hold your mask fallbacks because Safari failed three mask-repeat tests. I misread what those tests cover. Most of you can delete the fallback.

View full thread

Keep it when you mask SVG content, when a mask stacks more than one layer, or when you use mask-clip, mask-origin, mask-composite or mask-mode. Safari 26.6 still fails every one of those.

Masks finished their 30 months on June 7. The verdict was “Hold,” corrected to “Cleared” on August 27 with the exceptions above. The correction is on the issue and in the ledger. cleared.page/2026-06/

A correction. In June I told you to hold your mask fallbacks because Safari failed three mask-repeat tests. I misread what those tests cover. Most of you can delete the fallback.

The tests are named mask-repeat-1-svg, -2-svg and -3-svg. I read the -svg as naming the mask source, the image doing the masking. It names the masked content, the element underneath the mask.

Masking an HTML element is the common case, and Safari handles it. If that is what your mask does, delete the fallback.

Style ranges of text from script without wrapping them in elements: register a Highlight, style it with ::highlight(). It has been newly available since March. One browser is still holding it back.

View full thread

Safari’s failures are the visible kind: highlight painting over text sized with font-relative or container-relative units, vertical writing mode, and text shadows inside highlights.

Style ranges of text from script without wrapping them in elements: register a Highlight, style it with ::highlight(). It has been newly available since March. One browser is still holding it back.

::highlight(search-match) { background-color: Mark; } What that deletes is the span-wrapping your find-in-page or editor code does today, purely to get a background behind a range.

Chrome and Edge now pass 99% of the suite and Firefox 95%, up from 70% when I wrote this up in March. Safari passes 65%.

A web component keeps its markup and styles in a shadow root, and until recently the only way to make one was to call attachShadow() from JavaScript. You can write it in the HTML now, and delete the script that used to do it.

View full thread

One thing to check first. These shadow roots are only created as the browser parses the page. Any innerHTML that inserts markup containing shadowrootmode creates none: the template stays a template. Switch those calls to setHTMLUnsafe().

Declarative shadow DOM finished its 30 months on August 20. Verdict: Cleared. Keep the script for components that are HTML only, with no JavaScript to render them: those break completely without it. cleared.page/2026-08/

A web component keeps its markup and styles in a shadow root, and until recently the only way to make one was to call attachShadow() from JavaScript. You can write it in the HTML now, and delete the script that used to do it.

Before, on every page: document.querySelectorAll('template[shadowrootmode]').forEach((t) => { const mode = t.getAttribute('shadowrootmode'); t.parentNode.attachShadow({ mode }).append(t.content); t.remove(); });

After: <template shadowrootmode="open"> … </template> The parser turns it into that element’s shadow root as it reads the page. A server can send a component fully rendered, and it looks right before any script has loaded.

Seven CSS pseudo-classes match a video or audio element by what it is doing: :playing, :paused, :seeking, :buffering, :stalled, :muted and :volume-locked. Baseline says they are newly available. Chrome has not shipped them.

View full thread

The label rests on one browser compatibility data entry saying Chrome 152 shipped these on August 25. Chrome’s own status tracker lists the feature as proposed, targeting Chrome 155. Every stable Chrome run since 152 fails all six checks in the suite, and Edge fails the same way.

Safari passes everything and Firefox nearly everything. Chrome’s pre-release channel passes too, so the implementation exists. It is not in the browser most of your visitors are running. Verdict: Hold. cleared.page/2026-08/

Seven CSS pseudo-classes match a video or audio element by what it is doing: :playing, :paused, :seeking, :buffering, :stalled, :muted and :volume-locked. Baseline says they are newly available. Chrome has not shipped them.

If you have built a custom player, you listen for play and pause and toggle a class so the CSS can swap the button. That listener is what these would delete: .player.is-playing .play-button { display: none; } .player:has(video:playing) .play-button { display: none; }

Your CSS can nest itself now, natively. The build step that exists only to expand nesting can go, and your source stays exactly as written.

View full thread

The two agree until a parent selector list mixes specificity levels. Search your source for comma-separated parent selectors containing an ID and check those. Everything else expands identically.

Your CSS can nest itself now, natively. The build step that exists only to expand nesting can go, and your source stays exactly as written.

One difference to know before you switch. A nested rule resolves the way :is() does: & takes the specificity of the most specific selector in the parent list. A preprocessor expands the same source into a plain selector list, where each branch keeps its own.

#a, b { & c { color: blue; } /* & is [1,0,0] for both, so [1,0,1] */ } Your build step expanded that to: #a c { } /* [1,0,1], same */ b c { } /* [0,0,2], weaker than the native version */

On August 28, Baseline said the media element pseudo-classes were newly available. Chrome hadn’t shipped them. Every stable run failed every test. Issue 08 said Hold and named the data as the reason. cleared.page/2026-08/