@spark-web/design-system 5.1.4 → 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.
@@ -1,10 +1,10 @@
1
- # Internal admin — details page pattern
1
+ # Internal admin — detail page pattern
2
2
 
3
3
  ## Before using this pattern
4
4
 
5
- Read node_modules/@spark-web/design-system/patterns/internal-admin/CLAUDE.md
6
- fully before implementing this pattern. The interaction rules, badge tone
7
- mapping, and action dropdown rules defined there all apply to this pattern.
5
+ Surface rules: read
6
+ `node_modules/@spark-web/design-system/patterns/internal-admin/CLAUDE.md` in
7
+ full first its rules all apply here.
8
8
 
9
9
  ## What this pattern is
10
10
 
@@ -15,11 +15,9 @@ header dropdown.
15
15
 
16
16
  ## When to use this pattern
17
17
 
18
- Use this pattern when the PRD describes any of the following:
19
-
20
- - A single-record view reachable by clicking a row on a list page
21
- - A page showing a record's fields, history, or associated sub-records
22
- - The words "detail", "profile", "view", "record page", or "user/vendor page"
18
+ Any single-record detail/profile view the registry
19
+ (`node_modules/@spark-web/design-system/patterns/CLAUDE.md`) owns surface and
20
+ feature-type classification.
23
21
 
24
22
  ---
25
23
 
@@ -27,20 +25,23 @@ Use this pattern when the PRD describes any of the following:
27
25
 
28
26
  Read these before implementing — they own the component-level rules:
29
27
 
30
- - `packages/action-dropdown/CLAUDE.md` — dropdown construction, ordering, hide
31
- vs. disable
32
- - `packages/modal-dialog/CLAUDE.md` — ContentDialog API,
33
- ACCREDITATION_MODAL_CSS, destructive modal anatomy
34
- - `packages/data-table/CLAUDE.md` — DataTable API, loading/empty states,
35
- expandable rows
36
- - `packages/tabs/CLAUDE.md` — Tabs API, internal-admin background override, null
37
- guard
38
- - `packages/section-card/CLAUDE.md` — SectionCard API (note: portal-hub uses a
39
- custom wrapper; see Section 6 below)
40
- - `packages/badge/CLAUDE.md` — status tone mapping
41
- - `packages/columns/CLAUDE.md` — responsive two-column layout
42
- - `packages/box/CLAUDE.md` — flex layout utilities
43
- - `packages/stack/CLAUDE.md` — vertical stacking and gap
28
+ - `node_modules/@spark-web/action-dropdown/CLAUDE.md` — dropdown construction,
29
+ ordering, hide vs. disable
30
+ - `node_modules/@spark-web/modal-dialog/CLAUDE.md` — ContentDialog API, admin
31
+ modal sizing, destructive modal anatomy
32
+ - `node_modules/@spark-web/data-table/CLAUDE.md` — DataTable API, loading/empty
33
+ states, expandable rows
34
+ - `node_modules/@spark-web/tabs/CLAUDE.md` — Tabs API, internal-admin background
35
+ override, null guard
36
+ - `node_modules/@spark-web/section-card/CLAUDE.md` — SectionCard API
37
+ - `node_modules/@spark-web/section-header/CLAUDE.md` SectionHeader API, for
38
+ SectionCard headers
39
+ - `node_modules/@spark-web/badge/CLAUDE.md` — status tone mapping
40
+ - `node_modules/@spark-web/columns/CLAUDE.md` — responsive two-column layout
41
+ - `node_modules/@spark-web/box/CLAUDE.md` — flex layout utilities
42
+ - `node_modules/@spark-web/stack/CLAUDE.md` — vertical stacking and gap
43
+ - `node_modules/@spark-web/heading/CLAUDE.md` — detail-page H1
44
+ - `node_modules/@spark-web/alert/CLAUDE.md` — page-level feedback Alert
44
45
 
45
46
  ---
46
47
 
@@ -55,8 +56,8 @@ Outer wrapper Stack paddingX="xlarge" paddingY="xxlarge" gap="xlarg
55
56
  Left column Stack gap="xlarge" — primary record fields + primary sub-tables
56
57
  Right column Stack gap="xlarge" — secondary/contextual sections
57
58
  SectionCard (per section)
58
- DataTable PAGE_SIZE=5 items; see data-table/CLAUDE.md
59
- TablePagination only when total > PAGE_SIZE
59
+ DataTable PAGE_SIZE=5 items; see node_modules/@spark-web/data-table/CLAUDE.md
60
+ TablePagination consumer-supplied — only when total > PAGE_SIZE
60
61
  ```
61
62
 
62
63
  ---
@@ -92,7 +93,7 @@ All spacing uses Spark tokens. This is distinct from list-page spacing
92
93
  </Box>
93
94
 
94
95
  {actions.length > 0 && (
95
- <Box className={css({ minWidth: 130 })}>
96
+ <Box css={{ minWidth: 130 }}>
96
97
  <ActionDropdown label="Actions" actions={actions} />
97
98
  </Box>
98
99
  )}
@@ -109,7 +110,8 @@ Rules:
109
110
 
110
111
  ## Section 3 — Actions dropdown
111
112
 
112
- See `packages/action-dropdown/CLAUDE.md` for full API and ordering rules.
113
+ See `node_modules/@spark-web/action-dropdown/CLAUDE.md` for full API and
114
+ ordering rules.
113
115
 
114
116
  Page-level decisions:
115
117
 
@@ -141,14 +143,14 @@ State shape: `{ isSuccessful: boolean; message: string } | undefined`
141
143
 
142
144
  Rendered between the header and content columns. Only used for direct inline
143
145
  mutations. Modal confirmations own their own error Alert inside the
144
- `ContentDialog` — see `packages/modal-dialog/CLAUDE.md`.
146
+ `ContentDialog` — see `node_modules/@spark-web/modal-dialog/CLAUDE.md`.
145
147
 
146
148
  ---
147
149
 
148
150
  ## Section 5 — Confirmation modals
149
151
 
150
- See `packages/modal-dialog/CLAUDE.md` for ContentDialog API, sizing,
151
- form-in-modal anatomy, and the full destructive modal pattern.
152
+ See `node_modules/@spark-web/modal-dialog/CLAUDE.md` for ContentDialog API,
153
+ sizing, form-in-modal anatomy, and the full destructive modal pattern.
152
154
 
153
155
  Declare all modals in the component JSX, controlled by a single `openModal`
154
156
  state union:
@@ -190,38 +192,50 @@ Always equal columns (`template={[1, 1]}`), always collapse below desktop.
190
192
 
191
193
  ## Section 7 — Section cards
192
194
 
193
- Each content section is wrapped in a `SectionCard`.
194
-
195
- **Portal-hub uses a custom wrapper** at `@components/PortalTable/SectionCard`
196
- (not `@spark-web/section-card`). The API differs:
195
+ Each content section is wrapped in a `SectionCard` from
196
+ `@spark-web/section-card`, composed with `SectionHeader` from
197
+ `@spark-web/section-header` for the card header:
197
198
 
198
199
  ```tsx
199
- import { SectionCard } from '@components/PortalTable/SectionCard';
200
+ import { SectionCard } from '@spark-web/section-card';
201
+ import { SectionHeader } from '@spark-web/section-header';
200
202
 
201
- <SectionCard label="Section Title">{/* section content */}</SectionCard>;
203
+ <SectionCard header={<SectionHeader label="Section Title" />}>
204
+ {/* section content */}
205
+ </SectionCard>;
202
206
  ```
203
207
 
204
- | Prop | Type | Notes |
205
- | ---------- | ----------- | ------------------------------------- |
206
- | `label` | `string` | Required — card header text |
207
- | `action` | `ReactNode` | Optional — right-side header control |
208
- | `controls` | `ReactNode` | Optional — additional header controls |
208
+ | Prop | Type | Notes |
209
+ | ---------- | ----------- | ------------------------------------------------------- |
210
+ | `children` | `ReactNode` | Required — main content area |
211
+ | `header` | `ReactNode` | Optional — renders above the content; use SectionHeader |
212
+ | `footer` | `ReactNode` | Optional — rendered below a Divider |
213
+
214
+ The header text (`label`), optional `action`, and optional `controls` belong to
215
+ `SectionHeader` — see `node_modules/@spark-web/section-header/CLAUDE.md`.
209
216
 
210
217
  Return `null` for sections conditionally hidden — never render an empty card.
211
218
 
219
+ Working in portal-hub? Apply the substitutions in
220
+ `node_modules/@spark-web/design-system/patterns/internal-admin/portal-hub.md`.
221
+
212
222
  ---
213
223
 
214
224
  ## Section 8 — Section data tables
215
225
 
216
- See `packages/data-table/CLAUDE.md` for DataTable API, column definitions, and
217
- loading/empty state props.
226
+ See `node_modules/@spark-web/data-table/CLAUDE.md` for DataTable API, column
227
+ definitions, and loading/empty state props.
218
228
 
219
229
  Detail-page-specific rules (differ from list pages):
220
230
 
221
231
  - **`PAGE_SIZE = 5`** — always 5 items per section table (not 20 like list
222
232
  pages)
223
233
  - **Pagination threshold**: render `TablePagination` only when
224
- `total > PAGE_SIZE`
234
+ `total > PAGE_SIZE`. `TablePagination` is NOT a spark-web component (COMPONENT
235
+ GAP) — build a placeholder from `@spark-web/box` + `@spark-web/button`
236
+ (prev/next + "Show N of M") and flag it for product design review before
237
+ production. Props: `total`, `pageSize`, `current`, `dataShowing`,
238
+ `onChange(page)`
225
239
  - **Reset page**: reset to 1 when the record context changes (e.g. `userId`)
226
240
 
227
241
  ```tsx
@@ -255,6 +269,7 @@ const pageItems = data?.items ?? [];
255
269
  </Text>
256
270
  }
257
271
  />
272
+ {/* COMPONENT GAP: TablePagination — see the pagination rules above */}
258
273
  {total > PAGE_SIZE && (
259
274
  <TablePagination
260
275
  total={total}
@@ -271,8 +286,9 @@ const pageItems = data?.items ?? [];
271
286
 
272
287
  ## Section 9 — Tabbed sections
273
288
 
274
- See `packages/tabs/CLAUDE.md` for the Tabs API, dynamic tab construction, the
275
- required internal-admin background override, and the null guard pattern.
289
+ See `node_modules/@spark-web/tabs/CLAUDE.md` for the Tabs API, dynamic tab
290
+ construction, the required internal-admin background override, and the null
291
+ guard pattern.
276
292
 
277
293
  Use tabs when a section has multiple sub-views (e.g. Email / SMS history). Each
278
294
  tab panel follows the same PAGE_SIZE=5 and pagination rules as Section 8.
@@ -300,7 +316,7 @@ tab panel follows the same PAGE_SIZE=5 and pagination rules as Section 8.
300
316
  <Badge tone={statusTone}>{statusLabel}</Badge>
301
317
  </Box>
302
318
  {actions.length > 0 && (
303
- <Box className={css({ minWidth: 130 })}>
319
+ <Box css={{ minWidth: 130 }}>
304
320
  <ActionDropdown label="Actions" actions={actions} />
305
321
  </Box>
306
322
  )}
@@ -329,10 +345,14 @@ tab panel follows the same PAGE_SIZE=5 and pagination rules as Section 8.
329
345
  {/* Content */}
330
346
  <Columns gap="xlarge" template={[1, 1]} collapseBelow="desktop">
331
347
  <Stack gap="xlarge">
332
- <SectionCard label="Details">{/* fields */}</SectionCard>
348
+ <SectionCard header={<SectionHeader label="Details" />}>
349
+ {/* fields */}
350
+ </SectionCard>
333
351
  </Stack>
334
352
  <Stack gap="xlarge">
335
- <SectionCard label="History">{/* table + pagination */}</SectionCard>
353
+ <SectionCard header={<SectionHeader label="History" />}>
354
+ {/* table + pagination */}
355
+ </SectionCard>
336
356
  </Stack>
337
357
  </Columns>
338
358
  </Stack>
@@ -340,6 +360,17 @@ tab panel follows the same PAGE_SIZE=5 and pagination rules as Section 8.
340
360
 
341
361
  ---
342
362
 
363
+ ## Documented exceptions summary
364
+
365
+ These raw CSS values are required and have no Spark token equivalent. Use them
366
+ exactly as written.
367
+
368
+ | Value | Property | Reason |
369
+ | --------------- | ---------------------------- | -------------------------------------------------------------------- |
370
+ | `minWidth: 130` | ActionDropdown container Box | Prevents dropdown collapse on narrow content; no Spark minWidth prop |
371
+
372
+ ---
373
+
343
374
  ## Do NOTs
344
375
 
345
376
  - NEVER apply list-page spacing to a detail page — outer wrapper uses
@@ -347,12 +378,44 @@ tab panel follows the same PAGE_SIZE=5 and pagination rules as Section 8.
347
378
  - NEVER place a destructive action before non-destructive actions in the
348
379
  dropdown
349
380
  - NEVER call a destructive mutation directly from a dropdown item — open a modal
350
- - NEVER surface modal errors as page-level Alerts — see `modal-dialog/CLAUDE.md`
381
+ - NEVER surface modal errors as page-level Alerts — see
382
+ `node_modules/@spark-web/modal-dialog/CLAUDE.md`
351
383
  - NEVER use `PAGE_SIZE = 20` on section tables — always 5 on detail pages
352
384
  - NEVER render `TablePagination` when `total <= PAGE_SIZE`
353
385
  - NEVER render an empty `SectionCard` for hidden sections — return `null`
354
386
  - NEVER render `ActionDropdown` when `actions` is empty — gate with
355
387
  `actions.length > 0`
356
388
  - NEVER render `Tabs` inside `SectionCard` without the background override — see
357
- `tabs/CLAUDE.md`
389
+ `node_modules/@spark-web/tabs/CLAUDE.md`
358
390
  - NEVER use `Container` as the outer page wrapper
391
+
392
+ ---
393
+
394
+ ## Validation checklist
395
+
396
+ Run before marking any detail-page task complete and fix every violation; the
397
+ uplift protocol in `node_modules/@spark-web/design-system/CLAUDE.md` runs this
398
+ list PASS/FAIL against existing pages.
399
+
400
+ 1. Outer wrapper is
401
+ `Stack height="full" paddingX="xlarge" paddingY="xxlarge" gap="xlarge"` — not
402
+ list-page spacing, not Container.
403
+ 2. Header: `Heading level="1"` first, `Badge` after it; ActionDropdown only when
404
+ `actions.length > 0`; no inline action buttons in the header.
405
+ 3. Dropdown action order: non-destructive → restore-type → critical destructive
406
+ last; permanently unavailable actions hidden, temporarily unavailable actions
407
+ disabled.
408
+ 4. Destructive actions open a ContentDialog modal; direct actions surface
409
+ feedback via the page-level Alert between header and content — modal errors
410
+ render inside the modal, never as page-level Alerts.
411
+ 5. All modals declared in JSX, controlled by a single `openModal` union state.
412
+ 6. Content is `Columns template={[1, 1]} gap="xlarge" collapseBelow="desktop"`.
413
+ 7. Every content section is wrapped in a SectionCard (or the documented
414
+ consumer-overlay equivalent); conditionally hidden sections return `null`.
415
+ 8. Section tables use `PAGE_SIZE = 5`; pagination only when `total > PAGE_SIZE`;
416
+ page resets when the record context changes.
417
+ 9. Tabs inside SectionCard use the internal-admin background override per
418
+ `node_modules/@spark-web/tabs/CLAUDE.md`.
419
+ 10. No raw CSS beyond the Documented exceptions table; every component is
420
+ `@spark-web/*` or an explicit consumer-overlay substitute; gaps flagged with
421
+ `// COMPONENT GAP:`.