MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr 🔥 tagline

Hey there 👋 I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 — present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 — Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms — earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

Web Platform Roadmap 2025: What's Coming to Browsers

Upcoming web APIs and features: view transitions, invoker commands, speculative rules, and more.

Web PlatformBrowserFrontendStandards

By MinhVo

Introduction

The web platform is evolving faster than ever. 2025 brings features that were previously impossible without JavaScript or native code: built-in accordions, declarative popovers, scroll-driven animations, and cross-page view transitions. This guide covers the most impactful features arriving in browsers in 2025, with practical examples, cross-browser implementation status, and strategies for adopting these features in production applications.

The pace of web standardization has accelerated dramatically since the Interop project began in 2021. Browser vendors now coordinate on a shared set of focus areas each year, resulting in near-simultaneous shipping of major features across Chrome, Firefox, and Safari. The 2025 Interop focus areas include CSS Anchor Positioning, cross-document view transitions, the Popover API, and numerous developer experience improvements that collectively represent the largest platform upgrade in a decade.

Future of web development

CSS Anchor Positioning

Position elements relative to other elements without JavaScript. This feature eliminates the need for libraries like Popper.js or Floating UI for tooltip and dropdown positioning, reducing bundle size and simplifying component code.

/* Define an anchor element */
.menu-button {
  anchor-name: --menu-btn;
}
 
/* Position the dropdown relative to the anchor */
.dropdown {
  position: fixed;
  position-anchor: --menu-btn;
  top: anchor(bottom);
  left: anchor(left);
  margin-top: 8px;
}
 
/* Use inset-area for simpler positioning */
.tooltip {
  position: fixed;
  position-anchor: --info-icon;
  inset-area: top;
  margin-bottom: 8px;
}
 
/* Fallback positioning when anchor would cause overflow */
.dropdown {
  position-try-fallbacks: flip-block, flip-inline;
}
 
/* Multiple anchor references */
.context-menu {
  position-anchor: --trigger-btn;
  position-try-fallbacks: flip-block, flip-inline, --custom-fallback;
}
 
@position-try --custom-fallback {
  top: anchor(top);
  right: anchor(left);
  left: auto;
  bottom: auto;
}

Cross-browser status: Shipped in Chrome 125 and Edge 125. Safari 17.4 added partial support. Firefox is targeting full support in Firefox 130. Use @supports (anchor-name: --test) for feature detection and provide JavaScript-based fallbacks for unsupported browsers.

The practical impact is significant. A typical tooltip implementation using Floating UI adds 12-15KB to the bundle and requires JavaScript positioning logic. CSS Anchor Positioning achieves the same result with zero JavaScript and automatic overflow handling through the position-try-fallbacks mechanism.

Scroll-Driven Animations

Animate elements based on scroll position instead of time. Scroll-driven animations run on the compositor thread, meaning they bypass the main thread entirely and never trigger layout recalculations or repaints. This makes them fundamentally more performant than JavaScript-driven scroll animations.

/* Progress bar that fills as you scroll */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  background: linear-gradient(90deg, #0066cc, #00cc66);
  transform-origin: left;
  animation: grow linear;
  animation-timeline: scroll();
}
 
@keyframes grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
 
/* Fade in elements as they scroll into view */
.scroll-reveal {
  animation: reveal linear;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}
 
@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
/* Parallax effect */
.parallax-bg {
  animation: parallax linear;
  animation-timeline: scroll(root);
  animation-range: 0% 100%;
}
 
@keyframes parallax {
  from { transform: translateY(0); }
  to { transform: translateY(-200px); }
}
 
/* Pin an element while scrolling past a section */
.sticky-header {
  animation: pin linear;
  animation-timeline: view(block);
  animation-range: contain 0% contain 100%;
}
 
@keyframes pin {
  from, to { position: sticky; top: 0; }
}

Cross-browser status: Chrome 115+, Edge 115+, Safari 17.4+ (partial), Firefox 110+ (partial). The core scroll() and view() timelines are widely supported, but some advanced features like animation-range have limited availability.

The performance difference compared to JavaScript-based scroll libraries is dramatic. A typical Intersection Observer implementation that adds reveal animations consumes 2-4ms per frame on the main thread. Scroll-driven animations consume 0ms of main thread time because they execute entirely on the compositor. For pages with dozens of animated elements, this translates to consistent 60fps scrolling even on low-end devices.

Invoker Commands

Declarative command invocation without JavaScript. Invoker commands allow you to trigger actions on other elements using HTML attributes, eliminating the need for event listeners for common UI patterns like opening dialogs, toggling popovers, and showing/hiding content.

<!-- Toggle a popover -->
<button commandfor="my-popover" command="show-popover">Open</button>
<div id="my-popover" popover>
  Popover content
  <button commandfor="my-popover" command="hide-popover">Close</button>
</div>
 
<!-- Show a dialog modal -->
<button commandfor="my-dialog" command="show-modal">Open Modal</button>
<dialog id="my-dialog">
  <p>Dialog content</p>
  <button commandfor="my-dialog" command="close">Close</button>
</dialog>
 
<!-- Close the closest popover -->
<button commandfor="my-popover" command="close">Dismiss</button>
 
<!-- Custom commands with event handling -->
<button commandfor="my-component" command="--toggle-theme">Toggle Theme</button>
 
<script>
document.getElementById('my-component').addEventListener('command', (e) => {
  if (e.command === '--toggle-theme') {
    document.documentElement.classList.toggle('dark');
  }
});
</script>

Cross-browser status: Chrome 131+, Edge 131+, Safari 18.2+, Firefox is targeting 2025 H2. The commandfor and command attributes represent a fundamental shift toward declarative interactivity. Custom commands prefixed with -- allow developers to extend the command system for application-specific behaviors.

Speculation Rules API

Tell the browser to prefetch or prerender pages the user is likely to visit. This API replaces the older <link rel="prefetch"> and <link rel="prerender"> hints with a more powerful, rule-based system that can match URLs by pattern and respond to user behavior signals.

<script type="speculationrules">
{
  "prerender": [
    {
      "where": { "href_matches": "/*" },
      "eagerness": "moderate"
    }
  ],
  "prefetch": [
    {
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": { "href_matches": "/api/*" } }
        ]
      },
      "eagerness": "conservative"
    }
  ]
}
</script>

Eagerness levels:

  • immediate: Prerender/prefetch right away
  • eager: Similar to immediate, with slightly more flexibility
  • moderate: Trigger on hover or pointer down (default for prerender)
  • conservative: Trigger on click

Cross-browser status: Chrome 121+, Edge 121+, Safari and Firefox are evaluating proposals. The prerender capability is particularly powerful—Chrome reports that prerendered pages load in under 50ms, compared to 1-3 seconds for normal navigation. This represents a 20-60x improvement in perceived page load time.

For single-page applications, speculation rules work best when combined with server-side rendering or static generation. The browser can prerender the HTML shell, but dynamic content still requires JavaScript execution after navigation. Framework-specific adapters for Next.js, Nuxt, and Astro are emerging to handle speculation rules configuration automatically.

Exclusive Accordions

Native exclusive accordion behavior with the name attribute on <details>.

<details name="faq">
  <summary>What is web development?</summary>
  <p>Building websites and web applications using HTML, CSS, and JavaScript.</p>
</details>
 
<details name="faq">
  <summary>What is CSS?</summary>
  <p>A style sheet language for describing the presentation of HTML documents.</p>
</details>
 
<details name="faq">
  <summary>What is JavaScript?</summary>
  <p>A programming language that enables interactive web pages.</p>
</details>

When one <details> opens, all others with the same name close automatically. No JavaScript needed.

Cross-browser status: Chrome 120+, Firefox 130+, Safari 17.2+. This is now Baseline Widely Available, meaning it is safe to use in production without fallbacks. The name attribute creates an "accordion group" where only one member can be open at a time, matching the behavior users expect from FAQ sections and settings panels.

:user-valid and :user-invalid

Show validation styles only after the user has interacted with a form element. These pseudo-classes solve a long-standing UX problem where forms showed error states before the user had a chance to enter data.

/* Only validate after user interaction */
input:user-valid {
  border-color: #22c55e;
  background-image: url("data:image/svg+xml,..."); /* checkmark */
}
 
input:user-invalid {
  border-color: #ef4444;
  background-image: url("data:image/svg+xml,..."); /* x mark */
}
 
/* Combine with :has() for parent styling */
.form-group:has(input:user-invalid) {
  border-left: 3px solid #ef4444;
}
 
.form-group:has(input:user-valid) {
  border-left: 3px solid #22c55e;
}
 
/* Different from :valid/:invalid which apply immediately */
input:valid {
  /* Always applied when the input is valid */
}

Cross-browser status: Chrome 119+, Firefox 88+, Safari 16.5+. Baseline Widely Available. These pseudo-classes track whether the user has "interacted" with the field—either by typing, focusing and blurring, or submitting the form. Once interaction occurs, the :user-valid or :user-invalid state persists until the user changes their input.

@starting-style

Define initial styles for elements that are being rendered for the first time or transitioning from display: none. This solves the long-standing problem of animating elements into view when they first appear in the DOM.

/* Animate a dialog opening */
dialog[open] {
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.3s, transform 0.3s;
}
 
@starting-style {
  dialog[open] {
    opacity: 0;
    transform: scale(0.9);
  }
}
 
/* Animate popovers */
[popover] {
  &:popover-open {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.2s, transform 0.2s, display 0.2s allow-discrete;
  }
 
  @starting-style {
    &:popover-open {
      opacity: 0;
      transform: translateY(-10px);
    }
  }
}
 
/* Animate elements added to the DOM */
.new-item {
  animation: slide-in 0.3s ease-out;
}
 
@starting-style {
  .new-item {
    opacity: 0;
    transform: translateX(-20px);
  }
}

Cross-browser status: Chrome 117+, Edge 117+, Safari 17.5+, Firefox 129+. The allow-discrete keyword in the display transition is essential—it allows the element to transition from display: none to display: block while simultaneously animating opacity and transform.

Cross-Document View Transitions

View transitions that work across page navigations in multi-page applications. This is one of the most anticipated features for 2025, enabling smooth animated transitions between pages without the complexity of a single-page application architecture.

/* Enable cross-document view transitions */
@view-transition {
  navigation: auto;
}
 
/* Define transition animations */
::view-transition-old(root) {
  animation: fade-out 0.3s ease-out;
}
 
::view-transition-new(root) {
  animation: fade-in 0.3s ease-in;
}
 
/* Named transitions for specific elements */
.hero-image {
  view-transition-name: hero;
}
 
::view-transition-old(hero) {
  animation: slide-out 0.3s ease-out;
}
 
::view-transition-new(hero) {
  animation: slide-in 0.3s ease-in;
}
 
/* Customize transition based on navigation direction */
@view-transition {
  navigation: auto;
}
 
::view-transition-old(root) {
  animation: fade-out 0.25s ease-in;
}
 
::view-transition-new(root) {
  animation: fade-in 0.25s ease-out;
}
// JavaScript API for same-document transitions
async function navigateTo(url) {
  if (!document.startViewTransition) {
    window.location.href = url;
    return;
  }
 
  const transition = document.startViewTransition(async () => {
    const response = await fetch(url);
    const html = await response.text();
    document.body.innerHTML = new DOMParser()
      .parseFromString(html, 'text/html')
      .body.innerHTML;
  });
 
  await transition.finished;
}

Cross-browser status: Chrome 126+ for cross-document, Safari 18+ for same-document, Firefox targeting 2025. The cross-document variant requires the @view-transition { navigation: auto; } at-rule in the destination page's CSS. The browser automatically captures snapshots of the old and new pages and interpolates between them.

light-dark() Function

Easily support both light and dark color schemes without duplicating every color declaration.

:root {
  color-scheme: light dark;
}
 
body {
  background: light-dark(#ffffff, #1a1a1a);
  color: light-dark(#333333, #e0e0e0);
}
 
.card {
  background: light-dark(#f8f9fa, #2d2d2d);
  border: 1px solid light-dark(#e0e0e0, #404040);
}
 
.button-primary {
  background: light-dark(#0066cc, #4d9fff);
  color: light-dark(white, #1a1a1a);
}

Cross-browser status: Chrome 123+, Firefox 120+, Safari 17.5+. Baseline Widely Available. The light-dark() function evaluates its first argument in light mode and its second in dark mode, respecting the color-scheme property. This eliminates the need for separate :root and [data-theme="dark"] blocks that duplicate every color variable.

field-sizing Property

Auto-size form elements based on their content. This eliminates the common pattern of using JavaScript to auto-resize textareas or match input widths to content.

textarea {
  field-sizing: content;
  min-height: 3em;
  max-height: 10em;
}
 
input[type="text"] {
  field-sizing: content;
  min-width: 10em;
  max-width: 30em;
}
 
select {
  field-sizing: content;
}

Cross-browser status: Chrome 123+, Firefox 134+, Safari is evaluating. The field-sizing: content value makes the element grow or shrink to fit its content, similar to how <div> elements behave. This is particularly useful for textareas that should expand as the user types, and for select elements with varying option lengths.

JavaScript Proposals Advancing in 2025

Several TC39 proposals are reaching Stage 3 and Stage 4 in 2025, bringing new language capabilities to JavaScript.

Array.fromAsync

Create arrays from async iterables, making it easier to work with async generators and streaming data.

// Convert async generator to array
async function* streamData() {
  for await (const chunk of dataStream) {
    yield processChunk(chunk);
  }
}
 
const results = await Array.fromAsync(streamData());
 
// Fetch multiple URLs and collect results
const pages = await Array.fromAsync(
  urls.map(url => fetch(url).then(r => r.json()))
);

Iterator Helpers

Transform and consume iterators with methods like .map(), .filter(), .take(), and .drop().

// Lazy iteration with built-in methods
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}
 
const firstTenEven = fibonacci()
  .filter(n => n % 2 === 0)
  .take(10)
  .toArray();
 
// Process paginated API results
const allUsers = fetchUsers()
  .take(100)
  .filter(user => user.active)
  .map(user => ({ id: user.id, name: user.name }))
  .toArray();

Set Methods

New methods on Set for union, intersection, difference, and symmetric difference operations.

const frontend = new Set(['React', 'Vue', 'Angular', 'Svelte']);
const backend = new Set(['Node', 'Python', 'Go', 'React']);
 
// Union: all unique technologies
const allTech = frontend.union(backend);
// Set {'React', 'Vue', 'Angular', 'Svelte', 'Node', 'Python', 'Go'}
 
// Intersection: used on both sides
const fullstack = frontend.intersection(backend);
// Set {'React'}
 
// Difference: frontend-only technologies
const frontendOnly = frontend.difference(backend);
// Set {'Vue', 'Angular', 'Svelte'}
 
// Symmetric difference: not shared between sets
const specialized = frontend.symmetricDifference(backend);
// Set {'Vue', 'Angular', 'Svelte', 'Node', 'Python', 'Go'}

Promise.withResolvers

Create a promise with externally accessible resolve and reject functions.

// Before: verbose promise construction
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});
 
// After: clean one-liner
const { promise, resolve, reject } = Promise.withResolvers();
 
// Useful for event-driven patterns
function createEventPromise(target, eventType) {
  const { promise, resolve, reject } = Promise.withResolvers();
  target.addEventListener(eventType, resolve, { once: true });
  target.addEventListener('error', reject, { once: true });
  return promise;
}
 
const click = createEventPromise(button, 'click');
await click; // resolves when button is clicked

WebAssembly Improvements

WebAssembly continues to mature with several important features landing in 2025.

Garbage Collection (WasmGC)

WasmGC allows WebAssembly programs to interact with the host garbage collector, enabling languages like Kotlin, Dart, and Java to compile to WebAssembly without shipping their own GC runtime.

// Load a WasmGC module
const module = await WebAssembly.compileStreaming(
  fetch('app.wasm')
);
const instance = await WebAssembly.instantiate(module, {
  env: { /* imports */ }
});
 
// Kotlin/Wasm or Dart/Wasm modules can now share
// the browser's garbage collector, reducing memory
// overhead from ~5MB to ~500KB for GC-dependent languages

Cross-browser status: Chrome 119+, Firefox 120+, Safari 18.2+. This is transformative for language diversity on the web—Kotlin/Wasm and Dart/Wasm applications are now practical for production use.

Exception Handling

The exception handling proposal allows WebAssembly code to throw and catch exceptions that integrate with JavaScript's error handling.

// Wasm exceptions can be caught in JavaScript
try {
  instance.exports.riskyOperation();
} catch (e) {
  if (e instanceof WebAssembly.Exception) {
    console.error('Wasm error:', e.message);
  }
}

Stack Switching

The stack switching proposal enables lightweight concurrency in WebAssembly, allowing coroutines and green threads without blocking the main thread. This is particularly valuable for languages like Go that rely on goroutines.

Privacy Sandbox Updates

The Privacy Sandbox initiative continues to reshape how advertising and tracking work on the web. The Attribution Reporting API provides privacy-preserving conversion measurement without third-party cookies. Topics API enables interest-based advertising by classifying user browsing into broad topics without tracking individual sites. The Protected Audience API (formerly FLEDGE) supports remarketing by allowing advertisers to show ads to previous visitors without sharing browsing history.

These APIs represent a fundamental shift in how the web advertising ecosystem operates. Developers working on analytics, ad tech, and conversion tracking need to migrate their implementations before third-party cookies are fully deprecated. The Chrome team has published detailed migration guides for each API, and origin trials are available for testing.

Storage and File System APIs

The File System Access API and the Storage API provide web applications with capabilities that were previously exclusive to native applications. The File System Access API allows reading and writing files on the user's local file system with explicit permission. The Storage API provides persistent storage that is not subject to the eviction policies of other storage mechanisms. The Origin Private File System provides a sandboxed file system for web applications that need to store large amounts of structured data.

// Open a file picker and read the selected file
async function openFile() {
  const [handle] = await window.showOpenFilePicker({
    types: [{
      description: 'Text Files',
      accept: { 'text/plain': ['.txt', '.md'] }
    }]
  });
  const file = await handle.getFile();
  return await file.text();
}
 
// Write to a file with permission
async function saveFile(content) {
  const handle = await window.showSaveFilePicker({
    types: [{
      description: 'JSON Files',
      accept: { 'application/json': ['.json'] }
    }]
  });
  const writable = await handle.createWritable();
  await writable.write(content);
  await writable.close();
}

Accessibility Improvements

The web platform is gaining new accessibility features that improve the experience for users with disabilities. The Accessible Name and Description Computation specification has been updated to provide more consistent behavior across browsers. New ARIA attributes provide more semantic information for assistive technologies. The CSS prefers-reduced-transparency and prefers-contrast media queries allow developers to adapt their interfaces to user preferences.

/* Adapt to user transparency preferences */
@media (prefers-reduced-transparency: reduce) {
  .glass-card {
    backdrop-filter: none;
    background: solid-color;
  }
}
 
/* High contrast adaptations */
@media (prefers-contrast: more) {
  .button {
    border: 2px solid currentColor;
    font-weight: bold;
  }
}
 
/* Reduced motion for vestibular disorders */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Step-by-Step Adoption

Step 1: Feature Detection

// Check for CSS Anchor Positioning
if (CSS.supports('anchor-name', '--test')) {
  document.documentElement.classList.add('supports-anchor-positioning');
}
 
// Check for scroll-driven animations
if (CSS.supports('animation-timeline', 'scroll()')) {
  document.documentElement.classList.add('supports-scroll-animations');
}
 
// Check for View Transitions
if ('startViewTransition' in document) {
  document.documentElement.classList.add('supports-view-transitions');
}
 
// Check for Popover API
if ('popover' in HTMLElement.prototype) {
  document.documentElement.classList.add('supports-popover');
}

Step 2: Progressive Enhancement

/* Base: JavaScript-based positioning */
.dropdown {
  position: absolute;
  top: 100%;
  left: 0;
}
 
/* Enhanced: CSS Anchor Positioning */
@supports (anchor-name: --test) {
  .dropdown {
    position: fixed;
    position-anchor: --menu-btn;
    top: anchor(bottom);
    left: anchor(left);
  }
}
// Progressive enhancement for View Transitions
document.querySelectorAll('a[data-transition]').forEach(link => {
  link.addEventListener('click', async (e) => {
    if (!document.startViewTransition) {
      return; // Let browser handle normally
    }
    e.preventDefault();
    await document.startViewTransition(async () => {
      window.location.href = link.href;
    }).finished;
  });
});

Step 3: Remove Polyfills

As features become Baseline Widely Available, remove polyfills and libraries that provide the same functionality:

  • Remove Popper.js/Floating UI when using CSS Anchor Positioning
  • Remove Intersection Observer polyfills when using scroll-driven animations
  • Remove prefers-color-scheme polyfills when using light-dark()
  • Remove custom accordion JavaScript when using exclusive <details>
  • Remove dialog polyfills when using the native <dialog> element

Best Practices

  1. Use progressive enhancement: Provide fallbacks for features still in origin trials or limited browser support
  2. Feature detect: Use CSS.supports(), JavaScript feature detection, and @supports queries
  3. Test across browsers: Even with Interop, edge cases exist between implementations
  4. Remove polyfills: When features are widely available, remove unnecessary polyfills to reduce bundle size
  5. Stay informed: Follow the WebDX Community Group, browser release notes, and the Baseline status dashboard
  6. Prioritize Baseline Widely Available features: These are safe for production use without fallbacks
  7. Use @supports for CSS features: Wrap new CSS features in @supports blocks to provide graceful degradation
  8. Monitor browser release cycles: Chrome and Edge release every 4 weeks, Firefox every 4 weeks, Safari roughly twice per year

Common Pitfalls

PitfallImpactSolution
Using features too earlyBroken in some browsersCheck Baseline status on web.dev/baseline
Not providing fallbacksBroken in older browsersUse @supports and feature detection
Ignoring Safari supportiOS users affected (all iOS browsers use WebKit)Verify Safari implementation status
Overusing JavaScriptMissed native capabilitiesPrefer native HTML/CSS/JS features
Assuming polyfill availabilitySome new features have no polyfillsDesign with fallback UX, not just polyfills
Neglecting mobile browsersMobile Chrome/Safari may lag desktopTest on real mobile devices

Developer Tooling Evolution

Browser developer tools are evolving to support the new web platform features. Chrome DevTools includes enhanced performance profiling that tracks Core Web Vitals in real-time, a scroll-driven animation inspector, and a view transition debugger. Firefox Developer Tools provides CSS Grid and Flexbox inspectors that visualize layout relationships, and an anchor positioning overlay. Safari Web Inspector includes WebGPU debugging capabilities and CSS nesting visualization.

The evolution of developer tools keeps pace with the web platform, ensuring that developers have the instrumentation they need to build and debug applications using the latest features. Invest time in learning the latest devtools features to improve your development workflow.

Web Platform Testing

The Web Platform Tests project provides a comprehensive test suite that browsers use to verify their implementations. Check the WPT dashboard at wpt.fyi to see the current test pass rates for each browser on specific features. When developing features that depend on new web platform APIs, test across multiple browsers using the WPT infrastructure. Contribute test cases for features you use to help improve browser interoperability.

Conclusion

The web platform in 2025 is more capable than ever. CSS Anchor Positioning eliminates JavaScript positioning libraries, scroll-driven animations create engaging experiences without performance overhead, native HTML features like exclusive accordions and invoker commands reduce the need for JavaScript, and cross-document view transitions bring app-like navigation to multi-page sites.

Key takeaways:

  1. CSS Anchor Positioning replaces tooltip/popover positioning libraries with zero JavaScript
  2. Scroll-driven animations run on the compositor thread for 60fps performance
  3. Invoker commands eliminate JavaScript for common UI patterns like dialogs and popovers
  4. Speculation Rules enable intelligent prefetching and prerendering for near-instant navigation
  5. Cross-document view transitions bring smooth page transitions to traditional multi-page sites
  6. JavaScript proposals like Iterator Helpers, Set methods, and Array.fromAsync improve developer ergonomics
  7. WebAssembly GC enables Kotlin, Dart, and Java to run efficiently in the browser
  8. Progressive enhancement remains essential—feature detect, provide fallbacks, and remove polyfills as features mature

The gap between what the web can do and what native applications can do has never been smaller. Start adopting these features today with proper feature detection and fallbacks, and your users will experience a faster, more capable web.

Additional Features Worth Watching

Document Picture-in-Picture

The Document Picture-in-Picture API opens a floating, always-on-top window that can contain arbitrary HTML content. Unlike the existing Picture-in-Picture API that only works with <video> elements, this API lets you create custom PiP windows for dashboards, chat widgets, mini players, or video call controls:

async function openPiP() {
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: 400,
    height: 300,
  });
 
  pipWindow.document.body.innerHTML = `
    <div id="mini-player">
      <video id="pip-video" autoplay></video>
      <div class="controls">
        <button id="play-pause">⏸️</button>
        <button id="close-pip">âś•</button>
      </div>
    </div>
  `;
 
  pipWindow.document.getElementById('close-pip').addEventListener('click', () => {
    pipWindow.close();
  });
}

Cross-browser status: Chrome 116+, other browsers evaluating. This API is particularly useful for video conferencing applications where users want to see participant video while working in other tabs.

CSS @scope

The @scope at-rule provides native style scoping without JavaScript frameworks or CSS modules. Styles within @scope only apply to elements within the specified scope boundaries:

@scope (.card) to (.card-footer) {
  p {
    color: #333;
    line-height: 1.6;
  }
  a {
    color: #0066cc;
    text-decoration: underline;
  }
}

In this example, the p and a styles only apply to elements inside .card but NOT inside .card-footer. The to clause defines a scope boundary that prevents styles from leaking into nested components. This eliminates the need for BEM naming conventions, CSS Modules, or CSS-in-JS solutions for style isolation.

Cross-browser status: Chrome 118+, Firefox 128+, Safari 17.4+. Baseline Newly Available as of 2024.

WebTransport for Real-Time Applications

WebTransport provides a modern replacement for WebSocket with support for unreliable datagrams and bidirectional streams over QUIC. This enables low-latency communication for games, media streaming, and real-time collaboration:

async function connectWebTransport() {
  const transport = new WebTransport('https://example.com/game');
 
  await transport.ready;
  console.log('WebTransport connected');
 
  // Send unreliable datagrams (game position updates)
  const writer = transport.datagrams.writable.getWriter();
  await writer.write(new Uint8Array([1, 2, 3]));
 
  // Open reliable bidirectional stream (game events)
  const stream = await transport.createBidirectionalStream();
  const writer2 = stream.writable.getWriter();
  await writer2.write(new TextEncoder().encode('{"event":"kill","target":42}'));
}

Cross-browser status: Chrome 97+, Firefox 114+, Safari in development. WebTransport combines the best of WebSocket (bidirectional communication) with UDP-like semantics (unreliable datagrams) for applications where latency matters more than guaranteed delivery.

Shared Array Buffer and Atomics

For CPU-intensive web applications, Shared Array Buffer allows multiple web workers to share memory, enabling true parallel processing:

// Main thread
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
 
const worker = new Worker('compute.js');
worker.postMessage({ buffer: sharedBuffer });
 
// Worker (compute.js)
self.onmessage = ({ data }) => {
  const sharedArray = new Int32Array(data.buffer);
  // Atomically increment without race conditions
  Atomics.add(sharedArray, 0, 1);
  // Wait for notification from another worker
  Atomics.wait(sharedArray, 1, 0);
};

Cross-browser status: Available in all major browsers with Cross-Origin-Isolation headers required. This feature enables high-performance computing in the browser, including physics simulations, image processing, and data analysis that previously required server-side computation.