← All posts
Concepts

AI generates your UI. What guards the behavior?

A team prompts a v0-style tool for a checkout panel. The output looks right — clean spacing, sensible hierarchy, a confident primary button. Someone tweaks the copy, regenerates, likes the new version better, and ships it. The panel renders. It also no longer submits, because the regenerated form lost the handler wiring the previous version had. Nobody clicked through it first. The screenshot was the review.

That gap, output that looks coherent while it behaves badly, is the defining failure mode of AI-generated interfaces in 2026. The UX field has spent the whole year circling it. Once generation makes a decent-looking interface nearly free to produce, polish stops signaling quality and a recognizable AI sameness sets in. Nielsen Norman Group's state-of-UX read for the year and the 2026 experience-design trend roundups both land on the same tension: cheap to produce, much harder to trust.

Here's the part engineers own. The visual critique gets all the attention, but the behavioral failure is the one that costs money. A slightly generic layout is a taste problem you can live with. A form that stops submitting, a modal that traps keyboard focus, or a cart total that quietly detaches from the line items is a production outage that happens to have nice typography, and the screenshot review sailed right past it.

Regeneration is a mutation you didn't review

The old mental model of a UI change was a diff. Someone edited a component, you read the lines that changed, and you reasoned about the blast radius from there. Generation breaks that model. When you accept a regenerated component you aren't applying a diff at all; you're swapping the entire implementation for a fresh one that happens to resemble the old. New markup, new class names, new event wiring. The one thing that's supposed to stay stable across all that churn is what the component actually does for the user.

That inverts where your safety has to live. When the implementation is disposable and the behavior is the contract, any test pinned to the implementation is pinned to the wrong thing. It's the same discipline that makes AI-written application code shippable at all. The safety net that lets you move fast on generated code doesn't review the code's internals; it independently checks that the outcome still holds. Generated UI needs that same net, pointed at interaction rather than logic.

Why selector-pinned tests and pixel tests both churn

Two obvious guardrails fail in opposite directions when the markup regenerates.

Selector-pinned tests break on contact. A test that clicks .btn-primary-v2 or div > div:nth-child(3) > button is asserting on the exact DOM the last generation produced. Regenerate, and now the class is .button-primary, the nesting has shifted a level, and the test goes red while checkout works perfectly. The app is fine and the test is lying, and after a few rounds of that the team stops believing the bar at all. Anchoring to locators that survive markup churn instead is the whole game here.

Pixel-diff tests break in the other direction, and they do it by design. A visual regression test screenshots the page and diffs it against a baseline, so any legitimate style change lights the diff up, because it lights up for every pixel that moved regardless of intent. That's genuinely useful for catching unintended rendering drift. But against a workflow whose entire premise is regenerating the look on purpose, a pixel baseline is just a tripwire you re-arm and re-trip on every iteration, answering whether the pixels changed when what you needed to know was whether the thing still works.

Neither tool is broken; both are just aimed at the wrong altitude for this particular problem.

Pin to the outcome, not the output

The guardrail that scales with generated UI sits one level up from both: a behavior test tied to a user outcome. It doesn't mention class names or DOM positions or pixel baselines. It says things like submitting the form shows a confirmation, or opening the modal moves focus into it and Escape closes it, or the cart total equals the sum of the line items after you add a second product.

Phrased that way, the assertion is indifferent to how the markup got generated. You can swap the entire component, rename every class, and re-nest the tree, and as long as a user can still submit the form and see a confirmation, the test stays green. The moment a regeneration severs that outcome the test fails, and it fails on exactly the thing you cared about in the first place. That's the web-first assertion idea carried to its conclusion: assert on observable, user-facing state and let the check retry against reality instead of reaching into the implementation.

The locator strategy that makes this hold is role- and label-first. You find the button by its accessible role and name, the field by its label, and the confirmation by its text, which is the same semantics a screen-reader user navigates by. This pays off twice. It's the most regeneration-durable way to address an element, and when the regenerated markup drops the accessible name and the locator can no longer find the control, you've caught a real defect that a fraction of your users would have hit anyway. Checking durability and checking correctness turn out to be the same act.

The symmetry that actually closes the loop

There's a symmetry here worth pushing on. If AI is writing your interface, the counterweight that makes sense is an independent behavior suite, itself AI-generated but verified, built from a different prompt by a different system and pinned to user outcomes rather than to the code under test.

Independence is what does the work. The bottleneck in AI-assisted development is verification, not generation: models produce plausible output far faster than anyone can confirm it does the right thing, and letting a system grade its own homework just relocates the trust problem. A behavior suite that's authored separately and verified by actually running against the live app before you rely on it breaks that loop, because it never asks the UI generator whether the UI works. It's a second opinion that drove the flow like a real user and reported what it saw.

You describe the outcomes in plain language: submit the form and expect a confirmation, or open the modal, tab through it, and press Escape. Those turn into real tests, anchored to accessible semantics and run against the actual rendered app. Then you regenerate the component as often as taste demands, and the suite goes right on ignoring what the markup looks like this week and checking only whether the form still submits.

Generation drove the cost of producing an interface close to zero without doing anything about the cost of trusting how it behaves, and that gap is exactly the space a behavior suite is built to occupy: it clicks the button, waits for the confirmation, and tells you the truth about the version you're actually about to ship.


If your team is generating UI and shipping it fast, the missing half is a behavior suite that survives every regeneration. Get early access, or read how a safety net for AI-written code works in practice.

Early access

Ready for tests that write themselves?