BXUP Track progress & test yourself — free
Frontend & Mobile · Playbook · Core · ~4 min read
FRONTEND & MOBILE · PLAYBOOK · CORE

Write Instruction Files That Keep Generated UI on Your Design System

Every generator on your team — Copilot, Cursor, Claude Code — will invent its own buttons, colors, and spacing unless you condition it. Instruction files with named primitives, token rules, and one exemplar component turn "plausible React" into "our React," and they work on every PR from every teammate from now on.

Tier: Core · Workflow area: Component development · Tools: GitHub Copilot instructions, Cursor rules, Claude Code (CLAUDE.md)

Uninstructed models emit statistically average code: raw <button> elements, hex colors, margin: 12px, ad-hoc useState forms. Your design system is, by definition, not statistically average. This playbook builds the instruction layer once so on-system output becomes the default, not a per-prompt negotiation.

When to use

  • Two or more people generate UI in the repowithout shared instructions, each person's AI output diverges in its own direction.
  • You have a design system or component libraryinternal package, wrapped MUI, shadcn/ui fork — that generated code keeps ignoring.
  • Review comments repeat themselves"use <Button> not <button>", "that's not a token" — each repeated comment is a missing instruction line.
  • Before any larger AI adoptionregistry MCPs (pb-03) and Figma pipelines (pb-09) all assume this layer exists.

When NOT to use

  • No design system exists yetinstructions cannot reference primitives you don't have; stabilize a minimal token + primitive set first.
  • A throwaway prototypeoff-system output is fine behind the throwaway boundary (pb-01).
  • As a dumping grounda 2,000-line instruction file gets ignored (by models and humans); if it can't stay under ~150 lines, split per-directory.

The recipe

  1. Mine your review history for the rules that matter. Instructions should encode real failure modes, not aspirations. Pull the evidence:

    gh pr list --state merged --limit 50 --json number \
      --jq '.[].number' | head -20 > /tmp/prs.txt
    gh api repos/{owner}/{repo}/pulls/PR_NUMBER/comments \
      --jq '.[].body' | grep -iE 'token|primitive|button|spacing|a11y'
    

    Cluster the comments. The top five clusters are your first five rules.

  2. Write the core instruction file. One file, three sections: what to use, what is banned, where the exemplars live. For Claude Code this is CLAUDE.md at the repo root:

    ## UI generation rules
    
    - Components come from @acme/ui ONLY. Never write raw <button>, <input>,
      <select>, <dialog>. Import: Button, TextField, Select, Modal, Stack,
      Text, Icon. If a primitive is missing, STOP and ask — do not hand-roll.
    - Styling: Tailwind with our preset only. Banned: arbitrary values
      (w-[347px]), hex/rgb colors, inline style={}. Spacing uses the 4px
      scale via Stack gap or p-* / m-* utilities.
    - Color and type come from tokens: text-fg-*, bg-surface-*, text-heading-*.
    - Every interactive element: visible focus ring (our Button handles it),
      accessible name, keyboard operability.
    - Forms: react-hook-form + zodResolver. See exemplar below.
    - Exemplar component (canonical shape, copy its patterns):
      src/features/settings/ProfileForm.tsx
    - Exemplar list screen: src/features/orders/OrdersTable.tsx
    
  3. Mirror it for every tool your team actually uses. Same content, three homes — do not let tools drift apart:

    mkdir -p .github .cursor/rules
    cp docs/ui-rules.md .github/copilot-instructions.md
    cp docs/ui-rules.md .cursor/rules/ui.mdc
    

    For Cursor, add frontmatter to .cursor/rules/ui.mdc so it auto-attaches to UI code:

    ---
    description: Design-system rules for all UI code
    globs: ["src/**/*.tsx", "src/**/*.vue"]
    alwaysApply: true
    ---
    
  4. Add one or two exemplar components — few-shot beats rules. Models imitate concrete code far better than they follow abstract rules. Pick your best-reviewed component of each archetype (form, table/list, detail card), make sure it is genuinely canonical, and reference it by path in the instructions. If a real exemplar doesn't exist, write one now; it pays for itself within a week.

  5. Test the conditioning with an A/B generation. Same prompt, instructions off (scratch clone) vs. on:

    Create a NotificationPreferences component: email/push/SMS toggles per
    category (billing, security, product updates), save button with pending
    state, server-error banner.
    

    A good conditioned output imports Switch, Stack, Button, Banner from @acme/ui, uses token classes only, and follows the exemplar's form pattern. If it still hand-rolls a toggle, your instructions are ambiguous — name the missing primitive explicitly and rerun.

  6. Enforce with a lint gate so drift can't merge. Instructions steer; CI enforces:

    npx eslint --rulesdir tools/eslint src/ \
      --rule '{"acme/no-raw-interactive-elements": "error"}'
    

    Even a lightweight grep gate in CI catches the worst regressions:

    grep -rn --include='*.tsx' -E '<button|style=\{\{|#[0-9a-fA-F]{6}' src/ \
      && echo 'Off-system UI found' && exit 1 || true
    
  7. Assign ownership and a change ritual. Instruction files are code: PR-reviewed, owned by the design-system team (add them to CODEOWNERS), updated whenever a review comment repeats twice.

Remember:

Instruction files are read by every model run but written once — a single vague line ("follow our design system") produces vague output on every generation across the whole team. Name the primitives, name the banned patterns, point at exemplars by path.

Make it yours

  • React + shadcn/ui or internal packagethe recipe as written; the exemplar-component trick matters most here because React has the widest "statistically average" gravity to fight.
  • Vue 3 teamssame three files; rules reference your SFC conventions (<script setup lang="ts">, composables over mixins, defineModel for v-model) and point exemplars at a canonical .vue component; glob src/**/*.vue.
  • Angular teamsreference standalone components, signals-based state, and your Material wrapper module; ban direct mat-* usage if your system wraps it; exemplar is a canonical standalone component with its spec file.
  • React Native / Flutterrules name your themed primitives (<AppText>, ThemeData extensions), ban StyleSheet.create with raw values or hardcoded Color(0xFF...), and add platform rules: safe-area handling, touch targets ≥ 44pt.

Quality check

  • The A/B test passesidentical prompt with instructions produces imports from your package and zero raw interactive elements.
  • All three tool homes are in syncCLAUDE.md, .github/copilot-instructions.md, .cursor/rules/ generated from one source or diff-checked in CI.
  • Exemplars are real and currentthe referenced files exist, compile, and represent this quarter's conventions, not last year's.
  • A CI gate backs the instructionsat minimum the grep gate; ideally custom lint rules.
  • New teammates inherit it silentlya fresh clone plus any supported tool produces on-system output with no tribal knowledge.
Key Takeaway:

One well-mined instruction file with named primitives, explicit bans, and a canonical exemplar converts every AI tool on the team from a design-system liability into a design-system multiplier — and CI, not vigilance, keeps it that way.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in