Chrome 158 went into beta on 28 October 2026. It includes several CSS and HTML additions that remove the need for small JavaScript workarounds: a universal selector for custom highlights, pseudo-classes for audio and video state, proper object-fit support for embedded SVG, and a declarative way to offer PWA installation. All the features below are listed as “Enabled by default” for milestone 158 on Chrome Platform Status. They’re in Chrome only for now, so build them as progressive enhancements.

1. ::highlight(*): One Base Style for All Custom Highlights

The CSS Custom Highlight API lets you style text ranges, like search matches, spelling errors or collaborators’ selections, without wrapping them in extra <span> elements. Chrome 158 adds ::highlight(*), which matches the highlight pseudo-elements of every registered custom highlight. The * argument adds zero specificity, so any rule that names a specific highlight overrides it without a specificity fight.

<p id="text">Chrome 158 brings new CSS features. CSS keeps getting better.</p>
<input id="q" placeholder="Search…" value="CSS">
/* Shared base style for every custom highlight */
::highlight(*) {
  background-color: #fff0a8;
  color: #202020;
}

/* Specific highlights override the base */
::highlight(search) {
  background-color: #ffd166;
}
::highlight(critical) {
  background-color: #ffb3b3;
}
const textNode = document.getElementById("text").firstChild;
const input = document.getElementById("q");

function highlight(term) {
  CSS.highlights.delete("search");
  if (!term) return;

  const ranges = [];
  const text = textNode.textContent.toLowerCase();
  let i = text.indexOf(term.toLowerCase());
  while (i !== -1) {
    const r = new Range();
    r.setStart(textNode, i);
    r.setEnd(textNode, i + term.length);
    ranges.push(r);
    i = text.indexOf(term.toLowerCase(), i + term.length);
  }
  CSS.highlights.set("search", new Highlight(...ranges));
}

input.addEventListener("input", (e) => highlight(e.target.value));
highlight(input.value);

Fallback tip: browsers without the universal syntax ignore the whole ::highlight(*) rule, so always keep a named rule such as ::highlight(search) for each highlight you rely on. Don’t use colour as the only way to show meaning.

2. Media Pseudo-Classes: Style Players by State

Chrome 158 supports :playing, :paused, :seeking, :buffering, :stalled, :muted and :volume-locked on <audio> and <video>. Chrome Status notes this is one of the Interop 2026 focus areas. You no longer need JavaScript listeners that toggle classes on every play and pause event.

<figure class="player">
  <video src="/media/demo.mp4" controls muted></video>
  <figcaption>Product demo</figcaption>
</figure>
.player video {
  border-radius: 12px;
  outline: 3px solid transparent;
  transition: outline-color 0.2s, opacity 0.2s;
}

.player video:playing   { outline-color: #29a36a; }
.player video:paused    { opacity: 0.85; }
.player video:buffering { outline-style: dashed; outline-color: #f4a261; }
.player video:muted     { filter: saturate(0.8); }

/* Style the caption based on the video's state with :has() */
.player:has(video:playing) figcaption::after { content: " — playing"; }
.player:has(video:paused) figcaption::after  { content: " — paused"; }

Keep in mind that :playing can still match during short buffering or stalls. It reflects the element’s playing state, not whether frames are actually moving right now. Keep playback controls usable even when these styles don’t apply.

3. object-fit and object-position for Embedded SVG

Until now, Chrome ignored object-fit and object-position for SVG documents loaded through <object> or <embed>, and always stretched the document across the content box. In Chrome 158, embedded SVG is sized and positioned like <img> content.

<object data="/img/chart.svg" type="image/svg+xml" class="chart"></object>
.chart {
  width: 100%;
  height: 320px;
  object-fit: contain;          /* keep the SVG's aspect ratio */
  object-position: center top;  /* pin it to the top of the box */
  background: #f7f7f9;
}

If you have older workarounds, such as wrapper elements or hard-coded aspect ratios, test them in Chrome 158 beta. The new behaviour may make them unnecessary, or make them behave differently.

4. The <install> Element and navigator.install()

Chrome 158 adds a declarative <install> element and the navigator.install() method. Both ask the browser to install a web app, given a manifest URL and an optional manifest ID, and both support cross-origin installs. Chrome Status describes this as a better developer experience than handling beforeinstallprompt events. The browser still validates the app and asks the user to confirm. There’s no silent installation.

<install manifest="https://app.example/manifest.webmanifest">
  <!-- Fallback content for browsers without the element -->
  <a href="https://app.example/">Open the web app</a>
</install>
// Imperative version with feature detection
const btn = document.querySelector("#install-btn");

if ("install" in navigator) {
  btn.addEventListener("click", async () => {
    try {
      await navigator.install("https://app.example/manifest.webmanifest");
    } catch (err) {
      console.warn("Install was not completed:", err);
    }
  });
} else {
  btn.hidden = true;
}

Check the manifest has a valid id, start_url and icons. For now, treat this as Chromium-only.

5. Smaller Additions Worth Knowing

  • flow-start and flow-end alignment keywords: aliases for flex-start/flex-end, as resolved by the CSS Working Group. They’re needed for alignment when placement is reversed.
  • SVG textPath side attribute: side="right" reverses the direction text follows along a path. That makes it easy to put text inside or outside a circle without duplicating the path.
<svg viewBox="0 0 200 200" width="200">
  <path id="ring" d="M100,20 a80,80 0 1,1 0,160 a80,80 0 1,1 0,-160" fill="none" stroke="#ddd"/>
  <text font-size="14">
    <textPath href="#ring" side="right">Text on the other side of the path</textPath>
  </text>
</svg>

How to Try These Today

  • Install Chrome Beta on desktop or Android.
  • Wrap new CSS in @supports selector(...) where it helps. For example, @supports selector(video:playing) applies media-state styles only where they’re understood.
  • Test with your existing fallbacks still in place. These features are not Baseline yet.
@supports selector(video:playing) {
  .js-player-state-classes { display: none; } /* hide JS-driven state badges */
}

Takeaway

Chrome 158 moves more UI state into plain CSS and HTML. Use ::highlight(*) to set a base style for all highlights, media pseudo-classes for player styling, object-fit for embedded SVG, and <install> for simpler PWA prompts. Ship them as progressive enhancements and keep your fallbacks until other browsers catch up. Source: Chrome 158 release notes on Chrome Platform Status.