@deriv-ds/design-intelligence-layer 0.5.2 → 0.6.1
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 +449 -0
- package/.claude/skills/build/references/components-and-tokens.md +463 -0
- package/.cursor/commands/build.md +3 -0
- package/.kiro/steering/build.md +6 -0
- package/AGENTS.md +22 -0
- package/CHANGELOG.md +18 -1
- package/README.md +69 -55
- package/package.json +5 -1
|
@@ -0,0 +1,449 @@
|
|
|
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
|
+
**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):**
|
|
127
|
+
|
|
128
|
+
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.
|
|
129
|
+
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).
|
|
130
|
+
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.
|
|
131
|
+
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.
|
|
132
|
+
5. **If no block matches → compose from primitives + tokens** and flag the composition in the Build Report.
|
|
133
|
+
|
|
134
|
+
**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.
|
|
135
|
+
|
|
136
|
+
**Two triggers for a match — both are mandatory:**
|
|
137
|
+
|
|
138
|
+
- **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.
|
|
139
|
+
- **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>`.)
|
|
140
|
+
|
|
141
|
+
Record every region's outcome (`block: <Name>` or `composed`) — this feeds the Build Report's block audit.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Mode 1 — Router
|
|
146
|
+
|
|
147
|
+
When invoked, classify the user's message:
|
|
148
|
+
|
|
149
|
+
- **`bootstrap`** — package not installed, or phrases like "set up", "new project", "install Deriv DS", "start with Deriv"
|
|
150
|
+
- **`figma`** — message contains a `figma.com/design/...` URL
|
|
151
|
+
- **`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
|
|
152
|
+
- **`text-prompt`** — natural-language page request ("login page", "settings page", "dashboard", "build me an X")
|
|
153
|
+
|
|
154
|
+
Modes can compose on one turn (e.g. bootstrap → then text-prompt).
|
|
155
|
+
|
|
156
|
+
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.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Mode 2 — Bootstrap
|
|
161
|
+
|
|
162
|
+
Set up a fresh consuming project. Default to Next.js App Router.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx create-next-app@latest <name> --typescript --tailwind --app --src-dir
|
|
166
|
+
cd <name>
|
|
167
|
+
npm install @deriv-ds/design-intelligence-layer
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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:
|
|
171
|
+
|
|
172
|
+
```css
|
|
173
|
+
/* src/app/globals.css (Next.js with --src-dir, as above) */
|
|
174
|
+
@import "@deriv-ds/design-intelligence-layer/styles";
|
|
175
|
+
@import "tailwindcss";
|
|
176
|
+
@source "../../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```css
|
|
180
|
+
/* app/globals.css (no src dir) — one level up */
|
|
181
|
+
@source "../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
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).
|
|
185
|
+
|
|
186
|
+
For **Vite** projects only, add `@tailwindcss/vite` to `vite.config.ts`. Next.js needs no extra config.
|
|
187
|
+
|
|
188
|
+
Copy the package's `AGENTS.md` into the project root so other AI agents pick it up:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/AGENTS.md ./AGENTS.md
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Generate a `CLAUDE.md` at project root pointing to the mandatory guides:
|
|
195
|
+
|
|
196
|
+
```md
|
|
197
|
+
# Project guidance
|
|
198
|
+
|
|
199
|
+
This project uses `@deriv-ds/design-intelligence-layer`. Before building any UI, read:
|
|
200
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md`
|
|
201
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc`
|
|
202
|
+
|
|
203
|
+
Use the `build` skill for any "build me a page / build with this Figma / build this block" request.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Clean the Next.js scaffold** (create-next-app ships defaults that fight DS token resolution):
|
|
207
|
+
- 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`.
|
|
208
|
+
- 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.
|
|
209
|
+
|
|
210
|
+
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).)
|
|
211
|
+
|
|
212
|
+
If the user immediately follows with a build request, chain into the matching mode below.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Mode 3 — Text prompt ("build me an X page")
|
|
217
|
+
|
|
218
|
+
1. **Read the mandatory guides first** (per `AGENTS.md` Step 1):
|
|
219
|
+
- `node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md`
|
|
220
|
+
2. Read `node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc`.
|
|
221
|
+
3. Read [references/components-and-tokens.md](references/components-and-tokens.md) for the canonical component list and token cheatsheet.
|
|
222
|
+
4. **Run Preflight** (above) to clean any leftover Geist/font/`:root` scaffold defects.
|
|
223
|
+
5. **Run the Block Matching gate** (above) over the page's regions FIRST — any region the prompt names or implies that maps to a block (nav/navbar, hero/balance header, banner, highlights, explore, trading cards, action buttons, empty state, referral, header) is imported as that block, not mocked up. Only the regions with no block match proceed to primitive composition.
|
|
224
|
+
6. 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.
|
|
225
|
+
7. 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-*`).
|
|
226
|
+
8. Write copy in a voice aligned with `deriv-brand-voice.md`.
|
|
227
|
+
9. Run the Final Checklist below.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Mode 4 — Figma link → 1:1 replica
|
|
232
|
+
|
|
233
|
+
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.
|
|
234
|
+
|
|
235
|
+
### 4.1 Extract — everything, before any code
|
|
236
|
+
|
|
237
|
+
1. Confirm `mcp__figma__*` tools are available. **Run Preflight** (above).
|
|
238
|
+
2. Parse the URL: extract `fileKey` and `nodeId` (convert `-` → `:` in nodeId).
|
|
239
|
+
3. Call, in this order:
|
|
240
|
+
- `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.
|
|
241
|
+
- `mcp__figma__get_design_context` — layout, auto-layout values, constraints, text content, styles.
|
|
242
|
+
- `mcp__figma__get_variable_defs` — design variables → resolved values.
|
|
243
|
+
- `mcp__figma__get_screenshot` — the visual ground truth for the diff loop in 4.4.
|
|
244
|
+
- `mcp__figma__get_code_connect_map` — any Figma component already mapped to a package component MUST use that mapping.
|
|
245
|
+
|
|
246
|
+
### 4.1.5 Block Matching — before the contract
|
|
247
|
+
|
|
248
|
+
**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):
|
|
249
|
+
|
|
250
|
+
- If `get_code_connect_map` maps that region's Figma component to a block export → **binding**, use that block.
|
|
251
|
+
- 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.**
|
|
252
|
+
|
|
253
|
+
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.
|
|
254
|
+
|
|
255
|
+
### 4.2 Fidelity contract — every node, every value
|
|
256
|
+
|
|
257
|
+
Build a table with one row per **visible node** from `get_metadata` (not just "key elements"). Columns:
|
|
258
|
+
|
|
259
|
+
| Node | Type | Text (verbatim) | Fill/stroke (var → hex) | Font (size/weight/line-height) | Auto-layout (padding/gap/align) | Size (w×h) | Radius | → Resolution |
|
|
260
|
+
|
|
261
|
+
- **Text is copied character-for-character** from the frame — headings, labels, placeholder text, microcopy, button labels. Never paraphrase, never "improve", never translate.
|
|
262
|
+
- **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.
|
|
263
|
+
- **Every text node** records the exact weight (`800/ExtraBold` → `font-extrabold`) and the closest `heading-*`/`body-*` scale class, with explicit overrides for any delta.
|
|
264
|
+
- **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.
|
|
265
|
+
- **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.)
|
|
266
|
+
- **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`.
|
|
267
|
+
- **Placement**: decide layout scaffold A or B now, per the rules above.
|
|
268
|
+
|
|
269
|
+
**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.
|
|
270
|
+
|
|
271
|
+
### 4.3 Build
|
|
272
|
+
|
|
273
|
+
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.
|
|
274
|
+
|
|
275
|
+
### 4.4 Visual-diff loop — iterate until it matches
|
|
276
|
+
|
|
277
|
+
"Renders without errors" proves nothing about parity. With the dev server running:
|
|
278
|
+
|
|
279
|
+
1. `preview_resize` the preview viewport to the **Figma frame's exact dimensions** (from `get_metadata`).
|
|
280
|
+
2. `preview_screenshot` the render. Put it side-by-side with the Figma `get_screenshot`.
|
|
281
|
+
3. Compare top-to-bottom, systematically: element presence and order · spacing rhythm · colours · font sizes/weights · copy · alignment · radii. List every deviation.
|
|
282
|
+
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.
|
|
283
|
+
5. Then run the per-row computed-style checks (Final Checklist) — screenshots catch layout drift; `preview_eval` catches hallucinated classes rendering transparent.
|
|
284
|
+
|
|
285
|
+
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.
|
|
286
|
+
|
|
287
|
+
### 4.5 Finish
|
|
288
|
+
|
|
289
|
+
Run the Final Checklist (including the per-row fidelity verification) and present the Build Report.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Mode 5 — Pasted block
|
|
294
|
+
|
|
295
|
+
Triggered when the user pastes a code block and says something like "integrate this", "build this navbar into the page".
|
|
296
|
+
|
|
297
|
+
0. **Run Preflight** (above) before integrating the block.
|
|
298
|
+
1. Scan the imports in the pasted code:
|
|
299
|
+
- `@/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.
|
|
300
|
+
- `@deriv-com/quill-ui*` → flag and ask; this is the older library.
|
|
301
|
+
- `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).
|
|
302
|
+
- Anything else outside the package (except `react`) → HARD RULE 3: replace with package equivalents.
|
|
303
|
+
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).
|
|
304
|
+
3. Ask the user **where the block goes**:
|
|
305
|
+
> "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?"
|
|
306
|
+
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.
|
|
307
|
+
5. Run the Final Checklist.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Final Checklist — run at the end of EVERY mode
|
|
312
|
+
|
|
313
|
+
Before declaring done, verify:
|
|
314
|
+
|
|
315
|
+
**Guardrail self-audit** — grep the generated/modified files for forbidden patterns. Any hit = stop and fix.
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# From the project root, on the files you just touched:
|
|
319
|
+
grep -nE '#[0-9a-fA-F]{3,8}\b' <changed-tsx-files> # hex anywhere in JSX (hex in globals.css @theme is OK)
|
|
320
|
+
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
|
|
321
|
+
grep -nE '\b(bg|text|border|ring)-(black|white)\b' <changed-files> # raw black/white
|
|
322
|
+
grep -nE '(bg|text|border|ring)-\[(#|rgb|hsl|oklch|var)' <changed-files> # arbitrary colour values / inline vars
|
|
323
|
+
grep -nE 'hsl\(var\(|rounded-\[|font-family' <changed-files> # v3 syntax, arbitrary radius, font overrides
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
(`p-[18px]`, `gap-[12px]`, `text-[18px]`, `leading-[24px]` are fine — spacing/size are token-exempt.)
|
|
327
|
+
|
|
328
|
+
**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:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# colour class bg-X / text-X / border-X → token --color-X must be defined:
|
|
332
|
+
grep -- "--color-X:" node_modules/@deriv-ds/design-intelligence-layer/src/styles.css <project globals.css>
|
|
333
|
+
# zero hits in both = hallucinated → fix via the Token Resolution Loop
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
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.
|
|
337
|
+
|
|
338
|
+
**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.
|
|
339
|
+
|
|
340
|
+
**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).
|
|
341
|
+
|
|
342
|
+
**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.
|
|
343
|
+
|
|
344
|
+
**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.
|
|
345
|
+
|
|
346
|
+
**Runtime visual verification** — "renders without errors" is NOT enough. With the preview running, use `preview_eval` to read computed styles and confirm:
|
|
347
|
+
- The page/`<body>` background is the intended **light** surface (NOT near-black) — catches `bg-prominent`-as-background.
|
|
348
|
+
- Body text computed `color` is dark-on-light (and white on any coral/inverse surface).
|
|
349
|
+
- Computed `font-family` resolves to **Inter** — catches leftover Geist / `next/font`.
|
|
350
|
+
- The **primary CTA element is within the viewport** (visible, not pushed below the fold) — catches an unanchored footer. (`getBoundingClientRect().bottom <= innerHeight`.)
|
|
351
|
+
- 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.
|
|
352
|
+
- DS component internals are styled (a default `Button` has a coral fill) — catches a wrong `@source` path.
|
|
353
|
+
|
|
354
|
+
**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`:
|
|
355
|
+
|
|
356
|
+
- **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.
|
|
357
|
+
- **Typography** — `fontWeight`/`fontSize` match the contract (e.g. `"800"` for ExtraBold). DS component defaults often ship `font-semibold` (600) — override when the spec differs.
|
|
358
|
+
- **Copy** — `textContent` is verbatim from the frame.
|
|
359
|
+
- **Placement** — `getBoundingClientRect()` puts each key element in the correct region; bottom-pinned CTAs at the bottom, inline CTAs directly under their preceding content.
|
|
360
|
+
- **Completeness** — every contract row is rendered; nothing rendered lacks a contract row.
|
|
361
|
+
|
|
362
|
+
Any mismatch is a defect — fix it before the Build Report. Every contract row must be explicitly marked ✅ matched or listed as a deviation.
|
|
363
|
+
|
|
364
|
+
**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.
|
|
365
|
+
|
|
366
|
+
**Bundled deps untouched** — no separate `tailwindcss`, `tailwind.config.js`, or `lucide-react` (or any icon lib) installs in `package.json` diff.
|
|
367
|
+
|
|
368
|
+
**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).
|
|
369
|
+
|
|
370
|
+
**→ Next: run the Independent audit below, THEN present the Build Report.**
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Independent audit (critic pass) — REQUIRED before the Build Report
|
|
375
|
+
|
|
376
|
+
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**:
|
|
377
|
+
|
|
378
|
+
- **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.
|
|
379
|
+
- **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.
|
|
380
|
+
|
|
381
|
+
Hand the reviewer ONLY the list of changed files (`git status`) plus the ground-truth sources. Use this prompt verbatim:
|
|
382
|
+
|
|
383
|
+
> 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:
|
|
384
|
+
> 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).
|
|
385
|
+
> 2. No forbidden imports: `lucide-react`, `@fortawesome/*`, `@/components/ui/*`, `@deriv-com/quill-ui*`.
|
|
386
|
+
> 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`.
|
|
387
|
+
> 4. No forbidden style patterns: hex in JSX, raw Tailwind palette, raw black/white, arbitrary colour values, `hsl(var())`, arbitrary radius, `font-family`.
|
|
388
|
+
> 5. Scope: `git status` shows only files this build was meant to touch — no unrelated edits.
|
|
389
|
+
> 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.
|
|
390
|
+
|
|
391
|
+
The critic re-derives the **Final Checklist** gates above independently; it does not invent new rules.
|
|
392
|
+
|
|
393
|
+
**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.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Build Report — REQUIRED output of every build
|
|
398
|
+
|
|
399
|
+
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:
|
|
400
|
+
|
|
401
|
+
1. **Result** — a `preview_screenshot` (and the preview URL). In Mode 4, side-by-side with the Figma `get_screenshot`.
|
|
402
|
+
|
|
403
|
+
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:
|
|
404
|
+
|
|
405
|
+
| Element | Class / Component | Status | Evidence |
|
|
406
|
+
|---|---|---|---|
|
|
407
|
+
| Page background | `bg-card` | ✅ confirmed | `--color-card` in styles.css |
|
|
408
|
+
| Primary CTA | `Button` | ✅ confirmed | export in dist/index.d.ts |
|
|
409
|
+
| Heading colour | `text-on-subtle` | ❌ hallucinated → replaced | no `--color-on-subtle` → swapped to `text-subtle` |
|
|
410
|
+
| Profit figure | `text-success` | 🆕 created | registered `--color-success: var(--text-success-default)` |
|
|
411
|
+
| Brand border | `border-brand-coral` | 🆕 created | registered `--color-brand-coral: var(--primitive-coral-700)` |
|
|
412
|
+
|
|
413
|
+
**Hallucinated classes must be fixed before this report is shown** — the table is proof they were caught and corrected, not a to-do list.
|
|
414
|
+
|
|
415
|
+
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.
|
|
416
|
+
|
|
417
|
+
| Region | Outcome | Evidence / reason |
|
|
418
|
+
|---|---|---|
|
|
419
|
+
| Top nav | `block: Navbar` | export in dist/index.d.ts; Code Connect map |
|
|
420
|
+
| Balance header | `block: HeroDesktopHomeWithBalance` | matched home balance hero |
|
|
421
|
+
| Promo strip | `composed` | no block matches a 2-up promo grid — flagged |
|
|
422
|
+
|
|
423
|
+
4. **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.
|
|
424
|
+
|
|
425
|
+
5. **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.
|
|
426
|
+
|
|
427
|
+
6. **⚠ Flagged** — composed (non-package) elements, token substitutions, assumptions made. If nothing was flagged, say so explicitly.
|
|
428
|
+
|
|
429
|
+
7. **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.
|
|
430
|
+
|
|
431
|
+
8. **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.
|
|
432
|
+
|
|
433
|
+
9. **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.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Ship — open a PR (ask first)
|
|
438
|
+
|
|
439
|
+
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.)
|
|
440
|
+
|
|
441
|
+
1. **Ask the user** (Claude Code: `AskUserQuestion`; Cursor / Kiro: a plain question): *"Create a PR for this build?"* — **Yes** / **No (leave it uncommitted)**.
|
|
442
|
+
2. On **No** → stop; leave the working tree untouched and list the changed files so the user can commit later.
|
|
443
|
+
3. On **Yes**:
|
|
444
|
+
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.
|
|
445
|
+
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`.**
|
|
446
|
+
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.
|
|
447
|
+
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.
|
|
448
|
+
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.
|
|
449
|
+
f. **Return the PR URL** to the user.
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# Components & tokens — `@deriv-ds/design-intelligence-layer`
|
|
2
|
+
|
|
3
|
+
This is the **authoritative cheatsheet** for building in a consuming project, verified against the published package (v0.6.1). If something isn't here and isn't in the package's `dist/index.d.ts`, it doesn't exist — resolve it with the Token Resolution Loop (for styles) or compose it from real components (for UI). Never invent an import or a class. (The `npm run check:skill-catalog` script in the source repo guards this list against the package's real exports.)
|
|
4
|
+
|
|
5
|
+
> **Verification targets** (the package ships compiled — there is NO `src/index.ts`):
|
|
6
|
+
> - Components → `node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts`
|
|
7
|
+
> - Tokens / variables / primitives → `node_modules/@deriv-ds/design-intelligence-layer/src/styles.css`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Canonical component list (importable from the package)
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import {
|
|
15
|
+
// Layout & structure
|
|
16
|
+
Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction,
|
|
17
|
+
AspectRatio,
|
|
18
|
+
ScrollArea, ScrollBar,
|
|
19
|
+
ResizablePanelGroup, ResizablePanel, ResizableHandle, // NOTE: no bare `Resizable`
|
|
20
|
+
Separator,
|
|
21
|
+
Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarGroup, SidebarGroupLabel,
|
|
22
|
+
SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarProvider,
|
|
23
|
+
SidebarTrigger, SidebarInset, SidebarRail, SidebarSeparator,
|
|
24
|
+
|
|
25
|
+
// Forms & input
|
|
26
|
+
Input,
|
|
27
|
+
PhoneInput,
|
|
28
|
+
EmailOrPhoneInput,
|
|
29
|
+
InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea,
|
|
30
|
+
SearchField,
|
|
31
|
+
InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator,
|
|
32
|
+
CodeInput, CodeInputGroup, CodeInputSlot, CodeInputSeparator,
|
|
33
|
+
Textarea,
|
|
34
|
+
Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator,
|
|
35
|
+
NativeSelect, NativeSelectOption, NativeSelectOptGroup,
|
|
36
|
+
Combobox, ComboboxInput, ComboboxTrigger, ComboboxContent, ComboboxItem, ComboboxList,
|
|
37
|
+
ComboboxValue, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxChip, ComboboxChips,
|
|
38
|
+
Checkbox, CheckboxField,
|
|
39
|
+
RadioGroup, RadioGroupItem, RadioGroupField,
|
|
40
|
+
Switch,
|
|
41
|
+
Toggle,
|
|
42
|
+
ToggleGroup, ToggleGroupItem,
|
|
43
|
+
SegmentedControl, SegmentedControlList, SegmentedControlTrigger, SegmentedControlContent,
|
|
44
|
+
Slider,
|
|
45
|
+
Label,
|
|
46
|
+
Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend,
|
|
47
|
+
FieldSeparator, FieldSet, FieldTitle,
|
|
48
|
+
Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage,
|
|
49
|
+
|
|
50
|
+
// Navigation
|
|
51
|
+
Button,
|
|
52
|
+
SocialButton, // social-login button (provider variants), from button.tsx
|
|
53
|
+
Link,
|
|
54
|
+
Tab, TabList, TabTrigger, TabContent, // NOTE: singular — Tabs/TabsList/TabsTrigger/TabsContent do NOT exist
|
|
55
|
+
Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis,
|
|
56
|
+
Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious,
|
|
57
|
+
PaginationEllipsis, PaginationBullets,
|
|
58
|
+
DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuLabel,
|
|
59
|
+
DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuCheckboxItem, DropdownMenuRadioGroup,
|
|
60
|
+
DropdownMenuRadioItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger,
|
|
61
|
+
ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, ContextMenuSeparator,
|
|
62
|
+
Command, CommandDialog, CommandInput, CommandList, CommandItem, CommandGroup, CommandEmpty,
|
|
63
|
+
CommandSeparator, CommandShortcut,
|
|
64
|
+
Stepper,
|
|
65
|
+
|
|
66
|
+
// Overlays & surfaces
|
|
67
|
+
Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DialogClose,
|
|
68
|
+
Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, DrawerClose,
|
|
69
|
+
Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SheetClose,
|
|
70
|
+
Popover, PopoverContent, PopoverTrigger, PopoverAnchor, PopoverHeader, PopoverTitle, PopoverDescription,
|
|
71
|
+
HoverCard, HoverCardContent, HoverCardTrigger,
|
|
72
|
+
Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
|
|
73
|
+
|
|
74
|
+
// Feedback & status
|
|
75
|
+
Badge, BadgeDot,
|
|
76
|
+
Chip,
|
|
77
|
+
Tag,
|
|
78
|
+
NotificationBanner, NotificationItem, NotificationDivider, // NOTE: no bare `Notification`
|
|
79
|
+
SectionMessage, SectionMessageTitle, SectionMessageDescription,
|
|
80
|
+
Snackbar,
|
|
81
|
+
Spinner,
|
|
82
|
+
LoadingSpinner,
|
|
83
|
+
Skeleton,
|
|
84
|
+
Progress,
|
|
85
|
+
Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle,
|
|
86
|
+
|
|
87
|
+
// Data display
|
|
88
|
+
Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, TableFooter,
|
|
89
|
+
Calendar, CalendarDayButton,
|
|
90
|
+
Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious,
|
|
91
|
+
ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, // NOTE: no bare `Chart`
|
|
92
|
+
ListItem, ListItemTitle, ListItemDescription, ListItemContent, ListItemIcon, ListItemMedia,
|
|
93
|
+
ListItemActions, ListItemFlag, ListItemFooter, ListItemGroup, ListItemHeader, ListItemSeparator,
|
|
94
|
+
Accordion, AccordionContent, AccordionItem, AccordionTrigger,
|
|
95
|
+
Collapsible, CollapsibleContent, CollapsibleItem, CollapsibleTrigger,
|
|
96
|
+
|
|
97
|
+
// Utility
|
|
98
|
+
Avatar, AvatarFallback, AvatarImage, AvatarBadge, AvatarGroup, AvatarGroupCount,
|
|
99
|
+
Kbd, KbdGroup,
|
|
100
|
+
DirectionProvider, // NOTE: no bare `Direction`; hook is `useDirection`
|
|
101
|
+
} from "@deriv-ds/design-intelligence-layer"
|
|
102
|
+
|
|
103
|
+
// Helpers & hooks
|
|
104
|
+
import { cn, useIsMobile, useDirection, useSidebar, useFormField } from "@deriv-ds/design-intelligence-layer"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Anything NOT exported from `dist/index.d.ts` does not exist in the package.** To prove an export exists:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
grep -w "<ComponentName>" node_modules/@deriv-ds/design-intelligence-layer/dist/index.d.ts
|
|
111
|
+
# zero hits = doesn't exist → use the closest real component or compose one
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ⚠ Commonly hallucinated imports — look valid, don't exist
|
|
115
|
+
|
|
116
|
+
| ❌ Doesn't exist | ✅ Real export |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` | `Tab`, `TabList`, `TabTrigger`, `TabContent` |
|
|
119
|
+
| `Resizable` | `ResizablePanelGroup` + `ResizablePanel` + `ResizableHandle` |
|
|
120
|
+
| `Notification` | `NotificationBanner` / `NotificationItem` |
|
|
121
|
+
| `Chart` | `ChartContainer` (+ `ChartTooltip`, `ChartLegend`) |
|
|
122
|
+
| `Direction` | `DirectionProvider` / `useDirection` |
|
|
123
|
+
|
|
124
|
+
### ⚠ In the source tree but NOT exported — do not import
|
|
125
|
+
|
|
126
|
+
These files exist under `components/ui/` in the source repo but are **not** re-exported from the package, so they are not importable by consumers. Treat them as nonexistent and compose the equivalent from real exports:
|
|
127
|
+
|
|
128
|
+
- `navigation-menu` (`NavigationMenu*`) — use `Navbar` / the nav blocks, or compose with `DropdownMenu` / `Sidebar`.
|
|
129
|
+
- `theme-toggle` (`ThemeToggle`) — compose from `Button` + `Switch` + `<Icon />`.
|
|
130
|
+
|
|
131
|
+
### Secondary exports (real, but rarely imported directly)
|
|
132
|
+
|
|
133
|
+
Exported and valid, but niche — only reach for them when you specifically need them: `ChartStyle` (internal to the Chart family), `IconSizeContext` (advanced `<Icon />` sizing). Most builds never touch these.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Icons
|
|
138
|
+
|
|
139
|
+
Icons come from the package's `<Icon />` component (a wrapper over `@deriv/quill-icons`). **Never install or import `lucide-react` or any other icon library.** Import `Icon` from the package and pass a `name` (plus optional `weight`):
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { Icon } from "@deriv-ds/design-intelligence-layer"
|
|
143
|
+
|
|
144
|
+
<Icon name="arrow-right" /> // default weight: bold
|
|
145
|
+
<Icon name="chevron-down" weight="regular" className="size-4 text-subtle" />
|
|
146
|
+
<Icon name="circle-check" weight="fill" className="size-4 text-success" />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Three weights: `regular` (thin — subtle/unselected), `bold` (medium outline — the default, most UI), `fill` (solid — selected/strong actions). The full name catalog lives at `node_modules/@deriv-ds/design-intelligence-layer/guides/design-system-guide/icon-reference.md`; `ICON_NAMES` and `iconAvailableWeights(name)` are exported from the package at runtime.
|
|
150
|
+
|
|
151
|
+
Use an icon ONLY when the Figma frame / design actually shows that glyph — match it to the closest catalog `name`, and never substitute a "close enough" icon for an exported Figma asset.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Token cheatsheet — use these classes ONLY
|
|
156
|
+
|
|
157
|
+
> Every class below maps to a `--color-*` entry in the package's `src/styles.css` `@theme inline`
|
|
158
|
+
> block — that is the ONLY thing that makes a utility class real in Tailwind v4. A class with no
|
|
159
|
+
> `--color-*` definition produces **no CSS** and silently inherits the wrong colour.
|
|
160
|
+
>
|
|
161
|
+
> **Existence proof for any colour class** (`bg-X` / `text-X` / `border-X` / `ring-X` → token name `X`):
|
|
162
|
+
> ```bash
|
|
163
|
+
> grep -- "--color-X:" node_modules/@deriv-ds/design-intelligence-layer/src/styles.css
|
|
164
|
+
> # zero hits = the class does NOT exist → use a real token or register one (see Token Creation)
|
|
165
|
+
> ```
|
|
166
|
+
|
|
167
|
+
### ⚠ Background vs text tokens — the #1 trap
|
|
168
|
+
|
|
169
|
+
`prominent` and `subtle` are **text / foreground** colours, not surfaces:
|
|
170
|
+
|
|
171
|
+
- `--color-prominent → var(--text-prominent-default)` = #181C25 (near-black). So **`bg-prominent` paints near-black** — it's an *inverse* surface for dark elements, NOT a white page background.
|
|
172
|
+
- **Page / surface backgrounds are a separate family:** `bg-card`, `bg-primary-canvas`, etc.
|
|
173
|
+
|
|
174
|
+
**Never use `bg-prominent` / `bg-subtle` as a page or header background.** Map the Figma
|
|
175
|
+
variable `component/app/background/normal` (`#ffffff`) → **`bg-card`**.
|
|
176
|
+
|
|
177
|
+
### Backgrounds (surfaces)
|
|
178
|
+
|
|
179
|
+
| Class | Use | Value (light) |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `bg-card` | page / card / panel surface — **the default white background** | #FFFFFF |
|
|
182
|
+
| `bg-primary-surface` | white surface (alias of `bg-card`) | #FFFFFF |
|
|
183
|
+
| `bg-primary-canvas` | neutral page canvas (app shell behind cards) | #F6F7F8 |
|
|
184
|
+
| `bg-secondary-surface` | subtle muted surface | #F6F7F8 |
|
|
185
|
+
| `bg-secondary-canvas` | alternate canvas | #FFFFFF |
|
|
186
|
+
| `bg-popover` | popover / dropdown surface | #FFFFFF |
|
|
187
|
+
| `bg-prominent` | **inverse (near-black) surface only** — tooltips, inverted chips. NOT a page bg | #181C25 |
|
|
188
|
+
| `bg-overlay` | modal backdrop only | black 50% |
|
|
189
|
+
| `bg-primary` | brand primary (CTAs) — **coral** | #FF444F |
|
|
190
|
+
| `bg-primary-hover` | primary hover | coral-800 |
|
|
191
|
+
| `bg-secondary-hover` | outline / secondary hover | #F6F7F8 |
|
|
192
|
+
|
|
193
|
+
### Text
|
|
194
|
+
|
|
195
|
+
| Class | Use | Value (light) |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `text-prominent` | primary text — **near-black** (NOT `text-on-prominent`, which doesn't exist) | #181C25 |
|
|
198
|
+
| `text-subtle` | secondary / description text | #181C25 @ 48% |
|
|
199
|
+
| `text-on-prominent-static-inverse` | always white — text on coral / dark / inverse surfaces | #FFFFFF |
|
|
200
|
+
| `text-primary` | brand text — coral | #FF444F |
|
|
201
|
+
| `text-warning` | warning text | #C47D00 |
|
|
202
|
+
| `text-info` | informational text | #0777C4 |
|
|
203
|
+
|
|
204
|
+
### ⚠ Status / profit-loss colours — NOT built in, register them
|
|
205
|
+
|
|
206
|
+
**`text-success`, `text-error`, `bg-success`, `bg-error`, `text-win`, `text-loss` do NOT exist as
|
|
207
|
+
utilities** — there is no `--color-success` / `--color-error` in the theme. The package DOES ship
|
|
208
|
+
the underlying **theme-aware variables** (they flip automatically in dark mode). Register them once
|
|
209
|
+
in the consuming project's `globals.css` and then use the generated classes:
|
|
210
|
+
|
|
211
|
+
```css
|
|
212
|
+
/* globals.css — after the DS imports */
|
|
213
|
+
@theme inline {
|
|
214
|
+
--color-success: var(--text-success-default); /* #007A22 light / #4DBC6B dark → text-success */
|
|
215
|
+
--color-error: var(--text-error-default); /* #C40000 light / #FF4D4D dark → text-error */
|
|
216
|
+
--color-success-fill: var(--background-success-default); /* green tint → bg-success-fill */
|
|
217
|
+
--color-error-fill: var(--background-error-default); /* red tint → bg-error-fill */
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This is the canonical Token Creation pattern (below) — never use a raw green/red hex or `text-green-600`.
|
|
222
|
+
|
|
223
|
+
### Borders & ring
|
|
224
|
+
|
|
225
|
+
| Class | Use | Value (light) |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| `border-border-subtle` | default UI borders, dividers, cards | #181C25 @ 8% |
|
|
228
|
+
| `border-border-prominent` | outline-variant components, heavier dividers | #181C25 @ 16% |
|
|
229
|
+
| `border-default` | global default border | #181C25 @ 16% |
|
|
230
|
+
| `border-input` | input field borders | #181C25 @ 8% |
|
|
231
|
+
| `border-selected` | selected-state border (solid dark) | #181C25 |
|
|
232
|
+
| `ring-ring` | focus ring — **coral** | #FF444F |
|
|
233
|
+
|
|
234
|
+
> `border-border` is deprecated — use `border-border-subtle` or `border-border-prominent`.
|
|
235
|
+
> `border-success` / `border-error` do NOT exist — register them like the status colours above if needed.
|
|
236
|
+
|
|
237
|
+
### Radius
|
|
238
|
+
|
|
239
|
+
`--radius-xs`, `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl` are defined → use
|
|
240
|
+
`rounded-xs` … `rounded-xl`. Never `rounded-[Npx]` — if a Figma radius matches no step, register a
|
|
241
|
+
radius token (Token Creation pattern) instead.
|
|
242
|
+
|
|
243
|
+
### ⚠ Commonly hallucinated classes — look valid, don't exist, render the WRONG colour
|
|
244
|
+
|
|
245
|
+
These pass the forbidden-pattern grep (they look like semantic tokens) but have no `--color-*`
|
|
246
|
+
definition, so they produce **no CSS** — the element silently inherits a wrong colour:
|
|
247
|
+
|
|
248
|
+
| ❌ Doesn't exist | ✅ Real fix |
|
|
249
|
+
|---|---|
|
|
250
|
+
| `text-on-prominent` | `text-prominent` |
|
|
251
|
+
| `text-on-subtle` | `text-subtle` |
|
|
252
|
+
| `text-success` / `bg-success` / `text-error` / `bg-error` | register first (see Status colours above), then use |
|
|
253
|
+
| `bg-loss` / `text-loss` / `bg-win` / `text-win` | registered `text-error` / `text-success` |
|
|
254
|
+
| `bg-prominent` (as page bg) | `bg-card` |
|
|
255
|
+
| `bg-subtle` (as page bg) | `bg-card` / `bg-primary-canvas` |
|
|
256
|
+
| `text-heading-h1`, `text-body-md`, `text-heading-mega` | `heading-h1`, `body-md`, `heading-hero` (see Typography) |
|
|
257
|
+
|
|
258
|
+
**Runtime tell:** a token-intended element whose computed `background-color` is `rgba(0,0,0,0)` or
|
|
259
|
+
whose text colour inherited instead of applying = hallucinated class.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Token Creation — registering tokens the package doesn't ship
|
|
264
|
+
|
|
265
|
+
When a design value has no existing utility class, NEVER inline it and NEVER stop the build.
|
|
266
|
+
Register a token in the consuming project's `globals.css` (after the DS imports) and use the
|
|
267
|
+
generated class. Resolution order — first match wins:
|
|
268
|
+
|
|
269
|
+
1. **Existing semantic token** from the tables above → use it directly.
|
|
270
|
+
2. **Package semantic variable** (theme-aware — flips in dark mode automatically). Find it in
|
|
271
|
+
`src/styles.css` (`--text-*-default`, `--background-*-default`, etc.):
|
|
272
|
+
```css
|
|
273
|
+
@theme inline { --color-success: var(--text-success-default); }
|
|
274
|
+
```
|
|
275
|
+
3. **Package primitive** (1294 of them: `--primitive-coral-700`, `--primitive-slate-1200`,
|
|
276
|
+
`--primitive-blue-500`, …). Grep the resolved hex to find it:
|
|
277
|
+
```bash
|
|
278
|
+
grep -i "#1A2233" node_modules/@deriv-ds/design-intelligence-layer/src/styles.css
|
|
279
|
+
```
|
|
280
|
+
```css
|
|
281
|
+
@theme inline { --color-brand-coral: var(--primitive-coral-700); } /* → bg-brand-coral */
|
|
282
|
+
```
|
|
283
|
+
⚠ Primitives are static — they do NOT flip in dark mode. Prefer step 2 when a semantic var exists.
|
|
284
|
+
4. **Exact hex, centralized** — only when no package variable resolves to the design's value.
|
|
285
|
+
The hex lives in ONE place (the theme), never in JSX:
|
|
286
|
+
```css
|
|
287
|
+
@theme inline { --color-figma-hero-overlay: #1A2233; } /* → bg-figma-hero-overlay */
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Naming: follow the package's convention — `--color-<intent>` (e.g. `--color-success`,
|
|
291
|
+
`--color-brand-coral`), not `--color-green` or `--color-1A2233`. Same pattern applies to radius
|
|
292
|
+
(`--radius-<name>`) and font sizes. **Every registered token must be listed in the Build Report.**
|
|
293
|
+
|
|
294
|
+
Referencing a primitive or raw var **inline** is still forbidden — `bg-[var(--primitive-coral-700)]` never; register, then use the class.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Forbidden patterns — never write these
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
❌ #FF444F, #000000, rgba(0,0,0,0.5) — hex / rgba in JSX or class names (even brand coral — use bg-primary)
|
|
302
|
+
❌ bg-gray-100, text-zinc-500, bg-red-500,
|
|
303
|
+
text-blue-600, bg-amber-100, bg-black — ANY raw Tailwind palette, every hue
|
|
304
|
+
❌ bg-[#EEEEEE], text-[rgba(...)] — arbitrary colour values
|
|
305
|
+
❌ bg-[var(--border-subtle)] — raw CSS var inline (register a token instead)
|
|
306
|
+
❌ hsl(var(--primary)) — Tailwind v3 syntax
|
|
307
|
+
❌ rounded-[12px] — arbitrary radius (use rounded-xs…xl or register)
|
|
308
|
+
❌ font-[...] / style={{fontFamily}} — font-family overrides (use font-display / font-body)
|
|
309
|
+
❌ bg-prominent as a page/header background — near-black inverse surface (see trap above)
|
|
310
|
+
❌ bg-black/50 — raw opacity on non-token (backdrop → bg-overlay)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Opacity on a real token is fine
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
✅ bg-primary/20, border-border-subtle/50, ring-ring/10
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Layout utilities — exempt from token rules
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
✅ flex, grid, gap-4, p-6, m-2, w-full, h-screen, max-w-lg
|
|
323
|
+
✅ p-[18px], gap-[12px], w-[327px] — exact px from Figma auto-layout (fidelity!)
|
|
324
|
+
✅ z-50, overflow-hidden, opacity-50, transition-all
|
|
325
|
+
✅ col-span-2, items-center, justify-between
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Token rules apply **only** to: colour (bg, text, border, ring, shadow colour), border radius, and
|
|
329
|
+
font family. Spacing/sizing should use the **exact values from the design** — arbitrary px values
|
|
330
|
+
are encouraged there when the Figma auto-layout values don't match a Tailwind step.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Button variants
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
<Button variant="primary" /> // Coral filled — main CTA
|
|
338
|
+
<Button variant="secondary" /> // Black outline, white bg — secondary action
|
|
339
|
+
<Button variant="tertiary" /> // Text only, underline on hover — minimal
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Sizes: `lg` (48px), `md` (40px, default), `sm` (32px), plus `icon-lg | icon-md | icon-sm`.
|
|
343
|
+
Tones: `normal`, `inverse`, `static-light`, `static-dark`.
|
|
344
|
+
|
|
345
|
+
## Badge variants
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
// Solid
|
|
349
|
+
<Badge variant="default" /> // Coral (brand)
|
|
350
|
+
<Badge variant="default-success" /> // Green
|
|
351
|
+
<Badge variant="default-fail" /> // Red
|
|
352
|
+
|
|
353
|
+
// Tint
|
|
354
|
+
<Badge variant="fill" /> // Coral tint bg
|
|
355
|
+
<Badge variant="fill-success" /> // Green tint bg
|
|
356
|
+
<Badge variant="fill-fail" /> // Red tint bg
|
|
357
|
+
|
|
358
|
+
// Outline (black border, hover grey)
|
|
359
|
+
<Badge variant="outline" />
|
|
360
|
+
|
|
361
|
+
// Ghost (transparent bg)
|
|
362
|
+
<Badge variant="ghost" />
|
|
363
|
+
<Badge variant="ghost-success" />
|
|
364
|
+
<Badge variant="ghost-fail" />
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Blocks — importable from the package
|
|
370
|
+
|
|
371
|
+
Blocks ship as named exports — **import them directly, never re-implement.** Matching a page region to a block is the **Block Matching gate** in the build skill: if a region in a Figma frame or a text prompt corresponds to a block below, import that exact block and tune it to the content — do NOT mock up a look-alike. Only compose from primitives + tokens when **no** block matches, and flag the composition in the Build Report. Never invent a block import name.
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
import { Navbar, HeroDesktopMain, Section, TradingAccountCard, Referral } from "@deriv-ds/design-intelligence-layer"
|
|
375
|
+
// Navbar is responsive: sidebar on desktop (≥768px), bottom bar on mobile (<768px).
|
|
376
|
+
// For explicit control, NavMobileBottomBar and NavDesktopSidebar are also exported.
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The **"matches when"** column is what the Block Matching gate matches on — intent, not pixels.
|
|
380
|
+
|
|
381
|
+
| Region | Import(s) | Matches when (prompt or frame shows…) |
|
|
382
|
+
|---|---|---|
|
|
383
|
+
| **Navbar** | `Navbar` (responsive) · `NavMobileBottomBar` · `NavDesktopSidebar` | top/side nav, nav menu, sidebar nav, bottom tab bar, "navbar" / "nav menu" |
|
|
384
|
+
| **Header** | `HeaderApp` · `HeaderAppHome` · `HeaderBranding` | app top header strip, branded header bar, "header" |
|
|
385
|
+
| **Hero — home** | `HeroMobileHomeTitle` · `HeroMobileHomeTotalAssets` · `HeroDesktopHomeOnboarding` · `HeroDesktopHomeWithBalance` | home top area: greeting/title, total-assets/balance header, onboarding hero |
|
|
386
|
+
| **Hero — main/secondary/transaction** | `HeroMobileMain` · `HeroDesktopMain` · `HeroMobileSecondary` · `HeroDesktopSecondary` · `HeroMobileTransaction` | primary/secondary page hero, transaction hero |
|
|
387
|
+
| **Hero action button** | `HeroActionButton` | the prominent action button(s) sitting inside a hero |
|
|
388
|
+
| **Home — banner** | `HomeBanner` · `BannerCard` | promo / announcement banner card on home |
|
|
389
|
+
| **Home — highlights** | `HomeHighlights` · `HomeHighlightCard` | row/grid of highlight cards, "highlights" |
|
|
390
|
+
| **Home — explore** | `HomeExploreDeriv` | "explore Deriv" discovery section |
|
|
391
|
+
| **Trading — account card** | `TradingAccountCard` | account card with balance + actions |
|
|
392
|
+
| **Trading — action buttons** | `TradingActionButtonsPrimary` · `TradingActionButtonsSecondary` (+ `…Skeleton`) | deposit/withdraw/transfer action button cluster |
|
|
393
|
+
| **Trading — empty** | `TradingEmpty` (+ `TradingEmptySkeleton`) | empty trading list / no-accounts state |
|
|
394
|
+
| **Account activated card** | `AccountActivatedCard` | "account activated" confirmation card |
|
|
395
|
+
| **Feedback / success** | `FeedbackSuccessScreen` · `FeedbackTradeWithAccounts` · `FeedbackTransferSuggestion` | success screen, post-action confirmation/feedback |
|
|
396
|
+
| **Referral** | `Referral` | refer-a-friend / invite panel |
|
|
397
|
+
| **Section** | `Section` | eyebrow tag + title + control cluster + segmented control wrapping a content slot (children) |
|
|
398
|
+
|
|
399
|
+
Most heroes/trading blocks also export a `*Skeleton` variant for loading states. Pick the **mobile vs desktop** variant from the target viewport.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Typography
|
|
404
|
+
|
|
405
|
+
The package defines **bare utility classes** in `styles.css` (no `text-` prefix):
|
|
406
|
+
|
|
407
|
+
- Families: `font-display` / `font-body` (Inter loads automatically with the DS styles import)
|
|
408
|
+
- Headings: `heading-hero`, `heading-h1`, `heading-h2`, `heading-h3`, `heading-h4`, `heading-h5`, `heading-h6`
|
|
409
|
+
- Body: `body-xl`, `body-lg`, `body-md`, `body-sm`, `body-xs`
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
<h1 className="heading-h1 text-prominent">Title</h1>
|
|
413
|
+
<p className="body-md text-subtle">Description</p>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
⚠ `text-heading-h1`, `text-body-md`, `text-heading-mega` do NOT exist — they render at the browser
|
|
417
|
+
default size. Never override `font-family` manually in CSS. When a Figma text style doesn't match a
|
|
418
|
+
scale step exactly, start from the closest `heading-*`/`body-*` class and override the deltas with
|
|
419
|
+
standard utilities (`font-extrabold`, `text-[18px]`, `leading-[24px]`, `tracking-[-0.2px]`) — size,
|
|
420
|
+
weight, line-height, and letter-spacing are NOT colour tokens, so exact values from Figma are
|
|
421
|
+
allowed and preferred for fidelity. Only `font-family` itself is locked to `font-display`/`font-body`.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Intentional DS behaviours — do NOT "fix" these
|
|
426
|
+
|
|
427
|
+
- The **focus ring** on `Select`, `Input`, `SelectTrigger`, etc. (`ring-ring`, coral, shown on
|
|
428
|
+
`:focus-visible`) is built into the package as WCAG focus indication. It is **expected** — do
|
|
429
|
+
not remove it, override it to `ring-0`, or flag it as a bug.
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Quick install reference
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
npm install @deriv-ds/design-intelligence-layer
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
```css
|
|
440
|
+
/* The @source path is RELATIVE TO THIS CSS FILE — count the directories up to the project root: */
|
|
441
|
+
|
|
442
|
+
/* app/globals.css (Next.js, no src dir) */
|
|
443
|
+
@import "@deriv-ds/design-intelligence-layer/styles";
|
|
444
|
+
@import "tailwindcss";
|
|
445
|
+
@source "../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
446
|
+
|
|
447
|
+
/* src/app/globals.css (Next.js with --src-dir) */
|
|
448
|
+
@import "@deriv-ds/design-intelligence-layer/styles";
|
|
449
|
+
@import "tailwindcss";
|
|
450
|
+
@source "../../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
451
|
+
|
|
452
|
+
/* src/index.css (Vite) */
|
|
453
|
+
@import "@deriv-ds/design-intelligence-layer/styles";
|
|
454
|
+
@import "tailwindcss";
|
|
455
|
+
@source "../node_modules/@deriv-ds/design-intelligence-layer/dist";
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Verify the path resolved:** if DS component internals render unstyled (e.g. a Button with no
|
|
459
|
+
fill), the `@source` path is wrong — Tailwind found no classes to generate. Fix the `../` count.
|
|
460
|
+
|
|
461
|
+
Vite-only: add `@tailwindcss/vite` to `vite.config.ts`. Next.js needs no extra config.
|
|
462
|
+
|
|
463
|
+
**Do NOT** install `lucide-react`, `tailwindcss`, or create a `tailwind.config.js` — these are handled by the package.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Build UI strictly from the `@deriv-ds/design-intelligence-layer` package — pages, screens, and components.
|
|
2
|
+
|
|
3
|
+
First read and follow **`.claude/skills/build/SKILL.md`** in this repo and every file it links under `.claude/skills/build/references/`. Execute exactly as that skill specifies — package imports only, token-only styling, Figma visual fidelity, and the accessibility checklist.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: manual
|
|
3
|
+
---
|
|
4
|
+
Build UI strictly from the `@deriv-ds/design-intelligence-layer` package — pages, screens, and components.
|
|
5
|
+
|
|
6
|
+
First read and follow **`.claude/skills/build/SKILL.md`** in this repo and every file it links under `.claude/skills/build/references/`. Execute exactly as that skill specifies — package imports only, token-only styling, Figma visual fidelity, and the accessibility checklist.
|
package/AGENTS.md
CHANGED
|
@@ -48,6 +48,28 @@ node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-cons
|
|
|
48
48
|
cp node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc .cursor/rules/
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
**Claude Code users:** The package also ships a `build` skill that enforces these rules during every UI build. Copy it into your project (re-copy after each upgrade), then invoke it with `/build`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
mkdir -p .claude/skills && cp -R node_modules/@deriv-ds/design-intelligence-layer/.claude/skills/build .claude/skills/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Cursor users:** Copy the shipped `/build` command shim (points at the `.claude/skills/build/` copy above, so copy that first):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mkdir -p .cursor/commands
|
|
61
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/.cursor/commands/build.md .cursor/commands/build.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Kiro users:** Copy the shipped `#build` steering doc the same way:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
mkdir -p .kiro/steering
|
|
68
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/.kiro/steering/build.md .kiro/steering/build.md
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Re-copy both after every version bump so the shims match the installed release.
|
|
72
|
+
|
|
51
73
|
---
|
|
52
74
|
|
|
53
75
|
## Step 3 — Core rules summary
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.1]
|
|
11
|
+
|
|
12
|
+
### Note
|
|
13
|
+
- **`0.6.0` was published in error** by a tag pushed against the wrong commit before the skills PR was merged — it contains the old pre-merge codebase mislabeled as `0.6.0` and has been deprecated on npm. `0.6.1` is the real release of everything listed below; no code changes from `0.6.0`'s intended content, only the correct commit and version number.
|
|
14
|
+
|
|
15
|
+
## [0.6.0]
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Three AI agent skills**, runnable from Claude Code, Cursor, and Kiro:
|
|
19
|
+
- `build` — **npm-published**, for package consumers. Builds pages/screens/components strictly from the published package (text prompt, Figma link, or pasted block), enforcing token-only styling, the Block Matching gate, and an accessibility checklist. Ends with an independent critic pass and an offer to open a PR.
|
|
20
|
+
- `component` — **source-repo only**, for extending the design system. Authors a new UI primitive end-to-end (component file, package export, playground page, nav wiring, and consumer docs), with a dedupe gate against the existing catalog.
|
|
21
|
+
- `block` — **source-repo only**, for extending the design system. Authors a new composite Block end-to-end (block file, package export, playground page, nav wiring, and the `build` skill's block catalog), with the same dedupe gate and critic pass.
|
|
22
|
+
- **Cross-agent shims** — `.cursor/commands/{build,component,block}.md` (Cursor `/build`, `/component`, `/block`) and `.kiro/steering/{build,component,block}.md` (Kiro `#build`, `#component`, `#block`), all pointing at the same canonical `SKILL.md` files so every agent runs identical rules. Only the `build` shims are published to npm — `component`/`block` shims live in this repo only, alongside the skills they invoke.
|
|
23
|
+
- **Skill catalog drift guard** — `npm run check:skill-catalog` (wired into CI) fails the build if a component/block is exported but missing from the `build` skill's catalog, or documented in the catalog but not actually exported — keeping the Block Matching gate from ever hand-rolling a look-alike of a shipped block.
|
|
24
|
+
- **Skills foundation page** in the playground, documenting all three skills, their guardrails (dedupe-first, independent critic audit, bounded loops, idempotent edits, ask-then-PR), and how to invoke them per agent.
|
|
25
|
+
|
|
10
26
|
## [0.5.2]
|
|
11
27
|
|
|
12
28
|
### Added
|
|
@@ -57,7 +73,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
57
73
|
### Security
|
|
58
74
|
- Patched CVE-2026-53550 and CVE-2026-49356 (dependency bumps).
|
|
59
75
|
|
|
60
|
-
[Unreleased]: https://github.com/deriv-com/design-intelligence-layer/compare/v0.
|
|
76
|
+
[Unreleased]: https://github.com/deriv-com/design-intelligence-layer/compare/v0.6.1...HEAD
|
|
77
|
+
[0.6.1]: https://github.com/deriv-com/design-intelligence-layer/compare/v0.5.2...v0.6.1
|
|
61
78
|
[0.5.1]: https://github.com/deriv-com/design-intelligence-layer/compare/v0.5.0...v0.5.1
|
|
62
79
|
[0.5.0]: https://github.com/deriv-com/design-intelligence-layer/compare/v0.4.5...v0.5.0
|
|
63
80
|
[0.4.5]: https://github.com/deriv-com/design-intelligence-layer/releases/tag/v0.4.5
|
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ The full icon catalog is at `node_modules/@deriv-ds/design-intelligence-layer/gu
|
|
|
96
96
|
|
|
97
97
|
## What's inside
|
|
98
98
|
|
|
99
|
-
- **
|
|
99
|
+
- **60+ UI components** — buttons, forms, dialogs, charts, sidebars, and more
|
|
100
100
|
- **Design tokens** — three-layer architecture (primitives → foundation semantics → component tokens) synced from Figma
|
|
101
101
|
- **Light + Dark themes** — toggle `.dark` on `<html>` to switch; every semantic token has a paired override
|
|
102
102
|
- **Inter typography** — heading (mega → h6) and body (xl → xs) scales ready to use
|
|
@@ -117,7 +117,7 @@ The full icon catalog is at `node_modules/@deriv-ds/design-intelligence-layer/gu
|
|
|
117
117
|
| Avatar | `Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup` |
|
|
118
118
|
| Badge | `Badge, BadgeDot` |
|
|
119
119
|
| Breadcrumb | `Breadcrumb, BreadcrumbList, BreadcrumbItem, ...` |
|
|
120
|
-
| Button | `Button, buttonVariants` |
|
|
120
|
+
| Button | `Button, buttonVariants, SocialButton` |
|
|
121
121
|
| Calendar | `Calendar, CalendarDayButton` |
|
|
122
122
|
| Card | `Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter` |
|
|
123
123
|
| Carousel | `Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext` |
|
|
@@ -137,16 +137,15 @@ The full icon catalog is at `node_modules/@deriv-ds/design-intelligence-layer/gu
|
|
|
137
137
|
| Field | `Field, FieldLabel, FieldDescription, FieldError, FieldGroup` |
|
|
138
138
|
| Form | `Form, FormItem, FormLabel, FormControl, FormField, FormMessage` |
|
|
139
139
|
| Hover Card | `HoverCard, HoverCardTrigger, HoverCardContent` |
|
|
140
|
+
| Icon | `Icon` (wraps `@deriv/quill-icons`; `weight="regular\|bold\|fill"`) |
|
|
140
141
|
| Input | `Input` |
|
|
141
142
|
| Input Group | `InputGroup, InputGroupAddon, InputGroupButton, InputGroupText` |
|
|
142
|
-
| Item | `
|
|
143
|
+
| List Item | `ListItem, ListItemMedia, ListItemContent, ListItemTitle, ListItemDescription, ListItemIcon, ListItemActions` |
|
|
143
144
|
| Kbd | `Kbd, KbdGroup` |
|
|
144
145
|
| Label | `Label` |
|
|
145
146
|
| Link | `Link` |
|
|
146
147
|
| Loading Spinner | `LoadingSpinner` |
|
|
147
|
-
| Modal | `AlertDialog, AlertDialogTrigger, AlertDialogContent, ...` |
|
|
148
148
|
| Native Select | `NativeSelect, NativeSelectOptGroup, NativeSelectOption` |
|
|
149
|
-
| Navigation Menu | `NavigationMenu, NavigationMenuList, NavigationMenuTrigger, ...` |
|
|
150
149
|
| Notification | `NotificationBanner, NotificationItem, NotificationDivider` |
|
|
151
150
|
| OTP Field | `CodeInput, CodeInputGroup, CodeInputSlot, CodeInputSeparator` |
|
|
152
151
|
| Pagination | `Pagination, PaginationContent, PaginationLink, ...` |
|
|
@@ -171,7 +170,6 @@ The full icon catalog is at `node_modules/@deriv-ds/design-intelligence-layer/gu
|
|
|
171
170
|
| Switch | `Switch` |
|
|
172
171
|
| Tab | `Tab, TabList, TabTrigger, TabContent` |
|
|
173
172
|
| Table | `Table, TableHeader, TableBody, TableRow, TableHead, TableCell` |
|
|
174
|
-
| Tabs | `Tabs, TabsList, TabsTrigger, TabsContent` |
|
|
175
173
|
| Tag | `Tag, tagVariants` |
|
|
176
174
|
| Textarea | `Textarea` |
|
|
177
175
|
| Toggle | `Toggle, toggleVariants` |
|
|
@@ -182,34 +180,30 @@ The full icon catalog is at `node_modules/@deriv-ds/design-intelligence-layer/gu
|
|
|
182
180
|
|
|
183
181
|
## Blocks
|
|
184
182
|
|
|
185
|
-
Blocks are pre-composed UI patterns built from package primitives. They
|
|
183
|
+
Blocks are pre-composed UI patterns (navbars, heroes, cards, …) built from package primitives. **They ship as named exports** — import them directly, just like any component:
|
|
186
184
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
2. Ask you for values to replace each `// TODO`
|
|
191
|
-
3. Bundle in any shared dependencies (e.g. `HeroActionButton`)
|
|
185
|
+
```tsx
|
|
186
|
+
import { Navbar, HeroDesktopMain, Section, TradingAccountCard } from "@deriv-ds/design-intelligence-layer";
|
|
187
|
+
```
|
|
192
188
|
|
|
193
|
-
|
|
189
|
+
Wire each block's props/children to your real content — don't re-implement them. The `build` skill's Block Matching gate does this automatically (it imports the matching block instead of mocking up a look-alike).
|
|
194
190
|
|
|
195
191
|
### Available blocks
|
|
196
192
|
|
|
197
|
-
| Block |
|
|
193
|
+
| Block | Import(s) |
|
|
198
194
|
|---|---|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
| `
|
|
203
|
-
|
|
|
204
|
-
| `
|
|
205
|
-
|
|
|
206
|
-
| `
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
If you don't want to use an AI agent, open the block file directly on GitHub and copy everything between the `🟢 BLOCK TO COPY` and `🔵 PLAYGROUND ONLY` markers. Replace each `// TODO` with your own data. If the block imports `HeroActionButton`, also copy `app/components/hero-action-button.tsx`.
|
|
195
|
+
| NavBar | `Navbar` (responsive) · `NavMobileBottomBar` · `NavDesktopSidebar` |
|
|
196
|
+
| Header | `HeaderApp` · `HeaderAppHome` · `HeaderBranding` |
|
|
197
|
+
| Hero — home | `HeroMobileHomeTitle` · `HeroMobileHomeTotalAssets` · `HeroDesktopHomeOnboarding` · `HeroDesktopHomeWithBalance` |
|
|
198
|
+
| Hero — main / secondary / transaction | `HeroMobileMain` · `HeroDesktopMain` · `HeroMobileSecondary` · `HeroDesktopSecondary` · `HeroMobileTransaction` |
|
|
199
|
+
| Hero action button | `HeroActionButton` |
|
|
200
|
+
| Home — banner / highlights / explore | `HomeBanner` · `BannerCard` · `HomeHighlights` · `HomeHighlightCard` · `HomeExploreDeriv` |
|
|
201
|
+
| Trading | `TradingAccountCard` · `TradingActionButtonsPrimary` · `TradingActionButtonsSecondary` · `TradingEmpty` |
|
|
202
|
+
| Account / feedback | `AccountActivatedCard` · `FeedbackSuccessScreen` · `FeedbackTradeWithAccounts` · `FeedbackTransferSuggestion` |
|
|
203
|
+
| Referral | `Referral` |
|
|
204
|
+
| Section | `Section` |
|
|
205
|
+
|
|
206
|
+
Block source files live in `components/blocks/`. Most heroes and trading blocks also export a `*Skeleton` loading variant. See `.claude/skills/build/references/components-and-tokens.md` for the full catalog with "matches when" hints.
|
|
213
207
|
|
|
214
208
|
---
|
|
215
209
|
|
|
@@ -317,13 +311,8 @@ by existing primitives — reach for these instead:
|
|
|
317
311
|
|
|
318
312
|
## Component behaviour notes
|
|
319
313
|
|
|
320
|
-
###
|
|
321
|
-
`
|
|
322
|
-
|
|
323
|
-
```tsx
|
|
324
|
-
<AlertDialogContent size="sm"> // buttons fill width (50/50 grid)
|
|
325
|
-
<AlertDialogContent size="default"> // buttons natural width, right-aligned
|
|
326
|
-
```
|
|
314
|
+
### Dialog — footer buttons
|
|
315
|
+
`DialogFooter` makes its direct child buttons fill the full width automatically. Stack your buttons inside `DialogFooter` and they will always be full-width.
|
|
327
316
|
|
|
328
317
|
### Drawer — footer buttons
|
|
329
318
|
`DrawerFooter` makes all direct child buttons fill the full width automatically via `[&>*]:w-full`. Stack your buttons inside `DrawerFooter` and they will always be full-width.
|
|
@@ -349,8 +338,8 @@ The `lg` variant of `Link` uses `text-base` (16px). The `md` and `sm` variants u
|
|
|
349
338
|
<Link size="sm" /> // 12px — font-xs
|
|
350
339
|
```
|
|
351
340
|
|
|
352
|
-
###
|
|
353
|
-
|
|
341
|
+
### Tab — hover & sizes
|
|
342
|
+
`Tab` is a line/underline tab set (`Tab, TabList, TabTrigger, TabContent` — note the singular names; `Tabs`/`TabsList` do not exist). Hover shows coral text (`text-primary`, `#FF444F`) on a transparent background.
|
|
354
343
|
|
|
355
344
|
Tab sizes:
|
|
356
345
|
|
|
@@ -687,17 +676,17 @@ node_modules/@deriv-ds/design-intelligence-layer/guides/design-principles/quill-
|
|
|
687
676
|
|
|
688
677
|
**Accessibility standards file** (bundled in the package):
|
|
689
678
|
```
|
|
690
|
-
node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/
|
|
679
|
+
node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md
|
|
691
680
|
```
|
|
692
681
|
|
|
693
682
|
**Personas file** (bundled in the package):
|
|
694
683
|
```
|
|
695
|
-
node_modules/@deriv-ds/design-intelligence-layer/guides/personas/
|
|
684
|
+
node_modules/@deriv-ds/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md
|
|
696
685
|
```
|
|
697
686
|
|
|
698
687
|
**Brand voice** (bundled in the package):
|
|
699
688
|
```
|
|
700
|
-
node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/
|
|
689
|
+
node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md
|
|
701
690
|
```
|
|
702
691
|
|
|
703
692
|
All four files apply to all projects built with this package — landing pages, product screens, and games. Every AI agent must read all of them before starting any build. Run the design principles and accessibility checklists before completing any screen. Use the personas and brand voice to guide all player-facing copy.
|
|
@@ -722,9 +711,9 @@ Add the following to your project's `CLAUDE.md`:
|
|
|
722
711
|
|
|
723
712
|
This project uses @deriv-ds/design-intelligence-layer (Quill Design System). Before writing any UI:
|
|
724
713
|
1. Read node_modules/@deriv-ds/design-intelligence-layer/guides/design-principles/quill-design-principles.md — apply the 8 principles and run the 7-point checklist on every screen
|
|
725
|
-
2. Read node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/
|
|
726
|
-
3. Read node_modules/@deriv-ds/design-intelligence-layer/guides/personas/
|
|
727
|
-
4. Read node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/
|
|
714
|
+
2. Read node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/trading-game-accessibility-standards.md — apply WCAG 2.1 AA standards and run the 9-point accessibility checklist on every screen
|
|
715
|
+
3. Read node_modules/@deriv-ds/design-intelligence-layer/guides/personas/trading-game-player-field-guide.md — understand the player personas that shape all copy and UX
|
|
716
|
+
4. Read node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md — apply the brand voice: channel-specific voice, banned phrases, vocabulary, and formatting rules for all player-facing copy
|
|
728
717
|
5. Check if the component exists in the package — import it, don't re-implement
|
|
729
718
|
6. Use only design token classes (bg-prominent, text-on-prominent, border-border-subtle, etc.) — no hardcoded hex or raw Tailwind palette colors
|
|
730
719
|
7. Do not install lucide-react — icons come from @deriv/quill-icons via the bundled Icon component; do not install tailwindcss or other bundled dependencies separately
|
|
@@ -735,6 +724,29 @@ See node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-
|
|
|
735
724
|
See node_modules/@deriv-ds/design-intelligence-layer/README.md for complete token and component reference.
|
|
736
725
|
```
|
|
737
726
|
|
|
727
|
+
### Claude Code skill (`build`)
|
|
728
|
+
|
|
729
|
+
The package ships a Claude Code **skill** that drives every UI build strictly from the design system — bootstrap, build-from-prompt, build-from-Figma, and integrate-a-pasted-block, all with the token/component/icon rules enforced. It also runs a **Block Matching gate**: before mocking up any region (nav, hero, cards, …) it matches the region against the package's shipped blocks and imports the real one instead of re-creating a look-alike. Copy it into your project's skills directory:
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
mkdir -p .claude/skills
|
|
733
|
+
cp -R node_modules/@deriv-ds/design-intelligence-layer/.claude/skills/build .claude/skills/
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Re-copy after each version bump so the skill matches the installed release. Once copied, Claude Code triggers it automatically when you ask to build Deriv UI (or invoke `/build`).
|
|
737
|
+
|
|
738
|
+
**Cursor / Kiro:** copy the same skill folder as above, plus the shipped shim so `/build` (Cursor) or `#build` (Kiro) invokes it:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
# Cursor
|
|
742
|
+
mkdir -p .cursor/commands
|
|
743
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/.cursor/commands/build.md .cursor/commands/build.md
|
|
744
|
+
|
|
745
|
+
# Kiro
|
|
746
|
+
mkdir -p .kiro/steering
|
|
747
|
+
cp node_modules/@deriv-ds/design-intelligence-layer/.kiro/steering/build.md .kiro/steering/build.md
|
|
748
|
+
```
|
|
749
|
+
|
|
738
750
|
---
|
|
739
751
|
|
|
740
752
|
## Common mistakes
|
|
@@ -793,24 +805,26 @@ npm run build
|
|
|
793
805
|
# Build the Next.js showcase app
|
|
794
806
|
npm run build:next
|
|
795
807
|
|
|
796
|
-
#
|
|
797
|
-
|
|
798
|
-
npm run generate:blocks
|
|
808
|
+
# Check the build-skill catalog matches the package's real exports
|
|
809
|
+
npm run check:skill-catalog
|
|
799
810
|
```
|
|
800
811
|
|
|
801
|
-
###
|
|
812
|
+
### Authoring skills (`block`, `component`)
|
|
813
|
+
|
|
814
|
+
This repo ships two Claude Code **authoring skills** that scaffold a new export end-to-end so it lands consistently across the component file, the playground demo, the package export, the nav wiring, and the docs/catalog. They live only in this source repo (`.claude/skills/block/`, `.claude/skills/component/`) and are **not published to npm** — they edit `components/`, `src/index.ts`, and the playground, which only exist here. Anyone with this repo checked out gets them automatically. Package consumers building product UI use the `build` skill instead (npm-published, described above).
|
|
815
|
+
|
|
816
|
+
| Skill | Invoke | What it does |
|
|
817
|
+
|---|---|---|
|
|
818
|
+
| `block` | `/block` or "add a block" | Scaffolds a new block: `components/blocks/<name>.tsx`, playground page, `src/index.ts` export, nav wiring, **and the build-skill block catalog** (`.claude/skills/build/references/components-and-tokens.md`) so the consumer `build` skill's Block Matching gate can discover and import it. |
|
|
819
|
+
| `component` | `/component` or "add a component" | Scaffolds a new UI primitive: `components/ui/<name>.tsx`, export, playground, nav wiring, and consumer docs (this README's component table, the design guide, and the build-skill cheatsheet). |
|
|
802
820
|
|
|
803
|
-
|
|
821
|
+
**Other agents:** the `/`-invocation above is Claude Code's. Cursor users invoke the same skills as `/block` and `/component` via this repo's `.cursor/commands/` shims; Kiro users pull them in with `#block` / `#component` via `.kiro/steering/`. Each shim just points the agent at the canonical `.claude/skills/<name>/SKILL.md`, so all agents run the same rules. (These shims live in this repo only — not published to npm, same as the skills themselves.)
|
|
804
822
|
|
|
805
|
-
-
|
|
806
|
-
- `🔵 PLAYGROUND ONLY` — the wrapper with switchers, skeletons, and intersection observers (excluded from the prompt)
|
|
823
|
+
`npm run check:skill-catalog` guards the build-skill catalog these skills update — it fails if a component or block is exported but undocumented (or documented but not exported), so the `build` skill's catalog never drifts from the package's real surface.
|
|
807
824
|
|
|
808
|
-
|
|
825
|
+
### Adding or editing blocks
|
|
809
826
|
|
|
810
|
-
|
|
811
|
-
1. Create `app/components/hero-<name>-page.tsx` with both 🟢 and 🔵 markers
|
|
812
|
-
2. Add an entry to the `BLOCKS` array in `scripts/generate-block-sources.mjs`
|
|
813
|
-
3. Add `<CopyBlockPromptButton blockId="..." />` to the block's playground header row
|
|
827
|
+
Block source files live in `components/blocks/<name>.tsx`, with a matching playground page in `app/components/<name>-page.tsx`. To add a new block, use the **`block`** authoring skill (above) — it scaffolds the block file, playground page, `src/index.ts` export, nav wiring, and the build-skill block catalog in one pass. Run `npm run check:skill-catalog` afterwards to confirm the catalog stays in sync.
|
|
814
828
|
|
|
815
829
|
### Updating design tokens
|
|
816
830
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deriv-ds/design-intelligence-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Deriv Design System — shadcn/ui components with Tailwind CSS v4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"dev": "next dev -H 127.0.0.1 -p 3000",
|
|
27
27
|
"generate:registry": "node scripts/generate-component-registry.mjs",
|
|
28
28
|
"generate:icon-reference": "node scripts/generate-icon-reference.mjs",
|
|
29
|
+
"check:skill-catalog": "node scripts/check-skill-catalog.mjs",
|
|
29
30
|
"build": "tsup",
|
|
30
31
|
"build:next": "next build",
|
|
31
32
|
"start": "next start",
|
|
@@ -45,6 +46,9 @@
|
|
|
45
46
|
"public/assets/logo-deriv-x.png",
|
|
46
47
|
"AGENTS.md",
|
|
47
48
|
"README.md",
|
|
49
|
+
".claude/skills/build/",
|
|
50
|
+
".cursor/commands/build.md",
|
|
51
|
+
".kiro/steering/build.md",
|
|
48
52
|
"CHANGELOG.md"
|
|
49
53
|
],
|
|
50
54
|
"peerDependencies": {
|