I installed Modern Web Guidance in My Projects - Here's What Actually Changed

I ran a controlled refactor on my production Next.js app to see if Chrome's Modern Web Guidance (MWG) actually stops AI from writing outdated, framework-heavy code. Here is what actually changed.


As web developers, we have a love-hate relationship with AI coding agents. They are incredibly fast, but left to their own devices, they tend to write code like it’s 2019. They reach for heavy polyfills, legacy DOM hacks, and framework-specific workarounds because their training data is saturated with outdated StackOverflow answers.

Nowhere is this more obvious than in the React/Next.js ecosystem. React developers are trained to solve UI problems with JavaScript—reaching for useState, useEffect, and complex state machines to handle things the browser can now do natively.

But what happens when we give the AI agent the actual rulebook for the modern web?

To find out, I ran a controlled experiment on Parlez Bien, my production language-learning app built with Next.js. I installed Chrome’s Modern Web Guidance (MWG) into my AI agent (via Antigravity CLI) and asked it to refactor my UI components.

I didn’t just want a summary of what changed; I wanted the raw diffs. Here is exactly what happened when an AI agent stopped writing "React-first" code and started writing "Platform-first" code.


The Experiment Setup

The methodology was simple. I took four specific UI components in my Next.js app that relied on common, slightly hacky React patterns.

First, I asked the AI agent to refactor them using its default training. Then, I installed the Modern Web Guidance skill (agy skill install GoogleChrome/modern-web-guidance), which injects real-time Web Platform Baseline data into the agent's context window, and ran the exact same refactor prompts.

I asked the agent to act as a code auditor and surface the top 4 most significant modernizations. The results were staggering.


Win #1: Killing the "Exit Animation" React Hack

The Concept: Replaced custom React timer-based unmounting state hacks with the native browser popover top-layer API and CSS discrete transitions.

The Pain Point (Before)

In React, if you want a component (like a Toast notification) to fade out before it unmounts from the DOM, you usually have to write a brittle useEffect with a setTimeout. You keep the component in the DOM just long enough for the CSS animation to finish, then you flip a state flag to unmount it.

// ❌ BEFORE: The classic React animation hack
const [shouldRender, setShouldRender] = useState(visible)
useEffect(() => {
  if (visible) {
    setShouldRender(true)
  } else {
    const timer = setTimeout(() => {
      setShouldRender(false)
    }, 200)
    return () => clearTimeout(timer)
  }
}, [visible])

if (!shouldRender) return null
return (
  <div
    className={`... fixed bottom-6 right-6 z-[200] transition-all duration-200`}
  />
)

The MWG Fix (After)

With MWG aware that the native Popover API and CSS @starting-style are now Baseline, the agent completely deleted the useEffect timer. It let the browser's native Top-Layer handle the rendering and stacking context.

// ✅ AFTER: Native Top-Layer handles rendering and stacking
return (
  <div
    ref={popoverRef}
    popover="manual"
    className={`toast-notification fixed bottom-6 right-6 z-[200] ${visible ? 'toast-visible' : ''}`}
  />
)
// (Paired with native @starting-style animations in globals.css)

The Takeaway: MWG knows the browser can handle entry/exit animations natively. It saved us 15 lines of brittle state-management code and eliminated a potential memory leak from uncleared timers.


Win #2: Fixing "Div Soup" Accessibility in Toggles

The Concept: Refactored custom simulated button-based toggles to use a standard, accessible <input type="checkbox"> styled natively using Tailwind sibling state selectors (peer).

The Pain Point (Before)

It is incredibly common for AI (and humans) to build toggle switches using a <button> or a <div> with custom onClick handlers. This looks fine visually, but it completely breaks keyboard navigation and screen readers.

// ❌ BEFORE: Accessible label is unlinked; custom button simulates a checkbox
<button
  type="button"
  disabled={disabled}
  onClick={() => !disabled && onChange(!checked)}
  className={`relative h-6 w-10 rounded-full ${checked ? 'bg-[#2563eb]' : 'bg-[#d4cfc2]'}`}
>
  <span
    className={`... absolute ${checked ? 'translate-x-5' : 'translate-x-1'}`}
  />
</button>

The MWG Fix (After)

MWG enforced native form semantics. It refactored the component into a native checkbox hidden with Tailwind's sr-only, using peer-checked to style the visual switch.

// ✅ AFTER: Label HTML-linked via useId; native checkbox handles events
const switchId = useId()
return (
  <>
    <label htmlFor={switchId} className="cursor-pointer">
      {label}
    </label>
    <label className="relative inline-flex shrink-0 cursor-pointer items-center">
      <input
        id={switchId}
        type="checkbox"
        checked={checked}
        onChange={(e) => onChange(e.target.checked)}
        className="peer sr-only"
      />
      <div className="h-6 w-10 rounded-full bg-[#d4cfc2] after:absolute after:transition-transform after:content-[''] peer-checked:bg-[#2563eb] peer-checked:after:translate-x-4" />
    </label>
  </>
)

The Takeaway: It didn't just make it look like a toggle; it made it act like a real form element. We got Spacebar toggling and VoiceOver support for free, without writing a single line of ARIA code.


Win #3: Semantic Tag Resolution for UI Chips

The Concept: Stopped using <button> elements for non-interactive state displays, replacing them with dynamic rendering tags (span / button) based on the presence of an onClick callback.

The Pain Point (Before)

We had a <VocabChip> component that was always rendered as a <button>. If it wasn't clickable, we just passed disabled={true}. Screen readers would annoyingly announce "Vocabulary word, disabled button" to users.

// ❌ BEFORE: Element is always a button, forced disabled if not interactive
return (
  <button
    type={isInteractive ? 'button' : undefined}
    onClick={isInteractive ? onClick : undefined}
    disabled={!isInteractive}
    className={`... inline-flex items-center justify-center`}
  >
    <span>{word}</span>
  </button>
)

The MWG Fix (After)

MWG understands that semantic HTML matters. It implemented dynamic tag resolution, ensuring non-clickable components render as static <span> elements.

// ✅ AFTER: Suffix elements natively swap wrapper tag to <span> if not interactive
const Tag = isInteractive ? 'button' : 'span'
return (
  <Tag
    type={isInteractive ? 'button' : undefined}
    onClick={isInteractive ? onClick : undefined}
    className={`... inline-flex items-center justify-center focus-visible:ring-2`}
  >
    <span>{word}</span>
  </Tag>
)

The Takeaway: MWG stopped the AI from abusing interactive elements for static display, instantly improving the assistive technology experience.


Win #4: Purging dangerouslySetInnerHTML for CSS

The Concept: Removed inline stylesheet injections via React's dangerouslySetInnerHTML and migrated custom keyframes to the global stylesheet (globals.css).

The Pain Point (Before)

The AI had injected @keyframes directly into the DOM using React's dangerouslySetInnerHTML. In a Next.js environment, this is a massive red flag. It triggers Content Security Policy (CSP) violations and causes severe React hydration mismatches between the server and client.

// ❌ BEFORE: Inject keyframe stylesheets at run-time directly inside the DOM
<style
  dangerouslySetInnerHTML={{
    __html: `
      @keyframes waveform-bounce {
        0%, 100% { height: 20%; }
        50% { height: 100%; }
      }
    `,
  }}
/>

The MWG Fix (After)

MWG recognized that runtime CSS injection is an anti-pattern in modern, secure web apps. It extracted the keyframes and moved them to globals.css.

/* ✅ AFTER: Consolidated inside global styles.css */
@keyframes waveform-bounce {
  0%,
  100% {
    height: 20%;
  }
  50% {
    height: 100%;
  }
}

The Takeaway: It prioritized security and Next.js server/client hydration stability over component-level encapsulation.


The Meta-Analysis: Platform > Framework

Looking at these diffs, a clear pattern emerges. Left to its own devices, an AI coding agent solves problems using Framework Patterns (state, effects, custom components).

When you inject Modern Web Guidance, it forces the agent to pause and ask: "Wait, does the browser do this natively now?"

It drastically shifts the paradigm from Framework-First to Platform-First. It reduces JavaScript bundle size by offloading work to the browser engine, and it acts as an automated Accessibility Auditor, enforcing semantic HTML rules that AI usually ignores unless explicitly prompted.


The Friction: Where MWG Over-Corrected

Before I wrap up, I’d be doing you a disservice if I painted this as a flawless magic bullet. Injecting MWG into your agent's context window isn't without friction. Here is where the refactor actually caused me headaches:

1. The Encapsulation Trade-off In Win #4, moving keyframes to globals.css solved the CSP and hydration issues, but it broke my component-level CSS encapsulation. If I delete the WaveformVisualizer component later, I now have orphaned CSS sitting in my global file. MWG gave me the right architectural direction for security, but I still had to act as the senior dev to manually namespace the classes to prevent style leaking.

2. The "Bleeding Edge" Cliff In Win #1, the agent used CSS @starting-style and transition-behavior: allow-discrete. These are incredibly powerful, but they are new. While they are technically Baseline, they are highly obscure to a junior developer who is used to standard transition: opacity. MWG assumes you want the absolute newest Web Platform features, which sometimes means trading "familiar React patterns" for "cutting-edge CSS that requires Googling."


The Verdict

Asking an AI agent to refactor with Modern Web Guidance is a highly effective way to pay down technical debt, eliminate legacy hacks, and accidentally fix accessibility issues.

However, it is not a "set it and forget it" solution. You must still act as the senior reviewer. You need to verify that the agent's definition of "modern" aligns with your product's actual browser support requirements, and you need to manage the architectural trade-offs (like CSS encapsulation) that the agent doesn't fully grasp.

Ultimately, MWG turns your AI coding agent from a junior developer who blindly copies 2019 StackOverflow answers into a mid-level engineer that actually reads the MDN docs.