How to keep AI-generated dashboards consistent.
The second screen your agent builds rarely matches the first. Here is why it happens and three changes that stop it, each one something you can open and check in this repository.
The second screen never matches the first
Ask an AI coding agent for an analytics dashboard, then ask for a settings page. Both work, and they do not look like the same product: the buttons have different radii, the grays differ by a shade, the spacing follows a different rhythm. Nothing is broken. Each screen was generated on its own, and nothing told the agent to reuse what the first one decided. The result is design drift, and it grows with every screen.
Why it happens
No shared tokens
Each screen picks its own colors, spacing and radius, so two screens made an hour apart use two slightly different grays and two different paddings.
No component set
With nothing to reuse, the agent writes buttons, tables and cards from memory on every prompt, and memory differs from prompt to prompt.
No rule the agent follows
Nothing in the repository says what to do instead, so every prompt starts from the agent's defaults, not from your product.
Three mechanisms, one per cause
Each one removes a cause of drift. Together they give the agent something to reuse and something to obey.
One token file
Color, spacing, radius, shadow and type scale are CSS variables defined once in theme.css. Components read semantic tokens such as bg-primary and text-tertiary and never hard-code a value, so editing the file changes every screen. See Theming.
Dashboard
Settings page
A dashboard and a settings page, rendered live. They are different screens that share one token file, so their colors, type and corner radius change together.
One component set
The agent installs buttons, tables, cards and charts from the same registry instead of rewriting them. There are 89 published component groups, built on React Aria Components, and the full-page examples above are assembled from them. Browse them in Components.
A rule the agent reads
The Proper UI Skill is a file in your repository that the agent reads before it writes UI. Its component rules, the same ones listed under “Writing component code” in this repository's AGENTS.md, are:
- React Aria props, not DOM props:
onPressnotonClick,isDisablednotdisabled. - Semantic tokens only:
bg-primary,text-tertiary, neverbg-purple-600orp-[13px]. - No
dark:utilities: a.dark-modeclass on an ancestor repoints every token, so a token-based component is already correct in both themes. - Logical properties for anything directional:
ms-*andpe-*notml-*andpr-*, sodir="rtl"works.
Set up, then ask for a dashboard
Two commands, then one prompt. Run the first in your project.
npx @properui/cli@latest agent init --client claudeclaude mcp add --transport http properui https://properui.dev/api/mcpUsing Codex, Cursor or another client? Change --client to codex, cursor or all, and find the MCP setup for your client on the MCP page.
Build an analytics dashboard with KPI cards, a revenue chart and a recent activity table using Proper UI
Limits, plainly
- The components are React 19 only. For Vue, Angular, Svelte, Astro or plain HTML you get the same tokens plus
@properui/htmland@properui/elements, a smaller curated set. See Frameworks. - Examples ship with placeholder data and demo assets. Replace them before anything reaches a user.
- A rule is an instruction, not a guarantee. The agent can still ignore it, so run
npx @properui/cli@latest check, which flags raw palette classes and arbitrary values, and review the result. - Shared tokens keep colors, spacing and type aligned. They do not make a layout right for your users. That is still a design decision.
- Zero axe violations in the test suites is not the same as fully accessible. Automated checks miss things such as focus order and screen reader wording. See Accessibility.
Frequently asked questions
Why do AI-generated screens drift apart?+
Each prompt is generated on its own. Without shared tokens, a reusable component set and a written rule, the agent fills every gap from its defaults, and those defaults vary from one prompt to the next.
Does this only work with Claude Code?+
No. agent init also supports Codex, Cursor and Lovable, and the MCP server works in any client that supports MCP. See For Claude Code and the MCP page.
Can I use my own brand colors?+
Yes. Every visual decision is a CSS variable in theme.css. Editing the base palette re-brands every installed screen at once, including dark mode. See Theming.
How do I catch drift that slips through?+
Run npx @properui/cli@latest check. It flags raw palette classes and arbitrary values that should have been semantic tokens. Over the local MCP package the same check is the check_tokens tool.
Give the agent something to reuse.
Browse the components, then read how tokens work.