Vibe-coded UIs drift. I built Tavero to catch it.
If you build React UI with a coding agent, you've probably seen this: every page looks fine on its own, but put them side by side and something's off. The Team page has a flat grey button with a 2px black border. Reports has a pill. Settings has a red one with radius 4. Billing uses #4263eb, which is almost your primary color, but not quite.
Nobody decided this. The agent just wrote a new button inline each time, because it didn't know (or forgot) that src/components/Button.tsx exists.
I built Tavero to catch exactly that drift and hand it back to the agent as concrete fixes. It's free and open source.
What it does
Tavero scans your running React app — what the browser actually renders — at mobile, tablet and desktop widths. It runs four rules, and every finding names the component and the source line:
- Buttons — distinct button styles, near duplicates, and buttons written in page markup instead of the shared component.
- Cards — card-like containers that differ in background, border, radius, padding or shadow.
- Colors — colors outside your tokens, the nearest token, and where the literal is written.
- Scales — radius, spacing, font size and shadow values that aren't on the scale your tokens declare.
Here's what a button finding looks like on the Acme demo (6 pages, 3 widths), which turned up 7 text button styles:
Button "primary" · canonical · 9 renders src/components/Button.tsx:4 bg #3b5bdb, radius 6, padding 10/16, 14px/600 written in Team · 3 renders src/pages/Team.tsx:6 bg #f0f0f0, radius 0, padding 1/6, 13px/400, border 2px #000 written in Settings · 3 renders src/pages/Settings.tsx:12 bg #e03131, radius 4, padding 10/16, 14px/600 written in Billing · 3 renders src/pages/Billing.tsx:8 bg #4263eb, radius 8, padding 11/18, 14px/600 And a scale finding:
radius off the scale: 4px, 8px, 14px, 999px · declared: 10px, 6px The agent closes the findings
Tavero runs as an MCP server, so the agent can use it directly. The loop is:
- Read the context — canonical components, tokens and conventions, in about 1,500 tokens.
- Open a finding — file and line, the current value, and the token or component to use.
- Fix and rescan — only the routes and the rule it touched. Seconds, not minutes.
- Check the diff — done when the finding is listed as resolved.
In practice:
› tavero_context Action button is Button from src/components/Button.tsx (props: variant) --acme-primary #3b5bdb · --acme-radius 10px · --acme-space 16px › tavero_findings rule=colors f_ff30adc07c Color #4263eb is not a project token › tavero_finding f_ff30adc07c src/app.css:33 literal #4263eb in source expected --acme-primary #3b5bdb (ΔE 3.5) edit src/app.css:33 background: #4263eb → var(--acme-primary) › tavero_scan routes=/billing rules=colors › tavero_diff resolved f_ff30adc07c Color #4263eb is not a project token resolved f_18862a0332 Hardcoded color #4263eb in 1 place(s) That run was Claude Code with Opus 5.5, no shell, 29 seconds, 10 turns, on a copy of the Acme demo.
You decide what's intended
Not every difference is a bug. A red destructive button is probably on purpose. So there's a local panel for the person behind the agent: go through findings with the keyboard and mark each one:
- Fix — confirmed work; the agent sees it first.
- Intended — hidden from the agent until the finding grows.
- Canonical — the reference variant, with a name, used as the expected value.
You can attach a message to a finding, and each decision reaches the agent on its next call — no rescan needed.
Install
Add the MCP config to your client. For Claude Code (.mcp.json):
{ "mcpServers": { "tavero": { "type": "stdio", "command": "npx", "args": ["-y", "tavero", "mcp"] } } } Cursor uses the same block in .cursor/mcp.json. There are configs for Codex, Gemini CLI, Antigravity, VS Code and Windsurf too — tavero mcp --print-config prints all of them.
Then ask your agent to read tavero_context before it writes UI.
More, including the demo findings: https://taveroui.com
If you try it on your app, I'd love to hear what it finds.
原文:https://www.a8q9.cn/en/article/vibe-coded-uis-drift-i-built-tavero-to-catch-it-352b61.html