@spark-web/design-system 5.1.3 → 5.1.5
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.md +38 -192
- package/package.json +20 -21
- package/patterns/CLAUDE.md +75 -35
- package/patterns/internal-admin/CLAUDE.md +7 -19
- package/patterns/internal-admin/{details-page.md → detail-page.md} +114 -51
- package/patterns/internal-admin/list-page.md +199 -137
- package/patterns/internal-admin/portal-hub.md +165 -0
- package/patterns/vendor-admin/CLAUDE.md +216 -0
- package/patterns/vendor-admin/dashboard.md +681 -0
- package/patterns/vendor-admin/form-page.md +504 -0
- package/patterns/vendor-admin/list-page.md +709 -0
- package/patterns/vendor-admin/vendor-portal.md +309 -0
- package/ai-context/layer-1-root.md +0 -158
- package/ai-context/layer-2-surface-pattern.md +0 -236
- package/ai-context/layer-3-component.md +0 -271
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Vendor admin — vendor-portal consumer overlay
|
|
2
|
+
|
|
3
|
+
This is the consumer overlay for the **vendor-portal** repo, per the Consumer
|
|
4
|
+
overlays convention in
|
|
5
|
+
`node_modules/@spark-web/design-system/patterns/CLAUDE.md`. Apply these
|
|
6
|
+
substitutions only when working in the vendor-portal repo. This file overrides
|
|
7
|
+
pattern files AND component-level docs ONLY where it explicitly says so (each
|
|
8
|
+
heading names what it overrides); every other surface and pattern rule applies
|
|
9
|
+
to vendor-portal unchanged.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Pagination (overrides vendor-admin CLAUDE.md "List loading rule" — pagination control)
|
|
14
|
+
|
|
15
|
+
vendor-portal supplies its own `TablePagination` at
|
|
16
|
+
`src/components/TablePagination.tsx` — the consumer substitution for the surface
|
|
17
|
+
rules' "List loading rule" pagination control
|
|
18
|
+
(`// COMPONENT GAP: TablePagination needed — not yet in Spark`).
|
|
19
|
+
|
|
20
|
+
It wraps `rc-pagination` and renders a `Text` summary
|
|
21
|
+
`Show {dataShowing} of {total} Results` beside the page controls. Props used by
|
|
22
|
+
the patterns:
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Notes |
|
|
25
|
+
| ------------- | -------------------------- | ------------------------------------------------ |
|
|
26
|
+
| `total` | `number` | Total record count — from a count query |
|
|
27
|
+
| `current` | `number` | Current page number |
|
|
28
|
+
| `pageSize` | `number` | Defaults to `DATA_PER_PAGE` (`@/util/constants`) |
|
|
29
|
+
| `dataShowing` | `number` | Number of rows on the current page |
|
|
30
|
+
| `onChange` | `(page, pageSize) => void` | Page change handler |
|
|
31
|
+
|
|
32
|
+
`TablePagination` returns `null` when `total` is falsy — never render an empty
|
|
33
|
+
pagination bar.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Side panel (overrides vendor-admin CLAUDE.md "Detail interaction rule" — side panel implementation)
|
|
38
|
+
|
|
39
|
+
vendor-portal supplies its own `SidePanel` at `src/components/SidePanel.tsx` —
|
|
40
|
+
the consumer substitution for the surface rules' "Detail interaction rule"
|
|
41
|
+
drawer (`// COMPONENT GAP: SidePanel/Drawer needed — not yet in Spark`;
|
|
42
|
+
verified: the only overlay primitive published is `@spark-web/modal-dialog`, a
|
|
43
|
+
centered modal, not a right-anchored drawer).
|
|
44
|
+
|
|
45
|
+
It is an absolute-positioned right slide-in built on `@spark-web/stack` /
|
|
46
|
+
`@spark-web/box`, `90vw` wide on mobile up to `maxWidth: 500px` from tablet,
|
|
47
|
+
offset below the header (`top: 64`), with an optional `heading` + close
|
|
48
|
+
(`XIcon`) header row and a built-in `loading` skeleton state. Props:
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Notes |
|
|
51
|
+
| ----------- | ------------ | --------------------------------------------------------------------------------- |
|
|
52
|
+
| `children` | `ReactNode` | Panel body |
|
|
53
|
+
| `heading` | `string` | Optional — header title (renders header only when `heading` AND `onHide` are set) |
|
|
54
|
+
| `onHide` | `() => void` | Optional — close handler; renders the close button |
|
|
55
|
+
| `loading` | `boolean` | Optional — shows the skeleton placeholder state |
|
|
56
|
+
| `fullWidth` | `boolean` | Optional — removes horizontal body padding |
|
|
57
|
+
| `...props` | `StackProps` | Spread onto the outer `Stack` |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Multi-select filter (overrides list-page.md Section 4 filter usage — substitution note)
|
|
62
|
+
|
|
63
|
+
The canonical vendor-admin choice for a multi-select filter dropdown is
|
|
64
|
+
`@spark-web/multi-select` (verified current export: it exports `MultiSelect`,
|
|
65
|
+
not `MultiSelectField`). vendor-portal currently ships a local
|
|
66
|
+
`MultiselectCheckbox` at `src/components/MultiselectCheckbox.tsx`.
|
|
67
|
+
|
|
68
|
+
**Prefer `MultiSelect` from `@spark-web/multi-select`** for new work — the two
|
|
69
|
+
have effectively the same public API, so the substitution is direct:
|
|
70
|
+
|
|
71
|
+
| Prop | `MultiselectCheckbox` (local) | `@spark-web/multi-select` `MultiSelect` |
|
|
72
|
+
| ---------------- | ------------------------------------------------- | --------------------------------------- |
|
|
73
|
+
| `options` | `Options` (`Array<{ label, options: Option[] }>`) | same grouped shape |
|
|
74
|
+
| `onChange` | `(selected: SelectedOptions) => void` | same — grouped by group label |
|
|
75
|
+
| `placeholder` | `string` (defaults `"Filter By..."`) | same |
|
|
76
|
+
| `defaultOptions` | `DefaultOption[]` (`{ label, value }`) | `Option[]` — same shape |
|
|
77
|
+
|
|
78
|
+
Both group selections into `SelectedOptions` keyed by group label (e.g.
|
|
79
|
+
`{ Months: ["12", "24"] }` in `ProductSettings`). Because the shapes match, swap
|
|
80
|
+
the import and component name with no call-site changes. The local
|
|
81
|
+
`MultiselectCheckbox` is retained only where already wired; do not reach for it
|
|
82
|
+
in new code.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Form fields (overrides @spark-web/field CLAUDE.md "controlled usage" — react-hook-form wrapper)
|
|
87
|
+
|
|
88
|
+
vendor-portal wraps `@spark-web/field` in a local `FormField` exported as
|
|
89
|
+
`Field` (`src/components/FormField.tsx`). It maps a react-hook-form
|
|
90
|
+
`errors[name]` entry to the field's error presentation:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Field } from '@/components/FormField';
|
|
94
|
+
|
|
95
|
+
<Field label="Email" name="email" errors={errors}>
|
|
96
|
+
<TextInput {...register('email')} />
|
|
97
|
+
</Field>;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Wrapper signature: `FieldProps & { name: string; errors?: FieldErrors }`. When
|
|
101
|
+
`errors[name]` is present it surfaces `errors[name].message`; otherwise it falls
|
|
102
|
+
back to a passed `message`. Either way the wrapper sets `tone="critical"` — its
|
|
103
|
+
`(!!errors && errors[name]) || { message }` expression is always truthy. Note: a
|
|
104
|
+
static help `message` with no validation error still renders with critical tone
|
|
105
|
+
— a known quirk in the consumer wrapper. Wire forms with `react-hook-form` and
|
|
106
|
+
Zod schemas via `@hookform/resolvers` (`zodResolver`) — pass the resolver's
|
|
107
|
+
`errors` straight into `Field`. Prefer this `Field` wrapper over hand-wiring
|
|
108
|
+
`@spark-web/field` with manual error props on vendor-portal form pages.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Section / setting wrapper (overrides detail/form section grouping)
|
|
113
|
+
|
|
114
|
+
vendor-portal uses a local `SettingsPanel`
|
|
115
|
+
(`src/components/settings/SettingsPanel.tsx`) for settings sections: a 2-column
|
|
116
|
+
`Columns` layout with a left `heading` (`Heading level="4"`) + `description`
|
|
117
|
+
(`Text`) and a right control slot (`children`).
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { SettingsPanel } from '@/components/settings/SettingsPanel';
|
|
121
|
+
|
|
122
|
+
<SettingsPanel heading="Terms" description="Enable the terms available...">
|
|
123
|
+
<MultiSelect options={termsOptionList} onChange={handleTermUpdates} />
|
|
124
|
+
</SettingsPanel>;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Prop | Type | Notes |
|
|
128
|
+
| ------------- | ----------------------- | ---------------------------- |
|
|
129
|
+
| `heading` | `string` | Required — left-column title |
|
|
130
|
+
| `description` | `string \| JSX.Element` | Required — left-column body |
|
|
131
|
+
| `children` | `ReactNode` | Right-column control slot |
|
|
132
|
+
|
|
133
|
+
**Relationship to `@spark-web/section-card`:** these are NOT interchangeable.
|
|
134
|
+
`SectionCard` is a bordered card with `header` / `footer` / `children` slots
|
|
135
|
+
(compose with `@spark-web/section-header`) — use it for record-detail section
|
|
136
|
+
**cards**. `SettingsPanel` is a label-beside-control settings row with no card
|
|
137
|
+
chrome. Canonically, prefer `@spark-web/section-card` for detail-page section
|
|
138
|
+
cards; keep `SettingsPanel` for the settings-page label/description/control
|
|
139
|
+
layout it was built for. Do not substitute one for the other.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Feedback (overrides @spark-web/alert usage — local FlashMessage)
|
|
144
|
+
|
|
145
|
+
vendor-portal's `FlashMessage` (`src/components/settings/FlashMessage.tsx`) is a
|
|
146
|
+
thin wrapper over `@spark-web/alert`:
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<Alert
|
|
150
|
+
tone={success ? 'positive' : 'critical'}
|
|
151
|
+
onClose={onClose}
|
|
152
|
+
closeLabel="Dismiss"
|
|
153
|
+
>
|
|
154
|
+
{message}
|
|
155
|
+
</Alert>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
It maps `{ success, message, onClose }` to an `Alert` with `tone="positive"`
|
|
159
|
+
(success) or `tone="critical"` (failure). The canonical feedback component is
|
|
160
|
+
`@spark-web/alert` (`node_modules/@spark-web/alert/CLAUDE.md`); `FlashMessage`
|
|
161
|
+
is just the local success/error binding. Prefer `Alert` directly for new
|
|
162
|
+
inline-feedback needs; reuse `FlashMessage` only where the success/error toggle
|
|
163
|
+
is convenient.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Read-only display (overrides detail read-only display — local InfoBox to portal-table)
|
|
168
|
+
|
|
169
|
+
vendor-portal's profile page uses a local `InfoBox` (`src/pages/profile.tsx`)
|
|
170
|
+
that renders an icon + `label` + `value` row for read-only display. This is the
|
|
171
|
+
label/value display the canonical pattern fulfils with `@spark-web/portal-table`
|
|
172
|
+
(`PortalTable`), whose rows are `DescriptionListItem`s (`{ label, value }`,
|
|
173
|
+
where `value` may be a `ReactNode`). Prefer `@spark-web/portal-table` for
|
|
174
|
+
read-only label/value record displays; the local `InfoBox` is retained only for
|
|
175
|
+
its icon-decorated profile layout.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Data conventions (vendor-portal data fetching)
|
|
180
|
+
|
|
181
|
+
vendor-portal is a Next.js Pages-Router app with two data layers:
|
|
182
|
+
|
|
183
|
+
- **Server-side GraphQL** via Apollo Client inside `getServerSideProps`. Create
|
|
184
|
+
the client with `createClient(session.access_token)` from
|
|
185
|
+
`@/lib/apollo-client` and run queries (`@/queries/*`) there; pass results to
|
|
186
|
+
the page as props.
|
|
187
|
+
- **Client-side** via `@tanstack/react-query`. Use `useInfiniteQuery` for
|
|
188
|
+
infinite-scroll lists (e.g. leads) and `useQuery` for bounded reads. Mutations
|
|
189
|
+
live in custom hooks under `src/hooks/**` that wrap `useMutation` (typically
|
|
190
|
+
`fetch`-ing a Next.js API route) and expose `mutate` / `mutateAsync`.
|
|
191
|
+
|
|
192
|
+
These conventions are consumer logic, not a design-system rule — but follow them
|
|
193
|
+
when wiring vendor-portal screens rather than introducing a new data layer.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Legacy / embedded screens (overlay note — NOT a Layer-2 pattern)
|
|
198
|
+
|
|
199
|
+
Some vendor-portal routes do not build a Spark component tree at all:
|
|
200
|
+
|
|
201
|
+
- **Legacy iframe fallback** — `PortalIframe`
|
|
202
|
+
(`src/components/portal-iframe.tsx`) embeds the legacy portal in an
|
|
203
|
+
`<iframe>`. Used (often gated by a LaunchDarkly feature flag) on
|
|
204
|
+
`applications/[id]`, `leads/[id]`, `users`, `users/add`, and as a fallback on
|
|
205
|
+
`applications/index` (e.g. behind `vendorPortalOwnApplicationPage`).
|
|
206
|
+
- **Dynamic micro-frontend embed** — `referrals/add`
|
|
207
|
+
(`src/pages/referrals/add.tsx`) injects a remote `vendor_form` script and
|
|
208
|
+
mounts it into a container `div` (version pinned by a flag).
|
|
209
|
+
|
|
210
|
+
These screens have **no Layer-2 pattern**. Do not try to reconstruct them from a
|
|
211
|
+
pattern file. When working on them, follow the consumer's existing iframe /
|
|
212
|
+
micro-frontend convention (mount container, feature-flag gate, session token
|
|
213
|
+
plumbing) — they are intentionally outside the pattern system.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Dashboard pieces (overrides dashboard.md Sections 1-3 placeholders — consumer substitutions)
|
|
218
|
+
|
|
219
|
+
vendor-portal supplies its own dashboard components in
|
|
220
|
+
`src/components/dashboard/**` and `src/components/**`. These are the consumer
|
|
221
|
+
substitutions for the placeholders the dashboard pattern flags as COMPONENT
|
|
222
|
+
GAPs. Use them in vendor-portal in place of the primitives placeholders; in new
|
|
223
|
+
code outside vendor-portal, the pattern's placeholders remain the stop-gap until
|
|
224
|
+
a Spark component exists.
|
|
225
|
+
|
|
226
|
+
### Metrics — `DashboardMetrics` + `MetricCard` (overrides dashboard.md Section 1)
|
|
227
|
+
|
|
228
|
+
`DashboardMetrics` (`src/components/dashboard/DashboardMetrics.tsx`) renders a
|
|
229
|
+
`@spark-web/columns` `Columns` (`collapseBelow="wide"`) of local `MetricCard`s
|
|
230
|
+
(`src/components/MetricCard.tsx`). This is the consumer substitution for the
|
|
231
|
+
**MetricCard COMPONENT GAP** (dashboard.md Section 1).
|
|
232
|
+
|
|
233
|
+
`MetricCard` props:
|
|
234
|
+
|
|
235
|
+
| Prop | Type | Notes |
|
|
236
|
+
| ---------- | ------------ | -------------------------------------------------- |
|
|
237
|
+
| `header` | `string` | The headline number (the pattern's `value`) |
|
|
238
|
+
| `title` | `string` | The card label (the pattern's `label`) |
|
|
239
|
+
| `subTitle` | `string` | Optional secondary line (the pattern's `subtitle`) |
|
|
240
|
+
| `onClick` | `() => void` | Click-through (the pattern's `onClick`/`href`) |
|
|
241
|
+
|
|
242
|
+
`DashboardMetrics` props: `data` (`TMetricSummary[]`) and
|
|
243
|
+
`onMetricClick(metric)`. Note the local `MetricCard` renders the headline number
|
|
244
|
+
with a raw `style={{ fontSize: '34px' }}` — that inline `fontSize` is a known
|
|
245
|
+
deviation from the pattern (which mandates a `Text` size token); do not copy it
|
|
246
|
+
into new cards.
|
|
247
|
+
|
|
248
|
+
### Resources — `DashboardAds` / `AdCard` + `DocumentsDialog` (overrides dashboard.md Section 2)
|
|
249
|
+
|
|
250
|
+
`DashboardAds` (`src/components/dashboard/DashBoardAds.tsx`) renders a `Columns`
|
|
251
|
+
of local `AdCard`s (`src/components/AdCard.tsx`) plus a `DocumentsDialog`. These
|
|
252
|
+
are the consumer's resource/promo cards — the pattern treats resource cards as
|
|
253
|
+
buildable from primitives (no gap flag), so these are a convenience factoring,
|
|
254
|
+
not a substitution for a missing Spark primitive.
|
|
255
|
+
|
|
256
|
+
`AdCard` props: `mediaSrc`, `mediaAlt`, `cardContent` (`ReactNode`),
|
|
257
|
+
`cardActions` (`ReactNode`). It composes `@spark-web/box` + `@spark-web/text` +
|
|
258
|
+
a `next/image` media region.
|
|
259
|
+
|
|
260
|
+
`DocumentsDialog` (`src/components/dashboard/DocumentsDialog.tsx`) is the
|
|
261
|
+
document-list modal — built **canonically** on `@spark-web/modal-dialog`
|
|
262
|
+
`ContentDialog` (`size="small"`, `showCloseButton={false}`) with a body of
|
|
263
|
+
`next/link` items. Props: `links` (`Array<{ title; link }>`), `isOpen`,
|
|
264
|
+
`onClose`. (Canonically the dashboard pattern uses `@spark-web/text-link`
|
|
265
|
+
`TextLink` for the link rows; the local dialog uses raw `next/link` — prefer
|
|
266
|
+
`TextLink` in new code.)
|
|
267
|
+
|
|
268
|
+
### Announcements feed — `DashboardProductAnnouncements` (overrides dashboard.md Section 3)
|
|
269
|
+
|
|
270
|
+
`DashboardProductAnnouncements`
|
|
271
|
+
(`src/components/dashboard/DashboardProductAnnouncements.tsx`) is the consumer
|
|
272
|
+
substitution for the **AnnouncementsFeed COMPONENT GAP** (dashboard.md Section
|
|
273
|
+
3). It renders the "What's New" timeline (article items built from
|
|
274
|
+
`@spark-web/box as="article"` / `@spark-web/stack` / `@spark-web/text`) and the
|
|
275
|
+
**"Load More"** `@spark-web/button` `Button` (button-triggered incremental
|
|
276
|
+
loading — the third list-loading mode), with a `@tanstack/react-query`
|
|
277
|
+
`useQuery` driving the incremental fetch on the `/dashboard/articles` page.
|
|
278
|
+
Props: `data` (the announcements array) and `apiUrl`. The feed is gated by the
|
|
279
|
+
`newsFeedVendorPortal` LaunchDarkly flag in `src/pages/dashboard/index.tsx`
|
|
280
|
+
(consumer gating). The connector-line / `next/image` chrome is consumer styling
|
|
281
|
+
applied via `@emotion/css` `css(...)` — outside vendor-portal, keep to the
|
|
282
|
+
pattern's semantic placeholder.
|
|
283
|
+
|
|
284
|
+
### Consent modal — `ContentDialog` (no substitution)
|
|
285
|
+
|
|
286
|
+
The first-run consent modal in `src/pages/dashboard/index.tsx` uses
|
|
287
|
+
`@spark-web/modal-dialog` `ContentDialog` **canonically**
|
|
288
|
+
(`showCloseButton={false}`, a `Button tone="primary"` Continue footer that POSTs
|
|
289
|
+
`initialized: true` to `/api/user-profile`, gated on
|
|
290
|
+
`userProfile && !userProfile.initialized`). There is **no consumer
|
|
291
|
+
substitution** — build it exactly as the pattern's Section 4 describes.
|
|
292
|
+
|
|
293
|
+
### Dashboard route gating
|
|
294
|
+
|
|
295
|
+
The whole dashboard is gated by the `vendorPortalOwnDashboard` LaunchDarkly
|
|
296
|
+
flag; when off, `src/pages/dashboard/index.tsx` falls back to a `PortalIframe`
|
|
297
|
+
(`users/dashboard`) — a legacy embed with **no Layer-2 pattern** (see "Legacy /
|
|
298
|
+
embedded screens" above). This flag gating is consumer logic, not a
|
|
299
|
+
design-system rule.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## No `@brighte/ui-components`
|
|
304
|
+
|
|
305
|
+
Unlike the portal-hub overlay, vendor-portal does **NOT** use
|
|
306
|
+
`@brighte/ui-components` — it is not a dependency (verified: absent from
|
|
307
|
+
`package.json`). Do not import from `@brighte/ui-components` in vendor-portal.
|
|
308
|
+
Filter and form fields come from `@spark-web/*` (and the local `Field` /
|
|
309
|
+
`MultiselectCheckbox` wrappers documented above), not from that package.
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# Layer 1 — Root CLAUDE.md
|
|
2
|
-
|
|
3
|
-
## What this layer is
|
|
4
|
-
|
|
5
|
-
The root `CLAUDE.md` sits at the top of the repository. It is the first file an
|
|
6
|
-
AI agent reads in any session. Its job is to orient the agent: what this repo
|
|
7
|
-
is, how it is structured, and — most importantly — **what reading order to
|
|
8
|
-
follow before writing any code**.
|
|
9
|
-
|
|
10
|
-
Think of it as the agent's onboarding document. Without it, an agent has no
|
|
11
|
-
contract to work from and will make assumptions. With it, every build task
|
|
12
|
-
starts from the same controlled entry point.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Where it lives
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
/CLAUDE.md ← root of the repository
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## What it must contain
|
|
25
|
-
|
|
26
|
-
### 1. Repo overview (2–4 sentences)
|
|
27
|
-
|
|
28
|
-
A plain-language description of what the codebase is — the tech stack, the
|
|
29
|
-
package structure, and the general purpose. This gives the agent enough context
|
|
30
|
-
to interpret file paths and package names correctly.
|
|
31
|
-
|
|
32
|
-
```markdown
|
|
33
|
-
## Overview
|
|
34
|
-
|
|
35
|
-
Spark Web is the Brighte Design System — a React component library organized as
|
|
36
|
-
a Yarn monorepo (~50 packages). It uses Preconstruct for bundling, Emotion for
|
|
37
|
-
CSS-in-JS, and Changesets for versioning.
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 2. Common commands
|
|
41
|
-
|
|
42
|
-
Shell commands the agent can run for development, testing, building, and
|
|
43
|
-
linting. These prevent the agent from guessing or inventing commands.
|
|
44
|
-
|
|
45
|
-
```markdown
|
|
46
|
-
## Common Commands
|
|
47
|
-
|
|
48
|
-
yarn test:unit yarn check yarn build:packages
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### 3. The build task reading order — the most critical section
|
|
52
|
-
|
|
53
|
-
This is the mechanism that enforces layered reading. It is a numbered sequence
|
|
54
|
-
of steps the agent **must** follow before writing any code. Each step references
|
|
55
|
-
a specific file or layer. The agent is explicitly told never to skip steps.
|
|
56
|
-
|
|
57
|
-
```markdown
|
|
58
|
-
## How to approach any build task
|
|
59
|
-
|
|
60
|
-
### Step 1 — Classify the surface
|
|
61
|
-
|
|
62
|
-
Read docs/patterns/CLAUDE.md in full. Determine which surface type the task is
|
|
63
|
-
for based on language in the PRD.
|
|
64
|
-
|
|
65
|
-
### Step 2 — Read the surface rules
|
|
66
|
-
|
|
67
|
-
Read the surface rules file for the classified surface.
|
|
68
|
-
|
|
69
|
-
### Step 3 — Identify the feature type and read the pattern file
|
|
70
|
-
|
|
71
|
-
Match the task to a pattern file in the surface folder.
|
|
72
|
-
|
|
73
|
-
### Step 4 — Read the relevant component CLAUDE.md files
|
|
74
|
-
|
|
75
|
-
Only read the components the pattern file tells you to use.
|
|
76
|
-
|
|
77
|
-
### Step 5 — Read the component stories
|
|
78
|
-
|
|
79
|
-
Read the Storybook story file for each component you will use.
|
|
80
|
-
|
|
81
|
-
### Step 6 — Assemble, do not invent
|
|
82
|
-
|
|
83
|
-
Use only components that exist in packages/. Do not build custom components.
|
|
84
|
-
|
|
85
|
-
### Step 7 — Validate before marking complete
|
|
86
|
-
|
|
87
|
-
Run the validation checklist from the pattern file before marking the task
|
|
88
|
-
complete.
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 4. Architecture section
|
|
92
|
-
|
|
93
|
-
A description of the monorepo structure, dependency layers, and key patterns
|
|
94
|
-
(styling, accessibility, TypeScript conventions). This prevents the agent from
|
|
95
|
-
making incorrect assumptions about how components relate to each other.
|
|
96
|
-
|
|
97
|
-
### 5. New packages index
|
|
98
|
-
|
|
99
|
-
A brief entry for each new or in-progress package — its purpose, its
|
|
100
|
-
sub-components, and its key rules. This acts as a registry so the agent knows
|
|
101
|
-
what exists without having to explore the filesystem.
|
|
102
|
-
|
|
103
|
-
```markdown
|
|
104
|
-
## New packages (in progress)
|
|
105
|
-
|
|
106
|
-
### table
|
|
107
|
-
|
|
108
|
-
Composable table component. See packages/table/CLAUDE.md before writing any
|
|
109
|
-
code. Sub-components: Table, TableHeaderRow, TableHeaderCell, TableRow,
|
|
110
|
-
TableCell, TablePagination.
|
|
111
|
-
|
|
112
|
-
### status-badge
|
|
113
|
-
|
|
114
|
-
Pill badge with a colored status dot and text label. See
|
|
115
|
-
packages/status-badge/CLAUDE.md. Tones: positive, caution, critical, neutral,
|
|
116
|
-
pending.
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## How it connects to the other layers
|
|
122
|
-
|
|
123
|
-
The root file does not define _how_ to use any component. It only tells the
|
|
124
|
-
agent **what order to read things in**. Every rule lives in the layer it belongs
|
|
125
|
-
to:
|
|
126
|
-
|
|
127
|
-
| Concern | Lives in |
|
|
128
|
-
| ---------------------------- | -------------------------------------- |
|
|
129
|
-
| Surface classification logic | `docs/patterns/CLAUDE.md` |
|
|
130
|
-
| Surface interaction rules | `docs/patterns/[surface]/CLAUDE.md` |
|
|
131
|
-
| Feature assembly patterns | `docs/patterns/[surface]/[pattern].md` |
|
|
132
|
-
| Component-specific rules | `packages/[component]/CLAUDE.md` |
|
|
133
|
-
|
|
134
|
-
The root file is the table of contents. The other layers are the chapters.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## What happens if this layer is missing or incomplete
|
|
139
|
-
|
|
140
|
-
- The agent skips directly to component documentation and assembles UI without
|
|
141
|
-
understanding which surface it is building for.
|
|
142
|
-
- Surface-level interaction rules (hover states, overflow menus, badge tones)
|
|
143
|
-
are ignored.
|
|
144
|
-
- The agent invents components or custom styles that do not exist in the design
|
|
145
|
-
system.
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Implementation notes
|
|
150
|
-
|
|
151
|
-
- Keep this file short. Its job is orientation and reading-order enforcement,
|
|
152
|
-
not documentation.
|
|
153
|
-
- The build task steps should be imperatives, not suggestions: "Read X before Y.
|
|
154
|
-
Never skip steps."
|
|
155
|
-
- The new packages index should stay in sync with `packages/` — add an entry
|
|
156
|
-
here whenever a new package is scaffolded, even if it is not yet published.
|
|
157
|
-
- Do not duplicate rules here that already live in a lower layer. If a rule
|
|
158
|
-
belongs to a component, keep it in the component's CLAUDE.md.
|