{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Cleared",
  "description": "A monthly letter on the browser platform: the features that just became safe, the fallbacks you can finally remove, and a verdict on each one. Free.",
  "home_page_url": "https://cleared.page/",
  "feed_url": "https://cleared.page/feed.json",
  "language": "en",
  "authors": [
    {
      "name": "Yann",
      "url": "https://minimaldesign.net/"
    }
  ],
  "items": [
    {
      "id": "https://cleared.page/2026-06/",
      "url": "https://cleared.page/2026-06/",
      "title": "Nesting cleared: delete a build step",
      "summary": "What became safe to use, and to delete, in June 2026.",
      "content_html": "<p>June was a deletion month. A single Firefox release produced exactly one new arrival, while sixteen features quietly finished their 30 months of support. The best of them is nesting: your CSS can nest itself now, and a build step can go.</p>\n<h2>01. Nesting</h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 11 June 2026</p><p>CSS rules can nest inside other rules, natively.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the build step that exists only to expand nesting. The source stays as written.</p>\n<p>One seam. A nested rule resolves the way <code>:is()</code> does: <code>&#x26;</code> 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.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">#</span><span style=\"color:var(--ink);font-weight:bold\">a</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> b</span><span style=\"color:var(--muted)\"> {</span></span>\n<span class=\"line\"><span style=\"color:var(--ink)\">  &#x26; c { </span><span style=\"color:var(--ink);font-weight:bold\">color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> blue</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* &#x26; is [1,0,0] for both, so [1,0,1] */</span></span>\n<span class=\"line\"><span style=\"color:var(--ink)\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">/* Your build step expanded that to: */</span></span>\n<span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">#</span><span style=\"color:var(--ink);font-weight:bold\">a c</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* [1,0,1], same */</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">b c</span><span style=\"color:var(--muted)\">  {</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* [0,0,2], weaker than the native version */</span></span></code></pre></figure>\n<p>The two agree until a parent selector list mixes specificity levels. Before switching, search your source for comma-separated parent selectors containing an ID.</p>\n<h2>02. Lazy-loading images and iframes</h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 19 June 2026</p><p><code>loading=\"lazy\"</code> defers offscreen images and iframes until the user scrolls near them.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the lazy-loading script and its <code>data-src</code> markup.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>HTML · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">img</span><span style=\"color:var(--ink);font-weight:bold\"> data-src</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">chart.png</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-weight:bold\"> class</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">lazyload</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">script</span><span style=\"color:var(--muted)\">></span><span style=\"color:var(--muted)\">/* a script you no longer need */</span><span style=\"color:var(--muted)\">&#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">script</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">img</span><span style=\"color:var(--ink);font-weight:bold\"> src</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">chart.png</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-weight:bold\"> loading</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">lazy</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span></span></code></pre></figure>\n<p>The core image and iframe cases pass their tests in every browser. What still fails anywhere is edge cases: an image whose URL is changed repeatedly from script, base-URL changes mid-load, lazy iframes during unusual navigations. Keep the image at the top of the page eager; deferring what is already on screen only delays it.</p>\n<h2>03. <code>:has()</code></h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 19 June 2026</p><p>The parent selector: style an element by what it contains. It appears on about one in two hundred page loads.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the JavaScript that watches children and toggles state classes on their parents.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">/* a script adds .has-error to the form */</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">has-error</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> border-color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> firebrick</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--muted);font-weight:bold\">:</span><span style=\"color:var(--ink);font-weight:bold\">has</span><span style=\"color:var(--muted);font-weight:bold\">(</span><span style=\"color:var(--ink);font-weight:bold\">input</span><span style=\"color:var(--muted);font-weight:bold\">:</span><span style=\"color:var(--ink);font-weight:bold\">invalid</span><span style=\"color:var(--muted);font-weight:bold\">)</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> border-color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> firebrick</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>One boundary, from Safari’s current test run. Parent, ancestor, adjacent and sibling positions all match Chrome exactly: <span class=\"count\">211 of 211</span>, <span class=\"count\">337 of 337</span>, <span class=\"count\">295 of 295</span>, <span class=\"count\">295 of 295</span>. The gap is re-matching while a script inserts and removes siblings: <span class=\"count\">14 of 28</span>. If a <code>:has()</code> rule watches a sibling list under live mutation, keep the class for that one case.</p>\n<h2>04. <code>field-sizing</code></h2><p><strong>Verdict: Cleared</strong> · New this month, 16 June 2026</p><p><code>field-sizing: content</code> makes inputs and textareas grow and shrink with what is typed. June’s only new arrival, completed by Firefox 152.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the textarea autosize script, on new work.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">textarea</span><span style=\"color:var(--muted)\"> {</span></span>\n<span class=\"line\"><span style=\"color:var(--ink)\">  field-sizing</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> content</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">  min-height</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 4</span><span style=\"color:var(--ink);font-weight:bold\">rem</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">  max-height</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 16</span><span style=\"color:var(--ink);font-weight:bold\">rem</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">}</span></span></code></pre></figure>\n<p>Textarea sizing and resizing pass in every current browser run. The one failure anywhere is Firefox re-fitting a number input after a script changes its value; textareas are unaffected. A month of Baseline means some of your users are still catching up, and for them the failure is the one you already ship: a fixed-size box with a scrollbar.</p>\n<h2>05. Masks</h2><p><strong>Verdict: Hold</strong> · Not yet</p><p>CSS masks crossed 30 months of support on 7 June, and appear on about 0.4% of page loads. The label says delete your fallbacks. The tests say not yet.</p>\n<p>In Safari 26.5.2’s current run, masks repeat correctly from a raster image and fail the same three tests when the mask source is an SVG: <code>mask-repeat-1-svg</code>, <code>-2-svg</code> and <code>-3-svg</code>. Chrome passes all six. An SVG source is the common case, and a mask that fails to apply does not degrade: it reveals or hides the wrong thing. Keep whatever fallback you have until Safari’s SVG path catches up.</p>\n",
      "date_published": "2026-07-06T00:00:00.000Z",
      "tags": [
        "4 cleared",
        "1 hold"
      ]
    },
    {
      "id": "https://cleared.page/2026-05/",
      "url": "https://cleared.page/2026-05/",
      "title": "Delete your form's 'touched' classes",
      "summary": "What became safe to use, and to delete, in May 2026.",
      "content_html": "<p>Seven features became newly available in May, and five finished their 30 months of support. Four of the seven arrivals were old features that had been waiting on Chrome or Chrome Android; desktop Chrome has had shared workers since 2010, and Chrome Android shipped them this May. The thing worth deleting is in your forms.</p>\n<h2>01. <code>:user-valid</code> and <code>:user-invalid</code></h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 2 May 2026</p><p>Validity pseudo-classes that wait for the user. They match like <code>:valid</code> and <code>:invalid</code>, but only once the field has been altered, so a pristine required field stays quiet. A submit attempt counts as interaction too.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the script that listens for blur and input events just to add a “touched” class, so error styles don’t fire before the user has typed anything.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">/* a script marks fields the user has visited */</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink);font-weight:bold\">input</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">touched</span><span style=\"color:var(--muted);font-weight:bold\">:</span><span style=\"color:var(--ink);font-weight:bold\">invalid</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> border-color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> firebrick</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">/* the browser tracks interaction itself */</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">input:user-invalid</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> border-color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> firebrick</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>The core suite passes in every current stable run. One edge is unsettled: when these flip mid-edit inside multi-part fields like date inputs is covered only by a test still marked tentative, and Chrome is currently the only engine passing it. If your validation styling leans on the exact timing inside date or time inputs, keep the class for those. In a browser too old to have these, the rule never matches: no inline highlighting, not a broken form.</p>\n<h2>02. <code>lh</code> unit</h2><p><strong>Verdict: Hold</strong> · Not yet</p><p>One line-height, as a unit: <code>3lh</code> is three lines tall. It crossed 30 months of support on 21 May, and the deletion it promises is every magic number that hard-codes a line-height multiple.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--muted)\">/* The deletion waiting here: */</span></span>\n<span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">toast</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> min-height</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink);font-weight:bold\"> calc</span><span style=\"color:var(--muted)\">(</span><span style=\"color:var(--ink)\">3 </span><span style=\"color:var(--ink);font-weight:bold\">*</span><span style=\"color:var(--ink)\"> 1.4</span><span style=\"color:var(--ink);font-weight:bold\">em</span><span style=\"color:var(--muted)\">);</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* hoping 1.4 still matches */</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">/* Blocked by Safari, for now: */</span></span>\n<span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">toast</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> min-height</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 3lh</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>The blocker is specific. Safari 26.5.2’s current run fails the suite’s inherited-line-height test: an element whose <code>line-height</code> arrives from its parent, which is how line-height usually arrives, resolves <code>1lh</code> against the wrong value. The other eight tests pass there, and Chrome, Edge and Firefox pass everything. Until that fix ships, the magic number is more predictable than the unit that replaces it. Its root-relative sibling <code>rlh</code> cleared the same day and appears on about one page load in three million.</p>\n<h2>03. Container style queries</h2><p><strong>Verdict: Hold</strong> · Not yet</p><p>Style queries let a component ask what a custom property is set to on its container. Firefox 151 shipped the last piece on 19 May, so the label now says Baseline. The label covers more than the browsers agree on.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">@</span><span style=\"color:var(--ink);font-weight:bold\">container</span><span style=\"color:var(--ink)\"> style(--density: compact) </span><span style=\"color:var(--muted)\">{</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* passes everywhere */</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">@</span><span style=\"color:var(--ink);font-weight:bold\">container</span><span style=\"color:var(--ink)\"> style(--columns > 2) </span><span style=\"color:var(--muted)\">{</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> /* full marks only in Firefox */</span></span></code></pre></figure>\n<p>The equality form passes its tests in the current Chrome, Edge, Firefox and Safari runs. The range form is another story: the dynamic range suite passes <span class=\"count\">7 of 7</span> in Firefox, <span class=\"count\">2 of 7</span> in Chrome, <span class=\"count\">1 of 7</span> in Safari and Edge. Tests for attribute-driven values fail in all four. If you treat the equality form as a progressive enhancement, nothing stops you today. The Hold is for the feature as labelled, because the label does not tell you where the seam is.</p>\n<p>That was May: one deletion, and two labels running ahead of their implementations.</p>",
      "date_published": "2026-06-08T00:00:00.000Z",
      "tags": [
        "1 cleared",
        "2 hold"
      ]
    },
    {
      "id": "https://cleared.page/2026-04/",
      "url": "https://cleared.page/2026-04/",
      "title": "Delete your role=\"search\" wrapper",
      "summary": "What became safe to use, and to delete, in April 2026.",
      "content_html": "<p>A thin month, and a backwards one. Baseline’s rhythm is usually set by Safari, the browser that ships least often. April’s two arrivals were both waiting on the Chromium side: Firefox and Safari had shipped <code>contrast-color()</code> and <code>Math.sumPrecise()</code> months earlier, and Chrome 147 landed on 7 April with Edge two days behind. Four more features finished their 30 months of support. One of them is a deletion.</p>\n<h2>01. <code>&lt;search&gt;</code></h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 13 April 2026</p><p>An HTML element for the region that holds your search box. It maps to the same search landmark the role attribute gave you, so screen reader users can jump straight to it without you writing any ARIA.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the <code>role=\"search\"</code> attribute, and usually the <code>&#x3C;div></code> it sits on.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>HTML · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;!-- a div doing an element's job --></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">div</span><span style=\"color:var(--ink);font-weight:bold\"> role</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">search</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">  &#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--ink);font-weight:bold\"> action</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">/search</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span><span style=\"color:var(--ink)\">…</span><span style=\"color:var(--muted)\">&#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">div</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">search</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">  &#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--ink);font-weight:bold\"> action</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">/search</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span><span style=\"color:var(--ink)\">…</span><span style=\"color:var(--muted)\">&#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">form</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">search</span><span style=\"color:var(--muted)\">></span></span></code></pre></figure>\n<p>One styling seam. Browsers that know the tag give it <code>display: block</code> in the user agent stylesheet. A browser too old to know it gives it nothing, and the initial value of <code>display</code> is <code>inline</code>. If old browsers matter to you, declare <code>search { display: block }</code> yourself: one line, inert wherever the browser already does it. Beyond that, failure is graceful. The contents render everywhere, and an old browser loses the landmark, not the page.</p>\n<p>A note on the numbers: the summary scoreboard currently shows Safari at zero on this feature. The tests underneath disagree. In Safari’s current stable run, every test with the element’s name in its path passes, and the role-mapping suite passes whole. The zero is the scoreboard’s, not Safari’s.</p>\n<h2>02. <code>contrast-color()</code></h2><p><strong>Verdict: Hold</strong> · Not yet</p><p>Hand it a background color and it returns white or black, whichever contrasts more. New this month, 10 April 2026. The deletion it promises is every hand-maintained pair of background and text custom properties.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">badge</span><span style=\"color:var(--muted)\"> {</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">  background</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink);font-weight:bold\"> var</span><span style=\"color:var(--muted)\">(</span><span style=\"color:var(--ink)\">--accent</span><span style=\"color:var(--muted)\">);</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">  color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> contrast-color(</span><span style=\"color:var(--ink);font-weight:bold\">var</span><span style=\"color:var(--muted)\">(</span><span style=\"color:var(--ink)\">--accent</span><span style=\"color:var(--muted)\">)</span><span style=\"color:var(--ink)\">)</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">/* Which of white or black wins on a mid-tone</span></span>\n<span class=\"line\"><span style=\"color:var(--muted)\">   background is the browser's call, not yours. */</span></span></code></pre></figure>\n<p>The label is running ahead of the paperwork. The function’s spec is an editor’s draft that still carries the banner “This spec is not yet ready for implementation”, and in the only form browsers ship, with no target contrast stated, the spec leaves the choice of contrast algorithm to the browser. Two engines can legitimately disagree about which of white or black a mid-tone background gets. The tests say the seams are real: Firefox’s current stable run fails the case where <code>contrast-color()</code> feeds a container style query, Safari’s fails to repaint a gradient whose color stop uses <code>contrast-color(currentColor)</code>, and each of the four engines drops one of the seventeen valid-syntax parses. 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. For now, picking the text color yourself is the only way to know what it will be.</p>\n<p>That was April: one attribute to delete, and one new function whose own spec asks you to wait. The other four transitions gave you nothing to do, which is the honest truth about most features in most months.</p>",
      "date_published": "2026-05-04T00:00:00.000Z",
      "tags": [
        "1 cleared",
        "1 hold"
      ]
    },
    {
      "id": "https://cleared.page/2026-03/",
      "url": "https://cleared.page/2026-03/",
      "title": "Delete your equal-height card hack",
      "summary": "What became safe to use, and to delete, in March 2026.",
      "content_html": "<p>Eight of March’s ten arrivals landed on a single day, 24 March, when Firefox 149 and Safari 26.4 both shipped. One was baseline-shift, a property Chrome has carried since 2008. But the month’s real business is in the eleven features that finished their 30 months of support, because three of them let you delete something. Start with your card grids.</p>\n<h2>01. Subgrid</h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 15 March 2026</p><p>A nested grid can adopt its parent’s rows or columns instead of declaring its own: the track sizes come from the parent, and the subgrid’s items feed their sizes back up.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the script that measures card sections and equalises their heights, or the fixed row heights doing the same job with magic numbers.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">/* every card hard-codes the shared row plan */</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">card</span><span style=\"color:var(--muted)\">  {</span><span style=\"color:var(--ink);font-weight:bold\"> display</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> grid</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--ink);font-weight:bold\"> grid-template-rows</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 3</span><span style=\"color:var(--ink);font-weight:bold\">rem</span><span style=\"color:var(--ink)\"> 6</span><span style=\"color:var(--ink);font-weight:bold\">rem</span><span style=\"color:var(--ink)\"> 2</span><span style=\"color:var(--ink);font-weight:bold\">rem</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">/* cards borrow their rows from the grid they sit in */</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">cards</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> display</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> grid</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--ink);font-weight:bold\"> grid-template-columns</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink);font-weight:bold\"> repeat</span><span style=\"color:var(--muted)\">(</span><span style=\"color:var(--ink)\">3</span><span style=\"color:var(--muted)\">,</span><span style=\"color:var(--ink)\"> 1</span><span style=\"color:var(--ink);font-weight:bold\">fr</span><span style=\"color:var(--muted)\">);</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">         grid-template-rows</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink);font-weight:bold\"> repeat</span><span style=\"color:var(--muted)\">(</span><span style=\"color:var(--ink)\">3</span><span style=\"color:var(--muted)\">,</span><span style=\"color:var(--ink)\"> auto</span><span style=\"color:var(--muted)\">);</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">card</span><span style=\"color:var(--muted)\">  {</span><span style=\"color:var(--ink);font-weight:bold\"> display</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> grid</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--ink);font-weight:bold\"> grid-row</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> span 3</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">         grid-template-rows</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> subgrid</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>Track sizing, gaps and line names pass in all four current stable runs. The one seam is baseline alignment: Firefox still fails most of the suite’s baseline tests inside subgrids, so if your grid code contains <code>align-items: baseline</code> or <code>align-self: baseline</code>, check Firefox before deleting. That is the search. In a browser without subgrid the declaration drops and rows fall back to auto: misaligned cards, not broken ones.</p>\n<h2>02. Hyphenation</h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 18 March 2026</p><p><code>hyphens: auto</code> breaks words at dictionary-approved points. Safari 17 completed the set in September 2023; the 30 months ran out on 18 March. It already appears on about one page load in a thousand.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the script or build step that sprays soft hyphens into your text.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>HTML · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;!-- generated markup --></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink)\">Silben</span><span style=\"color:var(--muted);font-style:italic\">&#x26;</span><span style=\"color:var(--ink);font-style:italic\">shy</span><span style=\"color:var(--muted);font-style:italic\">;</span><span style=\"color:var(--ink)\">trennung</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;!-- requires lang=\"de\" (or whatever applies) on the markup --></span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">style</span><span style=\"color:var(--muted)\">></span><span style=\"color:var(--ink);font-weight:bold\"> article</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> hyphens</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> auto</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span><span style=\"color:var(--muted)\"> &#x3C;/</span><span style=\"color:var(--ink);font-weight:bold\">style</span><span style=\"color:var(--muted)\">></span></span></code></pre></figure>\n<p>The boundary is the language. The spec only requires hyphenation where the content language is known and the browser has a dictionary for it, so <code>hyphens: auto</code> without a <code>lang</code> attribute is a no-op by design. Coverage is also uneven: the suite’s eleven language-specific tests currently fail in all four stable runs, which names no single blocker but does say to spot-check the languages you actually publish. Failure is the gentle kind: no dictionary, no hyphens, a ragged edge.</p>\n<h2>03. <code>image-set()</code></h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 18 March 2026</p><p>Resolution switching for CSS backgrounds, the <code>srcset</code> of <code>background-image</code>. Chromium dropped the prefix in May 2023; Safari 17 was the last piece.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the <code>-webkit-image-set()</code> line.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink);font-weight:bold\">background-image: url(hero</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">png</span><span style=\"color:var(--ink)\">);</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink);font-weight:bold\">background-image: -webkit-image-set(url(hero</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">png</span><span style=\"color:var(--ink)\">) 1</span><span style=\"color:var(--ink);font-weight:bold\">x</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> url(hero</span><span style=\"color:var(--muted);font-weight:bold\">@</span><span style=\"color:var(--ink);font-weight:bold\">2x</span><span style=\"color:var(--ink)\">.png) 2x)</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--ink);font-weight:bold\">background-image: image-set(url(hero</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">png</span><span style=\"color:var(--ink)\">) 1</span><span style=\"color:var(--ink);font-weight:bold\">x</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> url(hero</span><span style=\"color:var(--muted);font-weight:bold\">@</span><span style=\"color:var(--ink);font-weight:bold\">2x</span><span style=\"color:var(--ink)\">.png) 2x)</span><span style=\"color:var(--muted)\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">/* keep the plain url() line */</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">background-image: url(hero</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">png</span><span style=\"color:var(--ink)\">);</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">background-image: image-set(url(hero</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">png</span><span style=\"color:var(--ink)\">) 1</span><span style=\"color:var(--ink);font-weight:bold\">x</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> url(hero</span><span style=\"color:var(--muted);font-weight:bold\">@</span><span style=\"color:var(--ink);font-weight:bold\">2x</span><span style=\"color:var(--ink)\">.png) 2x)</span><span style=\"color:var(--muted)\">;</span></span></code></pre></figure>\n<p>Every rendering test passes in all four current stable runs; the only misses anywhere are a handful of parsing subtests, 140 to <span class=\"count\">142 of 144</span>. Do keep the plain <code>url()</code> line: in a browser outside the core set that lacks <code>image-set()</code>, it is the difference between a low-resolution image and no image.</p>\n<h2>04. Custom highlights</h2><p><strong>Verdict: Hold</strong> · Not yet</p><p>Style ranges of text from script without wrapping them in elements: register a <code>Highlight</code>, style it with <code>::highlight()</code>. Firefox 149 shipped the last piece on 24 March.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">::highlight(search-match</span><span style=\"color:var(--ink)\">) </span><span style=\"color:var(--muted)\">{</span><span style=\"color:var(--ink);font-weight:bold\"> background-color</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> Mark</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>The browser that flipped the label is not the problem: Firefox passes 95% of the stable suite, Chrome and Edge 94%. Safari’s current stable run passes 66%, and the failures are the visible kind: highlight painting over text sized with font-relative or container-relative units, text shadows inside highlights, vertical writing mode. Safari has had the feature since late 2023, and its preview run now passes the full suite, so this Hold has an end in sight. Until that ships, the span-wrapping you already do is the more predictable renderer.</p>\n<h2>05. <code>text-indent: hanging</code></h2><p><strong>Verdict: Cleared</strong> · New this month, 13 March 2026</p><p>One keyword inverts <code>text-indent</code>, so every line except the first is indented: the hanging indent of bibliographies and transcripts.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the two-declaration pair that fakes it.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">/* indent everything, pull the first line back */</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">ref</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> padding-left</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 2</span><span style=\"color:var(--ink);font-weight:bold\">em</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--ink);font-weight:bold\"> text-indent</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> -2</span><span style=\"color:var(--ink);font-weight:bold\">em</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">ref</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> text-indent</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> 2</span><span style=\"color:var(--ink);font-weight:bold\">em</span><span style=\"color:var(--ink)\"> hanging</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>Chromium shipped it on 10 March, two weeks before the label; Safari has had it since 2021, Firefox since 2023. Every test passes in every current stable run, mobile included, which is rare enough this month to be worth saying. In an older browser the declaration is invalid and drops: lines sit flush, nothing breaks. Its sibling <code>each-line</code> went Baseline the same day.</p>\n<p>That was March: four deletions and one wait, courtesy of a Safari run that says what the label does not.</p>",
      "date_published": "2026-04-06T00:00:00.000Z",
      "tags": [
        "4 cleared",
        "1 hold"
      ]
    },
    {
      "id": "https://cleared.page/2026-02/",
      "url": "https://cleared.page/2026-02/",
      "title": "Delete your RTL-detection script",
      "summary": "What became safe to use, and to delete, in February 2026.",
      "content_html": "<p>Five features became newly available in February, and exactly one finished its 30 months of support. Four of the five arrivals live in JavaScript, WebAssembly, HTTP compression and a security API, outside this newsletter’s beat, so this is a thin month with two items: one CSS arrival worth starting now, and one deletion fourteen years in the making.</p>\n<h2>01. <code>dirname</code></h2><p><strong>Verdict: Cleared</strong> · Safe everywhere as of 1 February 2026</p><p>An attribute for text fields. Add <code>dirname</code> and the browser submits a second field alongside the value: the direction the text was typed in, <code>ltr</code> or <code>rtl</code>. Chrome and Safari have shipped it since 2012. Firefox 116 completed the set in August 2023, and the 30-month clock ran out on 1 February.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the direction-guessing code. If your site stores user text that might be Arabic or Hebrew, something today inspects each comment to decide whether it needs <code>dir=\"rtl\"</code> when displayed again, a client script or a server-side heuristic. The browser already knows; let it tell you.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>HTML · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;!-- a script sniffs the value, fills a hidden field --></span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">input</span><span style=\"color:var(--ink);font-weight:bold\"> type</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">text</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-weight:bold\"> name</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">comment</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;!-- the browser reports direction at submission --></span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;</span><span style=\"color:var(--ink);font-weight:bold\">input</span><span style=\"color:var(--ink);font-weight:bold\"> type</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">text</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-weight:bold\"> name</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">comment</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-weight:bold\"> dirname</span><span style=\"color:var(--muted)\">=</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--ink);font-style:italic\">comment.dir</span><span style=\"color:var(--muted);font-style:italic\">\"</span><span style=\"color:var(--muted)\">></span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">&#x3C;!-- comment=%D9%85%D8%B1%D8%AD%D8%A8%D8%A7&#x26;comment.dir=rtl --></span></span></code></pre></figure>\n<p>One seam at the edges. On input types where the spec says the attribute does not apply, current stable runs disagree: Firefox 153 and Safari 26.5.2 submit the direction pair for a <code>month</code> input anyway, while Chrome 151 and Edge 150 omit it. Keep <code>dirname</code> on plain text fields and textareas, where every current run passes, and treat the extra field as optional on the server. A browser too old to know the attribute never sends it: your default direction applies, and nothing breaks.</p>\n<h2>02. <code>shape()</code></h2><p><strong>Verdict: Cleared</strong> · New this month, 24 February 2026</p><p>A clip-path drawn with path commands in CSS syntax: lines, curves, arcs, in any CSS unit. The <code>path()</code> function it improves on takes one SVG string and, as the spec puts it, implicitly allows only the <code>px</code> unit. The spec defines <code>shape()</code> as a superset of <code>path()</code>: the same commands, but percentages, <code>em</code>, <code>var()</code> and <code>calc()</code> all work, so a curved shape can finally follow the box it clips.</p>\n<p>The scoreboard argues. webstatus.dev shows stable Safari passing a third of the suite, which reads like a Hold. It is not. The current Safari 26.5.2 run fails seven subtests, and every one is the same complaint: when style is read back, Safari keeps the default <code>from start</code> and <code>from origin</code> keywords instead of dropping them from the serialization. Every parsing and computing subtest for the geometry itself passes, as it does in the current Chrome, Edge and Firefox runs. Nothing renders differently.</p>\n<p>Firefox shipped it last, on 24 February, so declare a fallback on the line above; a browser without <code>shape()</code> drops the declaration it cannot parse and keeps the earlier one:</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample\"><code><span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">clip-path: polygon(0 0</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> 100% 0</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> 100% 85%</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> 0 100%</span><span style=\"color:var(--ink)\">); </span><span style=\"color:var(--muted)\">/* older browsers stop here */</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">clip-path: shape(from 0 0</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> line to 100% 0</span><span style=\"color:var(--muted);font-weight:bold\">,</span></span>\n<span class=\"line\"><span style=\"color:var(--ink);font-weight:bold\">                 line to 100% 85%</span><span style=\"color:var(--muted);font-weight:bold\">,</span><span style=\"color:var(--ink);font-weight:bold\"> curve to 0 100% with 50% 80%</span><span style=\"color:var(--ink)\">);</span></span></code></pre></figure>\n<p>Two footnotes. If your tests compare <code>getComputedStyle</code> strings for <code>clip-path</code>, Safari’s extra keywords will trip them; that is the entire defect. And Firefox Android has no current measurement: its newest test run predates the release that shipped the feature, so the low score there is a blank, not a fail. If it did turn out broken, the fallback line above is what those users would see.</p>\n<p>That was February: a thin month for arrivals, and one deletion that waited fourteen years for its clock to run out.</p>",
      "date_published": "2026-03-09T00:00:00.000Z",
      "tags": [
        "2 cleared"
      ]
    },
    {
      "id": "https://cleared.page/2026-01/",
      "url": "https://cleared.page/2026-01/",
      "title": "Delete your 'is-transitioning' class",
      "summary": "What became safe to use, and to delete, in January 2026.",
      "content_html": "<p>January was one release wide. Firefox 147, on 13 January, closed out all seven of the month’s arrivals by itself; nothing else moved. Four of the seven are the root-font versions of existing length units, full marks everywhere and nothing to do with them. The Navigation API reached Baseline the same day, but it is JavaScript, and this letter’s beat is CSS and HTML; it lives in the archive links below. The deletion this month belongs to everyone already running view transitions.</p>\n<h2>01. <code>:active-view-transition</code></h2><p><strong>Verdict: Cleared</strong> · New this month, 13 January 2026</p><p>A pseudo-class that matches the root element while a view transition is running, and stops matching when it settles. Chromium has shipped it since May 2024; Firefox 147 was the last browser in.</p>\n<p class=\"issueItem_delete\"><strong>Delete:</strong> the script that adds a marker class to the root before <code>document.startViewTransition()</code> and removes it when the transition finishes, just so your stylesheet can tell a transition is underway.</p>\n<figure class=\"codeBlock\"><figcaption class=\"codeBlock_caption\"><span>CSS · before <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" class=\"arrow arrow-lead\"><path d=\"M5 12h14\"></path><path d=\"m12 5 7 7-7 7\"></path></svg> after</span><button type=\"button\" class=\"codeBlock_copy\" data-copy=\"\" aria-live=\"polite\">Copy</button></figcaption><pre class=\"codeBlock_pre\" style=\"background-color:var(--code-bg);color:var(--ink)\" tabindex=\"0\" role=\"region\" aria-label=\"Code sample, before and after\"><code><span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted)\">/* a script toggles this class on &#x3C;html> */</span></span>\n<span class=\"codeBlock_line-removed\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">−</span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">is-transitioning </span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">toolbar</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> pointer-events</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> none</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span>\n<span class=\"line\"></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--muted)\">/* the selector tracks the transition itself */</span></span>\n<span class=\"codeBlock_line-added\"><span class=\"codeBlock_gutter\" aria-hidden=\"true\">+</span><span style=\"color:var(--ink);font-weight:bold\">html:active-view-transition </span><span style=\"color:var(--muted);font-weight:bold\">.</span><span style=\"color:var(--ink);font-weight:bold\">toolbar</span><span style=\"color:var(--muted)\"> {</span><span style=\"color:var(--ink);font-weight:bold\"> pointer-events</span><span style=\"color:var(--muted)\">:</span><span style=\"color:var(--ink)\"> none</span><span style=\"color:var(--muted)\">;</span><span style=\"color:var(--muted)\"> }</span></span></code></pre></figure>\n<p>One seam. In a browser too old to know this pseudo-class, the selectors spec makes any selector list containing an invalid selector invalid, whole. Appended to an existing comma-separated rule, <code>:active-view-transition</code> takes the other selectors down with it. Give it its own block, or wrap it in <code>:where()</code>, which parses forgivingly. Done that way, failure is graceful twice over: the rule never matches, and a browser without view transitions has no transition to style anyway.</p>\n<p>The four desktop stable runs pass the feature’s tests without exception. Neither Android runner produces a number worth reading: Chrome Android fails the suite’s reftests wholesale at the same engine version that passes on desktop, a known harness fault, and Firefox Android has not been tested publicly since February, when this implementation was four weeks old and still failing its tests. If current Firefox on Android is broken here, the cost is transition styles not applying, the graceful end of wrong. Usage is early, about one page load in ninety thousand, which is also the audience: no view transitions, nothing to delete yet.</p>\n<h2>02. <code>animation-composition</code></h2><p><strong>Verdict: Skip</strong> · Safe everywhere as of 4 January 2026</p><p>Skip, unless you run two animations against the same property of the same element. Then this is for you: it decides whether the second animation replaces the first, the default, or adds to it, or accumulates with it.</p>\n<p>It finished its 30 months of support on 4 January with a perfect score in every current stable run, and Chrome’s developer site gave it a dedicated write-up, more promotion than a property this situational usually gets. Hence the printed verdict: in most stylesheets two animations never meet on one property, so there is nothing to delete and nothing to start. Where they do meet, it works everywhere today.</p>\n<p>That was January: one release, one deletion, and a clearing shelf with nothing on it that needs removing.</p>",
      "date_published": "2026-02-09T00:00:00.000Z",
      "tags": [
        "1 cleared",
        "1 skip"
      ]
    }
  ]
}