Guidelines

Rules

8 rules for building an app with ui-lib components. Each one names what to import instead of what it forbids, shows a real wrong/right pair, and ships a check you can run in your own project. Agents can read the same rules as JSON from /api/rules.json.

Why these exist

These rules are written for the code an agent writes. Not because agents are careless — because they optimise for the shortest path to something that looks right, and three specific detours are the predictable result.

It rebuilds what already exists
Asked for a card, a button, a badge, an agent writes one. Producing four Tailwind classes is faster and more certain than discovering that the library ships the component — and the result renders correctly, so nothing in review objects. Repeated across a codebase, that is how a design system stops being a system: not through one bad decision, but through fifty reasonable ones in fifty files.
It hand-rolls forms it was told to bind
Forms are where an agent's training data pulls hardest against the project's conventions. It writes useState, value, onChange and a literal name because that is the shape it has seen most often, rather than reading the binding off Conform's field metadata or posting to a React Router action. The form submits, so it looks finished — while the error wiring, the default values, and the server-side validation are simply absent.
It appends until the file is unreadable
Adding to the file already open is the cheapest edit available, and an agent never has to scroll the result. Nothing pushes back at 400 lines, or 900, or 2,000 — and past that point no human reviews the file properly and every later agent edit starts from a worse position, because reading it costs more and landing an edit in the right place is less certain.

Why a linter, and not just instructions

Instructions are read once, at best
A CLAUDE.md or a skill competes for attention with the actual task, and loses — it is skipped when context is tight, skimmed when it is long, and interpreted generously when it is vague. None of that is visible to you: the agent does not report which of your conventions it decided not to apply.
A rule is checked every time, on every file
A lint run does not get tired, does not run out of context, and does not weigh your convention against the feature it is trying to ship. It applies the same way on the first file and the four-hundredth.
The error arrives where the mistake is
This is the part that changes agent behaviour. A rule does not say "follow the design system" — it says, on the line in question, "use <Button> from @/components/button". That is a correction an agent can act on immediately and verify, which is why every message here names the replacement rather than the offence.
It survives the people who wrote it
Instructions decay as the codebase changes and nobody notices. These rules are generated from records the build validates, so a rule pointing at a component that no longer exists fails CI instead of quietly misleading whoever reads it next.

Element usage

Layout is yours. Appearance is the library's.
When a raw HTML element is allowed in an app that uses quebi ui-lib, and which component to import when it is not. Three tiers: interactive and semantic elements are always the library's; layout elements are yours as long as they only lay things out; design values are always tokens.

Forms

Validation is yours. Wiring is the library's.
How a form is bound, where its errors come from, and what has to be true on the server. The schema is your app's — it encodes rules nobody else can know. Everything between that schema and the DOM (name, id, defaultValue, aria-describedby, the error text itself) belongs to the components, and hand-wiring it is how a form quietly loses its accessibility while still looking correct.
Enforcement
Run these in your own project
All 8 rules as one Biome config, with a GritQL plugin for each rule Biome has no built-in for and the documented exceptions already applied.
Set it up