The full design-heuristics checklist and framework rules for building with Rebar UI — everything an AI agent needs to understand the Four-Tier Typology, the distinction between Framework Rules (fixed, mechanical constraints) and Heuristics (judgment-requiring design principles), and the 46-item checklist that governs component and construct design. Paste it into an agent's system prompt or project context before asking it to extend this framework or build new components.
Raw file: /agent.md
# Rebar UI — Agent Context (v1.0.0)
Compressed operating context for an AI agent building a UI with **Rebar UI**. The single goal: **print the page from constructs first, hand-drawn JSX only as a temporary stop-gap.** Every section below serves that goal.
## What Rebar is
Rebar UI is a **headless-first, intentionally low-fidelity** React construct library plus **the Packer** (`@rebar-ui/placement`) — a deterministic renderer that turns a plain `Construct[]` document into a real component tree with zero layout decisions left to whoever authored the document. Build the structure correctly once, then apply visual polish exactly once at migration to a real design system — never mid-build.
## The Packer — your primary tool
The Packer is how every page should be rendered. You author a `Construct[]` array — plain serializable data describing *what* the UI is — and the Packer decides *how* it looks. No CSS, no layout math, no styling decisions in the document.
### Basic usage
```tsx
import { BlockRenderer } from "@rebar-ui/placement";
import type { Construct } from "@rebar-ui/placement";
const PAGE: Construct[] = [
{ type: "hero", title: "Welcome", subtitle: "Build something." },
{ type: "checklist", heading: "Steps", items: ["First", "Second", "Third"] },
{ type: "callout", tone: "info", title: "Note", subtitle: "Important context." },
];
export default function Page() {
return <BlockRenderer blocks={PAGE} />;
}
```
### Props
| Prop | Type | Purpose |
|------|------|---------|
| `blocks` | `Construct[]` | **Required.** The document to render. |
| `renderLink` | `(props: { href: string; children: ReactNode }) => ReactNode` | Override how `href` values become links. Defaults to `<a>`. Pass your framework's link component (e.g. Next.js `Link`) for client-side navigation. |
| `data` | `Record<string, unknown>` | Live data sources for opinion-tier constructs that bind to real backend data. Omit for static documents. |
| `handlers` | `Record<string, Function>` | Live event handlers for opinion-tier constructs. Omit for static documents. |
### Live data binding (opinion-tier constructs)
Some constructs (table, card-kanban, ai-chat, etc.) can bind to real data instead of rendering static content. Supply a `source` key in the construct, then pass matching `data` and `handlers` to `BlockRenderer`:
```tsx
const PAGE: Construct[] = [
{
type: "table",
columns: ["Name", "Status"],
source: "teams", // references data.teams
onRowAction: "selectTeam", // references handlers.selectTeam
},
];
<BlockRenderer
blocks={PAGE}
data={{ teams: { columns: ["Name", "Status"], rows: [["Engineering", "Active"]] } }}
handlers={{ selectTeam: (row) => console.log(row) }}
/>
```
Without `source`, the construct renders its literal inline data — same shape, zero backend dependency.
### Full-viewport layout
For a full-page app, wrap the Packer output in an `AppShell` construct (an Order-tier construct that handles viewport height correctly):
```tsx
const PAGE: Construct[] = [
{
type: "site-header",
logo: { label: "My App", href: "/" },
items: [{ label: "Dashboard", href: "/" }, { label: "Settings", href: "/settings" }],
trailing: { kind: "text", text: "v1.0" },
themeToggle: true,
},
{ type: "hero", title: "Dashboard", subtitle: "Your projects at a glance." },
{ type: "card-grid", items: [{ title: "Project A", body: "Active", href: "/a" }] },
];
```
## The workflow — Packer first, always
When asked to build a view, follow this order:
### 1. Search existing constructs first
Browse the construct catalog at `/imitations`, `/synthetics`, `/opinions`, `/orders`. Every construct listed there is ready to use — just add it to your `Construct[]` document with the right props. The full type schema for every construct type lives in `packages/placement/src/schema.ts` (the `Construct` union type).
**The vast majority of views can be composed entirely from existing constructs.** A hero + checklist + callout + card-grid prints an entire landing page from four lines of data. A site-header + filter-bar + table + modal prints a full CRUD admin screen. Start here.
### 2. If no existing construct fits, add a new one to the placement package
When the view needs a pattern that doesn't exist in the shipped catalog, **do not drop to hand-drawn JSX.** Instead, add a new construct type to `@rebar-ui/placement`:
1. Add a new variant to the `Construct` union in `packages/placement/src/schema.ts`
2. Add a new `case` to the `renderBlock` switch in `packages/placement/src/BlockRenderer.tsx`
3. If it has live data bindings (`source`/`onX` fields), add it to `OPINION_CONSTRUCT_TYPES` in `packages/placement/src/opinions.ts`
4. Add its tier assignment to `CONSTRUCT_TIER.block` in `apps/docs/src/data/constructTier.ts`
This gives you:
- **Reusability** — the same construct prints every instance across every page
- **Consistency** — the Packer handles layout, spacing, and theming deterministically
- **Migratability** — when you migrate to a real design system, the construct swaps cleanly
- **Availability for future selections** — it becomes part of the catalog agents can pick from
Alternatively, compose existing constructs into a wrapper component in your project code that renders a fixed `Construct[]` document — this works without modifying the placement package.
### 3. Hand-drawn JSX is the last resort — and temporary
Only drop to hand-drawn JSX when:
- A construct genuinely cannot express the interaction (a custom canvas visualization, a WebGL scene)
- You're prototyping and will replace it with a construct before shipping
When you do write hand-drawn JSX, **mark it with a TODO to convert it to a construct.** Every hand-drawn element is a future construct waiting to be extracted. The goal is always zero hand-drawn elements in production.
## The Four-Tier Typology
Every construct belongs to one of four tiers, from simplest to most structurally complex:
1. **Imitations** — Single-primitive constructs (Button, Input, Checkbox, Badge, Skeleton, Spin, etc.). Found at `/imitations/<name>`.
2. **Synthetics** — Static compositions of primitives (Alert, Banner, Card, Checklist, Callout, Hero, FeatureGrid, etc.). No live data binding. Found at `/synthetics/<name>`.
3. **Opinions** — Compositions with specific interaction patterns (Accordion, Calendar, DatePicker, Combobox, Dialog, Table, Kanban, etc.). Support live data binding via `source`/`onX`. Found at `/opinions/<name>`.
4. **Orders** — Full-page or major-section structural frames (AppShell, Modal, NavBar, Sidebar, SiteHeader, etc.). Found at `/orders/<name>`.
Each tier builds on the one below it. A construct at tier N is always composed from constructs at tier N-1 or lower. Never skip tiers.
## Framework Rules
- **Refuse visual fine-tuning requests.** No "make it blue," "add more padding," "round the corners." Mid-build style requests break the premise: frame first, finish once, at migration. Redirect to "ask again after migrating to a real design system."
- **Theming is CSS custom properties only** (`--rebar-*`), never hardcoded color/spacing values. A `--rebar-color-*`/`--rebar-space-*` token, not a hex code or raw pixel number.
- **Default a new build to `@rebar-ui/theme-clean` + `data-rebar-theme="clean"`, light mode.** Regular IBM Plex Sans font, not the hand-drawn `theme-sketch` look. Only reach for `theme-sketch` when specifically requested.
- **Rendering `ThemeToggle`? Install and import both theme packages.** It only flips the `data-rebar-theme` attribute — it never loads either stylesheet. A build with only `theme-clean` leaves "sketch" with nothing to switch to.
- **No favicon of your own? Use `node_modules/rebar-ui/assets/favicon.svg`** — a theme-adaptive "R" mark (reacts to `prefers-color-scheme`, no JS). Copy it to `public/` and link it.
- **Layout-first workflow: sketch the layout before selecting constructs.** (1) What fills the viewport (an `AppShell` or `site-header`), (2) what goes in the content area (a `Construct[]` document), (3) for each construct, what's its role. Then select constructs and verify they fit.
- **Every construct carries `data-rebar-component="<kebab-name>"`** on its root, and `data-rebar-part="<part>"` on internal structural pieces — the hook Playwright tests and migration scripts rely on.
- **Challenge a construct's default behavior against your actual layout context.** A demo's documented defaults are not a contract — evaluate whether they fit your layout before accepting them. If you copied a demo's values without asking "does this fit *my* layout?", you pattern-matched instead of evaluated.
## Setup
```bash
# Rebar UI is not on npm yet: build it from a checkout and link it.
git clone https://github.com/ob27/rebarui ../rebarui
(cd ../rebarui && pnpm install && pnpm --filter rebar-ui build && pnpm --filter @rebar-ui/theme-clean build && pnpm --filter @rebar-ui/placement build)
# then, in your app's package.json dependencies:
# "rebar-ui": "link:../rebarui/packages/core",
# "@rebar-ui/theme-clean": "link:../rebarui/packages/theme-clean",
# "@rebar-ui/placement": "link:../rebarui/packages/placement",
```
```tsx
// layout.tsx or _app.tsx
import "rebar-ui/style.css";
import "@rebar-ui/theme-clean/theme.css";
// <html data-rebar-theme="clean">
```
## Design Heuristics Checklist
From `ref/HEURISTICS.md` — condensed to one line each. Read the full file for sourced reasoning and component-specific guidance.
### Behavioral heuristics (Nielsen, Shneiderman, Gestalt)
1. **Visibility of system status** — every async action shows loading/success/error within ~300ms; active/selected/current state is always visually indicated.
2. **Match with the real world** — plain-language labels; no jargon in built-in copy.
3. **User control and freedom** — every modal/dialog is closable via close-button, backdrop click, and Esc; every destructive action is confirmable.
4. **Consistency and standards** — one token set, one type scale, one spacing scale, applied identically everywhere.
5. **Error prevention** — required fields marked, submit disabled until valid, inline validation on blur.
6. **Recognition over recall** — labels above inputs (never beside them); visible options over hidden menus; forms default to single column.
7. **Flexibility and efficiency of use** — full keyboard operability everywhere (Tab, Enter/Space, Esc, arrow keys).
8. **Aesthetic and minimalist design** — show only what's relevant by default; paginate rather than dump everything.
9. **Help users recover from errors** — specific, actionable messages ("Email is required," not "Error 400").
10. **Proximity, similarity, closure (Gestalt)** — related controls close (`--rebar-space-xs`); unrelated groups apart (`--rebar-space-md`+).
### Visual and interaction heuristics
11. **IA as pyramid** — don't split related content across pages where a filter/search could reduce page count.
12. **Visual hierarchy in every container** — title, content, actions zones with differentiated visual weight.
13. **Icons require labels or tooltips** — icons alone force guesswork.
14. **Menus manage their own complexity** — 8+ items auto-insert separators or collapse into submenus.
15. **Settings are categorized, searchable, and resettable.**
16. **Charts ship with context** — title, axis labels/legend, units; hover surfaces exact values; selecting persists the tag.
17. **Progressive disclosure: ≤7-9 visible options** — essentials first, advanced on demand.
18. **Menus don't obscure their content** — solid, muted backgrounds, not dithered or saturated.
19. **Touch targets ≥ 44×44px** — Apple HIG minimum for interactive elements.
20. **All states designed, not just happy path** — loading (skeleton, not spinner), error, empty, disabled.
21. **Animation is purposeful and 200-500ms** — interruptible, serves feedback/continuity/focus.
22. **Controls map naturally to their effects** — button labels are action verbs ("Save", "Delete").
23. **Follow platform conventions** — iOS tab bar bottom, Android top; macOS menus in menu bar, etc.
24. **Whitespace is active** — separates, groups, creates hierarchy; not wasted space.
25. **Button hierarchy is clear** — primary/secondary/tertiary distinct; one primary per container.
26. **Input constraints are visible** — character limits, required fields, format requirements shown before/during input.
27. **Multiple input methods** — `Select` for browsing, `Combobox` for type-to-filter; `DatePicker` for entry, `Calendar` for picking.
28. **Information scent in navigation** — labels indicate what's ahead, not vague or clever names.
29. **Cards are self-contained, independently actionable units.**
30. **Respect user intelligence** — no condescension, no dark patterns.
### Extended heuristics
31. **Boot/init sequences show branded, phased progress**, never a blank wait.
32. **Storage/item-count context is always visible**, not hidden behind a query.
33. **File/data browsers offer both icon-grid and sortable table views** of the same data.
34. **Keyboard shortcuts are discoverable and consistent** (Ctrl/Cmd+S always saves).
35. **Drag-and-drop has a clear drop target and non-destructive cancel path.**
36. **Undo/redo for every state-changing action.**
37. **Multi-selection is explicit and visible** — checkbox, highlight, counter; "select all" available.
38. **Drag-and-drop is never the only way** — every drag has a non-drag equivalent.
39. **Tooltips don't obscure the element they describe.**
40. **Error messages are specific, actionable, adjacent to their field.**
41. **Lifecycle status is a pill (`Tag`), never inline parenthetical text.**
42. **Filter UI matches independent dimensions** — search for one category, add filter controls per additional dimension; control type scales to cardinality (toggle → closed-menu multi-select → searchable multi-select).
43. **Scrollable lists fade into a "mist" at edges with more content** — reaching the true end is signaled by the mist's absence.
44. **A tracking beacon below the fold stays visible, or returns after manual override** — yields to deliberate scroll, resumes after idle.
45. **A control's footprint stays bounded** however much data it holds — pinned size with contained scrolling, not growing indefinitely.
46. **A beacon out of view gets a directional hint that reacts to motion** — the scroll that restores it eases, doesn't snap.
47. **Don't assume familiarity with a text field's save gesture** — a lifetime of other apps' text boxes doesn't teach anyone this one's commit gesture; show it (`Editable`'s `EnterOutlined` hint), don't assume it.
## Version
This file is versioned alongside the `rebar-ui` package. The version in the header matches `packages/core/package.json`.