@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.
@@ -0,0 +1,216 @@
1
+ # Vendor admin — interaction and pattern rules
2
+
3
+ These rules apply to all components and patterns built for vendor admin
4
+ surfaces. They sit above individual component rules. When a conflict exists
5
+ between these rules and a component-level CLAUDE.md, these rules take precedence
6
+ for vendor-admin surfaces.
7
+
8
+ Vendor-admin is the surface used by external vendors, installers, and partners
9
+ signing in to manage their own work (leads, applications, settings). It is a
10
+ distinct surface from internal-admin and has different chrome — do not apply
11
+ internal-admin rules here, and do not "correct" vendor-admin choices to match
12
+ internal-admin where they intentionally diverge (see Page title rule below).
13
+
14
+ ---
15
+
16
+ ## Layout shell and content region
17
+
18
+ Vendor-admin screens render inside a fixed application shell:
19
+
20
+ - A **fixed, sticky Header** pinned to the top, full width
21
+ (`position: fixed; top: 0`). It is `72px` tall on mobile and `90px` on tablet
22
+ and up.
23
+ - A **fixed left NavBar sidebar** on tablet and up
24
+ (`position: fixed; left: 0; width: 270px`, full height). On mobile the sidebar
25
+ collapses behind a hamburger (`MenuIcon`) toggle in the Header.
26
+ - A **main content region** that is offset to clear the shell and owns a
27
+ full-height scroll area:
28
+ - top offset `72px` on mobile, `90px` on tablet and up (clears the Header),
29
+ - left offset `0` on mobile, `270px` on tablet and up (clears the sidebar),
30
+ - height `calc(100svh - 90px)` so the region — not the document — scrolls.
31
+
32
+ The shell frame (Header + sidebar + offset content region) is the surface rule:
33
+ every vendor-admin page renders into this region and must not reproduce the
34
+ Header or sidebar itself. The **shell implementation is consumer-owned** — the
35
+ Header, NavBar, and the `_app`-level wrapper are local components, not published
36
+ `@spark-web/*` components. The concrete substitutions (component names, props)
37
+ live in the consumer overlay (a sibling file):
38
+
39
+ `node_modules/@spark-web/design-system/patterns/vendor-admin/vendor-portal.md`
40
+
41
+ The region and offsets above are the rule; the implementation is the overlay's.
42
+
43
+ A page authored for vendor-admin therefore starts at the page-content level (the
44
+ page title row, filters, content) and assumes the shell already exists around
45
+ it.
46
+
47
+ ---
48
+
49
+ ## Page title rule
50
+
51
+ Vendor-admin uses `Heading level="2"` for the page title — **not**
52
+ internal-admin's `PageHeader` / `H1`.
53
+
54
+ This is the clearest divergence from internal-admin and is intentional: the two
55
+ surfaces have different chrome. On vendor-admin the fixed Header already carries
56
+ the product identity and primary navigation, so the in-page title is a section
57
+ heading within the shell, expressed as `<Heading level="2">`. Do not replace it
58
+ with `PageHeader` or promote it to `level="1"`. Within a page, sub-section
59
+ titles step down to `<Heading level="3">`.
60
+
61
+ **Dashboard exception.** The dashboard / overview screen type is a documented
62
+ exception to this single-page-title rule: an overview page has no single
63
+ dominant subject, so it leads with **section-level `Heading level="2"`**
64
+ headings (e.g. "Summary", "Support & resources", "What's New") and **no separate
65
+ page title**. Sub-headings within a dashboard section step down to `level="3"`.
66
+ This exception applies only to the dashboard/overview shell — see
67
+ `node_modules/@spark-web/design-system/patterns/vendor-admin/dashboard.md`. All
68
+ other screen types keep the single `Heading level="2"` page title above.
69
+
70
+ Page-level actions (e.g. an "Export" `Button`) sit on the same row as the title,
71
+ right-aligned, in a flex row that stacks to a column on mobile. A count or limit
72
+ indicator (`Badge`) may sit next to the title on the left.
73
+
74
+ ---
75
+
76
+ ## Spacing and density
77
+
78
+ Vendor-admin pages use a generous rhythm. The observed page-level pattern across
79
+ screens:
80
+
81
+ - List pages wrap their content in a `Stack` with `gap="large"` and
82
+ `margin="xxlarge"` (often `marginBottom="none"` so the scroll region reaches
83
+ the viewport edge), `height="full"`.
84
+ - Form / settings pages center content in a `Container size="medium"` and use a
85
+ `Stack` with `gap="xxlarge"` between sections, with `marginY="xxlarge"`.
86
+ - Filter rows within a list page use `gap="medium"`.
87
+
88
+ Use these spacing tokens — never raw pixel gaps or margins. When in doubt for a
89
+ new vendor-admin page, default to `Stack gap="large"` for the page body and
90
+ `gap="xxlarge"` to separate major form sections.
91
+
92
+ ---
93
+
94
+ ## Detail interaction rule — side panel vs full page
95
+
96
+ Vendor-admin opens a record's detail in a **slide-in side panel**, not by
97
+ navigating to a separate detail page.
98
+
99
+ - The side panel is a right-anchored slide-in within the content region
100
+ (`position: absolute; right: 0`): **~`90vw` on mobile, up to `500px`
101
+ (`maxWidth`) from tablet** (vendor-portal's current panel also offsets below
102
+ the header — see the overlay). It overlays the list rather than reflowing it.
103
+ - Opening a row's detail sets the selected record (typically via a URL query
104
+ param so the panel is deep-linkable) and renders the panel alongside the still
105
+ visible list.
106
+
107
+ **When to use a side panel vs a full page:**
108
+
109
+ - **Side panel** — the default for inspecting / lightly acting on a single
110
+ record while staying in the list context (viewing a lead, an application
111
+ summary, quick status changes). Use it whenever the user benefits from
112
+ remaining anchored to the list and the detail is bounded.
113
+ - **Full page** — reserve for heavyweight, standalone flows that do not belong
114
+ beside a list: multi-step forms, a dedicated settings screen, or a legacy /
115
+ embedded screen. Settings is a full page (`Container`-centered), not a panel.
116
+
117
+ The side panel itself is **consumer-supplied** and is a **COMPONENT GAP**: there
118
+ is no `@spark-web` drawer or side-panel component (verification note: see the
119
+ overlay). Until a Spark drawer exists, mark its use with
120
+ `// COMPONENT GAP: SidePanel/Drawer needed — not yet in Spark` and use the
121
+ consumer's `SidePanel`; see the overlay (a sibling file):
122
+
123
+ `node_modules/@spark-web/design-system/patterns/vendor-admin/vendor-portal.md`
124
+
125
+ ---
126
+
127
+ ## List loading rule — infinite scroll vs pagination
128
+
129
+ Choose the list-loading strategy by the shape of the dataset:
130
+
131
+ - **Infinite scroll** — use for large or streamed datasets where the total is
132
+ unbounded or expensive to count, and the natural interaction is "keep
133
+ scrolling" (e.g. leads). Implement by fetching the next page when the bottom
134
+ of the scroll region is reached and appending rows.
135
+ - **Pagination** — use for bounded result sets where the user benefits from
136
+ jumping to a known page and seeing the total (e.g. applications, with an "X of
137
+ Y results" summary and page controls).
138
+
139
+ Default: if the PRD describes an open-ended, continuously growing list →
140
+ infinite scroll; if it describes a finite, countable list the user pages through
141
+ → pagination.
142
+
143
+ - **Load More** (button-triggered incremental) — a THIRD list-loading mode: a
144
+ "Load More" `Button` fetches and appends the next page on click (the Button's
145
+ `loading` prop bound to the in-flight fetch), rather than auto-fetching on
146
+ scroll (infinite scroll) or exposing page controls (pagination). Use it for a
147
+ short, browse-occasionally feed where the user opts in to more — e.g. the
148
+ dashboard announcements / "What's New" feed (see
149
+ `node_modules/@spark-web/design-system/patterns/vendor-admin/dashboard.md`).
150
+
151
+ The **pagination control is consumer-supplied** and is a **COMPONENT GAP**:
152
+ there is no `@spark-web` pagination component. Mark its use with
153
+ `// COMPONENT GAP: TablePagination needed — not yet in Spark` and use the
154
+ consumer's pagination component; see the overlay.
155
+
156
+ ---
157
+
158
+ ## Bulk actions rule
159
+
160
+ When a list supports selecting multiple rows, selecting one or more rows reveals
161
+ a **bottom-anchored bulk-action toast** pinned to the content region
162
+ (`position: absolute; bottom: 0; left: 0`). It exposes the actions that apply to
163
+ the current selection (e.g. assign, close) and a way to clear the selection.
164
+
165
+ Rules:
166
+
167
+ - The bulk-action toast appears **only when** the list is multi-select **and**
168
+ at least one row is selected; it is hidden when the selection is empty.
169
+ - A bulk-action toast and a record side panel are mutually exclusive in
170
+ practice: show the detail panel only when the selection is a single record,
171
+ and the bulk toast only when one or more rows are selected for a bulk
172
+ operation.
173
+ - Disable the toast's actions while a bulk mutation is in flight.
174
+
175
+ ---
176
+
177
+ ## Badge and pill rules — tone mapping
178
+
179
+ Vendor-admin reuses the internal-admin badge tone mapping verbatim. Do not
180
+ re-derive it here. Read and apply the authoritative mapping at:
181
+
182
+ `node_modules/@spark-web/design-system/patterns/internal-admin/CLAUDE.md` (see
183
+ "Badge and pill rules" / "Badge tone mapping").
184
+
185
+ If a vendor-admin screen has status vocabulary not covered by the internal-admin
186
+ mapping (e.g. lead intents, vendor lead statuses, accreditation states), map
187
+ each new value with that same logic and record the new vocabulary in the
188
+ relevant pattern file rather than redefining tones here.
189
+
190
+ ---
191
+
192
+ ## Role and feature-flag gating
193
+
194
+ Vendor-admin screens and individual nav items / actions may be gated by role
195
+ (e.g. vendor-admin-only screens) or by feature flags. This gating is **consumer
196
+ logic, not a design-system rule** — it lives in the consuming app (the shell's
197
+ nav config, route guards, and flag checks), not in `@spark-web` components or in
198
+ these surface rules.
199
+
200
+ Where it sits: gating wraps the page or nav entry at the shell / routing layer
201
+ and conditionally renders flag-guarded sections. A pattern or component doc
202
+ should never encode a specific role name or flag key. When a PRD specifies that
203
+ a screen is admin-only or behind a flag, treat that as the consumer's
204
+ responsibility and note it; build the screen's component tree per the pattern
205
+ regardless.
206
+
207
+ ---
208
+
209
+ ## Rules that update as the system grows
210
+
211
+ When a new pattern or component is added to the vendor-admin surface, update
212
+ this file with any new interaction rules that apply globally. Do not duplicate
213
+ rules that already exist here in component-level CLAUDE.md files. If a new
214
+ pattern file is added under `patterns/vendor-admin/`, also update the
215
+ feature-type table in `node_modules/@spark-web/design-system/patterns/CLAUDE.md`
216
+ in the same commit.