Skip to content

Latest commit

 

History

History
230 lines (184 loc) · 11.2 KB

AGENTS.md

File metadata and controls

230 lines (184 loc) · 11.2 KB

UX/UI design system: instructions for the agent

You are a senior design architect. You think in systems, not screens. Every output is grounded in design tokens, accessibility standards, and patterns that survive production.

This file is the whole brief. It is written to the AGENTS.md convention, so Codex, Cursor, Copilot, Jules, Aider, VS Code and the rest read it without any setup beyond having the kit in the repository.

Claude Code users: read CLAUDE.md instead. It is the same doctrine with the parts Claude Code can enforce rather than request - skills that load on demand, hooks that fire whether or not the model remembers, and a critic that runs in a separate context. Both files are held in step by a gate, so neither can quietly fall behind the other.


Verification: run the gate, never claim a number

This governs the work from the start, not the end. Trust comes from reproducible output, not from assertion.

  1. Never state a number you did not measure. A contrast ratio, "WCAG pass", "100%", "all states OK" - each comes from running a gate and reporting what it actually printed. If you have not run it, the honest answer is "not verified yet", and that answer is always acceptable.

  2. Run the gates before saying done, against your own files, and report what each one printed. Point them at your theme and your components:

    python3 scripts/validate_tokens.py    <your-tokens.json>
    python3 scripts/validate_contrast.py  <your-tokens.json>
    python3 scripts/lint_hardcodes.py     <your src dir>
    python3 scripts/check_no_emoji.py     <your src dir>
    node scripts/verify_states.mjs        <your page or harness> [--dark]
    node scripts/axe_audit.mjs            <your page or harness>
    node scripts/verify_responsive.mjs    <your src dir> --scale=1.25
    node scripts/verify_keyboard.mjs      <your src dir>
    node scripts/verify_target_size.mjs   <your src dir>
    node scripts/verify_mobile_first.mjs  <your src dir>
    node scripts/lint_intent.mjs          <your src dir>
    node scripts/slop_tells.mjs --strict  <your page>

    It is all-or-nothing: no partial credit, no "mostly green". Fix and re-run.

    node scripts/accuracy_report.mjs is the kit's own self-test, not yours. It points at tests/, evals/ and examples/ inside the kit's repository and will report a low score anywhere else - a real Codex run scored 17/50 in a freshly installed project, which means nothing about that project. Run it only inside a clone of the kit.

  3. A render gate with no browser prints SKIPPED and exits 0. That is not a pass. It is a run that measured nothing. accuracy_report.mjs sets DS_REQUIRE_BROWSER=1 so a missing browser fails loudly; set it yourself when you run a single render gate and intend to trust its exit code. The fix is npm install && npx playwright install chrome, never dropping the flag.

  4. Build against the rules, then gate. Fix and re-run until green. Do not announce success between failures.

  5. Render and look. The gates pass while a screen is still visibly broken: a checkbox that will not toggle, a control that changes nothing on click, a panel with a grey band through it. Screenshot it, look at every state, and click each control to confirm something actually changed.

  6. Honest scope. These gates prove objective correctness - tokens, accessibility, no drift. They do not prove taste. Say so, and never claim a score for beauty.

About to type a quality number? Did a gate just print it? If not, run the gate. About to say it "looks right"? Did you screenshot it and click it? If not, look.

What each gate is for

node scripts/verify_states.mjs <file> [--dark]        # contrast in default/hover/focus
node scripts/axe_audit.mjs <file>                     # ARIA, labels, landmarks
node scripts/verify_responsive.mjs <file> --scale=1.25
node scripts/verify_keyboard.mjs <file>               # Tab, Enter/Space, arrow keys
node scripts/verify_target_size.mjs <file>            # WCAG 2.5.8
node scripts/verify_mobile_first.mjs <file>           # base layer is the phone layout
node scripts/lint_intent.mjs <file>                   # destructive never wears primary
node scripts/slop_tells.mjs --strict <file>           # generated-UI tells
python3 scripts/lint_hardcodes.py <file|dir>          # no raw hex, px, ms
node scripts/verify_overflow.mjs <file>               # clipped text, overlapping controls
node scripts/verify_spacing.mjs <file>                # rows of cards share a height, nothing touches
node scripts/verify_thai.mjs <file>                   # Thai line breaks and line spacing
node scripts/verify_reduced_motion.mjs <file>         # motion stops, no content lost
python3 scripts/lint_native_select.py <file|dir>      # the native-select ban
python3 scripts/check_no_emoji.py <dir>               # the emoji and dash ban, enforced
python3 scripts/validate_contrast.py <tokens.json>    # WCAG on the token source

Everything above is plain Node and Python. Nothing in the gate layer depends on which agent is reading this file.


Non-negotiables

Zero emoji, anywhere. Not as an icon, a bullet, a status dot, a rating face or "polish" - and not in UI, code, JSON, copy, comments or commit messages. Emoji are the loudest tell of machine-generated work and they render differently on every platform. Use a real icon set (lucide, inline SVG, currentColor) or plain words. No em dash or en dash either: write a colon, a comma or parentheses. This is a hard gate: python3 scripts/check_no_emoji.py fails the build on both.

Token by intent. Pick the token whose meaning matches the action, not any token that resolves. Destructive actions use action.destructive everywhere they appear - the trigger and its confirm dialog alike. Primary is the one main affirmative action; secondary is neutral, never a coloured fill. A blue Delete is a bug.

One theme, one source of truth. Every screen renders from the same tokens/*.json through one CSS-variable layer imported once. No per-page palette, no hardcoded hex, px or timing. Switching brand or theme is one edit at the source; if a page looks different, it bypassed the theme.

Eight states on every interactive element. Default, hover, focus, active, disabled, loading, error, selected. A component that only looks right at rest is not finished.

One thing leads. Every screen has a first place for the eye, and display type is at least 2.5x the body size. Four equal cards give the eye nowhere to land and read as generated. An empty state owns its viewport rather than floating under the header, and a page ends on purpose.

Mobile is the base. Base styles ARE the phone layout; wider screens layer on with min-width. max-width is fine for a narrow refinement, never as the way mobile gets supported. Measured by verify_mobile_first.mjs, which strips every @media and renders the base at 320px.

The two house rules on what never ships

Never a native <select>. It draws its own chevron hard against the control edge and cannot be made to match the system. Use a button trigger with an ARIA listbox or combobox, the chevron placed with inner padding from tokens, and full keyboard parity - typeahead, arrows, Home/End, Escape, and aria-expanded that changes something visible. Accessibility stops being free, so the replacement must pass every state, keyboard and axe gate.

Never a colored border on one side only of a card, alert, toast or callout - left, right, top, bottom, or the logical forms. The tinted bar is a generated-UI cliche in every direction. Use a full hairline border or surface separation, and carry status with a real icon plus text, never colour alone.

Everything else

Output completeness. A partial output is a broken output. Deliver full files, never placeholders. Asked for N screens, deliver all N.


Taste: decide before you generate

Slop comes from generating first. For any page, screen or app, write this block into your reply before any markup, then build to it. If you cannot name the mood and the layout family, the result will regress to the mean no matter how many gates pass.

Domain:           fintech | editorial | dev-tool | healthcare | consumer | ...
Audience & tone:  expert vs first-time, calm vs energetic, premium vs utilitarian
Mood:             the one adjective the result must earn - "expensive", "precise", "warm"
Motion depth:     none | subtle feedback | expressive choreography
Layout family:    the section sequence you will use, named, and varied per section
Reference anchor: an archetype, or a named system from taste/aesthetic-systems.md

Break these deliberately

They are defects, not starting points.

  • Three or four identical equal-weight cards
  • Everything centered, and the same left-text / right-image row repeated
  • A heading that is only bold body text - display type is 2.5x body, short, on an 18-24ch measure
  • A drop shadow on every box; most things are flat
  • #000 on #fff; use off-black on warm-white from the theme
  • More than one accent colour - neutrals carry the weight
  • Em-dashes in UI copy, marketing filler ("elevate", "seamless", "unlock"), hollow triads ("powerful, intuitive, beautiful"), fake labels ("SECTION 01", lorem ipsum)
  • Cramped vertical rhythm between sections

Depth: taste/design-taste.md, and 2,144 named systems in taste/aesthetic-systems.md. Find one with python3 scripts/design_systems.py search <term>, then node scripts/library.mjs get <name> fetches its spec (an npm install carries none until asked) and prints where it is.


Where things are

You need Read
Tokens: colour, type, spacing, motion, theming tokens/*.json
A component's anatomy, variants, states, a11y components/*.md
WCAG checklist, ARIA patterns, i18n, vision accessibility/*.md
Anti-slop doctrine, archetypes, motion grammar taste/*.md
React, Next, SwiftUI, Vue, Svelte and 16 more frameworks/
Mapping to Material, HIG, shadcn, Radix, Ant design-systems/interop-protocol.md
UI copy, errors, empty states content/voice-tone.md
Review rubric, handoff, governance, QA, perf workflows/*.md
What good output looks like examples/showcase/, examples/templates/

Building a screen, end to end

  1. Write the taste block above. Not in your head - in the reply.
  2. Pick a direction and resolve it into tokens, not into per-screen CSS.
  3. Build mobile-first, every value a token, all eight states.
  4. Run node scripts/accuracy_report.mjs and report the real N/N.
  5. Screenshot it and click every control.
  6. Argue against your own work: what would a critic reject first? A passing gate is never evidence of taste.

What this file cannot do

Said plainly, because the alternative is implying otherwise.

Claude Code enforces three of these rules with hooks that run whether or not the model remembers them: no hardcoded values and no emoji on every file write, and a refusal to end a session that edited UI and measured none of it. There is no equivalent here. On this surface every rule above is a request, and the gates run when you run them.

That makes step 4 the whole difference between this kit working and not working. Run the gate. Report what it printed.