@deriv-ds/design-intelligence-layer 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/build/SKILL.md +509 -0
- package/.claude/skills/build/references/components-and-tokens.md +490 -0
- package/.cursor/commands/build.md +3 -0
- package/.kiro/steering/build.md +6 -0
- package/AGENTS.md +25 -3
- package/CHANGELOG.md +25 -1
- package/README.md +79 -59
- package/dist/index.cjs +1473 -1181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -6
- package/dist/index.d.ts +42 -6
- package/dist/index.js +1491 -1209
- package/dist/index.js.map +1 -1
- package/guides/design-system-guide/deriv-design-intelligence-guide.md +214 -176
- package/guides/design-system-guide/icon-reference.md +1 -1
- package/guides/design-system-guide/quill-ds-guide.md +31 -30
- package/guides/rules/design-system-consuming-project.mdc +42 -30
- package/package.json +5 -1
- package/src/styles.css +73 -3
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build
|
|
3
|
+
description: Build Deriv UI strictly from the @deriv-ds/design-intelligence-layer npm package — pages, screens, and components. Triggers when the user mentions "@deriv-ds/design-intelligence-layer" or the Deriv design system, asks to bootstrap/build a project with it, asks to build a page/screen/component in a project where the package is installed, pastes a figma.com URL to build with the Deriv design system, or pastes a code block importing from @deriv-ds/design-intelligence-layer or @/components/ui. Do NOT trigger for generic build requests unrelated to Deriv or this package.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# build
|
|
7
|
+
|
|
8
|
+
Build pages and screens with `@deriv-ds/design-intelligence-layer` — Deriv's npm-published design system. This skill handles four modes: **bootstrap** a fresh project, **build from a text prompt**, **build from a Figma link**, and **integrate a pasted block**.
|
|
9
|
+
|
|
10
|
+
**The sole goal: every build comes strictly from the package — its components, its tokens, its primitives. Figma builds must be 1:1 replicas of the frame. Never invent components, classes, copy, or layout.**
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## HARD RULES — never violate these
|
|
15
|
+
|
|
16
|
+
1. **Components come from the package.** Every import must exist in `node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts` (the package ships compiled — there is NO `src/index.ts`). The canonical list is in [references/components-and-tokens.md](references/components-and-tokens.md), including the **commonly hallucinated imports** table (`Tabs` → `Tab`, `Notification` → `NotificationBanner`, etc.). If the design needs an element with no package component, **compose it from package components + tokens** (like the Blocks pattern) — never invent an import, never pull in another UI library, and flag the composition in the Build Report.
|
|
17
|
+
|
|
18
|
+
2. **Every colour/radius/font-family value resolves through the Token Resolution Loop (below).** Raw values never appear in JSX or class names. Forbidden everywhere:
|
|
19
|
+
- hex / rgba literals (`#FFFFFF`, `rgba(0,0,0,.5)`) — even brand coral
|
|
20
|
+
- raw Tailwind palette, ANY hue (`bg-gray-*`, `bg-red-500`, `text-blue-600`, `bg-black`, `bg-white`, …)
|
|
21
|
+
- arbitrary colour values (`bg-[#EEE]`, `text-[rgba(...)]`, `bg-[var(--anything)]`)
|
|
22
|
+
- `hsl(var(--x))` (Tailwind v3 syntax)
|
|
23
|
+
- arbitrary radius (`rounded-[12px]`) and `font-family` overrides
|
|
24
|
+
|
|
25
|
+
When no existing token fits, **create one** (loop step 2–4) — do not stop, do not ask, do not silently substitute a wrong-but-existing token.
|
|
26
|
+
|
|
27
|
+
3. **Imports come from `@deriv-ds/design-intelligence-layer`.** Icons use the package's `<Icon />` component (`import { Icon } from "@deriv-ds/design-intelligence-layer"`) — it wraps `@deriv/quill-icons`. **Never install or import `lucide-react` or any other icon library.** Never import from local `@/components/ui/*` paths (playground-internal) and never from the older `@deriv-com/quill-ui*`.
|
|
28
|
+
|
|
29
|
+
4. **Don't install bundled deps** — no separate `tailwindcss`, no `tailwind.config.js`, no `lucide-react`. Tailwind v4 and the icon set (`@deriv/quill-icons`, surfaced via `<Icon />`) are already inside the package.
|
|
30
|
+
|
|
31
|
+
5. **Blocks are importable — and matching a block is MANDATORY before you mock anything up.** The package ships whole sections as named exports — navbar, heroes, home banner/highlights/explore, trading cards/action-buttons/empty, referral, header, feedback screens, section wrapper (full catalog + "matches when" hints in [references/components-and-tokens.md](references/components-and-tokens.md)). Before composing from primitives or writing JSX for ANY page region, run the **Block Matching gate** (below): if a region in the design or the prompt corresponds to a block, you MUST import and use that exact block — never re-implement, re-mock, or eyeball a look-alike of something the package already ships. Only compose from primitives when the Block Matching gate finds no block for that region, and flag the composition in the Build Report. Never invent a block import name.
|
|
32
|
+
|
|
33
|
+
6. **Figma builds are replicas, not interpretations** (Mode 4): no element in the build that isn't in the frame, no element in the frame missing from the build, no copy that isn't verbatim from the frame, no eyeballed spacing/sizes/weights — every value comes from the extracted design data.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Token Resolution Loop — run for EVERY colour/radius/font value, in every mode
|
|
38
|
+
|
|
39
|
+
For each value the design needs, resolve in order — **first match wins, never skip ahead to hardcoding, never stop to ask**:
|
|
40
|
+
|
|
41
|
+
1. **Existing semantic token** — check the [token cheatsheet](references/components-and-tokens.md). Intent match, not just colour match: `component/app/background/normal` (#FFFFFF) → `bg-card`, NOT `bg-prominent`.
|
|
42
|
+
|
|
43
|
+
2. **Package semantic variable** — `src/styles.css` ships theme-aware vars (`--text-success-default`, `--background-error-default`, …) that flip in dark mode. Register in the project's `globals.css`:
|
|
44
|
+
```css
|
|
45
|
+
@theme inline { --color-success: var(--text-success-default); } /* → text-success */
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
3. **Package primitive** — 1294 primitives (`--primitive-coral-700`, `--primitive-slate-1200`, …). Find by hex, then register:
|
|
49
|
+
```bash
|
|
50
|
+
grep -i "#0777C4" node_modules/@deriv-ds/design-intelligence-layer/src/styles.css
|
|
51
|
+
```
|
|
52
|
+
```css
|
|
53
|
+
@theme inline { --color-brand-blue: var(--primitive-blue-900); } /* → bg-brand-blue */
|
|
54
|
+
```
|
|
55
|
+
⚠ Primitives are static (no dark-mode flip) — prefer step 2 when a semantic var resolves to the same hex.
|
|
56
|
+
|
|
57
|
+
4. **Exact value, centralized** — only when nothing in the package resolves to the design's value. Create the token with the exact Figma value; the raw value lives in ONE place (the theme), never in JSX:
|
|
58
|
+
```css
|
|
59
|
+
@theme inline { --color-figma-hero-overlay: #1A2233; } /* → bg-figma-hero-overlay */
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Rules for created tokens: intent-based names following the package convention (`--color-<intent>`, `--radius-<name>`); registered after the DS imports in `globals.css`; **every created token is listed in the Build Report**. Referencing a primitive or var inline (`bg-[var(--primitive-…)]`) is still forbidden — register, then use the generated class. **Idempotent registration:** before adding a token, grep `globals.css` for an existing `--color-*` of the same intent/value and reuse it — never register a second token for a colour you already have.
|
|
63
|
+
|
|
64
|
+
**Loop until zero unresolved values remain. The build may not start while any value is unresolved.** *Loop discipline (exit · bound · escalate):* this loop always terminates — step 4 (create an exact-value token) is available for any value, so there is no "unresolvable" case and no hardcoding fallback. The only escalation is ambiguous **intent** (you can't tell which semantic token the design means): stop and ask the user — do not guess a token.
|
|
65
|
+
|
|
66
|
+
(Spacing, sizing, font-size/weight/line-height are NOT tokens — use the exact values from the design, including arbitrary px like `p-[18px]`, for fidelity.)
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Preflight — run before writing UI in EVERY build mode (3, 4, 5)
|
|
71
|
+
|
|
72
|
+
Even when you're not bootstrapping, the app may have been scaffolded earlier with create-next-app defaults that silently break DS tokens and fonts. Before generating any page:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
grep -rn "next/font\|geist\|--background:\|--foreground:\|prefers-color-scheme" app/ src/ 2>/dev/null
|
|
76
|
+
grep -n "@source" app/globals.css src/app/globals.css src/index.css 2>/dev/null
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If the first grep hits, fix it (same as Mode 2's "Clean the scaffold"):
|
|
80
|
+
- Strip `next/font`/Geist imports from `layout.tsx`; ensure `<body className="bg-card text-prominent">`.
|
|
81
|
+
- Remove default `:root --background/--foreground` and `@media (prefers-color-scheme: dark)` blocks from `globals.css` — leave only the DS `@import`/`@source` lines.
|
|
82
|
+
|
|
83
|
+
For the second grep, **verify the `@source` path actually resolves** — it is relative to the CSS file: `../node_modules/...` from `app/globals.css`, but `../../node_modules/...` from `src/app/globals.css`. A wrong path means Tailwind generates NO classes for package internals — components render unstyled and the build looks broken even when the code is right.
|
|
84
|
+
|
|
85
|
+
These leftovers cause near-black backgrounds and the wrong font even when the build code itself is correct.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Page layout scaffold — match the design's actual placement
|
|
90
|
+
|
|
91
|
+
**Replicate where the design puts each element — do NOT impose a layout pattern of your own.** In Figma mode especially, read the frame: are the action buttons *pinned to the bottom* of the viewport, or do they *flow inline* directly under the content? These need different scaffolds, and guessing wrong is a real defect.
|
|
92
|
+
|
|
93
|
+
**A — Buttons flow inline below content** (most onboarding/form steps; the buttons sit right under the field with empty space beneath them):
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<main className="min-h-screen flex flex-col">
|
|
97
|
+
<header className="shrink-0">…</header>
|
|
98
|
+
<div className="flex flex-col …">…content…</div> {/* NOT flex-1 */}
|
|
99
|
+
<div className="flex flex-col …">…actions…</div> {/* flows directly under content */}
|
|
100
|
+
</main>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**B — Action bar pinned to the bottom of the viewport** (only when the design clearly anchors it there):
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<main className="min-h-screen flex flex-col">
|
|
107
|
+
<header className="shrink-0">…</header>
|
|
108
|
+
<div className="flex-1 …">…scrolling body…</div> {/* flex-1 pushes footer down */}
|
|
109
|
+
<footer className="shrink-0">…primary CTA…</footer>
|
|
110
|
+
</main>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**How to distinguish A from B in a Figma frame (Mode 4):**
|
|
114
|
+
- Pattern B requires the footer element to be explicitly **constrained/pinned to the bottom** in Figma — a "Fixed position" or a bottom-anchored auto-layout constraint. Confirm this in `get_design_context`.
|
|
115
|
+
- **Trailing blank space below the action buttons is NOT a signal for Pattern B.** It means the frame has a fixed height with empty padding — the buttons still flow inline (Pattern A).
|
|
116
|
+
- When the Figma frame shows no explicit bottom constraint on the action area, **default to Pattern A**. Using Pattern B when the design doesn't anchor the footer is always a layout defect.
|
|
117
|
+
|
|
118
|
+
Rule: don't add `flex-1` to the body unless `get_design_context` confirms the footer is bottom-anchored. Either way, verify the primary CTA is visible in the default viewport and placed where the design shows it (see Final Checklist).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Block Matching gate — run BEFORE composing or writing JSX, in Modes 3, 4, 5
|
|
123
|
+
|
|
124
|
+
The package ships whole page sections as blocks. **Before you build any region from primitives, match it against the block catalog.** A region that corresponds to a shipped block must be *imported*, not mocked up — re-implementing a navbar, hero, or card the package already exports is a defect, even if your version "looks right".
|
|
125
|
+
|
|
126
|
+
**Step 0 — classify the page type FIRST, then apply the App Shell defaults.** Before matching individual regions, decide what kind of page this is (home/dashboard, trading, form/settings, marketing/landing, auth, standalone). Home/dashboard pages have a **mandatory default app shell** — a header block + `Navbar` — that must be included *even when the prompt or frame never mentions them* (see [App Shell defaults](#app-shell-defaults--homedashboard-pages) below). This is the fix for the most common defect: a home page rendered with no top header and no bottom nav. Match on **intent and implication, not just explicit mentions** — a "home page" implies the shell whether or not the word "navbar" appears.
|
|
127
|
+
|
|
128
|
+
**Procedure — for every distinct region of the page (header strip, nav, hero/balance area, banner, highlight cards, trading cards, action buttons, empty state, referral panel, footer/section wrapper):**
|
|
129
|
+
|
|
130
|
+
1. **Identify the region's intent** — not its pixels. "Top nav with menu items", "balance/total-assets header", "promo banner card", "row of highlight cards", "account card with balance + actions", "empty trading list", "referral invite panel". The intent is what you match on.
|
|
131
|
+
2. **Look it up in the block catalog** ([references/components-and-tokens.md](references/components-and-tokens.md) → *Blocks* table, which lists each export with a "matches when" hint).
|
|
132
|
+
3. **If a block matches → import and use it.** Resolve the responsive variant (mobile vs desktop hero, `Navbar` vs `NavMobileBottomBar`/`NavDesktopSidebar`) from the target viewport. Wire its props/children to the design's real content. Do NOT re-create its internals.
|
|
133
|
+
4. **Read the block's real prop types before wiring — never guess prop names.** The catalog gives the export name and intent, not the signature. Look the block's type up in `node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts` (e.g. `grep -A30 "interface NavbarProps\|type NavbarProps\|NavbarProps =" node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts`) and pass exactly the props it declares. Inventing a prop that doesn't exist is as much a defect as hand-rolling the block.
|
|
134
|
+
5. **If no block matches → compose from primitives + tokens** and flag the composition in the Build Report.
|
|
135
|
+
|
|
136
|
+
**The gate runs per region, and blocks compose.** A page is normally several blocks together (`Navbar` + a `Hero*` + one or more `Section`s, plus primitives for the gaps), not one block. Finding a block for the nav doesn't end the matching — run the procedure for every region, assemble the matched blocks into the page layout, and only fill the leftover regions with composed primitives.
|
|
137
|
+
|
|
138
|
+
**Two triggers for a match — both are mandatory:**
|
|
139
|
+
|
|
140
|
+
- **Prompt / text signal (Mode 3, 5):** the user names or describes a region that maps to a block — "navbar", "nav menu", "sidebar nav", "bottom bar", "hero", "balance header", "banner", "highlights", "explore Deriv", "trading account card", "action buttons", "empty state", "referral", "header". Identify it and pull the block. Example: a "build the home page with a nav menu" prompt → import `Navbar`, do not hand-roll a nav.
|
|
141
|
+
- **Figma signal (Mode 4):** a frame (or a node subtree) is visually/structurally the same as a shipped block — e.g. the frame's nav matches the `Navbar` block, the balance area matches a `HeroDesktopHome*`, a card matches `TradingAccountCard`. When `get_code_connect_map` already maps the Figma component to a block export, that mapping is binding. Otherwise match by structure + intent against the catalog. A matched region is imported as the block and tuned to the frame's content — **not** rebuilt node-by-node from the fidelity contract. (The contract still records the region; its "→ Resolution" column reads `block: <ExportName>`.)
|
|
142
|
+
|
|
143
|
+
Record every region's outcome (`block: <Name>` or `composed`) — this feeds the Build Report's block audit.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## App Shell defaults — home/dashboard pages
|
|
148
|
+
|
|
149
|
+
**Home and dashboard pages ship with an app shell by default — a header block on top and a `Navbar`.** These regions are *implied* by the page type, so the Block Matching gate must inject them even when the user's prompt or the Figma frame never names them. A home page rendered as a bare content column with no top header and no bottom nav is a defect — this section exists to kill it.
|
|
150
|
+
|
|
151
|
+
**When it applies:** the page is classified (gate Step 0) as **home** or **dashboard** — prompts like "home page", "dashboard", "wallet home", "main app screen", or a Figma frame that is clearly the app home/landing-after-login. It does NOT auto-apply to marketing/landing pages, auth screens, or explicitly standalone screens (a single card, a success screen, one onboarding step).
|
|
152
|
+
|
|
153
|
+
**What the shell is:**
|
|
154
|
+
|
|
155
|
+
| Shell region | Block | Notes |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| Top header | `HeaderAppHome` (home) or `HeaderApp` (in-app sub-page) | Match to the frame's header if one is shown; **inject `HeaderAppHome` if the design omits it** on a home/dashboard page. |
|
|
158
|
+
| Navigation | `Navbar` (responsive) | Renders `NavDesktopSidebar` ≥768px and `NavMobileBottomBar` <768px automatically. Wire `activeId` to the current page. |
|
|
159
|
+
|
|
160
|
+
**Rules:**
|
|
161
|
+
- Include the shell by default on home/dashboard builds. **Skip it only** when the user explicitly says "no nav / no header / just the content", or the page is clearly not a home/dashboard.
|
|
162
|
+
- The shell counts as matched blocks — read their real props (`HeaderAppHomeProps`, `Navbar`'s `activeId`/`badges`/`onNavChange`) from `dist/index.d.ts` before wiring, per gate step 4.
|
|
163
|
+
- Record the shell decision in the Build Report's block audit — either `block: HeaderAppHome` + `block: Navbar`, or `omitted (reason)`.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Responsive coverage — every build ships mobile AND desktop
|
|
168
|
+
|
|
169
|
+
**Every page build must render correctly at BOTH a mobile viewport (~375px) and a desktop viewport (~1440px).** A mobile-only render (the second common defect) is not "done". Blocks are designed for this: `Navbar` is responsive internally, and heroes/trading blocks ship as mobile/desktop pairs.
|
|
170
|
+
|
|
171
|
+
**How to cover both viewports:**
|
|
172
|
+
- **Responsive blocks** (`Navbar`, `Section`, and any block taking a `layout` prop) work at both widths out of the box — use them directly.
|
|
173
|
+
- **Paired blocks** (mobile/desktop hero + trading variants) have no single responsive export — render BOTH and switch on the package's **768px breakpoint** using visibility utilities, mirroring how `Navbar` picks its variant:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
{/* mobile hero <768px */}
|
|
177
|
+
<div className="md:hidden"><HeroMobileHomeTotalAssets /></div>
|
|
178
|
+
{/* desktop hero ≥768px */}
|
|
179
|
+
<div className="hidden md:block"><HeroDesktopHomeWithBalance /></div>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- The mobile/desktop pairing table lives in [references/components-and-tokens.md](references/components-and-tokens.md) (Blocks section) — resolve BOTH variants of every paired region before writing JSX.
|
|
183
|
+
- **Only render a single viewport** when the user explicitly restricts scope ("mobile only", "just the desktop view").
|
|
184
|
+
|
|
185
|
+
**Mode 4 (Figma) responsive rule:** the provided frame is the 1:1 ground truth **at its own viewport**. The *other* viewport is generated by default from the corresponding desktop/mobile block variants and brand-consistent layout — flagged in the Build Report as "generated, no frame provided". If the user supplies frames for both viewports, each is its own 1:1 replica. The visual-diff loop (4.4) runs against the frame's viewport; the generated viewport gets the runtime responsive checks (Final Checklist), not a pixel diff.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Mode 1 — Router
|
|
190
|
+
|
|
191
|
+
When invoked, classify the user's message:
|
|
192
|
+
|
|
193
|
+
- **`bootstrap`** — package not installed, or phrases like "set up", "new project", "install Deriv DS", "start with Deriv"
|
|
194
|
+
- **`figma`** — message contains a `figma.com/design/...` URL
|
|
195
|
+
- **`pasted-block`** — message contains a fenced code block importing from `@deriv-ds/design-intelligence-layer` or from `@/components/ui/*`, OR the user says "paste this block / build this navbar / integrate this hero" with code
|
|
196
|
+
- **`text-prompt`** — natural-language page request ("login page", "settings page", "dashboard", "build me an X")
|
|
197
|
+
|
|
198
|
+
Modes can compose on one turn (e.g. bootstrap → then text-prompt).
|
|
199
|
+
|
|
200
|
+
If invoked as `/build` with no args, ask one question: **"What are you building?"** — landing, dashboard, onboarding, marketing, form-heavy (login/signup/KYC/settings), or trading interface.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Mode 2 — Bootstrap
|
|
205
|
+
|
|
206
|
+
Set up a fresh consuming project. Default to Next.js App Router.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npx create-next-app@latest <name> --typescript --tailwind --app --src-dir
|
|
210
|
+
cd <name>
|
|
211
|
+
npm install @deriv-ds/design-intelligence-layer
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Wire the global stylesheet — REPLACE the existing Tailwind import block. **The `@source` path is relative to the CSS file**; with `--src-dir` (as scaffolded above) the file is `src/app/globals.css`, so it needs TWO levels up:
|
|
215
|
+
|
|
216
|
+
```css
|
|
217
|
+
/* src/app/globals.css (Next.js with --src-dir, as above) */
|
|
218
|
+
@import "@deriv-ds/design-intelligence-layer/styles";
|
|
219
|
+
@import "tailwindcss";
|
|
220
|
+
@source "../../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```css
|
|
224
|
+
/* app/globals.css (no src dir) — one level up */
|
|
225
|
+
@source "../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
A wrong `@source` path fails silently: Tailwind generates no CSS for package internals and every DS component renders unstyled. Verify after first render (Final Checklist).
|
|
229
|
+
|
|
230
|
+
For **Vite** projects only, add `@tailwindcss/vite` to `vite.config.ts`. Next.js needs no extra config.
|
|
231
|
+
|
|
232
|
+
Copy the package's `AGENTS.md` into the project root so other AI agents pick it up:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/AGENTS.md ./AGENTS.md
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Generate a `CLAUDE.md` at project root pointing to the mandatory guides:
|
|
239
|
+
|
|
240
|
+
```md
|
|
241
|
+
# Project guidance
|
|
242
|
+
|
|
243
|
+
This project uses `@deriv-ds/design-intelligence-layer`. Before building any UI, read:
|
|
244
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md`
|
|
245
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc`
|
|
246
|
+
|
|
247
|
+
Use the `build` skill for any "build me a page / build with this Figma / build this block" request.
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Clean the Next.js scaffold** (create-next-app ships defaults that fight DS token resolution):
|
|
251
|
+
- In `src/app/layout.tsx` (or `app/layout.tsx`): remove `next/font` imports (Geist etc.) and the `className={…font…}` on `<html>`/`<body>`. Set `<body className="bg-card text-prominent">`. The DS styles import loads **Inter** automatically — per `guides/rules/design-system-consuming-project.mdc`, do NOT use `next/font` and do NOT override `font-family`.
|
|
252
|
+
- In `globals.css`: after the DS import block, **delete** the default `:root { --background / --foreground }` declarations and the `@media (prefers-color-scheme: dark)` override that create-next-app emits — they shadow DS tokens. The file should be essentially just the three `@import`/`@source` lines.
|
|
253
|
+
|
|
254
|
+
Run `npm run dev` and confirm with the running preview that the page background renders **white** (`bg-card`) and body text renders **near-black** (`text-prominent`) — verify the computed colours, don't assume the class names. (Reminder: `bg-prominent` is a near-black *text* token, not a white background — see [references/components-and-tokens.md](references/components-and-tokens.md).)
|
|
255
|
+
|
|
256
|
+
If the user immediately follows with a build request, chain into the matching mode below.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Mode 3 — Text prompt ("build me an X page")
|
|
261
|
+
|
|
262
|
+
1. **Read the mandatory guides first** (per `AGENTS.md` Step 1):
|
|
263
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md`
|
|
264
|
+
2. Read `node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc`.
|
|
265
|
+
3. Read [references/components-and-tokens.md](references/components-and-tokens.md) for the canonical component list and token cheatsheet.
|
|
266
|
+
4. **Run Preflight** (above) to clean any leftover Geist/font/`:root` scaffold defects.
|
|
267
|
+
5. **Classify the page type** (gate Step 0) — home/dashboard, trading, form/settings, marketing, auth, standalone. If home/dashboard, the **App Shell defaults** apply: include `HeaderAppHome` (or `HeaderApp`) + `Navbar` by default, even though the prompt didn't name them.
|
|
268
|
+
6. **Run the Block Matching gate** (above) over the page's regions — any region the prompt names OR implies that maps to a block (app shell header + nav, hero/balance header, banner, highlights, explore, trading cards, action buttons, empty state, referral) is imported as that block, not mocked up. Only the regions with no block match proceed to primitive composition.
|
|
269
|
+
7. Map the remaining (non-block) regions to components. For each required UI element, confirm it's on the list (HARD RULE 1). Anything missing → compose from package components + tokens, flag in the Build Report.
|
|
270
|
+
8. Sketch the layout structurally (flex/grid utilities are fine); if the page has a bottom action bar, use the **Page layout scaffold** above. Then drop in matched blocks and package components. Every colour/radius/font value goes through the **Token Resolution Loop**. Page background → `bg-card`, primary text → `text-prominent` (never `bg-prominent` as a surface). Typography → `heading-*`/`body-*` classes (NOT `text-heading-*`).
|
|
271
|
+
9. **Cover both viewports** (see [Responsive coverage](#responsive-coverage--every-build-ships-mobile-and-desktop)) — use responsive blocks directly and render both variants of any paired block behind `md:hidden` / `hidden md:block`, unless the user restricted scope to one viewport.
|
|
272
|
+
10. Write copy in a voice aligned with `deriv-brand-voice.md`.
|
|
273
|
+
11. Run the Final Checklist below.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Mode 4 — Figma link → 1:1 replica
|
|
278
|
+
|
|
279
|
+
Triggered by a `figma.com/design/...` URL. **The output is a replica of the frame, not an interpretation.** The three failure modes this mode exists to kill: invented/omitted elements, paraphrased copy, eyeballed spacing/weights.
|
|
280
|
+
|
|
281
|
+
### 4.1 Extract — everything, before any code
|
|
282
|
+
|
|
283
|
+
1. Confirm `mcp__figma__*` tools are available. **Run Preflight** (above).
|
|
284
|
+
2. Parse the URL: extract `fileKey` and `nodeId` (convert `-` → `:` in nodeId).
|
|
285
|
+
3. Call, in this order:
|
|
286
|
+
- `mcp__figma__get_metadata` — the **complete node tree** of the frame. This is the inventory; every visible node in it must appear in the contract below.
|
|
287
|
+
- `mcp__figma__get_design_context` — layout, auto-layout values, constraints, text content, styles.
|
|
288
|
+
- `mcp__figma__get_variable_defs` — design variables → resolved values.
|
|
289
|
+
- `mcp__figma__get_screenshot` — the visual ground truth for the diff loop in 4.4.
|
|
290
|
+
- `mcp__figma__get_code_connect_map` — any Figma component already mapped to a package component MUST use that mapping.
|
|
291
|
+
|
|
292
|
+
### 4.1.5 Block Matching — before the contract
|
|
293
|
+
|
|
294
|
+
**Run the Block Matching gate** (above) over the frame's regions before building the node-by-node contract. For each major region of the frame (nav, hero/balance header, banner, highlight cards, trading cards, action buttons, empty state, referral, header):
|
|
295
|
+
|
|
296
|
+
- If `get_code_connect_map` maps that region's Figma component to a block export → **binding**, use that block.
|
|
297
|
+
- Else, match the region's structure + intent against the block catalog ([references/components-and-tokens.md](references/components-and-tokens.md)). A frame nav that matches the `Navbar` block, a balance area that matches `HeroDesktopHome*`, a card that matches `TradingAccountCard` → **import the block and tune it to the frame's content; do NOT rebuild it node-by-node.**
|
|
298
|
+
|
|
299
|
+
A region resolved to a block collapses to ONE contract row (`→ Resolution: block: <ExportName>`) — you do not enumerate its internal nodes. Only the regions with no block match expand into the full per-node contract in 4.2.
|
|
300
|
+
|
|
301
|
+
### 4.2 Fidelity contract — every node, every value
|
|
302
|
+
|
|
303
|
+
Build a table with one row per **visible node** from `get_metadata` (not just "key elements"). Columns:
|
|
304
|
+
|
|
305
|
+
| Node | Type | Text (verbatim) | Fill/stroke (var → hex) | Font (size/weight/line-height) | Auto-layout (padding/gap/align) | Size (w×h) | Radius | → Resolution |
|
|
306
|
+
|
|
307
|
+
- **Text is copied character-for-character** from the frame — headings, labels, placeholder text, microcopy, button labels. Never paraphrase, never "improve", never translate.
|
|
308
|
+
- **Every fill/stroke** resolves through the **Token Resolution Loop** — record which step resolved it (semantic / registered-var / registered-primitive / registered-exact). Zero unresolved rows before writing JSX.
|
|
309
|
+
- **Every text node** records the exact weight (`800/ExtraBold` → `font-extrabold`) and the closest `heading-*`/`body-*` scale class, with explicit overrides for any delta.
|
|
310
|
+
- **Spacing comes from auto-layout values**, not from your eye: `padding: 18px` → `p-[18px]` (or `p-4` only when the value is exactly 16). Arbitrary px values are correct here — spacing is token-exempt.
|
|
311
|
+
- **Components**: map each node to a package component (Code Connect mapping first, then name/shape match against the [canonical list](references/components-and-tokens.md) — beware the hallucinated-imports table). No match → compose from package components + tokens, flag it. (Regions already resolved to a block in 4.1.5 are not re-decomposed here.)
|
|
312
|
+
- **Assets**: icons in the frame are matched to the exact package icon via `<Icon name="…" weight="…" />` (catalog: `node_modules/@deriv-ds/design-intelligence-layer/guides/design-system-guide/icon-reference.md`, or the `ICON_NAMES` export). Images are exported from Figma. Never substitute a "close enough" icon, and never reach for `lucide-react`.
|
|
313
|
+
- **Placement**: decide layout scaffold A or B now, per the rules above.
|
|
314
|
+
|
|
315
|
+
**Do not write a single line of JSX until every row is resolved.** Anti-invention check on the contract itself: every node in `get_metadata` has a row; no row exists for a node that isn't in the frame.
|
|
316
|
+
|
|
317
|
+
### 4.3 Build
|
|
318
|
+
|
|
319
|
+
Generate the page from the contract — the contract is the spec, the screenshot is the ground truth. Register all created tokens in `globals.css` first, then write JSX using only generated classes and package components.
|
|
320
|
+
|
|
321
|
+
**App shell + responsive expansion:** if the frame is a home/dashboard page, apply the **App Shell defaults** (inject `HeaderAppHome`/`HeaderApp` + `Navbar` even if the frame omits them). Then expand to the viewport the frame does NOT cover per the **Mode 4 responsive rule** — swap paired blocks to their opposite variant (`HeroMobile*` ⇄ `HeroDesktop*`) behind `md:hidden` / `hidden md:block`, and let responsive blocks (`Navbar`, `Section`) handle themselves. The generated viewport is brand-consistent layout, not a pixel replica — flag it in the Build Report.
|
|
322
|
+
|
|
323
|
+
### 4.4 Visual-diff loop — iterate until it matches
|
|
324
|
+
|
|
325
|
+
"Renders without errors" proves nothing about parity. With the dev server running:
|
|
326
|
+
|
|
327
|
+
1. `preview_resize` the preview viewport to the **Figma frame's exact dimensions** (from `get_metadata`).
|
|
328
|
+
2. `preview_screenshot` the render. Put it side-by-side with the Figma `get_screenshot`.
|
|
329
|
+
3. Compare top-to-bottom, systematically: element presence and order · spacing rhythm · colours · font sizes/weights · copy · alignment · radii. List every deviation.
|
|
330
|
+
4. Fix every deviation → re-screenshot → re-compare. **Repeat until no visible deviation remains**, up to 5 iterations. Anything still deviating after 5 passes is listed explicitly in the Build Report with the reason.
|
|
331
|
+
5. Then run the per-row computed-style checks (Final Checklist) — screenshots catch layout drift; `preview_eval` catches hallucinated classes rendering transparent.
|
|
332
|
+
|
|
333
|
+
If the preview MCP tools are unavailable, say so explicitly in the Build Report, verify what you can (`npm run build`, grep audits), and ask the user to compare against the frame — never silently skip the visual verification.
|
|
334
|
+
|
|
335
|
+
### 4.5 Finish
|
|
336
|
+
|
|
337
|
+
Run the Final Checklist (including the per-row fidelity verification) and present the Build Report.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Mode 5 — Pasted block
|
|
342
|
+
|
|
343
|
+
Triggered when the user pastes a code block and says something like "integrate this", "build this navbar into the page".
|
|
344
|
+
|
|
345
|
+
0. **Run Preflight** (above) before integrating the block.
|
|
346
|
+
1. Scan the imports in the pasted code:
|
|
347
|
+
- `@/components/ui/...` → REWRITE to `@deriv-ds/design-intelligence-layer` (the playground uses local paths; consuming projects MUST use the package — HARD RULE 3). Verify each rewritten import actually exists in `dist/index.d.ts` — playground code can reference internals the package doesn't export; recompose those from real exports.
|
|
348
|
+
- `@deriv-com/quill-ui*` → flag and ask; this is the older library.
|
|
349
|
+
- `lucide-react` (or any other icon lib) → HARD RULE 3: replace with the package's `<Icon />` (map each glyph to a name in the icon catalog).
|
|
350
|
+
- Anything else outside the package (except `react`) → HARD RULE 3: replace with package equivalents.
|
|
351
|
+
2. Scan styles for forbidden patterns (hex, raw palette, arbitrary colour values, `hsl(var())`). Each hit → run the **Token Resolution Loop** (replace with semantic token, or register one).
|
|
352
|
+
3. Ask the user **where the block goes**:
|
|
353
|
+
> "Where should this block live? (a) new route at `app/<name>/page.tsx`, (b) replace a section in an existing page, (c) extract into `components/<name>.tsx` and import where needed?"
|
|
354
|
+
4. Wire any state the block expects (form state for hero CTAs, open/closed state for navbar drawers, etc.) using React hooks. Don't invent new stores.
|
|
355
|
+
5. Run the Final Checklist.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Final Checklist — run at the end of EVERY mode
|
|
360
|
+
|
|
361
|
+
Before declaring done, verify:
|
|
362
|
+
|
|
363
|
+
**Guardrail self-audit** — grep the generated/modified files for forbidden patterns. Any hit = stop and fix.
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# From the project root, on the files you just touched:
|
|
367
|
+
grep -nE '#[0-9a-fA-F]{3,8}\b' <changed-tsx-files> # hex anywhere in JSX (hex in globals.css @theme is OK)
|
|
368
|
+
grep -nE '\b(bg|text|border|ring|from|to|via)-(gray|zinc|slate|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]{2,3}\b' <changed-files> # raw palette, every hue
|
|
369
|
+
grep -nE '\b(bg|text|border|ring)-(black|white)\b' <changed-files> # raw black/white
|
|
370
|
+
grep -nE '(bg|text|border|ring)-\[(#|rgb|hsl|oklch|var)' <changed-files> # arbitrary colour values / inline vars
|
|
371
|
+
grep -nE 'hsl\(var\(|rounded-\[|font-family' <changed-files> # v3 syntax, arbitrary radius, font overrides
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
(`p-[18px]`, `gap-[12px]`, `text-[18px]`, `leading-[24px]` are fine — spacing/size are token-exempt.)
|
|
375
|
+
|
|
376
|
+
**Token classes resolve** — the forbidden-pattern grep does NOT catch *hallucinated* token classes: ones that look semantic (`text-on-subtle`, `text-success` without registration, `text-heading-h1`) but have no definition, so they produce no CSS and the element silently inherits the wrong style. For every colour/`bg`/`text`/`border`/`ring` class in your diff, confirm it's in the [token cheatsheet](references/components-and-tokens.md) tables OR registered in this project's `globals.css`. To prove a class exists, check the **definition**, not usage:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
# colour class bg-X / text-X / border-X → token --color-X must be defined:
|
|
380
|
+
grep -- "--color-X:" node_modules/@deriv-ds/design-intelligence-layer/src/styles.css <project globals.css>
|
|
381
|
+
# zero hits in both = hallucinated → fix via the Token Resolution Loop
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The runtime check below is the backstop: a token-intended element whose computed `background-color` is `rgba(0,0,0,0)`, or whose text colour clearly inherited rather than applied, means a hallucinated class.
|
|
385
|
+
|
|
386
|
+
**Blocks matched, not mocked** — every page region went through the Block Matching gate. No region hand-rolls a navbar, hero, card, banner, action-button cluster, empty state, or referral panel that the package already ships as a block. A region built from primitives must have NO matching block in the catalog — confirm before declaring done; this is recorded in the Build Report's block audit.
|
|
387
|
+
|
|
388
|
+
**App shell present (home/dashboard)** — if the page is a home/dashboard build, confirm the default shell is there: a header block (`HeaderAppHome`/`HeaderApp`) at the top AND `Navbar`. A home page with no header or no nav is a defect unless the user explicitly opted out (record the opt-out in the block audit).
|
|
389
|
+
|
|
390
|
+
**Responsive verification** — the build must work at BOTH viewports, not just mobile. With the preview running, `preview_resize` to **375px** and to **1440px** and confirm at each:
|
|
391
|
+
- At **1440px**: `Navbar` renders the **desktop sidebar** (not the mobile bottom bar); no mobile-only block is stretched full-width; paired blocks show their desktop variant.
|
|
392
|
+
- At **375px**: `Navbar` renders the **bottom bar**; paired blocks show their mobile variant; no horizontal overflow.
|
|
393
|
+
- The header block (on home/dashboard) is present at both widths.
|
|
394
|
+
- (Mode 4) the frame's own viewport still matches the visual-diff loop result; the generated viewport is laid out cleanly.
|
|
395
|
+
A single-viewport render is a defect unless the user restricted scope.
|
|
396
|
+
|
|
397
|
+
**Import audit** — every import in the diff comes from `@deriv-ds/design-intelligence-layer`, including icons via `<Icon />`. No `lucide-react` or other icon lib. No `@/components/ui/*`. No `@deriv-com/quill-ui*`. Every imported name exists in `dist/index.d.ts` (beware `Tabs`/`Notification`/`Chart`/`Resizable`/`Direction` — see the hallucinated-imports table).
|
|
398
|
+
|
|
399
|
+
**Created tokens registered** — every class generated by the Token Resolution Loop has its `@theme inline` registration in `globals.css`, named by intent, listed for the Build Report.
|
|
400
|
+
|
|
401
|
+
**Build & render** — `npm run dev` builds without errors and the page renders. Use `preview_snapshot` to confirm structure and `preview_screenshot` to capture proof. If preview tools are unavailable, run `npm run build` and state in the Build Report that visual checks could not run.
|
|
402
|
+
|
|
403
|
+
**Runtime visual verification** — "renders without errors" is NOT enough. With the preview running, use `preview_eval` to read computed styles and confirm:
|
|
404
|
+
- The page/`<body>` background is the intended **light** surface (NOT near-black) — catches `bg-prominent`-as-background.
|
|
405
|
+
- Body text computed `color` is dark-on-light (and white on any coral/inverse surface).
|
|
406
|
+
- Computed `font-family` resolves to **Inter** — catches leftover Geist / `next/font`.
|
|
407
|
+
- The **primary CTA element is within the viewport** (visible, not pushed below the fold) — catches an unanchored footer. (`getBoundingClientRect().bottom <= innerHeight`.)
|
|
408
|
+
- Every element you styled with a colour token actually **got that colour** — its computed `background-color`/`color` is not transparent or inherited. A coral CTA reading `rgba(0,0,0,0)`, or a "subtle" caption reading solid near-black, means the class doesn't exist (hallucinated) — fix per "Token classes resolve" above.
|
|
409
|
+
- DS component internals are styled (a default `Button` has a coral fill) — catches a wrong `@source` path.
|
|
410
|
+
|
|
411
|
+
**Figma fidelity (Mode 4 only)** — the visual-diff loop (4.4) has converged, AND verify the render against the fidelity contract row by row with `preview_eval`:
|
|
412
|
+
|
|
413
|
+
- **Colour** — `getComputedStyle(el).backgroundColor`/`color` matches the hex recorded in the contract. Transparent or near-black-when-it-shouldn't-be = wrong/hallucinated token → fix.
|
|
414
|
+
- **Typography** — `fontWeight`/`fontSize` match the contract (e.g. `"800"` for ExtraBold). DS component defaults often ship `font-semibold` (600) — override when the spec differs.
|
|
415
|
+
- **Copy** — `textContent` is verbatim from the frame.
|
|
416
|
+
- **Placement** — `getBoundingClientRect()` puts each key element in the correct region; bottom-pinned CTAs at the bottom, inline CTAs directly under their preceding content.
|
|
417
|
+
- **Completeness** — every contract row is rendered; nothing rendered lacks a contract row.
|
|
418
|
+
|
|
419
|
+
Any mismatch is a defect — fix it before the Build Report. Every contract row must be explicitly marked ✅ matched or listed as a deviation.
|
|
420
|
+
|
|
421
|
+
**Accessibility (WCAG 2.1 AA)** — focus states visible on every interactive element, contrast ratios meet AA, semantic HTML (`<main>`, `<nav>`, `<button>`), labels associated with inputs. Note: the package's built-in coral focus ring on fields is **expected** — keep it.
|
|
422
|
+
|
|
423
|
+
**Bundled deps untouched** — no separate `tailwindcss`, `tailwind.config.js`, or `lucide-react` (or any icon lib) installs in `package.json` diff.
|
|
424
|
+
|
|
425
|
+
**Copy** — Modes 2/3/5: reflects `deriv-brand-voice.md`, no banned phrases. Mode 4: verbatim from the frame (brand voice does NOT override the design's copy).
|
|
426
|
+
|
|
427
|
+
**→ Next: run the Independent audit below, THEN present the Build Report.**
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Independent audit (critic pass) — REQUIRED before the Build Report
|
|
432
|
+
|
|
433
|
+
You verified your own work in the same context that wrote it — confirmation bias lets hallucinated imports, unregistered tokens, and out-of-scope edits survive the self-checklist. Before the Build Report, get a **fresh pair of eyes**:
|
|
434
|
+
|
|
435
|
+
- **Claude Code:** spawn a reviewer with the Task tool (general-purpose agent). It may read files and run `grep` / `npm run build` / `tsc`, but it MUST NOT edit — it only reports.
|
|
436
|
+
- **Cursor / Kiro (no subagents):** do the audit yourself in a deliberately fresh pass — re-read each changed file and re-derive every fact from ground truth, ignoring your own build notes.
|
|
437
|
+
|
|
438
|
+
Hand the reviewer ONLY the list of changed files (`git status`) plus the ground-truth sources. Use this prompt verbatim:
|
|
439
|
+
|
|
440
|
+
> Independent reviewer; you did not write this code and must trust no claim about it. For the changed files `<list>`, verify ONLY the following and report each violation with `file:line` + the evidence you checked:
|
|
441
|
+
> 1. Every name imported from `@deriv-ds/design-intelligence-layer` exists in `node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts` (grep each name).
|
|
442
|
+
> 2. No forbidden imports: `lucide-react`, `@fortawesome/*`, `@/components/ui/*`, `@deriv-com/quill-ui*`.
|
|
443
|
+
> 3. Every colour class (`bg|text|border|ring-X`) has a real `--color-X` token in the package `styles.css` or the project `globals.css`.
|
|
444
|
+
> 4. No forbidden style patterns: hex in JSX, raw Tailwind palette, raw black/white, arbitrary colour values, `hsl(var())`, arbitrary radius, `font-family`.
|
|
445
|
+
> 5. Scope: `git status` shows only files this build was meant to touch — no unrelated edits.
|
|
446
|
+
> Output **PASS** (zero violations) or a numbered violation list, each with `file:line`, the offending name/token, and your evidence. Do not propose fixes — just report.
|
|
447
|
+
|
|
448
|
+
The critic re-derives the **Final Checklist** gates above independently; it does not invent new rules.
|
|
449
|
+
|
|
450
|
+
**Loop:** any violation → fix the root cause → re-run the critic. Bound = **3 rounds**; if it still fails, list the remainder under the Build Report's "Needs your review" and stop. Never loop forever, never silently ship.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Build Report — REQUIRED output of every build
|
|
455
|
+
|
|
456
|
+
The build is not "done" until the runtime checks pass **and** you present this report to the user. Never end a build with just "done." Show:
|
|
457
|
+
|
|
458
|
+
1. **Result** — a `preview_screenshot` (and the preview URL). In Mode 4, side-by-side with the Figma `get_screenshot`.
|
|
459
|
+
|
|
460
|
+
2. **Token & Component Audit** — a table of every token class and component used, with confirmed evidence. No row may be left as "assumed". Use this format:
|
|
461
|
+
|
|
462
|
+
| Element | Class / Component | Status | Evidence |
|
|
463
|
+
|---|---|---|---|
|
|
464
|
+
| Page background | `bg-card` | ✅ confirmed | `--color-card` in styles.css |
|
|
465
|
+
| Primary CTA | `Button` | ✅ confirmed | export in dist/index.d.ts |
|
|
466
|
+
| Heading colour | `text-on-subtle` | ❌ hallucinated → replaced | no `--color-on-subtle` → swapped to `text-subtle` |
|
|
467
|
+
| Profit figure | `text-success` | 🆕 created | registered `--color-success: var(--text-success-default)` |
|
|
468
|
+
| Brand border | `border-brand-coral` | 🆕 created | registered `--color-brand-coral: var(--primitive-coral-700)` |
|
|
469
|
+
|
|
470
|
+
**Hallucinated classes must be fixed before this report is shown** — the table is proof they were caught and corrected, not a to-do list.
|
|
471
|
+
|
|
472
|
+
3. **Block audit (Block Matching gate)** — one row per page region, proving each was matched against the block catalog and either imported as a block or consciously composed. A region that *should* have used a block but was hand-rolled is a defect — fix before showing this report. For home/dashboard pages, the **app shell rows (header + nav) are mandatory** — show them as imported blocks or as an explicit user opt-out.
|
|
473
|
+
|
|
474
|
+
| Region | Outcome | Evidence / reason |
|
|
475
|
+
|---|---|---|
|
|
476
|
+
| App header | `block: HeaderAppHome` | app shell default (home page); export in dist/index.d.ts |
|
|
477
|
+
| Navigation | `block: Navbar` | app shell default; responsive sidebar/bottom-bar; export in dist/index.d.ts |
|
|
478
|
+
| Balance header | `block: HeroMobileHomeTotalAssets` + `HeroDesktopHomeWithBalance` | paired mobile/desktop, `md:hidden` switch |
|
|
479
|
+
| Promo strip | `composed` | no block matches a 2-up promo grid — flagged |
|
|
480
|
+
|
|
481
|
+
4. **Responsive coverage** — state which viewports were built and verified (mobile ~375px, desktop ~1440px), which regions used responsive blocks vs paired mobile/desktop variants, and — in Mode 4 — which viewport was a 1:1 frame replica vs generated from the opposite block variant. A build verified at only one viewport must say so and why (explicit user scope).
|
|
482
|
+
|
|
483
|
+
5. **Created tokens** — every `@theme inline` registration added to `globals.css`, with which loop step produced it (semantic var / primitive / exact value) and whether it's dark-mode-aware.
|
|
484
|
+
|
|
485
|
+
6. **Fidelity results (Mode 4)** — visual-diff loop: how many iterations, what was fixed each pass. Then the contract: every row ✅ matched or listed as a deviation with the reason. State explicitly: zero invented elements, zero omitted elements, copy verbatim.
|
|
486
|
+
|
|
487
|
+
7. **⚠ Flagged** — composed (non-package) elements, token substitutions, assumptions made. If nothing was flagged, say so explicitly.
|
|
488
|
+
|
|
489
|
+
8. **Intentional DS behaviours** — surface things that look like bugs but aren't (e.g. the coral focus ring on inputs), so the user doesn't try to "fix" them.
|
|
490
|
+
|
|
491
|
+
9. **Needs your review** — anything uncertain or that you couldn't verify (e.g. preview tools unavailable), plus any critic violations still unresolved after 3 rounds.
|
|
492
|
+
|
|
493
|
+
10. **Independent audit** — the critic verdict: how many rounds ran, and what it caught and you fixed (hallucinated imports, unregistered tokens, out-of-scope edits). If the critic could not run (no subagent + you did the fresh pass yourself), say so.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Ship — open a PR (ask first)
|
|
498
|
+
|
|
499
|
+
After the Build Report, offer to ship. Creating a PR is outward-facing — never do it without an explicit yes. (This runs in the consumer's own product repo, so keep branch/commit conventions generic and follow whatever the host repo already uses.)
|
|
500
|
+
|
|
501
|
+
1. **Ask the user** (Claude Code: `AskUserQuestion`; Cursor / Kiro: a plain question): *"Create a PR for this build?"* — **Yes** / **No (leave it uncommitted)**.
|
|
502
|
+
2. On **No** → stop; leave the working tree untouched and list the changed files so the user can commit later.
|
|
503
|
+
3. On **Yes**:
|
|
504
|
+
a. **Branch** — if on the default/protected branch (`main`/`master`), create a feature branch (e.g. `feat/<page-or-feature-slug>`); if already on a feature branch, reuse it. Never commit on the default branch.
|
|
505
|
+
b. **Stage only the files this build touched** — explicit `git add` of the pages/components you created or edited plus the project `globals.css` (registered tokens). **Never `git add -A`.**
|
|
506
|
+
c. **Commit** — a clear, conventional message. Follow the **host project's and the running agent's own** commit conventions for message style, trailers, and footers — this skill imposes none.
|
|
507
|
+
d. **Auth preflight** — run `gh auth status`. If not authenticated, do NOT fail silently: print the exact `git push -u origin <branch>` and `gh pr create` commands, ask the user to run `gh auth login` first, then stop.
|
|
508
|
+
e. **Push + PR** — `git push -u origin <branch>`, then `gh pr create --title "<title>" --body "<body>"`. Body = the Build Report (token/component audit, block audit, created tokens, critic verdict). Never force-push.
|
|
509
|
+
f. **Return the PR URL** to the user.
|