@nadicodeai/design-system 12.1.81 → 12.1.83

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/DESIGN.md CHANGED
@@ -12,12 +12,12 @@ colors:
12
12
  action-hover: "#2e2e2e"
13
13
  action-active: "#000000"
14
14
  ink: "#111111"
15
- body: "#444444"
15
+ body: "#666666"
16
16
  muted: "#6b6b6b"
17
17
  canvas: "#ffffff"
18
18
  canvas-soft: "#fafafa"
19
19
  canvas-soft-2: "#f5f5f5"
20
- material-workspace: "#f7f7f7"
20
+ material-workspace: "#fafafa"
21
21
  material-solid: "#ffffff"
22
22
  material-resting: "#ffffff"
23
23
  material-raised: "#ffffff"
@@ -27,9 +27,9 @@ colors:
27
27
  input: "#757575"
28
28
  focus-ring: "#111111"
29
29
  scrim: "#000000"
30
- link: "#00713d"
31
- link-deep: "#00522c"
32
- link-bg-soft: "#c6efd9"
30
+ link: "#0068d6"
31
+ link-deep: "#0055b3"
32
+ link-bg-soft: "#ebf5ff"
33
33
  info: "#1e3cff"
34
34
  info-soft: "#e8ecff"
35
35
  info-deep: "#0e1d8a"
@@ -75,7 +75,7 @@ colors:
75
75
  dark-material-floating: "#111111"
76
76
  dark-material-modal: "#111111"
77
77
  dark-ink: "#ededed"
78
- dark-body: "#c7c7c7"
78
+ dark-body: "#a1a1a1"
79
79
  dark-muted: "#9e9e9e"
80
80
  dark-line: "#262626"
81
81
  dark-input: "#747474"
@@ -84,9 +84,9 @@ colors:
84
84
  dark-action-foreground: "#0a0a0a"
85
85
  dark-action-hover: "#d6d6d6"
86
86
  dark-action-active: "#c7c7c7"
87
- dark-link: "#4ecf8c"
88
- dark-link-deep: "#93e3b8"
89
- dark-link-bg-soft: "#00351c"
87
+ dark-link: "#52a8ff"
88
+ dark-link-deep: "#8ec8ff"
89
+ dark-link-bg-soft: "#0f223d"
90
90
  dark-focus-ring: "#ededed"
91
91
  dark-scrim: "#000000"
92
92
  dark-error: "#f5a3a1"
@@ -117,68 +117,141 @@ colors:
117
117
  dark-chart-5: "#33d1cb"
118
118
  dark-chart-6: "#9575cd"
119
119
 
120
+ category-teal-soft: "#e0f7f4"
121
+ category-teal-deep: "#08665e"
122
+ dark-category-teal-soft: "#062b28"
123
+ dark-category-teal-deep: "#75e0d5"
124
+ category-purple-soft: "#f3e8ff"
125
+ category-purple-deep: "#7525a7"
126
+ dark-category-purple-soft: "#2b143e"
127
+ dark-category-purple-deep: "#d5a3f7"
128
+ category-pink-soft: "#fce7f1"
129
+ category-pink-deep: "#a32464"
130
+ dark-category-pink-soft: "#381525"
131
+ dark-category-pink-deep: "#f7a3cf"
132
+
120
133
  typography:
121
134
  display-xl:
122
135
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
123
136
  fontSize: 72px
124
- fontWeight: 500
125
- lineHeight: 74px
126
- letterSpacing: -2.52px
137
+ fontWeight: 600
138
+ lineHeight: 72px
139
+ letterSpacing: -4.32px
140
+ display-wide:
141
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
142
+ fontSize: 64px
143
+ fontWeight: 600
144
+ lineHeight: 64px
145
+ letterSpacing: -3.84px
127
146
  display-lg:
128
147
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
129
- fontSize: 52px
130
- fontWeight: 500
131
- lineHeight: 54px
132
- letterSpacing: -1.5px
148
+ fontSize: 56px
149
+ fontWeight: 600
150
+ lineHeight: 56px
151
+ letterSpacing: -3.36px
152
+ display-section:
153
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
154
+ fontSize: 48px
155
+ fontWeight: 600
156
+ lineHeight: 56px
157
+ letterSpacing: -2.88px
133
158
  display-md:
134
159
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
135
160
  fontSize: 40px
136
- fontWeight: 500
137
- lineHeight: 42px
138
- letterSpacing: -1px
161
+ fontWeight: 600
162
+ lineHeight: 48px
163
+ letterSpacing: -2.4px
139
164
  heading-lg:
140
165
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
141
- fontSize: 30px
166
+ fontSize: 32px
142
167
  fontWeight: 600
143
- lineHeight: 32px
144
- letterSpacing: 0px
168
+ lineHeight: 40px
169
+ letterSpacing: -1.28px
145
170
  heading-md:
146
171
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
147
172
  fontSize: 24px
148
173
  fontWeight: 600
149
174
  lineHeight: 32px
150
- letterSpacing: 0px
175
+ letterSpacing: -0.96px
151
176
  heading-sm:
152
177
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
153
178
  fontSize: 20px
154
179
  fontWeight: 600
155
- lineHeight: 28px
180
+ lineHeight: 26px
181
+ letterSpacing: -0.4px
182
+ heading-xs:
183
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
184
+ fontSize: 16px
185
+ fontWeight: 600
186
+ lineHeight: 24px
187
+ letterSpacing: -0.32px
188
+ heading-compact:
189
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
190
+ fontSize: 14px
191
+ fontWeight: 600
192
+ lineHeight: 20px
193
+ letterSpacing: -0.28px
194
+ button-lg:
195
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
196
+ fontSize: 16px
197
+ fontWeight: 500
198
+ lineHeight: 20px
156
199
  letterSpacing: 0px
157
- body-lg:
200
+ button:
201
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
202
+ fontSize: 14px
203
+ fontWeight: 500
204
+ lineHeight: 20px
205
+ letterSpacing: 0px
206
+ button-xs:
207
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
208
+ fontSize: 12px
209
+ fontWeight: 500
210
+ lineHeight: 16px
211
+ letterSpacing: 0px
212
+ label-display:
213
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
214
+ fontSize: 20px
215
+ fontWeight: 400
216
+ lineHeight: 32px
217
+ letterSpacing: 0px
218
+ label-lg:
158
219
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
159
220
  fontSize: 18px
160
221
  fontWeight: 400
161
- lineHeight: 27px
222
+ lineHeight: 20px
162
223
  letterSpacing: 0px
163
- body-md:
224
+ label-md:
164
225
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
165
226
  fontSize: 16px
166
227
  fontWeight: 400
167
- lineHeight: 25px
228
+ lineHeight: 20px
168
229
  letterSpacing: 0px
169
- body-sm:
230
+ label:
170
231
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
171
232
  fontSize: 14px
172
233
  fontWeight: 400
173
234
  lineHeight: 20px
174
235
  letterSpacing: 0px
236
+ label-sm:
237
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
238
+ fontSize: 13px
239
+ fontWeight: 400
240
+ lineHeight: 16px
241
+ letterSpacing: 0px
175
242
  caption:
176
243
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
177
244
  fontSize: 12px
178
245
  fontWeight: 400
179
246
  lineHeight: 16px
180
247
  letterSpacing: 0px
181
- code:
248
+ label-mono-lg:
249
+ fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
250
+ fontSize: 14px
251
+ fontWeight: 400
252
+ lineHeight: 20px
253
+ letterSpacing: 0px
254
+ label-mono-md:
182
255
  fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
183
256
  fontSize: 13px
184
257
  fontWeight: 400
@@ -187,9 +260,51 @@ typography:
187
260
  label-mono:
188
261
  fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
189
262
  fontSize: 12px
190
- fontWeight: 500
263
+ fontWeight: 400
191
264
  lineHeight: 16px
192
265
  letterSpacing: 0px
266
+ body-display:
267
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
268
+ fontSize: 24px
269
+ fontWeight: 400
270
+ lineHeight: 36px
271
+ letterSpacing: 0px
272
+ body-lead:
273
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
274
+ fontSize: 20px
275
+ fontWeight: 400
276
+ lineHeight: 36px
277
+ letterSpacing: 0px
278
+ body-lg:
279
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
280
+ fontSize: 18px
281
+ fontWeight: 400
282
+ lineHeight: 28px
283
+ letterSpacing: 0px
284
+ body-md:
285
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
286
+ fontSize: 16px
287
+ fontWeight: 400
288
+ lineHeight: 24px
289
+ letterSpacing: 0px
290
+ body-sm:
291
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
292
+ fontSize: 14px
293
+ fontWeight: 400
294
+ lineHeight: 20px
295
+ letterSpacing: 0px
296
+ body-compact:
297
+ fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
298
+ fontSize: 13px
299
+ fontWeight: 400
300
+ lineHeight: 18px
301
+ letterSpacing: 0px
302
+ code:
303
+ fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
304
+ fontSize: 13px
305
+ fontWeight: 400
306
+ lineHeight: 18px
307
+ letterSpacing: 0px
193
308
 
194
309
  rounded:
195
310
  none: 0px
@@ -219,8 +334,12 @@ spacing:
219
334
  icon-stroke-sm: 1.25px
220
335
  focus-outline-width: 2px
221
336
  focus-outline-offset: 2px
222
- control-height: 32px
223
- control-padding-inline: 10px
337
+ control-height: 36px
338
+ control-height-sm: 32px
339
+ control-height-lg: 40px
340
+ control-height-xs: 24px
341
+ control-padding-inline: 12px
342
+ card-padding: 20px
224
343
  touch-target: 44px
225
344
 
226
345
  ---
@@ -232,7 +351,7 @@ This file is nadicode's design system and its only design authority. The tokens
232
351
  nadicode puts AI Agents to work inside small and mid-sized companies. Its sites and products read as a technology company with a real product: the interface is neutral and exact, the product is shown working, and colour and depth give the page its energy. Three ideas carry every screen:
233
352
 
234
353
  - **Neutral structure.** Chrome, controls and text sit on an achromatic spine, so a desaturated screen loses nothing structural. The strongest contrast on a screen belongs to the action.
235
- - **Colour as stage.** The five identity hues appear at full strength only in the places "Colour as stage" lists, the colour of Agents among them, and never as a section's ground. They carry the brand; they never decorate a control.
354
+ - **Colour as stage.** The five identity hues appear at full strength only in the places "Colour as stage" lists, the colour of Agents among them, and never as a section's ground. They carry the brand; they never decorate a control. Semantic colour identifies statuses, categories and named actions through the component palette below.
236
355
  - **Real depth.** Surfaces sit at four heights. A 1 px edge shows the height in both modes, with a faint shadow under it. Glass floats over content that scrolls or moves under it.
237
356
 
238
357
  The products are still: only Agents move on their own. The website moves with purpose: the product at work, Agents and its few shader objects ("Motion").
@@ -246,7 +365,7 @@ One foundation serves four registers. The foundation is every value in this file
246
365
  The neutral spine carries grounds, chrome, text and every control: `{colors.ink}`, `{colors.body}`, `{colors.muted}`, `{colors.canvas}`, `{colors.canvas-soft}`, `{colors.canvas-soft-2}`, `{colors.line}`, the material tones, and their dark pairs. Every neutral is an equal-channel grey (OKLCH chroma 0) in both themes, so no tint paints a surface. Colours are authored here as sRGB hex and emitted as `oklch()`.
247
366
 
248
367
  - **Action** (`{colors.action}`, `{colors.action-foreground}`, `{colors.action-hover}`, `{colors.action-active}`): the primary control is ink with a white label in light, and dark ink `{colors.dark-action}` with a near-black `{colors.dark-action-foreground}` label in dark, never pure white. The shadcn `primary` role maps here.
249
- - **Text**: `{colors.ink}` for headings and primary text, `{colors.body}` for supporting text, which clears AA on every canvas. `{colors.muted}` is for placeholders and disabled or inactive text only, never for descriptions, labels or help.
368
+ - **Text**: `{colors.ink}` for headings and primary text, `{colors.body}` for supporting text, which clears AA on every canvas. `{colors.muted}` is for placeholders and disabled or inactive text only, never for descriptions, labels or help. These roles distinguish purpose, not a guaranteed contrast step: their similar neutrals keep supporting text and placeholders near the Geist reference while both meet our AA text floor.
250
369
  - **Lines**: `{colors.line}` is the one decorative 1 px divider and is deliberately quiet. `{colors.input}` is the boundary of a field or any control whose edge identifies it (an input, a select, a checkbox, a radio), and clears the 3:1 non-text floor. A button is identified by its label, so an outline or secondary button's edge is the `{colors.line}` hairline, never `{colors.input}`. Never darken a decorative line locally to make it act as a control edge.
251
370
  - **Focus** (`{colors.focus-ring}`): a full-opacity outline of `{spacing.focus-outline-width}` at `{spacing.focus-outline-offset}`, the maximum-contrast neutral on each ground, never a box-shadow ring and never a hue.
252
371
  - **Interaction states** are contrast steps on the spine. Hover takes the `accent` role (`{colors.canvas-soft-2}`) and a selected row the `muted` role (`{colors.canvas-soft}`); a control's own hover is a step inside its family, as `{colors.action-hover}` is. Persistent navigation marks the current destination with `{colors.navigation-selection-bg}` and `{colors.navigation-selection-fg}`, and local tabs mark the current view with the same pair through the `selection` role.
@@ -266,15 +385,19 @@ The hues appear at full strength and flat, with no tints, steps, gradients or ho
266
385
  - **The logo.** The dot of the wordmark and the icon tile ("Brand media authority").
267
386
  - **Imagery.** Art direction reads these tokens and keeps no private palette.
268
387
 
269
- Text on a colour ground is the pole that clears AA on that hue: `{colors.identity-white}` on cobalto and rosso; `{colors.identity-ink}` on verde, arancio and giallo. A control on a colour ground uses the button's on-colour variants ("Components"). Product chrome in the Portal and Nadia stays neutral: the identity hues enter a product screen only as Agents, charts and the feedback roles below.
388
+ Text on a colour ground is the pole that clears AA on that hue: `{colors.identity-white}` on cobalto and rosso; `{colors.identity-ink}` on verde, arancio and giallo. A control on a colour ground uses the button's on-colour variants ("Components"). Product chrome in the Portal and Nadia stays neutral. Colour inside that structure identifies Agents, providers, categories, links and feedback. Choose a typed component tone from the semantic palette; never paint a control with an arbitrary identity hex.
270
389
 
271
390
  ### Meaning
272
391
 
273
392
  - **Feedback** pairs a hue with a soft ground and a deep text step: success (verde), warning (giallo `{colors.warning}`), error (rosso `{colors.error}`), info (cobalto `{colors.info}`). The `-soft` value is the tinted ground of a tag or banner and the `-deep` value is the AA text on it; a component reaches the pair through the `destructive-soft`, `warning-soft`, `success-soft` and `info-soft` roles and their foregrounds. On a solid fill, error takes white text and warning and success take ink in light; in dark all three take `{colors.dark-on-primary}`.
274
- - **Link** (`{colors.link}`, `{colors.link-deep}`, `{colors.link-bg-soft}`): inline links and the green status tone. Text green is always the link green; verde as a mark is always identity verde.
275
- - **Workflow states** (`{colors.state-ready}`, `{colors.state-running}`, `{colors.state-review}`, `{colors.state-blocked}`, `{colors.state-complete}`) colour non-text marks only: meter fills and trend icons. Cobalto means in motion and verde means done. A state's word stays on the neutral text roles.
393
+ - **Link** (`{colors.link}`, `{colors.link-deep}`, `{colors.link-bg-soft}`): blue inline links. Success uses its own green roles; link colour never doubles as a health signal.
394
+ - **Workflow states** (`{colors.state-ready}`, `{colors.state-running}`, `{colors.state-review}`, `{colors.state-blocked}`, `{colors.state-complete}`) colour non-text marks only: meter fills and trend icons. Cobalto means in motion and verde means done. A state's word uses its `StatusBadge` foreground pair; never put a raw meter-fill token on text.
276
395
  - **Status** is always a Badge tone with a mark and a word ("Components").
277
396
 
397
+ ### Category colour
398
+
399
+ Labels and their icons may carry blue, teal, purple or pink to distinguish named categories. `Tag` owns these categorical tones; `Badge` also offers the corresponding soft fill and contrasting foreground. The same category keeps the same tone across a surface and after sorting. A word remains visible, and a colour never invents status. Green, amber and red remain feedback meanings; the five-tone `StatusBadge` owns those. Icons inherit the tone of their label rather than adding a second colour. Brand marks keep their genuine colours. This uses Geist’s light fill / strong text technique with nadicode’s semantic roles; agents never choose raw palette values in pages.
400
+
278
401
  ### Dark mode
279
402
 
280
403
  Every token `X` with a `dark-X` pair takes the dark value under a `.dark` ancestor; a component uses the same role in both modes and never ships a `dark:` utility or a mode branch of its own. The dark page is true black, `{colors.dark-canvas}`. Elevated surfaces lift from it by tone ("Elevation & Depth"); the soft steps `{colors.dark-canvas-soft}` and `{colors.dark-canvas-soft-2}` are interaction fills and never become a page, section or panel ground. On a lifted surface the hover and muted fills step up from that surface's own tone. Dark feedback and chart values are brightened so they read on black; `{colors.dark-input}` stays brighter than `{colors.dark-line}` so a field's edge clears 3:1. A surface that is dark by nature, such as Nadia's entry screen, a terminal console, a black website section or a black brand plane inside a product (`BrandField`), stays dark in every theme as one fixed treatment: it scopes `.dark`, so every role and control inside it takes its dark value.
@@ -312,24 +435,41 @@ A hue follows its entity, never its rank or position: one assignment per surface
312
435
 
313
436
  ## Typography
314
437
 
315
- Geist and Geist Mono are the two families. The twelve tokens are named by role, never by size; strong is a weight, so a 500 run of body copy is `body-md` at weight 500. `code` and `label-mono` are Geist Mono; every other token is Geist.
438
+ Geist and Geist Mono are the two families. The role scale follows the measured [Geist typography](https://vercel.com/geist/typography): headings, buttons, single-line labels and multiline copy each have their own line height and weight. The `label-mono` family and `code` use Geist Mono. Fonts ship with the package and their SIL Open Font License; no network font fetch is required.
316
439
 
317
- | Token | Use |
318
- | --- | --- |
319
- | `{typography.display-xl}` | The largest page title. |
320
- | `{typography.display-lg}` | A hero headline and the one statement of a page. |
321
- | `{typography.display-md}` | A section heading. |
322
- | `{typography.heading-lg}` | A standard section title inside a page or panel. |
323
- | `{typography.heading-md}` | A card, artifact or panel title. |
324
- | `{typography.heading-sm}` | The smallest heading, above a group or a step. |
325
- | `{typography.body-lg}` | A lead paragraph. |
326
- | `{typography.body-md}` | Default body copy; large control labels at 500. |
327
- | `{typography.body-sm}` | Secondary copy, navigation, compact cells; control labels at 500. |
328
- | `{typography.caption}` | Badges, compact chrome labels, footer lines. |
329
- | `{typography.code}` | Tool calls, snippets, traces. |
330
- | `{typography.label-mono}` | Opaque identifiers, codes and mono eyebrows; never a person's, Agent's or computer's display name. |
331
-
332
- A token has one size at every width; nothing is sized in `vw` or `vh`. Only the three display tokens carry negative tracking. Card, panel and section titles are sentence-case Geist. The website and the CSS components name the tokens by role, through the `nc-type-*` classes or the theme's `text-<token>` utilities (`text-display-md`); product UI in `@nadicodeai/ui`, the Portal and Nadia sizes text with Tailwind's `text-xs`, `text-sm` and `text-base`, which match `caption`, `body-sm` and `body-md` in size.
440
+ | Token | Size / line height | Use |
441
+ | --- | --- | --- |
442
+ | `{typography.display-xl}` | 72 / 72 px | Largest display heading. |
443
+ | `{typography.display-wide}` | 64 / 64 px | Wide display heading. |
444
+ | `{typography.display-lg}` | 56 / 56 px | Hero headline. |
445
+ | `{typography.display-section}` | 48 / 56 px | Large section heading. |
446
+ | `{typography.display-md}` | 40 / 48 px | Section headline. |
447
+ | `{typography.heading-lg}` | 32 / 40 px | Large product section. |
448
+ | `{typography.heading-md}` | 24 / 32 px | Page or object title. |
449
+ | `{typography.heading-sm}` | 20 / 26 px | Card, settings section or dialog title. |
450
+ | `{typography.heading-xs}` | 16 / 24 px | Compact card or group title. |
451
+ | `{typography.heading-compact}` | 14 / 20 px | Dense subsection title. |
452
+ | `{typography.button-lg}` | 16 / 20 px | Large button label. |
453
+ | `{typography.button}` | 14 / 20 px | Default and small button label. |
454
+ | `{typography.button-xs}` | 12 / 16 px | Embedded button label only. |
455
+ | `{typography.label-display}` | 20 / 32 px | Large single-line label. |
456
+ | `{typography.label-lg}` | 18 / 20 px | Prominent single-line label. |
457
+ | `{typography.label-md}` | 16 / 20 px | Object or item label. |
458
+ | `{typography.label}` | 14 / 20 px | Menu, navigation and field label. |
459
+ | `{typography.label-sm}` | 13 / 16 px | Secondary metadata label. |
460
+ | `{typography.caption}` | 12 / 16 px | Tertiary label or badge. |
461
+ | `{typography.label-mono-lg}` | 14 / 20 px | Mono beside large labels. |
462
+ | `{typography.label-mono-md}` | 13 / 20 px | Mono beside 14 px labels. |
463
+ | `{typography.label-mono}` | 12 / 16 px | Compact identifier. |
464
+ | `{typography.body-display}` | 24 / 36 px | Large marketing lead. |
465
+ | `{typography.body-lead}` | 20 / 36 px | Marketing lead. |
466
+ | `{typography.body-lg}` | 18 / 28 px | Long lead or quotation. |
467
+ | `{typography.body-md}` | 16 / 24 px | Dialog copy and entry instructions. |
468
+ | `{typography.body-sm}` | 14 / 20 px | Default product description. |
469
+ | `{typography.body-compact}` | 13 / 18 px | Dense secondary copy. |
470
+ | `{typography.code}` | 13 / 18 px | Inline code or code block. |
471
+
472
+ A token has one size at every width; nothing is sized in `vw` or `vh`. A role includes family, size, line height, weight and tracking together. Use the shared `nc-type-*` classes or the generated `text-<token>` utilities, such as `text-display-md`, with `font-label-mono` for the `label-mono` family and `font-code` for `code`, rather than assembling role metrics at a call site. Existing `text-xs`, `text-sm` and `text-base` body sizes match `caption`, `body-sm` and `body-md`. Card, panel and section titles are sentence-case Geist. Headings use weight 600 and their optical negative tracking. A softer run inside a heading uses 500; a strong run inside a label uses 500; strong paragraph copy uses 550. Labels align single lines and icons; paragraphs use the copy roles and can wrap. Numeric comparisons use tabular digits, and identifiers use mono. Semantic heading levels express document structure independently of visual size. Never shrink type to fit: wrap, reflow or move supporting detail.
333
473
 
334
474
  ### The website's text
335
475
 
@@ -367,7 +507,7 @@ Control geometry is the same in every product:
367
507
  | `{spacing.icon-stroke}`, `{spacing.icon-stroke-sm}` | The icon line ("Iconography") |
368
508
  | `{spacing.focus-outline-width}`, `{spacing.focus-outline-offset}` | The focus outline |
369
509
 
370
- Product chrome uses the 32 px control. A page header, a toolbar, a card's header and footer, a tab row, a filter row and a collection row's actions take `{spacing.control-height}` with a `{typography.body-sm}` label at 500; a button's `sm` and `xs` sizes are for a control nested inside dense content, such as a composer or an input group, and never for chrome.
510
+ Product chrome uses the 36 px control. Compact controls use 32 px and prominent controls 40 px; 24 px is reserved for an embedded control inside a field. A page header, a toolbar, a card's header and footer, a tab row, a filter row and a collection row's actions take `{spacing.control-height}` with a `{typography.body-sm}` label at 500; a button's `sm` and `xs` sizes are for a control nested inside dense content, such as a composer or an input group, and never for chrome.
371
511
 
372
512
  ### The website column
373
513
 
@@ -381,7 +521,7 @@ The Portal and Nadia are product shells: persistent navigation on the left, the
381
521
 
382
522
  - **Navigation names stable destinations.** An object opened from search or a link still shows its parent in a breadcrumb; local tabs directly below the top bar ask different questions about the same object, and they are always the package's Tabs ("Controls"); a return link remembers a visit and never replaces the parent. A scope selector keeps the current tab when the next object has it, and otherwise opens its overview.
383
523
  - **Compact screens keep the same relationships.** Tabs scroll horizontally, primary content stacks before supporting facts, and no second sidebar appears.
384
- - **The desktop window has one top band.** In Nadia, the window's header row and the left column's top band are one 44 px band that holds the 32 px chrome controls and centres the macOS window buttons; the Portal keeps its own top bar.
524
+ - **The desktop window has one top band.** In Nadia, the window's header row and the left column's top band are one 44 px band that holds the shared chrome controls and centres the macOS window buttons; the Portal keeps its own top bar.
385
525
 
386
526
  ### Page compositions
387
527
 
@@ -392,22 +532,22 @@ A product page is one of five compositions, each answering one question first:
392
532
  | Overview | What needs attention? | Scoped facts, the exceptions that matter, links to their owners. Never a stack of previews of every section. |
393
533
  | Collection | Which object? | The page header (its title with the page's actions on the title's row, and nothing under it), then one toolbar (filters, search, sort), one line per record with its identity and a few comparable facts, and the row's menu. One row is one hover and one open target. |
394
534
  | Object | What is this, where does it belong, what can I do? | The parent in the breadcrumb and its sections (tabs, or the sidebar for an object worked inside); then the object's header on the page ground, never in a card: its picture, its name at `{typography.heading-md}`, one line saying what it is, a row of key facts as label and value, and at the trailing edge the page's one primary action and the three dots; then grouped facts and settings sections as the cards below. An Agent is the exception: her first section, Profile, opens with her card, the same in every product (`AgentProfileCard`): on the left her orb at 64 px, her name with a ghost pencil that turns her name, description and, where the product holds it, her look into fields in place, saved by the card's own Save, with no drawer or overlay, her description and her facts (status, last activity, the template she was made from); on the right her model, drawn with its maker's mark, her reasoning and, where the product holds them, her permissions, saved from the card's footer; her instructions follow as the next card. The page's commands sit in the window's three dots where the product has one, and otherwise at the end of her name's row. |
395
- | Configuration | What can I change? | A centred reading column of titled sections, help beside each field, a local save and recovery. |
535
+ | Configuration | What can I change? | A centred configuration column of titled sections, labels and help above each control, a local save and recovery. |
396
536
  | Library | What reusable thing can I use? | The shelf's title with its one definition line, the search, then the entries as two-column rows: each its kind's tile, its name, one line of description and its add control; detail shows content, requirements, source and revision. |
397
537
 
398
- Every setting is that Configuration composition, whether it belongs to the person, to the platform or to one record's Settings tab, so the column keeps its width and its field layout from one entry of a settings menu to the next, and each section saves its own fields from its own footer ("Cards and sections"). A table that reaches a reading column keeps the identity and the action beside it and lets its remaining columns fall to the row's supporting detail, rather than widening the page or scrolling sideways.
538
+ Every setting is that Configuration composition, whether it belongs to the person, to the platform or to one record's Settings tab, so the column keeps its width and its field layout from one entry of a settings menu to the next, and each section saves its own fields from its own footer ("Cards and sections"). The Portal configuration column is at most 56rem. Inputs are bounded at 20rem under their labels and descriptions, and related fields may share two columns when the section can accommodate them. Section headings use heading-sm. The header and fields share one uninterrupted body; only the local save footer has a divider. A profile avatar is the upload button in the section header. Structured tables keep visible column names and remain tabular while their container can accommodate the data; only compact containers move supporting facts beneath the identity.
399
539
 
400
- Every page in every product opens with the same header (`PageHeader`). The top bar holds only the path to the page: its sidebar group or its parents, ending at the destination or the object, such as "Business › Agents" or "Beta Studio › Beta Desk", and never the page's own title. The content then starts with the title at `{typography.heading-md}`, on the left, and on the same row at the trailing edge the page's actions: the page's three dots when it has two further commands or more, then at most one `outline` button, then the one `default` primary, rightmost. `PageActions` is their only owner: it takes a `primary`, a `secondary` and a `menu` of two commands or more, and draws the order and the variants, so a page never passes buttons of its own. Nothing sits under the title: no line introduces, explains or counts the page, and the toolbar, the list or the page follows the title row directly. The one exception is a catalog shelf of the Library (Skills, Plugins): one line under its title says what kind of thing the shelf holds, because a skill and a plugin are drawn alike and that line is what tells the two shelves apart; it never counts or introduces anything else. Inside an object worked from a section sidebar, the title is the section's name ("Apps"); a page that opens with the object's own header or card, such as an Agent's Profile (`AgentProfileCard`), has it as its title and no title row above it.
540
+ Collections and working pages open with the same header (`PageHeader`). Configuration pages open directly with their titled settings sections and retain a screen-reader h1 for the destination; an extra visible title would duplicate the navigation and delay the first setting. The top bar holds only the path to the page: its sidebar group or its parents, ending at the destination or the object, such as "Business › Agents" or "Beta Studio › Beta Desk", and never the page's own title. The content then starts with the title at `{typography.heading-md}`, on the left, and on the same row at the trailing edge the page's actions: at most one `outline` secondary action, then the `default` primary action, then the overflow menu at the trailing edge. `PageActions` is their only owner: it takes a `primary`, a `secondary` and a `menu`, and draws their order and variants. An existing record-command menu can occupy that same menu slot without rebuilding its confirmation logic. Navigation remains a visible link or part of the navigation, never an overflow command. Nothing sits under the title: no line introduces, explains or counts the page, and the toolbar, the list or the page follows the title row directly. The one exception is a catalog shelf of the Library (Skills, Plugins): one line under its title says what kind of thing the shelf holds, because the shelf explains how its kind of item is used; it never counts or introduces anything else. Inside an object worked from a section sidebar, the title is the section's name ("Apps"); a page that opens with the object's own header or card, such as an Agent's Profile (`AgentProfileCard`), has it as its title and no title row above it.
401
541
 
402
- A collection is named once, by the page's title, and the collection's card never adds a header that only describes it. A populated collection carries no picture; a picture belongs only to a collection's first use ("Elevation & Depth"). The table's footer holds only a count or the paging, never a help sentence: guidance a step needs lives in that step.
542
+ A collection is named once, by the page's title, and the collection's card never adds a header that only describes it. A populated collection carries no picture; a product that uses illustrated presentation may show one only for a collection's first use ("Elevation & Depth"). The table's footer holds only a count or the paging, never a help sentence: guidance a step needs lives in that step.
403
543
 
404
- A section earns a card when it owns a decision, not for every label and value. A row links to its object and keeps its commands in its menu ("Menus and commands"). A summary on an overview opens the same owner as its tab. Supporting detail collapses only behind a summary that keeps a count, state or consequence.
544
+ A section earns a card when it owns a decision, not for every label and value. A row links to its object and keeps secondary mutations in its menu ("Menus and commands"). A summary on an overview opens the same owner as its tab. Supporting detail collapses only behind a summary that keeps a count, state or consequence.
405
545
 
406
546
  ## Elevation & Depth
407
547
 
408
548
  Product UI sits on one continuous workspace ground, `{colors.material-workspace}` (true black in dark), and solid objects rise above it at four heights: `nc-elevation-resting` for resting content, then `nc-elevation-raised`, `nc-elevation-floating` and `nc-elevation-modal` when an object is physically raised, floating or modal. Physical role chooses the height, never the importance of the content. Each recipe carries its own edge, so an elevated surface never adds a border, a ring or a second shadow.
409
549
 
410
- **The edge carries height, the same way in both modes.** Each recipe is a 1 px ring around the surface and a faint black shadow under it; the ring is black at 8% on light and white at 14.5% on dark, and it is the last layer. Light and dark share the shadow geometry: a 2 px contact shadow for resting, a short drop added for raised, and one soft shadow of 24 px blur for floating and 32 px for modal, at no more than 6% in light. Shadows stay black: a tinted or wide shadow makes a card float off the page and parts light from dark.
550
+ **The edge carries height, the same way in both modes.** Each recipe is a 1 px ring around the surface and a faint black shadow under it; the ring is black at 8% on light and white at 14.5% on dark, and it is the first layer. A final opaque backing edge prevents the shadows beneath from darkening that optical ring. Light and dark share the shadow geometry: a 2 px contact shadow for resting, a short drop added for raised, and one soft shadow of 24 px blur for floating and 32 px for modal, at no more than 6% in light. Shadows stay black: a tinted or wide shadow makes a card float off the page and parts light from dark.
411
551
 
412
552
  **In dark, every surface stays near black.** The page is `{colors.dark-canvas}`; a surface is a few points above it, so the ring, not a grey fill, separates it: resting `{colors.dark-material-resting}`, raised `{colors.dark-material-raised}`, floating `{colors.dark-material-floating}`, modal `{colors.dark-material-modal}`. The design-system checks fail a height darker than the one below it or past 1.15:1 against the page, and a dark ring that does not clear 1.5:1 against the page over the resting tone. `{colors.dark-ink}` and `{colors.dark-muted}` keep the WCAG AA text floor on the top step. Hover and selected fills step up from the surface they sit on. A component names only its height and its role (`bg-card`, `bg-popover`); `@nadicodeai/ui/globals.css` sets those roles to the height's tone on the element.
413
553
 
@@ -421,19 +561,21 @@ Product UI sits on one continuous workspace ground, `{colors.material-workspace}
421
561
  | Outlined | `{colors.material-solid}` | None; one `line` border | A flat panel: fact cells inside a record, the website's explanatory cards. |
422
562
  | Plain | `{colors.material-solid}` | None | A structural card with no edge of its own. |
423
563
 
424
- **Glass** is a material, not a height: the floating fill at 60% over a 20 px backdrop blur at 180% saturation, so colour behind it comes through saturated rather than grey. Its edge is the floating recipe, its 1 px ring and faint shadow, and a 5% monochrome grain keeps the blurred colour from banding. Glass may go where something behind it moves or carries colour:
564
+ **Glass** is a material, not a height: the floating fill at 60% over a 20 px backdrop blur at 180% saturation, so colour behind it comes through saturated rather than grey. Its edge uses the floating recipe plus the tokenized inset top highlight, and a 5% monochrome grain keeps the blurred colour from banding. Glass may go where something behind it moves or carries colour:
425
565
 
426
566
  - floating chrome over our own content: popovers, selects, comboboxes, the menubar, navigation popups, the command palette and control bars, including a collection's toolbar over the rows that scroll under it (on a page whose rows never pass under it, the toolbar is the plain row, with no fill or edge of its own) and Nadia's chat bar over her conversation;
427
567
  - the website header as the page scrolls under it;
428
568
  - a small floating card over moving Agents.
429
569
 
430
- Content stays solid: cards, KPIs, tables, chat, forms, and dialog, sheet and drawer bodies. Menus stay solid: a dropdown menu and a context menu open over dense rows their labels would compete with, and one set of commands reached both ways looks the same both ways. Glass over a plain white or black ground shows nothing, so it never goes there. Reduced transparency replaces glass with the same solid surface at the same height.
570
+ Content stays solid: cards, KPIs, tables, chat, forms, and dialog, sheet and drawer bodies. Menus stay solid: a dropdown menu and a context menu open over dense rows their labels would compete with, and one set of commands reached both ways looks the same both ways. Glass over a plain white or black ground shows nothing, so it never goes there. Reduced transparency and browsers without backdrop-filter replace glass with the same solid surface at the same height. Transparency never reduces the opacity of labels or controls.
571
+
572
+ Related membership collections share one tabbed workspace: Members, Invitations and, where supported, Teams. One collection is visible at a time and its selection is retained in the URL.
431
573
 
432
574
  **Product views on the website** are the product itself, built from the real components with example data that is labelled as such; never a screenshot, a device mockup or an invented interface. A product window takes the raised height on its ground; a card that overlaps it floats; in a black section it renders in dark mode.
433
575
 
434
- Absence follows its container. A page or region with nothing in it is an opaque region with a dashed boundary; an empty state inside a card is unframed, because the card already owns the edge. `EmptyState` (`@nadicodeai/ui/components/empty-state`) is the only owner of an absence: nothing else draws one, and its `scope` (`page`, `region` or `card`) sets that frame. A collection's `DataTable` takes the same props as `empty`. Its kind decides everything it shows:
576
+ Absence follows its container. The Portal uses plain operational empty states throughout: no collection illustrations, a solid boundary for a page or region, and the same spacing and typography for first use, no data, and no results. Other products may use the illustrated register with a dashed boundary. In every product, an empty state inside a card is unframed, because the card already owns the edge. `EmptyState` (`@nadicodeai/ui/components/empty-state`) is the only owner of an absence: nothing else draws one, and its `scope` (`page`, `region` or `card`) sets that frame. A collection's `DataTable` takes the same props as `empty`. Its kind determines the message and recovery; presentation determines whether first-use artwork is present:
435
577
 
436
- - `firstUse`: a collection with no records yet whose kind has a picture in the approved set in `@nadicodeai/design-system/assets/empty`, whoever the viewer is and however the collection fills. The picture, a title and one line saying what will live there, and, when this viewer can add the first record, the action that adds it.
578
+ - `firstUse`: a collection with no records yet, whoever the viewer is and however the collection fills. Illustrated presentation takes its picture from the approved set in `@nadicodeai/design-system/assets/empty`. A title and one line saying what will live there, the picture when the product uses illustrated presentation, and, when this viewer can add the first record, the action that adds it.
437
579
  - `nothingYet`: a list whose kind has no picture of its own, such as invoices or history, or a table inside a page. One line, with no picture and nothing to press.
438
580
  - `noResults`: a search or filter that matches nothing. One line and Clear filters (or Clear search), with no picture.
439
581
  - `failed`: a read that failed where content would be. The warning mark, the message's title and body, and its controls, Retry among them.
@@ -444,7 +586,7 @@ Modal overlays use the scrim with no blur. Focus is not elevation: it is always
444
586
 
445
587
  ## Shapes
446
588
 
447
- A corner follows what the thing is, on one scale: buttons, inputs, badges, tabs, menu rows and app-mark tiles take `{rounded.md}` (8 px); cards, tables, menus, popovers, dialogs, sheets, tooltips and toasts take `{rounded.lg}` (12 px), and so does a drawer on each edge not flush with the window; a collection's glass toolbar takes `{rounded.xl}` (16 px); a conversation bubble takes `{rounded.2xl}` (20 px); Nadia's chat bar takes `{rounded.3xl}` (24 px). A checkbox mark is a square at `{rounded.sm}` (6 px), because `{rounded.md}` on its 16 px mark would draw a circle and read as a radio. A surface never earns a larger corner by sitting higher. A corner nested inside another takes the outer radius minus the gap between them. Full-bleed sections take `{rounded.none}`. `{rounded.full}` is for avatars, switch and radio marks, slider thumbs, filter chips and tags. The other radii are for inner marks and geometry a component owns, such as a progress track.
589
+ A corner follows what the thing is, on one scale: buttons, inputs, badges, tabs and menu rows take `{rounded.sm}` (6 px); resting cards and tables also take `{rounded.sm}`; menus, popovers, dialogs, sheets and toasts take `{rounded.lg}` (12 px), and so does a drawer on each edge not flush with the window; tooltips take `{rounded.sm}` (6 px); a collection's glass toolbar takes `{rounded.lg}` (12 px); a conversation bubble takes `{rounded.2xl}` (20 px); Nadia's chat bar takes `{rounded.3xl}` (24 px). A checkbox mark is a square at `{rounded.xs}` (2 px), keeping its 16 px mark distinct from a circular radio. A surface never earns a larger corner by sitting higher. A corner nested inside another takes the outer radius minus the gap between them. Full-bleed sections take `{rounded.none}`. `{rounded.full}` is for avatars, switch and radio marks, slider thumbs, filter chips and tags. The other radii are for inner marks and geometry a component owns, such as a progress track.
448
590
 
449
591
  ## Motion
450
592
 
@@ -468,31 +610,191 @@ The easings are `emphasized`, `out-strong`, `in-out-strong` and `drawer`; the wa
468
610
 
469
611
  ## Components
470
612
 
471
- Every product builds from `@nadicodeai/ui`: stock shadcn components on Base UI, the chart kit, the assistant-ui conversation interface, and the compositions below. A component styles itself only through the roles and tokens in this file; it carries no raw colour, no `dark:` utility and no theme of its own. A page arranges and sizes a component and never restyles it: a new appearance is a typed variant or slot added to the component, and a missing composition is added to the package, even while one product alone needs it. Visible controls always come from the package.
613
+ Every product builds from `@nadicodeai/ui`: stock shadcn components on Base UI, the chart kit, the assistant-ui conversation interface, and the compositions below. A component styles itself only through the roles and tokens in this file; it carries no raw colour, no `dark:` utility and no theme of its own. A page arranges and sizes a component and never restyles it: a new appearance is a typed variant or slot added to the component, and a missing reusable composition is added to the shared package, even while one product alone needs it. Product-specific data and persistence stay in the app adapter. Visible controls always come from the package.
614
+
615
+ ### Information shapes
616
+
617
+ Choose the presentation from the answer the data gives. A change of page or
618
+ viewer scope changes the records and available actions, while the same kind of
619
+ answer keeps its component, density, value format and action placement.
620
+
621
+ | Information | Component and context |
622
+ | --- | --- |
623
+ | Records with the same comparable facts | `DataTable`: one identity per row, typed columns, visible headings by default, and a menu when secondary mutations exist. Use the compact heading exception only for a self-describing directory. |
624
+ | A descriptive item with supporting prose | `ItemGroup` and `Item`: identity, description and trailing actions. Use this for a feed or a descriptive list rather than inventing table columns. |
625
+ | The identity of a record in a collection | `RecordIdentity`: name with its kind's optional mark; the containing table owns navigation. Agent and model identities keep their specialized owners. |
626
+ | Facts about one object | `ObjectFacts` or `SettingsFacts`: labelled values in one coherent section. A relation uses its same identity or `Tag`; it does not become a separate card. |
627
+ | A coherent set of changes | `SettingsSection` and `SettingsField`: the section owns its fields, help, recovery and local Save. Native uncontrolled forms use `SettingsForm` and `SettingsSubmitButton`; controlled editors derive their changed state from their own committed values. |
628
+ | A classification, lifecycle or operation | `Badge`, `StatusBadge` or `ActionStatus`, according to the existing state contract. A missing value, an empty list and a failed read remain distinct. |
629
+ | Another view of the current scope | `TabsNav`; an object's ancestry remains in breadcrumbs. A tab does not introduce a new scope or a second collection owner. |
630
+ | A measurement over time or across objects | The existing KPI or chart composition for the reading; a named comparison uses the same `DataTable`, with units and visible headings. |
631
+
632
+ One section holds one complete task or answer. A status, explanatory sentence
633
+ or Change button belongs beside the facts or fields it qualifies; it does not
634
+ earn a second section by itself.
635
+
636
+ ### Component selection and usage
637
+
638
+ This catalog maps all 72 entries in the [Geist component reference](https://vercel.com/geist/introduction) to our shared owners. **Component** means a public owner exists for the job; it does not promise every Geist prop. **Composition** means existing parts can provide the described presentation together; the row names any behavior still needed. **Available upstream composition** identifies a stock shadcn example not yet packaged in our library. An **enhanced capability gap** names specific behavior, not the absence of a matching component name. Use our exported APIs and names, not Geist's prop names: for example, our Button appearance is `variant`, while HTML submission behavior is `type`.
639
+
640
+ The shadcn column identifies official primitives, examples and blocks that supply the pattern. Prefer these compositions before adding another registry or dependency. A dedicated name in Geist does not require another primitive in our library. The separate [shadcn.io Code Block](https://www.shadcn.io/components/code-block) supplies richer code display in the wider shadcn ecosystem; it is not the official `@shadcn` registry. Neither it nor AI Elements is required for plain code, snippets or copyable values.
641
+
642
+ Read the relevant row before building a surface, then use the [complete compositions](#complete-compositions) before assembling primitives. Component mechanics and supported props live in [the shared UI source](../ui/src/components); product adapters supply data, routes, translations, permissions and persistence. A component's visual owner supplies its geometry, typography, colour, focus and surface zones. A caller supplies layout and content.
643
+
644
+ Every example and consuming surface covers its reachable populated, empty, loading, invalid, disabled, failed and compact states. A failed request is not empty data; a disabled control has a reason. Controls keep their visible names, keyboard behavior and focus return. Keep product wording in sentence case. A source reference establishes a pattern, while rendered examples establish what the implementation actually looks like.
645
+
646
+ | Geist reference | Shared owner and coverage | Official shadcn pattern | Use and composition contract |
647
+ | --- | --- | --- | --- |
648
+ | [Avatar](https://vercel.com/geist/avatar) | Component: `Avatar`, `AvatarImage`, `AvatarFallback`, `AvatarGroup`, `AvatarGroupCount`, `AvatarBadge` in `avatar.tsx`; Agent identity remains `AgentOrb`. | [Avatar](https://ui.shadcn.com/docs/components/base/avatar) | One person/team gets one image with literal identity and initials fallback. Groups use the group owner and a group label; overflow count is explicit. Use the [avatar owner](../ui/src/components/avatar.tsx) for a compact identity; larger object identity uses the existing object composition. Image failure retains identity. Decorative repeated image avoids double announcement. |
649
+ | [Badge](https://vercel.com/geist/badge) | Component: `Badge`; `StatusBadge` for state; `Tag` for a categorical fact. Choose a typed tone; the owner supplies label geometry. | [Badge](https://ui.shadcn.com/docs/components/base/badge) | Short metadata beside its subject. A status, category and filter have different semantics. Use a word with semantic color; never a click handler on a static label. Explain lifecycle restrictions through supplementary help. Do not nest badges or pack paragraphs inside one. |
650
+ | [Banner](https://vercel.com/geist/banner) | Composition: `NoticeBanner` / `MessageNotice`; no announcement-specific owner. | [Alert](https://ui.shadcn.com/docs/components/base/alert) + action Button. | Container-wide important message at its scope. Persistent condition has a resolver; transient success is a toast. Choose meaning and announcement urgency through the [NoticeBanner owner](../ui/src/components/notice-banner.tsx); a field error stays with its field. Only announce on a meaningful change. |
651
+ | [Book](https://vercel.com/geist/book) | Optional composition: `Card` / `PictureLabel` + `AspectRatio` and cover artwork. No dedicated book-frame export. | [Card](https://ui.shadcn.com/docs/components/base/card) + [Aspect Ratio](https://ui.shadcn.com/docs/components/base/aspect-ratio) + cover artwork; decorative composition, no stock book primitive. | Marketing/docs cover artwork only. Never required to represent product records or Library entries. If adopted, keep its heading semantics, honest link target and stable aspect ratio; no fake spine/control behavior. |
652
+ | [Breadcrumbs](https://vercel.com/geist/breadcrumbs) | Component: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, `BreadcrumbEllipsis`. | [Breadcrumb](https://ui.shadcn.com/docs/components/base/breadcrumb) | Express hierarchy, not browsing history. Ancestors link; current location uses `BreadcrumbPage`. Separators are decorative. Long paths may disclose middle ancestors, but preserve parent and current item; never replace the path with a back button. |
653
+ | [Browser](https://vercel.com/geist/browser) | Optional composition: `Card` + `AspectRatio` and decorative browser chrome. `DesktopShell` remains real product chrome. | [Card](https://ui.shadcn.com/docs/components/base/card) + [Aspect Ratio](https://ui.shadcn.com/docs/components/base/aspect-ratio) + decorative header; no stock browser frame. | Use only when an actual captured browser view needs a frame. Its controls are decorative/nonfocusable. Do not wrap our live product examples in fictional browser controls or treat `DesktopShell` as an equivalent prop API. |
654
+ | [Button](https://vercel.com/geist/button) | Component: `Button`, `buttonVariants`, `PendingLabel`; no native `loading` prop. | [Button](https://ui.shadcn.com/docs/components/base/button) + [Spinner](https://ui.shadcn.com/docs/components/base/spinner) for upstream pending treatment. | State-changing command uses a button; navigation uses `render` with a real anchor/router link. Primary, supporting, tertiary and destructive roles use typed variants. Name the action/object. Preserve dimensions and busy announcement while working; use `PendingLabel`, `aria-busy` and the caller's duplicate-submit protection. Icon-only needs its own action label. |
655
+ | [Calendar](https://vercel.com/geist/calendar) | Component: `Calendar`; `DateRangeControl` for metric windows. | [Calendar](https://ui.shadcn.com/docs/components/base/calendar); [Date Picker](https://ui.shadcn.com/docs/components/base/date-picker) combines Calendar and Popover. | Dates/ranges whose month/day context matters. Respect locale, timezone, available window and committed selection. Presets are real buttons. Pair trigger with selected range, not a forgotten placeholder. Retain DayPicker keyboard operation; announce completed range and return focus after closing. |
656
+ | [Checkbox](https://vercel.com/geist/checkbox) | Component: `Checkbox` + `FieldSet`, `FieldLegend`, `FieldLabel`. | [Checkbox](https://ui.shadcn.com/docs/components/base/checkbox) + [Field](https://ui.shadcn.com/docs/components/base/field) | Independent options, acknowledgments and row selection. Group related options semantically; label each row selection by record name. Derive mixed state from child selection. Required acknowledgment validates on submit. Mixed selection uses a distinct mark, not the checked mark. |
657
+ | [Choicebox](https://vercel.com/geist/choicebox) | Composition: `FieldLabel` containing `Field`, `FieldContent`, `FieldTitle`/`FieldDescription` plus `RadioGroupItem` or `Checkbox`; no `Choicebox` export. | [Field](https://ui.shadcn.com/docs/components/base/field) choice-card examples with Checkbox or Radio Group. | Rich choices with description/icon, usually 4–6 or fewer. One selection model per group. Whole tile activates its radio/checkbox; no nested link/button stealing selection. Selected mark accompanies border. Fieldset/legend names the set; disabled reasons remain available. Make this one documented recipe rather than custom card styles. |
658
+ | [Clearable Input](https://vercel.com/geist/clearable-input) | Composition: `InputGroup`, `InputGroupInput`, `InputGroupAddon`, `InputGroupButton`; no clearable wrapper. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) + InputGroupButton reset action. | Editable single-line text with an explicit reset. A named clear button resets controlled state and restores input focus. Escape clears only while the field owns the interaction and has a value; do not swallow an overlay's dismissal otherwise. Preserve disabled, invalid and empty states. |
659
+ | [Code](https://vercel.com/geist/code) | Composition: semantic `code` with the shared mono typography roles. Conversation `MarkdownText` already owns its inline code; no standalone `Code` export is required for that use. | [Typography inline-code example](https://ui.shadcn.com/r/styles/new-york-v4/typography-inline-code.json); [Typeset](https://ui.shadcn.com/docs/typeset) for rendered prose. | Inline tokens and short syntax need mono, selectable text, sufficient contrast and no visual line numbers in copied value. Static documentation code and conversation rendering have different owners. Do not expose assistant internals as a general component contract. |
660
+ | [Code Block](https://vercel.com/geist/code-block) | Component composition: `CodeBlock` owns `InputGroup`, read-only `InputGroupTextarea`, optional filename/language addons and `CopyButton`. Conversation `MarkdownText` and `FileDiff` retain their specialized jobs; plain display does not imply syntax highlighting. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) code-editor example: textarea, filename addon, copy button; use readOnly for display. | Multi-line code with honest filename/language, optional copy and line context. Preserve exact runnable text and whitespace. Overflow stays readable. Name the code region and translate copy feedback through the [CodeBlock owner](../ui/src/components/code-block.tsx). Its language label describes plain read-only text; syntax highlighting, highlighted lines, language selection and line numbers remain enhanced capabilities. |
661
+ | [Collapse](https://vercel.com/geist/collapse) | Component: `Accordion`, `Collapsible`; complete `Disclosure` and `ExpandableBlock`. | [Collapsible](https://ui.shadcn.com/docs/components/base/collapsible) or [Accordion](https://ui.shadcn.com/docs/components/base/accordion) | Optional/advanced content, not the page's primary answer. Related independent sections may remain open together; mutually exclusive sections may use single-open mode. Trigger names the topic and exposes expansion. Preserve useful summary and field state. Do not bury essential warning or nest many disclosure levels. |
662
+ | [Combobox](https://vercel.com/geist/combobox) | Component: `Combobox`, input/content/list/item/group/label/empty/chips parts. | [Combobox](https://ui.shadcn.com/docs/components/base/combobox) | Type to filter a known set. Short fixed choices use Select; free text uses Input. Keep async loading distinct from no matches; retain current selection while fetching. Visible label, scoped query hint, deterministic keyboard selection and no accidental parent-form submit. Use the [Combobox owner](../ui/src/components/combobox.tsx) for its clear control and selection behavior. |
663
+ | [Command Menu](https://vercel.com/geist/command-menu) | Component: `CommandDialog`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandShortcut`. | [Command](https://ui.shadcn.com/docs/components/base/command) + [Dialog](https://ui.shadcn.com/docs/components/base/dialog) | Global resource/action finding, not a row menu. App binds platform shortcut once. Name scope and groups; show useful defaults, retain query through nested navigation if implemented. Trap/restore focus through dialog; announce results appropriately. Page stacks/recents are app behavior, not provided public props. |
664
+ | [Context Card](https://vercel.com/geist/context-card) | Component: `HoverCard`, `HoverCardTrigger`, `HoverCardContent`. | [Hover Card](https://ui.shadcn.com/docs/components/base/hover-card) | Supplementary entity preview with a few facts. A short explanation is Tooltip; a persistent editor is Sheet/page. Preserve trigger name and a stable route to full detail. Never put the only destructive action or essential information behind hover. Interactive-preview keyboard behavior must be exercised. |
665
+ | [Context Menu](https://vercel.com/geist/context-menu) | Component: `ContextMenu` family; same commands as `ActionMenu`/`DropdownMenu`. | [Context Menu](https://ui.shadcn.com/docs/components/base/context-menu) | Right-click convenience on a record/canvas target. Every command remains reachable from a visible trigger. Keep same permission and consequence. Confine interception to that target; support keyboard menu invocation, Escape and focus return. Destructive commands group last; avoid deep submenu trees. |
666
+ | [Copy Button](https://vercel.com/geist/copy-button) | Component composition: `CopyButton` owns Button + existing clipboard behavior, pending state and an announced result. Portal `CopyableIdentifier` binds product identifiers to it. | [Button](https://ui.shadcn.com/docs/components/base/button) + Clipboard API; [Input Group](https://ui.shadcn.com/docs/components/base/input-group) has copy-action examples. | Copy exact underlying value and acknowledge only clipboard success; expose failure/retry. Keep control stable and label the target. Translate copy feedback through the [CopyButton owner](../ui/src/components/copy-button.tsx). |
667
+ | [Description](https://vercel.com/geist/description) | Component: `ObjectFacts`, `SettingsFactsSection`, `TableValue`. | [Card](https://ui.shadcn.com/docs/components/base/card) title/description or [Item](https://ui.shadcn.com/docs/components/base/item); semantic description list for key/value facts. | Key/value object facts in a semantic definition list. Static metadata is not a two-column comparison table. Key names the fact; value retains exact identifiers, links or copy action. Unknown uses accessible dash, zero remains zero. Long prose may use a block fact. |
668
+ | [Destructive Action Modal](https://vercel.com/geist/destructive-action-modal) | Component for ordinary confirmation: `ConfirmDialog`; gap for typed-name confirmation. | [Alert Dialog](https://ui.shadcn.com/docs/components/base/alert-dialog) + [Field](https://ui.shadcn.com/docs/components/base/field) and Input for typed verification. | Name object, irreversible effects and recovery before destructive command. Keep failed mutation open, preserve focus and prevent duplicate commit. Use the [ConfirmDialog owner](../ui/src/components/confirm-dialog.tsx) for ordinary confirmation. A typed-name gate for a high-stakes operation needs a shared extension before a surface promises it. |
669
+ | [Dots Menu](https://vercel.com/geist/dots-menu) | Component: `ActionMenu` and `DropdownMenuCommand`. | [Dropdown Menu](https://ui.shadcn.com/docs/components/base/dropdown-menu) + named icon Button. | One discoverable overflow trigger for secondary record actions. Name whose actions the [ActionMenu owner](../ui/src/components/action-menu.tsx) opens and supply real menu commands. Keep the target easy to hit and the menu inside the viewport. |
670
+ | [Drawer](https://vercel.com/geist/drawer) | Component: low-level `Drawer` family; `FormDrawer` owns substantial right-side form workflows. | [Drawer](https://ui.shadcn.com/docs/components/base/drawer) pattern; keep our Base UI implementation. | Geist uses mobile bottom drawers; do not silently call our right-side form behavior identical. Choose one shared adaptive recipe if parity is adopted. Preserve dirty fields, visible footer actions, contained scroll, dismissal and focus return. Destructive decisions use a blocking confirmation. |
671
+ | [Empty State](https://vercel.com/geist/empty-state) | Component: [EmptyState](../ui/src/components/empty-state.tsx), following the [absence contract](#elevation--depth). | [Empty](https://ui.shadcn.com/docs/components/base/empty) + appropriate action. | Absence, unmatched filters and failed fetch are different states. First-use action exists only with permission and a real creation path. Search offers clear/reset; failure offers actual recovery. Put outside an empty table body. Permission/upgrade/cleared variants are not exported and must not be faked as noResults. |
672
+ | [Entity](https://vercel.com/geist/entity) | Component: `ItemGroup`, `Item`, `ItemMedia`, `ItemContent`, `ItemTitle`, `ItemDescription`, `ItemActions`; domain `CatalogEntry`, `AgentListRow`. | [Item](https://ui.shadcn.com/docs/components/base/item) with media, content and actions. | Descriptive row with identity, secondary detail and action, not sortable comparative columns. Use divided group for peers and one coherent hit target with independent nested controls. Skeleton matches row geometry. Primary direct action may remain visible; secondary commands use overflow. |
673
+ | [Error](https://vercel.com/geist/error) | Component: `FieldError`, `MessageNotice`, `EmptyState kind="failed"`, `FeedbackState` for existing owned surfaces. | [Field](https://ui.shadcn.com/docs/components/base/field) error or [Alert](https://ui.shadcn.com/docs/components/base/alert) at the relevant scope. | Put field validation by field, action failure by action, failed read where content would be. State what failed, retained work and concrete recovery. Catalog-resolved messages own product wording. Diagnostic identifier only when available; never raw exception or fake error ID. Async failure is announced without stealing useful input. |
674
+ | [Error Card](https://vercel.com/geist/error-card) | Composition: existing failed-state owner within `Card`/section; no ErrorCard export. | [Card](https://ui.shadcn.com/docs/components/base/card) + [Alert](https://ui.shadcn.com/docs/components/base/alert) or [Empty](https://ui.shadcn.com/docs/components/base/empty) | One failed region retains surrounding useful page and its geometry. Do not render an empty card or turn a whole page into an error because one card failed. Recovery remains inside its region. |
675
+ | [Feedback](https://vercel.com/geist/feedback) | Composition: `Popover`, `Field`, `Textarea`, `RadioGroup`, optional `Select`, and `Button`. The app supplies the submission destination and result; no collection backend is implied. | [Popover](https://ui.shadcn.com/docs/components/base/popover) + [Field](https://ui.shadcn.com/docs/components/base/field), Textarea, Radio Group and Button; optional Select. | Explicit feedback submission with optional sentiment, context and a real destination. Preserve text on error and acknowledge success. Emotion controls need text names; do not fabricate sending or collect unsolicited sensitive page content. Public Geist demos supply no detailed reusable behavior contract. |
676
+ | [Fieldset](https://vercel.com/geist/fieldset) | Component: `SettingsSection`, `SettingsField`, `SettingsForm` and `SettingsSubmitButton`; `FieldSet`/`FieldLegend` for semantic subgroups. | [Card](https://ui.shadcn.com/docs/components/base/card) + [Field](https://ui.shadcn.com/docs/components/base/field) and footer action. | One independently saved setting group: heading/description, content, feedback, recessed footer with local save. Use `controlProps` from SettingsField to bind id/help/error/disabled/required. Gated state explains how to resolve it; a disabled wall is not yet a shared prop. Header and content share one body plane; the local save footer uses the recessed fill. |
677
+ | [File Tree](https://vercel.com/geist/file-tree) | Composition: `SidebarMenu` + `Collapsible` + `FileTypeIcon` can follow sidebar-11 for nested files. Full tree-widget selection and roving arrow-key navigation require additional behavior. | [sidebar-11](https://ui.shadcn.com/blocks/sidebar#sidebar-11): Sidebar + Collapsible for nested file navigation. | Hierarchical files when structure is the task. Use semantic nested lists and Collapsible for expandable file navigation, following sidebar-11. A tree widget with `role="tree"` additionally needs roving keyboard focus, expand/collapse and selection semantics; do not claim those from the disclosure pattern alone. |
678
+ | [Gauge](https://vercel.com/geist/gauge) | Available upstream composition: chart-radial-text uses Card + Chart + installed Recharts. Our existing `UsageMeter`/`Progress` cover linear readings; a radial wrapper is not yet exported. | [Radial Charts](https://ui.shadcn.com/charts/radial), especially chart-radial-text; existing Recharts dependency. | Ratio/allowance with a meaningful maximum and adjacent value/label; never fake a percentage for unknown work. The product owns thresholds; a source example cannot change them. Circular form is a visual choice requiring a shared owner if adopted; no radial API exists today. |
679
+ | [Grid](https://vercel.com/geist/grid) | Composition: layout grid, `CardRow`, website `nc-page`; no ruled `Grid` API. | [Card](https://ui.shadcn.com/docs/components/base/card) in CSS grid; ruled presentation guides are decorative layout. | Ordinary card layout uses layout utilities/CardRow. Geist visible guides are for marketing/docs composition; guides are decorative, do not affect reading order, and are drawn once at joins. Do not install a grid mechanism merely for normal responsive columns. |
680
+ | [Input](https://vercel.com/geist/input) | Component: `Input`, `InputGroup`, `SettingsField`/`Field`. | [Input](https://ui.shadcn.com/docs/components/base/input) + [Field](https://ui.shadcn.com/docs/components/base/field) | One free-form value. Correct type/autocomplete/inputMode, visible label, stable help and field error. Domain rules decide trimming; never trim passwords/secrets/code indiscriminately. Preserve entry through request failures. Size and state belong to owner; label/error strings are composed, not built-in Input props. |
681
+ | [JSON View](https://vercel.com/geist/json-view) | Composition for static JSON: code display with exact serialized text. `Collapsible` supports grouped disclosure; Geist-style keyboard tree navigation, search highlighting and depth controls remain an enhanced capability gap. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) for static code; [Collapsible](https://ui.shadcn.com/docs/components/base/collapsible) for disclosure. Full JSON tree navigation/search is additional behavior. | Structured payload inspection warrants an accessible expandable tree with selectable/copyable values. Static documentation can use a code block. No `defaultExpandDepth`/`highlightPattern` API exists in our package; do not claim conversation output is parity. |
682
+ | [Keyboard Input](https://vercel.com/geist/keyboard-input) | Component: `Kbd`, `KbdGroup`, `CommandShortcut`; platform mapping is caller-owned. | [Kbd](https://ui.shadcn.com/docs/components/base/kbd) and KbdGroup. | Shortcut hints near a command, not an interactive input. Keep each key meaningful, surrounding prose outside the key. Show OS-correct modifiers and only shortcuts actually bound. Our Kbd has no Geist modifier booleans or automatic OS mapping. |
683
+ | [Label](https://vercel.com/geist/label) | Component: `Label`, `FieldLabel`, `FieldLegend`. | [Field](https://ui.shadcn.com/docs/components/base/field) label/legend or [Label](https://ui.shadcn.com/docs/components/base/label) | Visible name attached to the control or semantic group. Placeholder is example/hint, not label replacement. Mark requirement and help separately. Do not use another aria-label that overrides an already correct visible label. |
684
+ | [Load More Button](https://vercel.com/geist/load-more-button) | Composition: `Button` + `PendingLabel` and app pagination; no complete owner. | [Button](https://ui.shadcn.com/docs/components/base/button) + pending feedback and app pagination. | Append records while preserving scroll, previous rows and focus. Expose pending/error/exhausted states; no duplicate fetch or disappearance while focused. A request for another page differs from exposing already-loaded hidden content. |
685
+ | [Loading Dots](https://vercel.com/geist/loading-dots) | Personalization: `PendingLabel` / `WaitingLine`; no LoadingDots. | [Spinner](https://ui.shadcn.com/docs/components/base/spinner) supplies upstream indeterminate feedback; retain our waiting treatment. | Inline indeterminate wait names actual work, announces busy state once, and uses one indicator. Agent work uses the Agent's own state; system waiting follows the activity rules below. Never animate completed text. |
686
+ | [Menu](https://vercel.com/geist/menu) | Component: `DropdownMenu` family, `DropdownMenuCommand`, `ActionMenu`. | [Dropdown Menu](https://ui.shadcn.com/docs/components/base/dropdown-menu) | Resource actions from a discoverable trigger. Choice lists use appropriate radio/checkbox menu items. Keep label first, target clear, related groups separated and destructive group last. Use library positioning, typeahead, keyboard selection, Escape and focus return. A locked command needs readable reason. |
687
+ | [MiddleTruncate](https://vercel.com/geist/middle-truncate) | Composition for technical text: mono value + `Tooltip` + copy action. The width-aware middle-truncation behavior is not provided by those parts or a public shared owner. | [Tooltip](https://ui.shadcn.com/docs/components/base/tooltip) + code/text layout and copy. No stock middle-truncation algorithm. | Paths/URLs/IDs where prefix and suffix matter. Keep original text available and copy the original, not ellipsis. Prose uses wrapping or end truncation. Avoid jitter under resize, preserve accessible full value, and never make tooltip the only way to recover an essential action. |
688
+ | [Modal](https://vercel.com/geist/modal) | Component: `Dialog` family, `FormDialog` for short forms, and `ConfirmDialog` for consequences. | [Dialog](https://ui.shadcn.com/docs/components/base/dialog) | Blocking decision or bounded focused job. Title, description when useful, scrollable body and footer; return focus predictably. Save failure retains data. Allow safe dismissal; explicitly protect unsaved/destructive context. Persistent context uses Sheet/page, not stacked dialogs. |
689
+ | [Multi Select](https://vercel.com/geist/multi-select) | Composition: `Combobox multiple` with chips/list; `CollectionFilters` for query facets; checkbox menus for small sets. | [Combobox](https://ui.shadcn.com/docs/components/base/combobox) multiple mode and chips. | Multiple known values with visible selection/count, labelled remove controls and empty/search states. Match controlled state to URL/server. Keep keyboard movement/removal delegated to Base UI. No separate MultiSelect export or built-in select-only/all contract. |
690
+ | [Note](https://vercel.com/geist/note) | Component: `Alert`, `NoticeBanner`, `MessageNotice`. | [Alert](https://ui.shadcn.com/docs/components/base/alert) | Persistent contextual explanation/constraint next to its subject. Choose semantic tone, one concept and one resolver. Success/violet/cyan extensions must be real typed variants before taught. Avoid dismissing an unresolved important condition, or using a toast as its only home. |
691
+ | [Pagination](https://vercel.com/geist/pagination) | Component for numbered/data pager: `Pagination` family / `DataTable`; compose Geist's previous/next article rail from links. | [Pagination](https://ui.shadcn.com/docs/components/base/pagination); named links for an article rail. | Distinguish navigating sibling pages from paging rows. Data pager keeps count/range and disabled boundaries; article rail names the next destination and omits absent ends. Announce current page and route through the app. |
692
+ | [Phone](https://vercel.com/geist/phone) | Optional composition: `AspectRatio` + decorative frame and captured content. No dedicated device-frame export. | [Aspect Ratio](https://ui.shadcn.com/docs/components/base/aspect-ratio) + decorative frame; no stock phone primitive. | Captured mobile showcase only; decorative chrome hidden from accessibility, content has meaningful alternative. Preserve aspect ratio and paused/reduced-motion behavior. Product parity does not require fake device surrounds. |
693
+ | [Pill](https://vercel.com/geist/badge#pill) | Component for static category: `Tag`; filters: `CollectionFilters`; links/buttons keep their semantics. | [Badge](https://ui.shadcn.com/docs/components/base/badge) for metadata; [Toggle Group](https://ui.shadcn.com/docs/components/base/toggle-group) or buttons for interactive state. | A shape is not a new interaction. Static metadata stays static; active filter has remove/reset and state; navigation is a link. Avoid wrapping arbitrary clickable badge spans. |
694
+ | [Progress](https://vercel.com/geist/progress) | Component: `Progress`, `ProgressTrack`, `ProgressIndicator`, `ProgressLabel`, `ProgressValue`; `UsageMeter`. | [Progress](https://ui.shadcn.com/docs/components/base/progress) | Known completed amount against real `max`; print work and units. Unknown wait uses waiting treatment. Semantic warnings come from product thresholds. Accessible value/name and sensible update frequency; completion becomes the actual finished state. Stops/labels are not a provided prop. |
695
+ | [Project Banner](https://vercel.com/geist/project-banner) | Composition: scoped `MessageNotice`/`NoticeBanner`; prioritization belongs to the app. | [Alert](https://ui.shadcn.com/docs/components/base/alert) + action at the page scope. | One strongest unresolved object-wide condition above its content, with a resolving action. It remains until underlying state changes. App chooses urgency and authorization. Never stack duplicate section notices or dismiss away a blocking condition. |
696
+ | [Radio](https://vercel.com/geist/radio) | Component: `RadioGroup`, `RadioGroupItem` with FieldSet/legend. | [Radio Group](https://ui.shadcn.com/docs/components/base/radio-group) + [Field](https://ui.shadcn.com/docs/components/base/field) | Small mutually exclusive choices; rich ones use choice tiles; large searchable ones use Combobox. Safe default when justified; require deliberate choice when consequential. Group label, named options, arrow selection, one tab stop and disabled reasons. |
697
+ | [Relative Time Card](https://vercel.com/geist/relative-time-card) | Composition: `HoverCard` + semantic time + locale formatting. Portal already has `formatRelativeTime` and `portalRelativeTimeFormatter`; a shared wrapper can accept formatted strings from the product. | [Hover Card](https://ui.shadcn.com/docs/components/base/hover-card) + semantic time and locale formatting. | Recent time in dense rows with exact date/timezone on focus/hover. Centralize compact locale format and transition to absolute dates; preserve machine-readable timestamp. Missing/stale/loading differ. Do not claim Vercel's seven-day cutoff already exists. |
698
+ | [Scroller](https://vercel.com/geist/scroller) | Component: `ScrollArea`, `ScrollBar`; `Carousel` for a controlled rail. | [Scroll Area](https://ui.shadcn.com/docs/components/base/scroll-area); [Carousel](https://ui.shadcn.com/docs/components/base/carousel) for a controlled rail. | One overflow axis where appropriate; preserve reading order, keyboard focus visibility and meaningful controls. Large datasets require a measured virtualization need and owner. Our ScrollArea has no Geist direct-child scroll-button API. |
699
+ | [Search Input](https://vercel.com/geist/search-input) | Component: `CollectionSearch` in `CollectionToolbar`; richer reset/loading is a gap. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) search icon, input, reset button and pending state. | Scope-specific query, clear/reset, retained results while updating and no-results recovery. Use [CollectionSearch](../ui/src/components/collection-search.tsx) for the query control; compose a reset when the task requires one. Preserve the global command-menu shortcut. |
700
+ | [Select](https://vercel.com/geist/select) | Component: `Select` family, `NativeSelect` family. | [Select](https://ui.shadcn.com/docs/components/base/select) or [Native Select](https://ui.shadcn.com/docs/components/base/native-select) | Short fixed known options; a few meaningful options may be radio/segmented; many searchable ones use Combobox. Label and validation are field composition. Keep placeholder distinct from an actual option, groups semantic and keyboard selection native to primitive. |
701
+ | [Separator](https://vercel.com/geist/separator) | Component: `Separator`; `FieldSeparator` and menu/group separators for scoped roles. | [Separator](https://ui.shadcn.com/docs/components/base/separator) | Divide distinct groups, not every paragraph. Each join has one line. Preserve semantic orientation and decorative treatment. Frame inset is needed only where overlaid edge would double a line; do not add caller borders to an elevated owner. |
702
+ | [Sheet](https://vercel.com/geist/sheet) | Component: `Sheet` family; actual modal/dismissal defaults differ from Geist. | [Sheet](https://ui.shadcn.com/docs/components/base/sheet) | Persistent associated context such as row inspection. Title, explicit close, contained scrolling and focus return to originating record. Decide nonmodal behavior deliberately; do not repeat Geist's contradictory “nonmodal but trap focus” guidance mechanically. Blocking destructive consequence uses confirmation. |
703
+ | [Show more](https://vercel.com/geist/show-more) | Composition: `Disclosure`/`Collapsible`/`ExpandableBlock` depending content. | [Collapsible](https://ui.shadcn.com/docs/components/base/collapsible) + named trigger. | Reveal already-loaded supporting content with meaningful hidden count and stable context. Trigger exposes expansion and target; preserve reading position and focus. Do not call a network fetch merely “show more” without pending/error behavior. |
704
+ | [Skeleton](https://vercel.com/geist/skeleton) | Component: `Skeleton`. | [Skeleton](https://ui.shadcn.com/docs/components/base/skeleton) | Known loading geometry only; match final shape and avoid layout shift. Loading region is busy, skeleton decorative/nonfocusable. It must resolve to data, absence or failure. Static is our current material choice; shimmer adoption must reconcile motion owner. |
705
+ | [Slider](https://vercel.com/geist/slider) | Component: `Slider`; compose with `Input` for precision. | [Slider](https://ui.shadcn.com/docs/components/base/slider) + [Input](https://ui.shadcn.com/docs/components/base/input) for exact values. | Bounded range where position matters, with explicit minimum/maximum/step, current value/unit and accessible thumb labels. Exact numeric entry remains available when precision matters. Keyboard operation stays with primitive; no fabricated threshold colors. |
706
+ | [Snippet](https://vercel.com/geist/snippet) | Composition: read-only mono `InputGroupInput` + addon `CopyButton`, or `CodeBlock` for multiple lines. A shell prompt is separate from the copied value; no separate selection or clipboard engine is needed. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) read-only mono value + copy button. | A short exact command or value intended for copying. Displayed shell prompt is not copied. One command, selectable code and honest copy success/failure. Secrets need explicit reveal policy and must not leak into logs/labels. Build on existing clipboard capability. |
707
+ | [Spinner](https://vercel.com/geist/spinner) | Personalization: `PendingLabel`/`WaitingLine`, no Spinner export. | [Spinner](https://ui.shadcn.com/docs/components/base/spinner) is the upstream alternative; retain our waiting treatment. | Short indeterminate system action. The shared waiting recipe owns stable sizing, focus and announcement; never replace Agent identity with spinner. Respect reduced motion and avoid two indicators for one wait. |
708
+ | [Split Button](https://vercel.com/geist/split-button) | Composition: `ButtonGroup`, `Button`, `DropdownMenu`; no unified SplitButton. | [Button Group](https://ui.shadcn.com/docs/components/base/button-group) split/dropdown examples. | Clear default with a few closely related alternatives. Menu trigger names the action family. Include default in menu, share pending/disabled state and keep destructive actions out of an ambiguous split primary. A reusable wrapper avoids caller seam/rounding mistakes. |
709
+ | [Status Dot](https://vercel.com/geist/status-dot) | Component: [StatusDot](../ui/src/components/status-dot.tsx) and `StatusBadge`. | [Badge](https://ui.shadcn.com/docs/components/base/badge) + labelled status dot; existing StatusDot owns our visual. | Our dot is generic; Geist's is specifically deployment lifecycle. Do not import Vercel state vocabulary into Agents. Pair with readable state; decorative dot when text already names it. Motion/status meaning remains truthful to product state. |
710
+ | [Switch](https://vercel.com/geist/switch) | Composition: `RadioGroup` for mutually exclusive segmented values; `Tabs` for panels; `ToggleGroup` where pressed-button semantics fit. | [Radio Group](https://ui.shadcn.com/docs/components/base/radio-group), [Toggle Group](https://ui.shadcn.com/docs/components/base/toggle-group) or [Tabs](https://ui.shadcn.com/docs/components/base/tabs) according to selection semantics. | Geist Switch means segmented selection, not our boolean `Switch`. Two/three short peer values, one selected state, stable widths and named options. Choose radio or tab semantics by job; do not mislabel button toggles as radios. |
711
+ | [Table](https://vercel.com/geist/table) | Component: [Table](../ui/src/components/table.tsx) family + `DataTable` + `TableCard` + `TableValue`; density, striping and borders belong to the shared owner. Virtualization remains a separate capability. | [Table](https://ui.shadcn.com/docs/components/base/table) + [Data Table](https://ui.shadcn.com/docs/components/base/data-table) with TanStack Table. | Comparable shared columns use table; descriptive rows use Item; key/value facts use ObjectFacts. Visible column labels when comparing/sorting. Numeric columns tabular/right aligned. Shared toolbar, body, totals and footer own consistent geometry. Empty state sits outside body; rows keep stable IDs, nested controls and compact details. |
712
+ | [Tabs](https://vercel.com/geist/tabs) | Component: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`; URL views use `TabsNav`/`TabsLink`. | [Tabs](https://ui.shadcn.com/docs/components/base/tabs) | Sibling views of the same object. App owns route/query persistence. Choose the shared `line` variant for an underline or the default variant for a segmented treatment, keeping siblings consistent; never draw either in route styles. Keep overflow navigable and selection/focus distinct; label tablist, preserve keyboard behavior and use nouns for views. |
713
+ | [Text With Copy Button](https://vercel.com/geist/text-with-copy-button) | Composition: mono text + `CopyButton`; Portal `CopyableIdentifier` supplies the product value and translated labels. Copying and result feedback stay in the shared owner. | [Input Group](https://ui.shadcn.com/docs/components/base/input-group) or text + [Button](https://ui.shadcn.com/docs/components/base/button) and clipboard behavior. | IDs/addresses users actually need elsewhere. Keep exact value and target-specific copy label; acknowledge true success, handle failure and expose full value if truncated. Avoid turning ordinary prose into copyable chrome. |
714
+ | [Textarea](https://vercel.com/geist/textarea) | Component: `Textarea` + Field/SettingsField. | [Textarea](https://ui.shadcn.com/docs/components/base/textarea) + [Field](https://ui.shadcn.com/docs/components/base/field) | Multiline description, notes or code. Useful rows/maximum growth, retained content, visible label/help/error and character limit only when real. Do not trim deliberate whitespace in code or secrets. Ensure footer actions stay reachable on compact screens. |
715
+ | [Theme Switcher](https://vercel.com/geist/theme-switcher) | Component: `ThemeModeSwitcher`, `ThemeModeMenu`. | [Dark mode](https://ui.shadcn.com/docs/dark-mode) + [Dropdown Menu](https://ui.shadcn.com/docs/components/base/dropdown-menu) or radio choices; app theme provider owns persistence. | One canonical light/system/dark preference per shell/settings context, app provider is source of truth. Bind the [theme controls](../ui/src/components/theme-mode-switcher.tsx) to that provider, name the group and localize its choices. Respect forced mode in the product adapter. |
716
+ | [Toast](https://vercel.com/geist/toast) | Component: `Toaster`; `showMessageToast` from `@nadicodeai/ui/components/toast`. | [Toast](https://ui.shadcn.com/docs/components/base/toast) for current Base UI source; our existing Toaster/message adapter remains the public owner. | One terminal acknowledgment per user action. Field validation and persistent urgent problems retain their local home. Product catalog supplies wording. Only show Undo when safe rollback exists; do not make transient toast the only route to recovery or essential navigation. |
717
+ | [Toggle](https://vercel.com/geist/toggle) | Component: our `Switch` for boolean setting; our `Toggle` is a pressed button. | [Switch](https://ui.shadcn.com/docs/components/base/switch) for a boolean setting; [Toggle](https://ui.shadcn.com/docs/components/base/toggle) for a pressed action. | Name what ON means. Persist immediately only when the actual setting contract says so; otherwise belong to a saved setting group. Expose pending failure and recover to truthful state. Checkbox is for selection/acknowledgment, segmented control for exclusive values. |
718
+ | [Tooltip](https://vercel.com/geist/tooltip) | Component: `TooltipProvider`, `Tooltip`, `TooltipTrigger`, `TooltipContent`. | [Tooltip](https://ui.shadcn.com/docs/components/base/tooltip) | Short supplementary constraint or meaning, hover and focus. Visible control already names itself. Never sole home for important action/error or substitute for icon button's accessible name. Rich entity preview uses HoverCard; forms help remains connected field text. |
719
+ | [Video](https://vercel.com/geist/video) | Composition: `AspectRatio` + native video, controls, poster and caption tracks. A richer player needs a concrete media requirement; no shared Video export is needed for native playback. | [Aspect Ratio](https://ui.shadcn.com/docs/components/base/aspect-ratio) + native video controls, poster and caption tracks. | Real media with controls, captions/transcript where speech matters, deliberate autoplay/mute and reduced-motion poster. Preserve layout while loading and recover from unavailable media. Do not create dummy controls or claim a general video system from one marketing embedding. |
720
+
721
+ ### Additional shared building blocks
722
+
723
+ These shared components also follow the same foundations, although Geist does not list each as a separate component. Use the existing product composition before choosing one directly.
724
+
725
+ | Owner | Use and composition contract |
726
+ | --- | --- |
727
+ | `Sidebar`, `DashboardShell`, `DesktopShell`, `PortalSidebarFrame` | Persistent product navigation and workspace chrome. The product owns destinations and active state; the shell owns compact behavior, landmarks and layout. Never add a competing sidebar to a page. |
728
+ | `NavigationMenu` | A site's grouped navigation links, with genuine destinations and usable keyboard paths. Product object views use shared tabs; commands use a menu. |
729
+ | `Menubar` | A persistent application command bar with related menus. Use only when the workspace has that command model; do not substitute it for website navigation or a row's actions. |
730
+ | `Popover` | A bounded contextual control such as filters or a scope chooser. Name its trigger, keep essential meaning visible outside hover, and restore focus when it closes. A short independently saved record form uses `FormDialog`; a substantial workflow may use `FormDrawer`. |
731
+ | `InputOTP` and its group/slot parts | A fixed-length verification code. Keep one logical input with paste and keyboard behavior, a visible label, invalid/retry state and a real resend policy. The product owns length, expiry and submission. |
732
+ | `Carousel` | A finite horizontal rail where adjacent media or items have a meaningful sequence. Give previous/next controls clear names and boundaries; preserve keyboard access and reduced motion. A sortable record collection uses a table or entity list. |
733
+ | `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle` | A workspace with genuinely adjustable panes. Keep keyboard resizing, meaningful minimum sizes, readable content and a compact alternative. The product owns persistence of pane sizes. |
734
+ | `AspectRatio` | Stable layout for real media while it loads. Supply a meaningful ratio and correct media alternative; the wrapper creates neither playback controls nor a device-frame component. |
735
+ | `Form`, Field parts and form adapters | Connect actual validation to controls, help and errors. Use the field owner that matches the existing form engine; preserve values through recoverable failure. Do not change the form engine during visual styling. |
736
+ | `DirectionProvider` | Direction context when the product supports right-to-left content. Keep reading and focus order meaningful; direction does not translate strings or change locale by itself. |
737
+ | `QrCode` | A real scannable value with a readable fallback and an alternative action when scanning is unavailable. Keep secrets out of accessible labels and show expiry or invalidity from actual state. |
738
+ | `FileDiff` | Existing code-change comparison, with file identity and additions/removals explained. A diff is not a general code editor or JSON viewer. Preserve exact source and the established conversation/tool owner. |
739
+ | Chart kit and `KpiCard` | Choose the reading through [Data Visualization](#data-visualization), then use its shared chart owner with units, period, legend and accessible data. Geometry never decides business thresholds. |
740
+
741
+ ### Complete compositions
742
+
743
+ Choose the owner for the whole task. Keep its slots and state handling together so pages supply content without recreating card zones, toolbar geometry or overlay behavior.
744
+
745
+ | Task | Shared composition | What the product adapter supplies |
746
+ | --- | --- | --- |
747
+ | Independently saved settings | `ReadingColumn` → `SettingsSection` → `SettingsField`; Portal selects the configuration width and form presentation through `SettingsPage` and its `SettingsSection` adapter. `SettingsForm` and `SettingsSubmitButton` own native form dirty/pending state. `footerHint` and `footer` stay in the recessed strip. | Values, validation, dirty/pending state, local Save, retained input and failure. Header and content share one body plane; the footer has one seam. |
748
+ | Read-only facts | `SettingsFactsSection` or `ObjectFacts`, with `TableValue` for missing facts. | Label/value pairs, actual timestamps, identifier format, links and permission-aware actions. Unknown remains distinct from zero. |
749
+ | Comparable collection | `PageHeader`/`PageActions`, `CollectionToolbar`, `DataTable` within `TableCard` when framed. | Stable record IDs, columns, visible headings, search/filter/sort, URL state, row commands, pagination and `empty`. Numeric columns align at the trailing edge; totals and paging use their own footer zone. |
750
+ | Descriptive entity list | `ItemGroup divided` with `ItemMedia`, `ItemContent`, `ItemTitle`, `ItemDescription`, `ItemActions`; domain owners include `AgentListRow` and `CatalogEntry`. | Identity, a short description, categorical facts, one primary task and secondary menu. Long values wrap or truncate predictably; a row never gains a second nested hit target by accident. |
751
+ | Overview figures | `CardRow` with `KpiCard` or the chosen chart card. | Measurement, unit, comparison period, truthful delta and stable categorical assignment. Related cards align; status colour and series colour retain different meanings. |
752
+ | Object workspace | `ObjectOverview`, `ObjectIdentity`, `ObjectFacts`, `ObjectSection` and shared tabs. | The actual object, scope, hierarchy, sections, routes and commands. Agent identity retains its orb and the Agent-specific composition. |
753
+ | Short create/edit form | `FormDialog` with Field parts; Portal binds it through `ActionDialogForm`. Substantial workflows explicitly select `FormDrawer`. | Submit operation, labels, validation, dirty state, retained input, permissions and dismissal policy. Use the owned scroll body and fixed footer; do not nest another overlay. |
754
+ | Destructive confirmation | `ConfirmDialog`; the app's confirm-action adapter. | Named target, real consequence, labels, pending state, failure and operation. Typed-name verification is not a supported prop; extend the owner when that capability is needed. |
755
+ | Global commands or record actions | `CommandDialog` for cross-resource search; `ActionMenu` and `DropdownMenuCommand` for one record. | Real commands, grouping, permissions, disabled reasons and keyboard shortcuts. Choose by scope; keep the destructive group last. |
756
+ | Absence or failed read | `EmptyState` through `DataTable.empty` or the affected content region; `MessageNotice` for persistent conditions. | First use, nothing yet, no matches or failed read, with the real recovery action. Empty state belongs outside the semantic table body. |
757
+ | Rich choices | `FieldSet`/`FieldLegend`, then `FieldLabel` containing `Field`, its content and a radio or checkbox. | Known options, selection model, labels, descriptions, validation and disabled reasons. The tile label activates its control; a nested link cannot steal the choice. |
758
+ | Split action | `ButtonGroup` with the default `Button` and a `DropdownMenu` trigger. | One action family, default action, alternatives and shared pending/disabled state. Label the group and menu trigger. Preserve one seam and include the default in the menu. |
759
+ | Copyable identifier or command | Mono text or read-only `InputGroupInput` plus `CopyButton`; put the optional shell prompt outside the copied value. | Exact source value, visible representation and translated `copyLabel`, `copiedLabel`, `copyFailedLabel`. Preserve the full source when the displayed representation is shortened. |
760
+ | Plain code or static JSON | `CodeBlock` owns the read-only InputGroup composition and CopyButton. | Exact `code`, accessible `label`, optional filename/language and copy labels. Serialize static JSON in the adapter; use FileDiff for an actual before/after comparison. |
761
+ | Relative timestamp preview | Semantic `time` with `HoverCard`; keep the exact timestamp available to assistive technology. | Machine-readable timestamp, formatted relative and absolute text, timezone, and the product's refresh policy. Portal keeps its existing locale formatter. |
762
+ | Expandable file navigation | `SidebarMenu` and `Collapsible`, following shadcn sidebar-11, with file icons and real file links or commands. | Hierarchy, stable identity, current file, labels and actions. Preserve semantic nested lists; a full tree widget needs a separately verified keyboard contract. |
763
+ | Feedback collection | `Popover` with Field, Textarea, optional RadioGroup/Select and Button; inline rendering uses the same form parts. | Prompt, topic choices, permitted context, destination, pending/result state and retained input on failure. |
764
+ | Native media playback | `AspectRatio` around a native video with its controls, poster and caption tracks. | Media source, captions, transcript where needed, aspect ratio, playback policy and unavailable-media recovery. |
765
+
766
+ Use `CopyButton` and `CodeBlock` for their complete compositions. Relative-time previews, feedback and expandable file navigation combine existing parts with product adapters. Enhanced code highlighting and full JSON/tree-widget navigation remain specific behavior gaps; do not invent undocumented props or copy a local implementation into every caller. [shadcn Button Group](https://ui.shadcn.com/docs/components/base/button-group), [Field](https://ui.shadcn.com/docs/components/base/field) and [Data Table](https://ui.shadcn.com/docs/components/base/data-table) provide the underlying composition patterns; our public wrappers and this document own their product use.
767
+
768
+ ### Personalization boundaries
769
+
770
+ Keep Geist's hierarchy, control metrics, typography roles, neutral surface zones and disciplined semantic colour. Keep nadicode's stronger visible materials, glass transmission and highlight, Agent orbs, genuine provider marks, categorical Library colours and restrained activity language. These differences are tokenized or owned by a typed component variant. Business status, permissions, transaction boundaries and product copy remain the product's truth; a visual reference cannot change them.
472
771
 
473
772
  ### Controls
474
773
 
475
- - **Buttons.** Every button is `{spacing.control-height}` high with a `{typography.body-sm}` label at 500 and `{rounded.md}` corners ("Layout" says where the smaller sizes may go). `default` is the action (ink, white label; `{colors.dark-action}` with a `{colors.dark-action-foreground}` label in dark); `outline` and `secondary` are the quieter steps, edged with the `{colors.line}` hairline; `ghost` has no edge and is only for an icon-only button (the three dots, close, collapse), never a text button in a list; `destructive` is the error pair; `link` is inline. On a colour or black ground a control takes a variant whose label clears AA there: `brandSolid` (white, ink label) or `brandGhost` (white label, no fill) on cobalto, rosso and black, and the default ink button on verde, arancio and giallo.
774
+ - **Buttons.** Every button is `{spacing.control-height}` high with a `{typography.body-sm}` label at 500 and `{rounded.sm}` corners ("Layout" says where the smaller sizes may go). `default` is the action (ink, white label; `{colors.dark-action}` with a `{colors.dark-action-foreground}` label in dark); `outline` and `secondary` are the quieter steps, edged with the `{colors.line}` hairline; `ghost` has no edge and serves a tertiary text action or an icon-only command (the three dots, close, collapse); `destructive` is the error pair; `link` is inline. On a colour or black ground a control takes a variant whose label clears AA there: `brandSolid` (white, ink label) or `brandGhost` (white label, no fill) on cobalto, rosso and black, and the default ink button on verde, arancio and giallo.
476
775
  - **Dialog footers.** A dialog's actions sit at the footer's trailing edge, the primary last. A choice to stop being asked is a checkbox at the footer's leading edge, remembered by whichever action is taken, never a third button.
477
- - **Tabs** switch between one object's views. They are pills in one row directly under the top bar: `{spacing.control-height}` high, a `{typography.body-sm}` label at 500, `{spacing.sm}` inline padding and `{rounded.md}` corners. The current tab is filled with the `selection` role (`{colors.navigation-selection-bg}` with `{colors.navigation-selection-fg}`, `{colors.dark-navigation-selection-bg}` in dark); the others have no fill and `{colors.muted}` text, which turns ink on hover. A tab that opens a URL is a link in `TabsNav` with the same look; the `line` variant is for tabs inside a panel.
478
- - **Links** read as text. A link inside a sentence, or offered alone as a section's way on, is `{colors.link}` and underlined at rest; a link that is itself the value, such as a record's name or a fact, keeps the text's colour and underlines on hover; a secondary way out beside the content, such as a card header's "see everything" or a return link, is `{colors.muted}` and turns ink with its underline on hover. The underline sits 4 px under the text.
479
- - **Fields** keep the `{colors.input}` edge, `{rounded.md}`, no shadow and at least `{spacing.control-height}`. Help sits beside its field, and validation stays with it.
776
+ - **Tabs** switch between sibling views of one object. Use `Tabs` with `TabsList`, `TabsTrigger` and `TabsContent` for panels; use `TabsNav` and `TabsLink` for URLs. Both page sections and local panels may use the shared `line` treatment for an underline or the default segmented treatment. Choose by the surrounding composition: existing shells retain their segmented tabs, and the inbox retains its local line tabs. Select the treatment through the component API, never a page border or colour override. Keep one treatment among siblings, label the group, and retain keyboard navigation and compact overflow. A choice that changes a value rather than a view uses radio or toggle semantics instead.
777
+ - **Links** read as text. A link inside a sentence, or offered alone as a section's way on, is `{colors.link}` and underlined at rest; a link that is itself the value, such as a record's name or a fact, keeps the text's colour and underlines on hover; a secondary way out beside the content, such as a card header's "see everything", uses the same blue link treatment. Breadcrumb and back navigation retain their quieter navigation treatment. The underline sits 4 px under the text.
778
+ - **Fields** keep the `{colors.input}` edge, `{rounded.sm}`, no shadow and at least `{spacing.control-height}`. Help stays with its field; in configuration sections, the label and help sit above the control, and validation stays with it.
480
779
  - **Focus** is drawn once, by the base layer, on every `:focus-visible` element. A component never repaints it; it may move it to a proxy that stands for the focused element, as a field group does for its input.
481
780
  - Enabled buttons show a pointer; a disabled control advertises nothing. On coarse or non-hover input every target is at least `{spacing.touch-target}`, while a checkbox or radio mark stays compact and its label supplies the target. An unchecked checkbox or radio mark is its `{colors.input}` edge with no fill of its own, so it shows the surface it sits on; checked, it fills with the action pair.
482
781
 
483
782
  ### Cards and sections
484
783
 
485
- `Card` is the product's container, at the resting height by default ("Elevation & Depth"). A row of cards is a `CardRow`: siblings share one height, and the row collapses to one column below its breakpoint. A card or settings section is composed around a real task, collection or form; its header names the content and may explain scope or consequence, and it is never a card holding only an introduction. A settings section owns its heading, a one-line description of what the setting changes, its fields and its local save. On a settings page each card saves its own fields with its own Save in its footer, always visible and disabled until something in that card changes; nothing on a settings page applies on change. Only the preferences in the account menu, Theme and Language, apply at once. Its footer is a strip one tone off the card body inside the card's edge, the `recessed` role behind a `{colors.line}` seam: `{colors.canvas-soft}` below the white card in light, and `{colors.dark-material-raised}` one step above the resting tone in dark: an optional hint on the left, such as the plan a feature needs or a link to learn more, and the section's action on the right; a facts section shows labelled facts as plain pairs, the label above its value, in two equal columns, stacked on compact screens, with every label kept in place when a value is missing; a fact never gets a box of its own inside the card. A KPI card is one figure: label, value, the change as a badge in the top right, and a footer with the comparison.
784
+ `Card` is the product's container, at the resting height by default ("Elevation & Depth"). A row of cards is a `CardRow`: siblings share one height, and the row collapses to one column below its breakpoint. A card or settings section is composed around a real task, collection or form; its header names the content and may explain scope or consequence, and it is never a card holding only an introduction. A settings section owns its heading, a one-line description of what the setting changes, its fields and its local save. On a settings page each card saves its own fields with its own Save in its footer, always visible and disabled until something in that card changes; nothing on a settings page applies on change. Only the preferences in the account menu, Theme and Language, apply at once. Its footer is a strip one tone off the card body inside the card's edge, the `recessed` role behind a `{colors.line}` seam: `{colors.canvas-soft}` below the white card in light, and `{colors.dark-canvas}` below the resting body in dark: an optional hint on the left, such as the plan a feature needs or a link to learn more, and the section's action on the right; a facts section shows labelled facts as plain pairs, the label above its value, in two equal columns, stacked on compact screens, with every label kept in place when a value is missing; a fact never gets a box of its own inside the card. A KPI card is one figure: label, value, the change as a badge in the top right, and a footer with the comparison.
486
785
 
487
786
  ### Menus and commands
488
787
 
489
- A record's actions sit behind one three-dot trigger, a `{spacing.control-height}` ghost icon button that opens an end-aligned menu. In a collection every row's commands live in that menu, enabling, disabling, editing, pairing again and removing included, as plain text commands in task order: Edit, then the record's own commands, then the views that read it; a command that cannot run now stays in place, greyed, with its reason; the destructive command comes last, in the error colour, after a separator. A row shows no other text button, with one exception: a row whose main job is to start or open something it is not doing yet shows that one outline button ("Connect", "Connect again", "Manage"), and a row that has any other command keeps its three dots after that button. Menu rows put the label first and a decorative icon at the trailing edge.
788
+ A record's primary destination is its visible name or a labelled link. Navigation is never hidden in its action menu. Keep the main task visible: Edit can expand a row in place, as in Vercel Domains; Connect belongs beside an available connection. Additional mutations use an end-aligned `ActionMenu`, with destructive commands after a separator. A menu is not required when the row has no secondary mutations. Disabled commands retain an explanation. The three-dot trigger is a `{spacing.control-height}` ghost icon button. Menu rows put the label first and a decorative icon at the trailing edge; a destructive command never becomes the loudest repeated action.
789
+
790
+ Choose the surface from the task. Settings use a centred column of independently saved `SettingsSection` fieldsets with labels above bounded controls. A short row action uses `FormDialog`; editing a fact on a detail page stays in place. An invitation can be an inline fieldset above the member directory. A substantial workflow that needs persistent context may opt into `FormDrawer`. Confirmation of a consequence uses `ConfirmDialog`. A dedicated workflow page remains a page. Never select a drawer merely because an action accepts input. These choices follow the live Vercel General settings, Members and Domains pages and the [Geist Modal](https://vercel.com/geist/modal) and [Fieldset](https://vercel.com/geist/fieldset) patterns.
791
+
792
+ Collection search and filters stay immediately above their rows. The title row holds the collection's primary action, unless an inline form already exposes that task. Scope selectors preserve the authorized organization or account. Filters do not compete with primary actions. The account menu identifies the signed-in person and exposes their account settings, theme and language.
490
793
 
491
- Every collection has the same toolbar: Add filter when the collection has facets, the search filling the row, then sort; the page's actions are never in it, they sit on the title's row ("Page compositions"). Creating a record (New or Add) is exactly one `default` button there; it opens a drawer from the right with the "Add …" title and a close control at the top, the fields in its body and the save as the primary button in its footer, and never an inline form on the page. Editing a record and connecting one (an app, a channel, a server) open the same drawer, from the record's Edit or Connect; a centred dialog only confirms a consequence: delete, revoke, sign out. One component draws that drawer in every product: `FormDrawer` (`@nadicodeai/ui/components/form-drawer`). A page has at most one other visible action, an `outline` button left of the create button; anything more goes in the page's three dots. A search box never stands alone in a box of its own. A collection's filters are chips, never a dropdown above the table: an Add filter button, a dashed chip for each suggested filter that applies in one click, and one outline chip per active filter with its own remove control. A sort control, an outline button naming the current order, sits at the toolbar's trailing edge. The scope a page works in, such as the company or the account it reports on, is a scope switcher: a ghost trigger reading the current name and ⇅ that opens a popover with a Find search, the choices with the current one checked, and a Create row at the bottom when the scope can be created there. The account menu names who is signed in, carries one gear to the account section, and holds the Theme and Language choices as compact inline rows. The command palette is glass and fills its dialog.
492
794
 
493
795
  ### Tables and missing values
494
796
 
495
- A table is native table structure at one density: 48 px body rows. A collection row is one line: the record's identity and at most about five facts, and anything more lives on the record's page. Rows explain themselves by their cells (an identity with its orb or mark, a status badge, a tag, a relative time), so a collection's column header row is kept for screen readers only. A table whose columns are figures a reader cannot tell apart without their names, such as a statement or an analytics comparison, opts in to a visible header row of 36 px. The table inherits its enclosing surface, whose Card owns the outer edge; a visible header and every record except the last end with a quiet 1 px `border` divider. A body row takes the full `accent` fill on hover, on keyboard focus within it and while its menu is open; a selected row takes `muted`; headers and footers never highlight. A collection fits its container without sideways scrolling: identity and actions keep their columns, and supporting facts move into a detail row inside the same record before the table gets cramped. The identity column claims up to a third of the table, gives it back down to a floor, and holds its name on one line: a long name ends in an ellipsis and is whole in its title, as a dashboard row does. A fact holds one line at its own width; a column of words, such as a summary or a sentence, holds one line up to a cap of `16rem`, shrinks to a floor of its own in a narrow table, and past the cap ends in an ellipsis, whole in its title, as a long name does. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it shows its header row. In the compact layout every cell but the identity wraps between words. Text anywhere in the product breaks between words, and inside a word only when that word alone cannot fit a line; a machine string such as a hash or a key may break at any character. One categorical fact in a row, such as an installation or a partner, is a `Tag`: a 24 px pill with its mark, dashed when the fact states an absence. An app in a row is its brand mark in the 28 px tile.
797
+ A table is native table structure: 40 px rows for comparable facts and 48 px rows for entity collections. Visible headings use 14 px / 20 px at weight 500 in a 36 px row; numeric columns align to the trailing edge with tabular figures. A collection row is one line: the record's identity and at most about five facts, and anything more lives on the record's page. Column headings are visible by default and remain visible when people compare or sort values. A compact entity collection whose cells identify themselves may keep accessible headings visually hidden with `showColumnHeaders={false}`; choose this exception deliberately. A simple descriptive list uses `ItemGroup` and `Item` instead of table semantics. The table inherits its enclosing surface, whose Card owns the outer edge; a visible header and every record except the last end with a quiet 1 px `border` divider. An interactive body row takes the full `accent` fill on hover, on keyboard focus within it and while its menu is open; a passive table does not imply an action on hover; a selected row takes `muted`; headers and footers never highlight. A collection fits its container without sideways scrolling: identity and actions keep their columns, and supporting facts move into a detail row inside the same record before the table gets cramped. The identity column claims up to a third of the table, gives supporting columns more room in a wide comparison, gives it back down to a floor, and holds its name on one line: a long name ends in an ellipsis and is whole in its title, as a dashboard row does. A fact holds one line at its own width; a status sentence in a figure column wraps at word boundaries while the figure keeps its numeric alignment. A column of words, such as a summary or a sentence, holds one line up to a cap of `16rem`, shrinks to a floor of its own in a narrow table, and past the cap ends in an ellipsis, whole in its title, as a long name does. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it shows its header row. In the compact layout every cell but the identity wraps between words. Text anywhere in the product breaks between words, and inside a word only when that word alone cannot fit a line; a machine string such as a hash or a key may break at any character. One categorical fact in a row, such as an installation or a partner, is a `Tag`: a 24 px pill with its mark, dashed when the fact states an absence. An app in a row is its brand mark in the 28 px tile.
496
798
 
497
799
  An unknown or not-applicable value in a table, a comparison list or a set of facts is a muted `—` with screen-reader text, in the same slot as a present value; zero stays zero. An absent report is not `None`: `None` is only for a complete report that found nothing. A stale value keeps its last reading with a short `Stale` qualifier. Loading, failure, missing permission and actionable setup each keep their own visible state and never turn into a dash. Essential reasons and actions never live only in a hover tooltip.
498
800
 
@@ -503,7 +805,7 @@ A status is one Badge: a mark and a word in one of five tones, so it reads witho
503
805
  | Tone | Means | Pair |
504
806
  | --- | --- | --- |
505
807
  | Gray | Default, and every terminal state that needs no action | `{colors.canvas-soft-2}` / `{colors.body}` |
506
- | Green | Healthy, complete | `{colors.link-bg-soft}` / `{colors.link-deep}` |
808
+ | Green | Healthy, complete | `{colors.success-soft}` / `{colors.success-deep}` |
507
809
  | Red | Unusable, failed | `{colors.error-soft}` / `{colors.error-deep}` |
508
810
  | Amber | Waiting on a person's decision | `{colors.warning-soft}` / `{colors.warning-deep}` |
509
811
  | Blue | A step in motion | `{colors.info-soft}` / `{colors.info-deep}` |
@@ -555,11 +857,11 @@ An Agent is shown as one card shape wherever it appears as an object, in the Por
555
857
 
556
858
  ### Company card
557
859
 
558
- The sign-up page draws the account being made as the company's card: one object of the ID-1 proportion, dark by nature in every theme, carrying on its top line the wordmark and, once Sofia has found it on the company's site, the company's own logo after a hairline, stamped in `{colors.identity-white}` as one ink (a company with no logo found has the wordmark alone); below, the holder's name and the company's domain. No Agent stands on it, and nothing sits in the engraving: a guilloché engraving in `{colors.identity-white}` at 10%. Beside a wide form it fills in as the form is typed and answers the pointer; on a phone the form keeps the screen, and the card appears once it is issued. It is the one object besides an Agent that moves on its own, once: when it first appears, and again when the account is asked for and it is issued, it turns in the light in 2.2 s, leaning one way and back while the light crosses it from corner to corner, so a person sees it is a card that answers the hand, and then it is still. The foil wakes as the pointer comes within one card width and eases toward it; a pointer that near ends the turn, and a pointer anywhere else on the page moves nothing. The account's Profile shows the person the same card. Where the browser has WebGPU, its foil is vgpu's holographic card shader: matte graphite (`{colors.dark-material-floating}`) whose etched contours diffract the pointer's light, turning toward it by at most 9°. The diffraction and its pearlescent wash fall in pure bands of the identity hues, never a colour between two of them: a company's card takes the cool half, cobalto, azzurro and verde, and a Partner's the warm half, giallo, arancio and rosso. Without WebGPU the engraving catches the light as flat, hard-edged stripes of the same hues. Under reduced motion the card keeps still and the light still finds the foil. The form owns every word the card repeats, so the card is hidden from assistive technology.
860
+ The sign-up page draws the account being made as the company's card: one object of the ID-1 proportion, dark by nature in every theme, carrying on its top line the wordmark and, once Sofia has found it on the company's site, the company's own logo after a hairline, stamped in `{colors.identity-white}` as one ink (a company with no logo found has the wordmark alone); below, the holder's name and the company's domain. No Agent stands on it, and nothing sits in the engraving: a guilloché engraving in `{colors.identity-white}` at 10%. Beside a wide form it fills in as the form is typed and answers the pointer; on a phone the form keeps the screen, and the card appears once it is issued. It is the one object besides an Agent that moves on its own, once: when it first appears, and again when the account is asked for and it is issued, it turns in the light in 2.2 s, leaning one way and back while the light crosses it from corner to corner, so a person sees it is a card that answers the hand, and then it is still. The foil wakes as the pointer comes within one card width and eases toward it; a pointer that near ends the turn, and a pointer anywhere else on the page moves nothing. The account's Profile shows the person the same card, and the Partner invitation panel opens with its card, the Partner edition, on the identity plane, as the invitation it shares. Arriving through a Partner's referral link, the sign-up plane stands the Partner's card behind the one being made, leaning back, so the person's own card stays in front and whole; the Partner's email invitation dialog shows the card the invited company will be issued. Where the browser has WebGPU, its foil is vgpu's holographic card shader: matte graphite (`{colors.dark-material-floating}`) whose etched contours diffract the pointer's light, turning toward it by at most 9°. The diffraction and its pearlescent wash fall in pure bands of the identity hues, never a colour between two of them: a company's card takes the cool half, cobalto, azzurro and verde, and a Partner's the warm half, giallo, arancio and rosso. Without WebGPU the engraving catches the light as flat, hard-edged stripes of the same hues. Under reduced motion the card keeps still and the light still finds the foil. The form owns every word the card repeats, so the card is hidden from assistive technology.
559
861
 
560
862
  ### Identity marks
561
863
 
562
- A recognised app, platform or model maker is drawn with its genuine mark beside its readable name, in a tile that owns the box; an unknown one keeps its name and a text monogram, never a guessed logo. Where a mark names a row, the tile is 28 px for an app or a channel, in a collection's 48 px row, and 24 px for a model's maker, in a model list, a routing table or a setting, and on the website's lists of apps and makers; a mark grows only where it stands for the object itself, as on its card or at the head of its page, and there every surface of that object draws it at one size. A mark identifies and never replaces the label, and its colour identifies the maker, never a connection's health. Marks are used nominatively, to name a platform, an integration or a provider, never next to wording that implies endorsement. A brand mark never stands in for a UI icon, and a UI icon never stands in for a brand. A Library skill is drawn with its line icon on a `card` tile with a hairline `border`, the tile an app mark sits in, and a plugin with its line icon on the `info-soft` tile (`CatalogTile`), at 40 px in a row and 64 px at the head of its page, so the tile's ground says which kind it is; the icon is one of the catalog's fixed set, never a brand mark, and an app keeps its brand mark. In a chart, a model is named by its label, and marks stay out of legends.
864
+ A recognised app, platform or model maker is drawn with its genuine mark beside its readable name, in a tile that owns the box; an unknown one keeps its name and a text monogram, never a guessed logo. Where a mark names a row, the tile is 28 px for an app or a channel, in a collection's 48 px row, and 24 px for a model's maker, in a model list, a routing table or a setting, and on the website's lists of apps and makers; a mark grows only where it stands for the object itself, as on its card or at the head of its page, and there every surface of that object draws it at one size. A mark identifies and never replaces the label, and its colour identifies the maker, never a connection's health. Marks are used nominatively, to name a platform, an integration or a provider, never next to wording that implies endorsement. A brand mark never stands in for a UI icon, and a UI icon never stands in for a brand. A Library skill uses the purple `CatalogTile` tone and a plugin uses its blue `info` tone, each with the catalog's line icon, at 40 px in a row and 64 px at the head of its page, so the tile's ground says which kind it is; the icon is one of the catalog's fixed set, never a brand mark, and an app keeps its brand mark. In a chart, a model is named by its label, and marks stay out of legends.
563
865
 
564
866
  ### Product views
565
867
 
@@ -579,7 +881,7 @@ These four ship as framework-neutral CSS; `@nadicodeai/ui` wraps the last three
579
881
 
580
882
  ## Iconography
581
883
 
582
- Every product draws Lucide's shapes, unedited, in the house render: a 1.5 px line (`{spacing.icon-stroke}`) measured on screen at every size and 1.25 px (`{spacing.icon-stroke-sm}`) at 12 px, round caps and joins, no fill, and the text colour. Sizes are 12, 16, 20 and 24 px, 16 by default, and inside a stock component the registry's own icon size stands; persistent navigation uses 20 so its entries read before the content beside them, and a collapsed rail keeps 16. A surface draws a small, disciplined set, never the whole library.
884
+ Every product draws Lucide's shapes, unedited, in the house render: a 1.5 px line (`{spacing.icon-stroke}`) measured on screen at every size and 1.25 px (`{spacing.icon-stroke-sm}`) at 12 px, round caps and joins, no fill, and the text colour by default. Semantic icons may use their status or category tone with an adjacent readable label; navigation icons stay neutral. Sizes are 12, 16, 20 and 24 px, 16 by default, and inside a stock component the registry's own icon size stands; persistent navigation uses 20 so its entries read before the content beside them, and a collapsed rail keeps 16. A surface draws a small, disciplined set, never the whole library.
583
885
 
584
886
  Three icons are the house's own, drawn on Lucide's grid in the same render: `arch` for the company and its doors, `approvals`, the arch with a check, for work waiting on a person, and `agent`, the orb seen face-on with the Agent as the dot on its path. They name those three things wherever an icon does, and no Lucide glyph stands in for them. An icon beside text is decorative; an icon-only control carries an accessible name.
585
887
 
@@ -637,70 +939,953 @@ The YAML front matter holds the four groups the DESIGN.md format defines: colors
637
939
  "core": {
638
940
  "material": {
639
941
  "$type": "number",
640
- "scrim-opacity": {"$value":0.5},
641
- "glass-opacity": {"$value":0.6},
642
- "glass-saturation": {"$value":1.8},
643
- "glass-blur": {"$type":"dimension","$value":{"value":20,"unit":"px"}}
942
+ "scrim-opacity": {
943
+ "$value": 0.5
944
+ },
945
+ "glass-opacity": {
946
+ "$value": 0.6
947
+ },
948
+ "glass-saturation": {
949
+ "$value": 1.8
950
+ },
951
+ "glass-blur": {
952
+ "$type": "dimension",
953
+ "$value": {
954
+ "value": 20,
955
+ "unit": "px"
956
+ }
957
+ },
958
+ "glass-highlight": {
959
+ "$type": "shadow",
960
+ "$value": {
961
+ "offsetX": {
962
+ "value": 0,
963
+ "unit": "px"
964
+ },
965
+ "offsetY": {
966
+ "value": 1,
967
+ "unit": "px"
968
+ },
969
+ "blur": {
970
+ "value": 0,
971
+ "unit": "px"
972
+ },
973
+ "spread": {
974
+ "value": 0,
975
+ "unit": "px"
976
+ },
977
+ "color": {
978
+ "colorSpace": "srgb",
979
+ "components": [
980
+ 1,
981
+ 1,
982
+ 1
983
+ ],
984
+ "alpha": 0.18
985
+ },
986
+ "inset": true
987
+ }
988
+ }
644
989
  },
645
990
  "elevation": {
646
991
  "$type": "shadow",
647
992
  "light": {
648
993
  "resting": {
649
994
  "$value": [
650
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":2,"unit":"px"},"blur":{"value":2,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
651
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.08}}
995
+ {
996
+ "offsetX": {
997
+ "value": 0,
998
+ "unit": "px"
999
+ },
1000
+ "offsetY": {
1001
+ "value": 0,
1002
+ "unit": "px"
1003
+ },
1004
+ "blur": {
1005
+ "value": 0,
1006
+ "unit": "px"
1007
+ },
1008
+ "spread": {
1009
+ "value": 1,
1010
+ "unit": "px"
1011
+ },
1012
+ "color": {
1013
+ "colorSpace": "srgb",
1014
+ "components": [
1015
+ 0,
1016
+ 0,
1017
+ 0
1018
+ ],
1019
+ "alpha": 0.08
1020
+ }
1021
+ },
1022
+ {
1023
+ "offsetX": {
1024
+ "value": 0,
1025
+ "unit": "px"
1026
+ },
1027
+ "offsetY": {
1028
+ "value": 2,
1029
+ "unit": "px"
1030
+ },
1031
+ "blur": {
1032
+ "value": 2,
1033
+ "unit": "px"
1034
+ },
1035
+ "spread": {
1036
+ "value": 0,
1037
+ "unit": "px"
1038
+ },
1039
+ "color": {
1040
+ "colorSpace": "srgb",
1041
+ "components": [
1042
+ 0,
1043
+ 0,
1044
+ 0
1045
+ ],
1046
+ "alpha": 0.04
1047
+ }
1048
+ },
1049
+ {
1050
+ "offsetX": {
1051
+ "value": 0,
1052
+ "unit": "px"
1053
+ },
1054
+ "offsetY": {
1055
+ "value": 0,
1056
+ "unit": "px"
1057
+ },
1058
+ "blur": {
1059
+ "value": 0,
1060
+ "unit": "px"
1061
+ },
1062
+ "spread": {
1063
+ "value": 1,
1064
+ "unit": "px"
1065
+ },
1066
+ "color": {
1067
+ "colorSpace": "srgb",
1068
+ "components": [
1069
+ 1,
1070
+ 1,
1071
+ 1
1072
+ ],
1073
+ "alpha": 1
1074
+ }
1075
+ }
652
1076
  ]
653
1077
  },
654
1078
  "raised": {
655
1079
  "$value": [
656
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":2,"unit":"px"},"blur":{"value":2,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
657
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":8,"unit":"px"},"blur":{"value":8,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
658
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.08}}
1080
+ {
1081
+ "offsetX": {
1082
+ "value": 0,
1083
+ "unit": "px"
1084
+ },
1085
+ "offsetY": {
1086
+ "value": 0,
1087
+ "unit": "px"
1088
+ },
1089
+ "blur": {
1090
+ "value": 0,
1091
+ "unit": "px"
1092
+ },
1093
+ "spread": {
1094
+ "value": 1,
1095
+ "unit": "px"
1096
+ },
1097
+ "color": {
1098
+ "colorSpace": "srgb",
1099
+ "components": [
1100
+ 0,
1101
+ 0,
1102
+ 0
1103
+ ],
1104
+ "alpha": 0.08
1105
+ }
1106
+ },
1107
+ {
1108
+ "offsetX": {
1109
+ "value": 0,
1110
+ "unit": "px"
1111
+ },
1112
+ "offsetY": {
1113
+ "value": 2,
1114
+ "unit": "px"
1115
+ },
1116
+ "blur": {
1117
+ "value": 2,
1118
+ "unit": "px"
1119
+ },
1120
+ "spread": {
1121
+ "value": 0,
1122
+ "unit": "px"
1123
+ },
1124
+ "color": {
1125
+ "colorSpace": "srgb",
1126
+ "components": [
1127
+ 0,
1128
+ 0,
1129
+ 0
1130
+ ],
1131
+ "alpha": 0.04
1132
+ }
1133
+ },
1134
+ {
1135
+ "offsetX": {
1136
+ "value": 0,
1137
+ "unit": "px"
1138
+ },
1139
+ "offsetY": {
1140
+ "value": 8,
1141
+ "unit": "px"
1142
+ },
1143
+ "blur": {
1144
+ "value": 8,
1145
+ "unit": "px"
1146
+ },
1147
+ "spread": {
1148
+ "value": -8,
1149
+ "unit": "px"
1150
+ },
1151
+ "color": {
1152
+ "colorSpace": "srgb",
1153
+ "components": [
1154
+ 0,
1155
+ 0,
1156
+ 0
1157
+ ],
1158
+ "alpha": 0.04
1159
+ }
1160
+ },
1161
+ {
1162
+ "offsetX": {
1163
+ "value": 0,
1164
+ "unit": "px"
1165
+ },
1166
+ "offsetY": {
1167
+ "value": 0,
1168
+ "unit": "px"
1169
+ },
1170
+ "blur": {
1171
+ "value": 0,
1172
+ "unit": "px"
1173
+ },
1174
+ "spread": {
1175
+ "value": 1,
1176
+ "unit": "px"
1177
+ },
1178
+ "color": {
1179
+ "colorSpace": "srgb",
1180
+ "components": [
1181
+ 1,
1182
+ 1,
1183
+ 1
1184
+ ],
1185
+ "alpha": 1
1186
+ }
1187
+ }
659
1188
  ]
660
1189
  },
661
1190
  "floating": {
662
1191
  "$value": [
663
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":1,"unit":"px"},"blur":{"value":1,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
664
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":4,"unit":"px"},"blur":{"value":8,"unit":"px"},"spread":{"value":-4,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
665
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":16,"unit":"px"},"blur":{"value":24,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.06}},
666
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.08}}
1192
+ {
1193
+ "offsetX": {
1194
+ "value": 0,
1195
+ "unit": "px"
1196
+ },
1197
+ "offsetY": {
1198
+ "value": 0,
1199
+ "unit": "px"
1200
+ },
1201
+ "blur": {
1202
+ "value": 0,
1203
+ "unit": "px"
1204
+ },
1205
+ "spread": {
1206
+ "value": 1,
1207
+ "unit": "px"
1208
+ },
1209
+ "color": {
1210
+ "colorSpace": "srgb",
1211
+ "components": [
1212
+ 0,
1213
+ 0,
1214
+ 0
1215
+ ],
1216
+ "alpha": 0.08
1217
+ }
1218
+ },
1219
+ {
1220
+ "offsetX": {
1221
+ "value": 0,
1222
+ "unit": "px"
1223
+ },
1224
+ "offsetY": {
1225
+ "value": 1,
1226
+ "unit": "px"
1227
+ },
1228
+ "blur": {
1229
+ "value": 1,
1230
+ "unit": "px"
1231
+ },
1232
+ "spread": {
1233
+ "value": 0,
1234
+ "unit": "px"
1235
+ },
1236
+ "color": {
1237
+ "colorSpace": "srgb",
1238
+ "components": [
1239
+ 0,
1240
+ 0,
1241
+ 0
1242
+ ],
1243
+ "alpha": 0.02
1244
+ }
1245
+ },
1246
+ {
1247
+ "offsetX": {
1248
+ "value": 0,
1249
+ "unit": "px"
1250
+ },
1251
+ "offsetY": {
1252
+ "value": 4,
1253
+ "unit": "px"
1254
+ },
1255
+ "blur": {
1256
+ "value": 8,
1257
+ "unit": "px"
1258
+ },
1259
+ "spread": {
1260
+ "value": -4,
1261
+ "unit": "px"
1262
+ },
1263
+ "color": {
1264
+ "colorSpace": "srgb",
1265
+ "components": [
1266
+ 0,
1267
+ 0,
1268
+ 0
1269
+ ],
1270
+ "alpha": 0.04
1271
+ }
1272
+ },
1273
+ {
1274
+ "offsetX": {
1275
+ "value": 0,
1276
+ "unit": "px"
1277
+ },
1278
+ "offsetY": {
1279
+ "value": 16,
1280
+ "unit": "px"
1281
+ },
1282
+ "blur": {
1283
+ "value": 24,
1284
+ "unit": "px"
1285
+ },
1286
+ "spread": {
1287
+ "value": -8,
1288
+ "unit": "px"
1289
+ },
1290
+ "color": {
1291
+ "colorSpace": "srgb",
1292
+ "components": [
1293
+ 0,
1294
+ 0,
1295
+ 0
1296
+ ],
1297
+ "alpha": 0.06
1298
+ }
1299
+ },
1300
+ {
1301
+ "offsetX": {
1302
+ "value": 0,
1303
+ "unit": "px"
1304
+ },
1305
+ "offsetY": {
1306
+ "value": 0,
1307
+ "unit": "px"
1308
+ },
1309
+ "blur": {
1310
+ "value": 0,
1311
+ "unit": "px"
1312
+ },
1313
+ "spread": {
1314
+ "value": 1,
1315
+ "unit": "px"
1316
+ },
1317
+ "color": {
1318
+ "colorSpace": "srgb",
1319
+ "components": [
1320
+ 1,
1321
+ 1,
1322
+ 1
1323
+ ],
1324
+ "alpha": 1
1325
+ }
1326
+ }
667
1327
  ]
668
1328
  },
669
1329
  "modal": {
670
1330
  "$value": [
671
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":1,"unit":"px"},"blur":{"value":1,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
672
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":8,"unit":"px"},"blur":{"value":16,"unit":"px"},"spread":{"value":-4,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
673
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":24,"unit":"px"},"blur":{"value":32,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.06}},
674
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.08}}
1331
+ {
1332
+ "offsetX": {
1333
+ "value": 0,
1334
+ "unit": "px"
1335
+ },
1336
+ "offsetY": {
1337
+ "value": 0,
1338
+ "unit": "px"
1339
+ },
1340
+ "blur": {
1341
+ "value": 0,
1342
+ "unit": "px"
1343
+ },
1344
+ "spread": {
1345
+ "value": 1,
1346
+ "unit": "px"
1347
+ },
1348
+ "color": {
1349
+ "colorSpace": "srgb",
1350
+ "components": [
1351
+ 0,
1352
+ 0,
1353
+ 0
1354
+ ],
1355
+ "alpha": 0.08
1356
+ }
1357
+ },
1358
+ {
1359
+ "offsetX": {
1360
+ "value": 0,
1361
+ "unit": "px"
1362
+ },
1363
+ "offsetY": {
1364
+ "value": 1,
1365
+ "unit": "px"
1366
+ },
1367
+ "blur": {
1368
+ "value": 1,
1369
+ "unit": "px"
1370
+ },
1371
+ "spread": {
1372
+ "value": 0,
1373
+ "unit": "px"
1374
+ },
1375
+ "color": {
1376
+ "colorSpace": "srgb",
1377
+ "components": [
1378
+ 0,
1379
+ 0,
1380
+ 0
1381
+ ],
1382
+ "alpha": 0.02
1383
+ }
1384
+ },
1385
+ {
1386
+ "offsetX": {
1387
+ "value": 0,
1388
+ "unit": "px"
1389
+ },
1390
+ "offsetY": {
1391
+ "value": 8,
1392
+ "unit": "px"
1393
+ },
1394
+ "blur": {
1395
+ "value": 16,
1396
+ "unit": "px"
1397
+ },
1398
+ "spread": {
1399
+ "value": -4,
1400
+ "unit": "px"
1401
+ },
1402
+ "color": {
1403
+ "colorSpace": "srgb",
1404
+ "components": [
1405
+ 0,
1406
+ 0,
1407
+ 0
1408
+ ],
1409
+ "alpha": 0.04
1410
+ }
1411
+ },
1412
+ {
1413
+ "offsetX": {
1414
+ "value": 0,
1415
+ "unit": "px"
1416
+ },
1417
+ "offsetY": {
1418
+ "value": 24,
1419
+ "unit": "px"
1420
+ },
1421
+ "blur": {
1422
+ "value": 32,
1423
+ "unit": "px"
1424
+ },
1425
+ "spread": {
1426
+ "value": -8,
1427
+ "unit": "px"
1428
+ },
1429
+ "color": {
1430
+ "colorSpace": "srgb",
1431
+ "components": [
1432
+ 0,
1433
+ 0,
1434
+ 0
1435
+ ],
1436
+ "alpha": 0.06
1437
+ }
1438
+ },
1439
+ {
1440
+ "offsetX": {
1441
+ "value": 0,
1442
+ "unit": "px"
1443
+ },
1444
+ "offsetY": {
1445
+ "value": 0,
1446
+ "unit": "px"
1447
+ },
1448
+ "blur": {
1449
+ "value": 0,
1450
+ "unit": "px"
1451
+ },
1452
+ "spread": {
1453
+ "value": 1,
1454
+ "unit": "px"
1455
+ },
1456
+ "color": {
1457
+ "colorSpace": "srgb",
1458
+ "components": [
1459
+ 1,
1460
+ 1,
1461
+ 1
1462
+ ],
1463
+ "alpha": 1
1464
+ }
1465
+ }
675
1466
  ]
676
1467
  }
677
1468
  },
678
1469
  "dark": {
679
1470
  "resting": {
680
1471
  "$value": [
681
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":1,"unit":"px"},"blur":{"value":2,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.16}},
682
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.145}}
1472
+ {
1473
+ "offsetX": {
1474
+ "value": 0,
1475
+ "unit": "px"
1476
+ },
1477
+ "offsetY": {
1478
+ "value": 0,
1479
+ "unit": "px"
1480
+ },
1481
+ "blur": {
1482
+ "value": 0,
1483
+ "unit": "px"
1484
+ },
1485
+ "spread": {
1486
+ "value": 1,
1487
+ "unit": "px"
1488
+ },
1489
+ "color": {
1490
+ "colorSpace": "srgb",
1491
+ "components": [
1492
+ 1,
1493
+ 1,
1494
+ 1
1495
+ ],
1496
+ "alpha": 0.145
1497
+ }
1498
+ },
1499
+ {
1500
+ "offsetX": {
1501
+ "value": 0,
1502
+ "unit": "px"
1503
+ },
1504
+ "offsetY": {
1505
+ "value": 1,
1506
+ "unit": "px"
1507
+ },
1508
+ "blur": {
1509
+ "value": 2,
1510
+ "unit": "px"
1511
+ },
1512
+ "spread": {
1513
+ "value": 0,
1514
+ "unit": "px"
1515
+ },
1516
+ "color": {
1517
+ "colorSpace": "srgb",
1518
+ "components": [
1519
+ 0,
1520
+ 0,
1521
+ 0
1522
+ ],
1523
+ "alpha": 0.16
1524
+ }
1525
+ },
1526
+ {
1527
+ "offsetX": {
1528
+ "value": 0,
1529
+ "unit": "px"
1530
+ },
1531
+ "offsetY": {
1532
+ "value": 0,
1533
+ "unit": "px"
1534
+ },
1535
+ "blur": {
1536
+ "value": 0,
1537
+ "unit": "px"
1538
+ },
1539
+ "spread": {
1540
+ "value": 1,
1541
+ "unit": "px"
1542
+ },
1543
+ "color": {
1544
+ "colorSpace": "srgb",
1545
+ "components": [
1546
+ 0,
1547
+ 0,
1548
+ 0
1549
+ ],
1550
+ "alpha": 1
1551
+ }
1552
+ }
683
1553
  ]
684
1554
  },
685
1555
  "raised": {
686
1556
  "$value": [
687
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":2,"unit":"px"},"blur":{"value":2,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.2}},
688
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":8,"unit":"px"},"blur":{"value":8,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.3}},
689
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.145}}
1557
+ {
1558
+ "offsetX": {
1559
+ "value": 0,
1560
+ "unit": "px"
1561
+ },
1562
+ "offsetY": {
1563
+ "value": 0,
1564
+ "unit": "px"
1565
+ },
1566
+ "blur": {
1567
+ "value": 0,
1568
+ "unit": "px"
1569
+ },
1570
+ "spread": {
1571
+ "value": 1,
1572
+ "unit": "px"
1573
+ },
1574
+ "color": {
1575
+ "colorSpace": "srgb",
1576
+ "components": [
1577
+ 1,
1578
+ 1,
1579
+ 1
1580
+ ],
1581
+ "alpha": 0.145
1582
+ }
1583
+ },
1584
+ {
1585
+ "offsetX": {
1586
+ "value": 0,
1587
+ "unit": "px"
1588
+ },
1589
+ "offsetY": {
1590
+ "value": 2,
1591
+ "unit": "px"
1592
+ },
1593
+ "blur": {
1594
+ "value": 2,
1595
+ "unit": "px"
1596
+ },
1597
+ "spread": {
1598
+ "value": 0,
1599
+ "unit": "px"
1600
+ },
1601
+ "color": {
1602
+ "colorSpace": "srgb",
1603
+ "components": [
1604
+ 0,
1605
+ 0,
1606
+ 0
1607
+ ],
1608
+ "alpha": 0.2
1609
+ }
1610
+ },
1611
+ {
1612
+ "offsetX": {
1613
+ "value": 0,
1614
+ "unit": "px"
1615
+ },
1616
+ "offsetY": {
1617
+ "value": 8,
1618
+ "unit": "px"
1619
+ },
1620
+ "blur": {
1621
+ "value": 8,
1622
+ "unit": "px"
1623
+ },
1624
+ "spread": {
1625
+ "value": -8,
1626
+ "unit": "px"
1627
+ },
1628
+ "color": {
1629
+ "colorSpace": "srgb",
1630
+ "components": [
1631
+ 0,
1632
+ 0,
1633
+ 0
1634
+ ],
1635
+ "alpha": 0.3
1636
+ }
1637
+ },
1638
+ {
1639
+ "offsetX": {
1640
+ "value": 0,
1641
+ "unit": "px"
1642
+ },
1643
+ "offsetY": {
1644
+ "value": 0,
1645
+ "unit": "px"
1646
+ },
1647
+ "blur": {
1648
+ "value": 0,
1649
+ "unit": "px"
1650
+ },
1651
+ "spread": {
1652
+ "value": 1,
1653
+ "unit": "px"
1654
+ },
1655
+ "color": {
1656
+ "colorSpace": "srgb",
1657
+ "components": [
1658
+ 0,
1659
+ 0,
1660
+ 0
1661
+ ],
1662
+ "alpha": 1
1663
+ }
1664
+ }
690
1665
  ]
691
1666
  },
692
1667
  "floating": {
693
1668
  "$value": [
694
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":4,"unit":"px"},"blur":{"value":8,"unit":"px"},"spread":{"value":-4,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.4}},
695
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":16,"unit":"px"},"blur":{"value":24,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.5}},
696
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.145}}
1669
+ {
1670
+ "offsetX": {
1671
+ "value": 0,
1672
+ "unit": "px"
1673
+ },
1674
+ "offsetY": {
1675
+ "value": 0,
1676
+ "unit": "px"
1677
+ },
1678
+ "blur": {
1679
+ "value": 0,
1680
+ "unit": "px"
1681
+ },
1682
+ "spread": {
1683
+ "value": 1,
1684
+ "unit": "px"
1685
+ },
1686
+ "color": {
1687
+ "colorSpace": "srgb",
1688
+ "components": [
1689
+ 1,
1690
+ 1,
1691
+ 1
1692
+ ],
1693
+ "alpha": 0.145
1694
+ }
1695
+ },
1696
+ {
1697
+ "offsetX": {
1698
+ "value": 0,
1699
+ "unit": "px"
1700
+ },
1701
+ "offsetY": {
1702
+ "value": 4,
1703
+ "unit": "px"
1704
+ },
1705
+ "blur": {
1706
+ "value": 8,
1707
+ "unit": "px"
1708
+ },
1709
+ "spread": {
1710
+ "value": -4,
1711
+ "unit": "px"
1712
+ },
1713
+ "color": {
1714
+ "colorSpace": "srgb",
1715
+ "components": [
1716
+ 0,
1717
+ 0,
1718
+ 0
1719
+ ],
1720
+ "alpha": 0.4
1721
+ }
1722
+ },
1723
+ {
1724
+ "offsetX": {
1725
+ "value": 0,
1726
+ "unit": "px"
1727
+ },
1728
+ "offsetY": {
1729
+ "value": 16,
1730
+ "unit": "px"
1731
+ },
1732
+ "blur": {
1733
+ "value": 24,
1734
+ "unit": "px"
1735
+ },
1736
+ "spread": {
1737
+ "value": -8,
1738
+ "unit": "px"
1739
+ },
1740
+ "color": {
1741
+ "colorSpace": "srgb",
1742
+ "components": [
1743
+ 0,
1744
+ 0,
1745
+ 0
1746
+ ],
1747
+ "alpha": 0.5
1748
+ }
1749
+ },
1750
+ {
1751
+ "offsetX": {
1752
+ "value": 0,
1753
+ "unit": "px"
1754
+ },
1755
+ "offsetY": {
1756
+ "value": 0,
1757
+ "unit": "px"
1758
+ },
1759
+ "blur": {
1760
+ "value": 0,
1761
+ "unit": "px"
1762
+ },
1763
+ "spread": {
1764
+ "value": 1,
1765
+ "unit": "px"
1766
+ },
1767
+ "color": {
1768
+ "colorSpace": "srgb",
1769
+ "components": [
1770
+ 0,
1771
+ 0,
1772
+ 0
1773
+ ],
1774
+ "alpha": 1
1775
+ }
1776
+ }
697
1777
  ]
698
1778
  },
699
1779
  "modal": {
700
1780
  "$value": [
701
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":8,"unit":"px"},"blur":{"value":16,"unit":"px"},"spread":{"value":-4,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.4}},
702
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":24,"unit":"px"},"blur":{"value":32,"unit":"px"},"spread":{"value":-8,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.55}},
703
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.145}}
1781
+ {
1782
+ "offsetX": {
1783
+ "value": 0,
1784
+ "unit": "px"
1785
+ },
1786
+ "offsetY": {
1787
+ "value": 0,
1788
+ "unit": "px"
1789
+ },
1790
+ "blur": {
1791
+ "value": 0,
1792
+ "unit": "px"
1793
+ },
1794
+ "spread": {
1795
+ "value": 1,
1796
+ "unit": "px"
1797
+ },
1798
+ "color": {
1799
+ "colorSpace": "srgb",
1800
+ "components": [
1801
+ 1,
1802
+ 1,
1803
+ 1
1804
+ ],
1805
+ "alpha": 0.145
1806
+ }
1807
+ },
1808
+ {
1809
+ "offsetX": {
1810
+ "value": 0,
1811
+ "unit": "px"
1812
+ },
1813
+ "offsetY": {
1814
+ "value": 8,
1815
+ "unit": "px"
1816
+ },
1817
+ "blur": {
1818
+ "value": 16,
1819
+ "unit": "px"
1820
+ },
1821
+ "spread": {
1822
+ "value": -4,
1823
+ "unit": "px"
1824
+ },
1825
+ "color": {
1826
+ "colorSpace": "srgb",
1827
+ "components": [
1828
+ 0,
1829
+ 0,
1830
+ 0
1831
+ ],
1832
+ "alpha": 0.4
1833
+ }
1834
+ },
1835
+ {
1836
+ "offsetX": {
1837
+ "value": 0,
1838
+ "unit": "px"
1839
+ },
1840
+ "offsetY": {
1841
+ "value": 24,
1842
+ "unit": "px"
1843
+ },
1844
+ "blur": {
1845
+ "value": 32,
1846
+ "unit": "px"
1847
+ },
1848
+ "spread": {
1849
+ "value": -8,
1850
+ "unit": "px"
1851
+ },
1852
+ "color": {
1853
+ "colorSpace": "srgb",
1854
+ "components": [
1855
+ 0,
1856
+ 0,
1857
+ 0
1858
+ ],
1859
+ "alpha": 0.55
1860
+ }
1861
+ },
1862
+ {
1863
+ "offsetX": {
1864
+ "value": 0,
1865
+ "unit": "px"
1866
+ },
1867
+ "offsetY": {
1868
+ "value": 0,
1869
+ "unit": "px"
1870
+ },
1871
+ "blur": {
1872
+ "value": 0,
1873
+ "unit": "px"
1874
+ },
1875
+ "spread": {
1876
+ "value": 1,
1877
+ "unit": "px"
1878
+ },
1879
+ "color": {
1880
+ "colorSpace": "srgb",
1881
+ "components": [
1882
+ 0,
1883
+ 0,
1884
+ 0
1885
+ ],
1886
+ "alpha": 1
1887
+ }
1888
+ }
704
1889
  ]
705
1890
  }
706
1891
  }
@@ -708,154 +1893,517 @@ The YAML front matter holds the four groups the DESIGN.md format defines: colors
708
1893
  "motion": {
709
1894
  "duration": {
710
1895
  "$type": "duration",
711
- "control": {"$value":{"value":100,"unit":"ms"}},
712
- "close": {"$value":{"value":140,"unit":"ms"}},
713
- "confirm": {"$value":{"value":180,"unit":"ms"}},
714
- "open": {"$value":{"value":220,"unit":"ms"}},
715
- "msg-in": {"$value":{"value":240,"unit":"ms"}},
716
- "transition": {"$value":{"value":240,"unit":"ms"}},
717
- "drawer-swipe": {"$value":{"value":400,"unit":"ms"}},
718
- "drawer": {"$value":{"value":450,"unit":"ms"}},
719
- "wait": {"$value":{"value":1.6,"unit":"s"}}
1896
+ "control": {
1897
+ "$value": {
1898
+ "value": 100,
1899
+ "unit": "ms"
1900
+ }
1901
+ },
1902
+ "close": {
1903
+ "$value": {
1904
+ "value": 140,
1905
+ "unit": "ms"
1906
+ }
1907
+ },
1908
+ "confirm": {
1909
+ "$value": {
1910
+ "value": 180,
1911
+ "unit": "ms"
1912
+ }
1913
+ },
1914
+ "open": {
1915
+ "$value": {
1916
+ "value": 220,
1917
+ "unit": "ms"
1918
+ }
1919
+ },
1920
+ "msg-in": {
1921
+ "$value": {
1922
+ "value": 240,
1923
+ "unit": "ms"
1924
+ }
1925
+ },
1926
+ "transition": {
1927
+ "$value": {
1928
+ "value": 240,
1929
+ "unit": "ms"
1930
+ }
1931
+ },
1932
+ "drawer-swipe": {
1933
+ "$value": {
1934
+ "value": 400,
1935
+ "unit": "ms"
1936
+ }
1937
+ },
1938
+ "drawer": {
1939
+ "$value": {
1940
+ "value": 450,
1941
+ "unit": "ms"
1942
+ }
1943
+ },
1944
+ "wait": {
1945
+ "$value": {
1946
+ "value": 1.6,
1947
+ "unit": "s"
1948
+ }
1949
+ }
720
1950
  },
721
1951
  "ease": {
722
1952
  "$type": "cubicBezier",
723
- "emphasized": {"$value":[0.3,0.7,0.2,1]},
724
- "out-strong": {"$value":[0.23,1,0.32,1]},
725
- "in-out-strong": {"$value":[0.77,0,0.175,1]},
726
- "drawer": {"$value":[0.32,0.72,0,1]}
1953
+ "emphasized": {
1954
+ "$value": [
1955
+ 0.3,
1956
+ 0.7,
1957
+ 0.2,
1958
+ 1
1959
+ ]
1960
+ },
1961
+ "out-strong": {
1962
+ "$value": [
1963
+ 0.23,
1964
+ 1,
1965
+ 0.32,
1966
+ 1
1967
+ ]
1968
+ },
1969
+ "in-out-strong": {
1970
+ "$value": [
1971
+ 0.77,
1972
+ 0,
1973
+ 0.175,
1974
+ 1
1975
+ ]
1976
+ },
1977
+ "drawer": {
1978
+ "$value": [
1979
+ 0.32,
1980
+ 0.72,
1981
+ 0,
1982
+ 1
1983
+ ]
1984
+ }
727
1985
  }
728
1986
  }
729
1987
  },
730
1988
  "semantic": {
731
- "radius": {"$type":"dimension","$value":"{core.rounded.md}"},
1989
+ "radius": {
1990
+ "$type": "dimension",
1991
+ "$value": "{core.rounded.md}"
1992
+ },
732
1993
  "color": {
733
1994
  "$type": "color",
734
1995
  "light": {
735
- "background": {"$value":"{core.color.canvas}"},
736
- "foreground": {"$value":"{core.color.ink}"},
737
- "card": {"$value":"{core.color.material-solid}"},
738
- "card-foreground": {"$value":"{core.color.ink}"},
739
- "popover": {"$value":"{core.color.material-solid}"},
740
- "popover-foreground": {"$value":"{core.color.ink}"},
741
- "primary": {"$value":"{core.color.action}"},
742
- "primary-foreground": {"$value":"{core.color.action-foreground}"},
743
- "primary-hover": {"$value":"{core.color.action-hover}"},
744
- "primary-active": {"$value":"{core.color.action-active}"},
745
- "secondary": {"$value":"{core.color.canvas-soft}"},
746
- "secondary-foreground": {"$value":"{core.color.ink}"},
747
- "muted": {"$value":"{core.color.canvas-soft}"},
748
- "muted-foreground": {"$value":"{core.color.body}"},
749
- "placeholder": {"$value":"{core.color.muted}"},
750
- "accent": {"$value":"{core.color.canvas-soft-2}"},
751
- "accent-foreground": {"$value":"{core.color.ink}"},
752
- "destructive": {"$value":"{core.color.error}"},
753
- "destructive-foreground": {"$value":"{core.color.identity-white}"},
754
- "warning": {"$value":"{core.color.warning}"},
755
- "warning-foreground": {"$value":"{core.color.ink}"},
756
- "success": {"$value":"{core.color.success}"},
757
- "success-foreground": {"$value":"{core.color.ink}"},
758
- "info": {"$value":"{core.color.info}"},
759
- "info-foreground": {"$value":"{core.color.identity-white}"},
760
- "destructive-soft": {"$value":"{core.color.error-soft}"},
761
- "destructive-soft-foreground": {"$value":"{core.color.error-deep}"},
762
- "warning-soft": {"$value":"{core.color.warning-soft}"},
763
- "warning-soft-foreground": {"$value":"{core.color.warning-deep}"},
764
- "success-soft": {"$value":"{core.color.success-soft}"},
765
- "success-soft-foreground": {"$value":"{core.color.success-deep}"},
766
- "info-soft": {"$value":"{core.color.info-soft}"},
767
- "info-soft-foreground": {"$value":"{core.color.info-deep}"},
768
- "border": {"$value":"{core.color.line}"},
769
- "input": {"$value":"{core.color.input}"},
770
- "ring": {"$value":"{core.color.focus-ring}"},
771
- "scrim": {"$value":"{core.color.scrim}"},
772
- "chart-1": {"$value":"{core.color.chart-1}"},
773
- "chart-2": {"$value":"{core.color.chart-2}"},
774
- "chart-3": {"$value":"{core.color.chart-3}"},
775
- "chart-4": {"$value":"{core.color.chart-4}"},
776
- "chart-5": {"$value":"{core.color.chart-5}"},
777
- "selection": {"$value":"{core.color.navigation-selection-bg}"},
778
- "selection-foreground": {"$value":"{core.color.navigation-selection-fg}"},
779
- "recessed": {"$value":"{core.color.canvas-soft}"},
780
- "sidebar": {"$value":"{core.color.canvas-soft}"},
781
- "sidebar-foreground": {"$value":"{core.color.ink}"},
782
- "sidebar-primary": {"$value":"{core.color.navigation-selection-bg}"},
783
- "sidebar-primary-foreground": {"$value":"{core.color.navigation-selection-fg}"},
784
- "sidebar-primary-hover": {"$value":"{core.color.navigation-selection-hover}"},
785
- "sidebar-accent": {"$value":"{core.color.canvas-soft-2}"},
786
- "sidebar-accent-foreground": {"$value":"{core.color.ink}"},
787
- "sidebar-border": {"$value":"{core.color.line}"},
788
- "sidebar-ring": {"$value":"{core.color.focus-ring}"}
1996
+ "background": {
1997
+ "$value": "{core.color.canvas}"
1998
+ },
1999
+ "foreground": {
2000
+ "$value": "{core.color.ink}"
2001
+ },
2002
+ "card": {
2003
+ "$value": "{core.color.material-solid}"
2004
+ },
2005
+ "card-foreground": {
2006
+ "$value": "{core.color.ink}"
2007
+ },
2008
+ "popover": {
2009
+ "$value": "{core.color.material-solid}"
2010
+ },
2011
+ "popover-foreground": {
2012
+ "$value": "{core.color.ink}"
2013
+ },
2014
+ "primary": {
2015
+ "$value": "{core.color.action}"
2016
+ },
2017
+ "primary-foreground": {
2018
+ "$value": "{core.color.action-foreground}"
2019
+ },
2020
+ "primary-hover": {
2021
+ "$value": "{core.color.action-hover}"
2022
+ },
2023
+ "primary-active": {
2024
+ "$value": "{core.color.action-active}"
2025
+ },
2026
+ "secondary": {
2027
+ "$value": "{core.color.canvas-soft}"
2028
+ },
2029
+ "secondary-foreground": {
2030
+ "$value": "{core.color.ink}"
2031
+ },
2032
+ "muted": {
2033
+ "$value": "{core.color.canvas-soft}"
2034
+ },
2035
+ "muted-foreground": {
2036
+ "$value": "{core.color.body}"
2037
+ },
2038
+ "placeholder": {
2039
+ "$value": "{core.color.muted}"
2040
+ },
2041
+ "accent": {
2042
+ "$value": "{core.color.canvas-soft-2}"
2043
+ },
2044
+ "accent-foreground": {
2045
+ "$value": "{core.color.ink}"
2046
+ },
2047
+ "destructive": {
2048
+ "$value": "{core.color.error}"
2049
+ },
2050
+ "destructive-foreground": {
2051
+ "$value": "{core.color.identity-white}"
2052
+ },
2053
+ "warning": {
2054
+ "$value": "{core.color.warning}"
2055
+ },
2056
+ "warning-foreground": {
2057
+ "$value": "{core.color.ink}"
2058
+ },
2059
+ "success": {
2060
+ "$value": "{core.color.success}"
2061
+ },
2062
+ "success-foreground": {
2063
+ "$value": "{core.color.ink}"
2064
+ },
2065
+ "info": {
2066
+ "$value": "{core.color.info}"
2067
+ },
2068
+ "info-foreground": {
2069
+ "$value": "{core.color.identity-white}"
2070
+ },
2071
+ "destructive-soft": {
2072
+ "$value": "{core.color.error-soft}"
2073
+ },
2074
+ "destructive-soft-foreground": {
2075
+ "$value": "{core.color.error-deep}"
2076
+ },
2077
+ "warning-soft": {
2078
+ "$value": "{core.color.warning-soft}"
2079
+ },
2080
+ "warning-soft-foreground": {
2081
+ "$value": "{core.color.warning-deep}"
2082
+ },
2083
+ "success-soft": {
2084
+ "$value": "{core.color.success-soft}"
2085
+ },
2086
+ "success-soft-foreground": {
2087
+ "$value": "{core.color.success-deep}"
2088
+ },
2089
+ "info-soft": {
2090
+ "$value": "{core.color.info-soft}"
2091
+ },
2092
+ "info-soft-foreground": {
2093
+ "$value": "{core.color.info-deep}"
2094
+ },
2095
+ "border": {
2096
+ "$value": "{core.color.line}"
2097
+ },
2098
+ "input": {
2099
+ "$value": "{core.color.input}"
2100
+ },
2101
+ "ring": {
2102
+ "$value": "{core.color.focus-ring}"
2103
+ },
2104
+ "scrim": {
2105
+ "$value": "{core.color.scrim}"
2106
+ },
2107
+ "chart-1": {
2108
+ "$value": "{core.color.chart-1}"
2109
+ },
2110
+ "chart-2": {
2111
+ "$value": "{core.color.chart-2}"
2112
+ },
2113
+ "chart-3": {
2114
+ "$value": "{core.color.chart-3}"
2115
+ },
2116
+ "chart-4": {
2117
+ "$value": "{core.color.chart-4}"
2118
+ },
2119
+ "chart-5": {
2120
+ "$value": "{core.color.chart-5}"
2121
+ },
2122
+ "selection": {
2123
+ "$value": "{core.color.navigation-selection-bg}"
2124
+ },
2125
+ "selection-foreground": {
2126
+ "$value": "{core.color.navigation-selection-fg}"
2127
+ },
2128
+ "recessed": {
2129
+ "$value": "{core.color.canvas-soft}"
2130
+ },
2131
+ "sidebar": {
2132
+ "$value": "{core.color.canvas-soft}"
2133
+ },
2134
+ "sidebar-foreground": {
2135
+ "$value": "{core.color.ink}"
2136
+ },
2137
+ "sidebar-primary": {
2138
+ "$value": "{core.color.navigation-selection-bg}"
2139
+ },
2140
+ "sidebar-primary-foreground": {
2141
+ "$value": "{core.color.navigation-selection-fg}"
2142
+ },
2143
+ "sidebar-primary-hover": {
2144
+ "$value": "{core.color.navigation-selection-hover}"
2145
+ },
2146
+ "sidebar-accent": {
2147
+ "$value": "{core.color.canvas-soft-2}"
2148
+ },
2149
+ "sidebar-accent-foreground": {
2150
+ "$value": "{core.color.ink}"
2151
+ },
2152
+ "sidebar-border": {
2153
+ "$value": "{core.color.line}"
2154
+ },
2155
+ "sidebar-ring": {
2156
+ "$value": "{core.color.focus-ring}"
2157
+ },
2158
+ "category-teal": {
2159
+ "$value": "{core.color.category-teal-soft}"
2160
+ },
2161
+ "category-teal-foreground": {
2162
+ "$value": "{core.color.category-teal-deep}"
2163
+ },
2164
+ "category-purple": {
2165
+ "$value": "{core.color.category-purple-soft}"
2166
+ },
2167
+ "category-purple-foreground": {
2168
+ "$value": "{core.color.category-purple-deep}"
2169
+ },
2170
+ "category-pink": {
2171
+ "$value": "{core.color.category-pink-soft}"
2172
+ },
2173
+ "category-pink-foreground": {
2174
+ "$value": "{core.color.category-pink-deep}"
2175
+ }
789
2176
  },
790
2177
  "dark": {
791
- "background": {"$value":"{core.color.dark-canvas}"},
792
- "foreground": {"$value":"{core.color.dark-ink}"},
793
- "card": {"$value":"{core.color.dark-material-solid}"},
794
- "card-foreground": {"$value":"{core.color.dark-ink}"},
795
- "popover": {"$value":"{core.color.dark-material-solid}"},
796
- "popover-foreground": {"$value":"{core.color.dark-ink}"},
797
- "primary": {"$value":"{core.color.dark-action}"},
798
- "primary-foreground": {"$value":"{core.color.dark-action-foreground}"},
799
- "primary-hover": {"$value":"{core.color.dark-action-hover}"},
800
- "primary-active": {"$value":"{core.color.dark-action-active}"},
801
- "secondary": {"$value":"{core.color.dark-canvas-soft}"},
802
- "secondary-foreground": {"$value":"{core.color.dark-ink}"},
803
- "muted": {"$value":"{core.color.dark-canvas-soft}"},
804
- "muted-foreground": {"$value":"{core.color.dark-body}"},
805
- "placeholder": {"$value":"{core.color.dark-muted}"},
806
- "accent": {"$value":"{core.color.dark-canvas-soft-2}"},
807
- "accent-foreground": {"$value":"{core.color.dark-ink}"},
808
- "destructive": {"$value":"{core.color.dark-error}"},
809
- "destructive-foreground": {"$value":"{core.color.dark-on-primary}"},
810
- "warning": {"$value":"{core.color.dark-warning}"},
811
- "warning-foreground": {"$value":"{core.color.dark-on-primary}"},
812
- "success": {"$value":"{core.color.dark-success}"},
813
- "success-foreground": {"$value":"{core.color.dark-on-primary}"},
814
- "info": {"$value":"{core.color.dark-info}"},
815
- "info-foreground": {"$value":"{core.color.dark-on-primary}"},
816
- "destructive-soft": {"$value":"{core.color.dark-error-soft}"},
817
- "destructive-soft-foreground": {"$value":"{core.color.dark-error-deep}"},
818
- "warning-soft": {"$value":"{core.color.dark-warning-soft}"},
819
- "warning-soft-foreground": {"$value":"{core.color.dark-warning-deep}"},
820
- "success-soft": {"$value":"{core.color.dark-success-soft}"},
821
- "success-soft-foreground": {"$value":"{core.color.dark-success-deep}"},
822
- "info-soft": {"$value":"{core.color.dark-info-soft}"},
823
- "info-soft-foreground": {"$value":"{core.color.dark-info-deep}"},
824
- "border": {"$value":"{core.color.dark-line}"},
825
- "input": {"$value":"{core.color.dark-input}"},
826
- "ring": {"$value":"{core.color.dark-focus-ring}"},
827
- "scrim": {"$value":"{core.color.dark-scrim}"},
828
- "chart-1": {"$value":"{core.color.dark-chart-1}"},
829
- "chart-2": {"$value":"{core.color.dark-chart-2}"},
830
- "chart-3": {"$value":"{core.color.dark-chart-3}"},
831
- "chart-4": {"$value":"{core.color.dark-chart-4}"},
832
- "chart-5": {"$value":"{core.color.dark-chart-5}"},
833
- "selection": {"$value":"{core.color.dark-navigation-selection-bg}"},
834
- "selection-foreground": {"$value":"{core.color.dark-navigation-selection-fg}"},
835
- "recessed": {"$value":"{core.color.dark-material-raised}"},
836
- "sidebar": {"$value":"{core.color.dark-canvas}"},
837
- "sidebar-foreground": {"$value":"{core.color.dark-ink}"},
838
- "sidebar-primary": {"$value":"{core.color.dark-navigation-selection-bg}"},
839
- "sidebar-primary-foreground": {"$value":"{core.color.dark-navigation-selection-fg}"},
840
- "sidebar-primary-hover": {"$value":"{core.color.dark-navigation-selection-hover}"},
841
- "sidebar-accent": {"$value":"{core.color.dark-canvas-soft-2}"},
842
- "sidebar-accent-foreground": {"$value":"{core.color.dark-ink}"},
843
- "sidebar-border": {"$value":"{core.color.dark-line}"},
844
- "sidebar-ring": {"$value":"{core.color.dark-focus-ring}"}
2178
+ "background": {
2179
+ "$value": "{core.color.dark-canvas}"
2180
+ },
2181
+ "foreground": {
2182
+ "$value": "{core.color.dark-ink}"
2183
+ },
2184
+ "card": {
2185
+ "$value": "{core.color.dark-material-solid}"
2186
+ },
2187
+ "card-foreground": {
2188
+ "$value": "{core.color.dark-ink}"
2189
+ },
2190
+ "popover": {
2191
+ "$value": "{core.color.dark-material-solid}"
2192
+ },
2193
+ "popover-foreground": {
2194
+ "$value": "{core.color.dark-ink}"
2195
+ },
2196
+ "primary": {
2197
+ "$value": "{core.color.dark-action}"
2198
+ },
2199
+ "primary-foreground": {
2200
+ "$value": "{core.color.dark-action-foreground}"
2201
+ },
2202
+ "primary-hover": {
2203
+ "$value": "{core.color.dark-action-hover}"
2204
+ },
2205
+ "primary-active": {
2206
+ "$value": "{core.color.dark-action-active}"
2207
+ },
2208
+ "secondary": {
2209
+ "$value": "{core.color.dark-canvas-soft}"
2210
+ },
2211
+ "secondary-foreground": {
2212
+ "$value": "{core.color.dark-ink}"
2213
+ },
2214
+ "muted": {
2215
+ "$value": "{core.color.dark-canvas-soft}"
2216
+ },
2217
+ "muted-foreground": {
2218
+ "$value": "{core.color.dark-body}"
2219
+ },
2220
+ "placeholder": {
2221
+ "$value": "{core.color.dark-muted}"
2222
+ },
2223
+ "accent": {
2224
+ "$value": "{core.color.dark-canvas-soft-2}"
2225
+ },
2226
+ "accent-foreground": {
2227
+ "$value": "{core.color.dark-ink}"
2228
+ },
2229
+ "destructive": {
2230
+ "$value": "{core.color.dark-error}"
2231
+ },
2232
+ "destructive-foreground": {
2233
+ "$value": "{core.color.dark-on-primary}"
2234
+ },
2235
+ "warning": {
2236
+ "$value": "{core.color.dark-warning}"
2237
+ },
2238
+ "warning-foreground": {
2239
+ "$value": "{core.color.dark-on-primary}"
2240
+ },
2241
+ "success": {
2242
+ "$value": "{core.color.dark-success}"
2243
+ },
2244
+ "success-foreground": {
2245
+ "$value": "{core.color.dark-on-primary}"
2246
+ },
2247
+ "info": {
2248
+ "$value": "{core.color.dark-info}"
2249
+ },
2250
+ "info-foreground": {
2251
+ "$value": "{core.color.dark-on-primary}"
2252
+ },
2253
+ "destructive-soft": {
2254
+ "$value": "{core.color.dark-error-soft}"
2255
+ },
2256
+ "destructive-soft-foreground": {
2257
+ "$value": "{core.color.dark-error-deep}"
2258
+ },
2259
+ "warning-soft": {
2260
+ "$value": "{core.color.dark-warning-soft}"
2261
+ },
2262
+ "warning-soft-foreground": {
2263
+ "$value": "{core.color.dark-warning-deep}"
2264
+ },
2265
+ "success-soft": {
2266
+ "$value": "{core.color.dark-success-soft}"
2267
+ },
2268
+ "success-soft-foreground": {
2269
+ "$value": "{core.color.dark-success-deep}"
2270
+ },
2271
+ "info-soft": {
2272
+ "$value": "{core.color.dark-info-soft}"
2273
+ },
2274
+ "info-soft-foreground": {
2275
+ "$value": "{core.color.dark-info-deep}"
2276
+ },
2277
+ "border": {
2278
+ "$value": "{core.color.dark-line}"
2279
+ },
2280
+ "input": {
2281
+ "$value": "{core.color.dark-input}"
2282
+ },
2283
+ "ring": {
2284
+ "$value": "{core.color.dark-focus-ring}"
2285
+ },
2286
+ "scrim": {
2287
+ "$value": "{core.color.dark-scrim}"
2288
+ },
2289
+ "chart-1": {
2290
+ "$value": "{core.color.dark-chart-1}"
2291
+ },
2292
+ "chart-2": {
2293
+ "$value": "{core.color.dark-chart-2}"
2294
+ },
2295
+ "chart-3": {
2296
+ "$value": "{core.color.dark-chart-3}"
2297
+ },
2298
+ "chart-4": {
2299
+ "$value": "{core.color.dark-chart-4}"
2300
+ },
2301
+ "chart-5": {
2302
+ "$value": "{core.color.dark-chart-5}"
2303
+ },
2304
+ "selection": {
2305
+ "$value": "{core.color.dark-navigation-selection-bg}"
2306
+ },
2307
+ "selection-foreground": {
2308
+ "$value": "{core.color.dark-navigation-selection-fg}"
2309
+ },
2310
+ "recessed": {
2311
+ "$value": "{core.color.dark-canvas}"
2312
+ },
2313
+ "sidebar": {
2314
+ "$value": "{core.color.dark-canvas}"
2315
+ },
2316
+ "sidebar-foreground": {
2317
+ "$value": "{core.color.dark-ink}"
2318
+ },
2319
+ "sidebar-primary": {
2320
+ "$value": "{core.color.dark-navigation-selection-bg}"
2321
+ },
2322
+ "sidebar-primary-foreground": {
2323
+ "$value": "{core.color.dark-navigation-selection-fg}"
2324
+ },
2325
+ "sidebar-primary-hover": {
2326
+ "$value": "{core.color.dark-navigation-selection-hover}"
2327
+ },
2328
+ "sidebar-accent": {
2329
+ "$value": "{core.color.dark-canvas-soft-2}"
2330
+ },
2331
+ "sidebar-accent-foreground": {
2332
+ "$value": "{core.color.dark-ink}"
2333
+ },
2334
+ "sidebar-border": {
2335
+ "$value": "{core.color.dark-line}"
2336
+ },
2337
+ "sidebar-ring": {
2338
+ "$value": "{core.color.dark-focus-ring}"
2339
+ },
2340
+ "category-teal": {
2341
+ "$value": "{core.color.dark-category-teal-soft}"
2342
+ },
2343
+ "category-teal-foreground": {
2344
+ "$value": "{core.color.dark-category-teal-deep}"
2345
+ },
2346
+ "category-purple": {
2347
+ "$value": "{core.color.dark-category-purple-soft}"
2348
+ },
2349
+ "category-purple-foreground": {
2350
+ "$value": "{core.color.dark-category-purple-deep}"
2351
+ },
2352
+ "category-pink": {
2353
+ "$value": "{core.color.dark-category-pink-soft}"
2354
+ },
2355
+ "category-pink-foreground": {
2356
+ "$value": "{core.color.dark-category-pink-deep}"
2357
+ }
845
2358
  }
846
2359
  }
847
2360
  },
848
2361
  "register": {
849
2362
  "website": {
850
- "$extensions": {"ai.nadicode.targets":["css","data"]},
2363
+ "$extensions": {
2364
+ "ai.nadicode.targets": [
2365
+ "css",
2366
+ "data"
2367
+ ]
2368
+ },
851
2369
  "layout": {
852
2370
  "$type": "dimension",
853
- "content-max": {"$value":{"value":1200,"unit":"px"}},
854
- "gutter-mobile": {"$value":{"value":20,"unit":"px"}},
855
- "gutter-tablet": {"$value":{"value":40,"unit":"px"}},
856
- "gutter-desktop": {"$value":{"value":64,"unit":"px"}},
857
- "section-step-mobile": {"$value":{"value":64,"unit":"px"}},
858
- "section-step-desktop": {"$value":{"value":96,"unit":"px"}}
2371
+ "content-max": {
2372
+ "$value": {
2373
+ "value": 1200,
2374
+ "unit": "px"
2375
+ }
2376
+ },
2377
+ "gutter-mobile": {
2378
+ "$value": {
2379
+ "value": 20,
2380
+ "unit": "px"
2381
+ }
2382
+ },
2383
+ "gutter-tablet": {
2384
+ "$value": {
2385
+ "value": 40,
2386
+ "unit": "px"
2387
+ }
2388
+ },
2389
+ "gutter-desktop": {
2390
+ "$value": {
2391
+ "value": 64,
2392
+ "unit": "px"
2393
+ }
2394
+ },
2395
+ "section-step-mobile": {
2396
+ "$value": {
2397
+ "value": 64,
2398
+ "unit": "px"
2399
+ }
2400
+ },
2401
+ "section-step-desktop": {
2402
+ "$value": {
2403
+ "value": 96,
2404
+ "unit": "px"
2405
+ }
2406
+ }
859
2407
  }
860
2408
  }
861
2409
  }