astryx-dracula 0.4.0 → 0.5.0

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.
Files changed (50) hide show
  1. package/package.json +6 -2
  2. package/shared/auth-chrome.tsx +156 -6
  3. package/shared/chaptered-doc.tsx +3 -4
  4. package/shared/chart-hues.ts +0 -5
  5. package/shared/chart-legend.tsx +2 -3
  6. package/shared/data-bar.tsx +5 -5
  7. package/shared/format-money.ts +3 -0
  8. package/shared/metric-card.tsx +127 -0
  9. package/shared/scene-hues.ts +0 -8
  10. package/shared/scene-tile.tsx +3 -11
  11. package/shared/settings-data.ts +28 -0
  12. package/shared/settings-rows.tsx +1 -6
  13. package/shared/stars.tsx +28 -0
  14. package/templates/ai-chat-landing.tsx +88 -58
  15. package/templates/ai-chat.tsx +65 -78
  16. package/templates/centered-hero.tsx +2 -2
  17. package/templates/dashboard-portfolio.tsx +77 -155
  18. package/templates/dashboard.tsx +17 -134
  19. package/templates/detail-page.tsx +14 -18
  20. package/templates/documentation-design.tsx +66 -519
  21. package/templates/documentation-technical.tsx +2 -22
  22. package/templates/editor.tsx +168 -185
  23. package/templates/file-explorer.tsx +10 -24
  24. package/templates/ide.tsx +12 -18
  25. package/templates/incident-console.tsx +48 -46
  26. package/templates/kanban-board.tsx +36 -85
  27. package/templates/library.tsx +42 -66
  28. package/templates/login-card.tsx +33 -126
  29. package/templates/login-split.tsx +91 -123
  30. package/templates/login-sso.tsx +12 -33
  31. package/templates/login.tsx +21 -121
  32. package/templates/messaging-shell.tsx +162 -168
  33. package/templates/payment-form.tsx +24 -26
  34. package/templates/product-detail.tsx +7 -40
  35. package/templates/product-tour.tsx +38 -60
  36. package/templates/settings-dialog.tsx +10 -32
  37. package/templates/settings-sidebar.tsx +44 -77
  38. package/templates/settings.tsx +208 -121
  39. package/templates/shell-nav.tsx +20 -75
  40. package/templates/shell-side-nav.tsx +16 -90
  41. package/templates/shell-top-nav.tsx +20 -53
  42. package/templates/side-gallery.tsx +19 -19
  43. package/templates/table-grouped.tsx +17 -164
  44. package/templates/table-page-chart.tsx +12 -11
  45. package/templates/table-page-heatmap-status.tsx +0 -17
  46. package/templates/table-page-shoe-store-heatmap.tsx +12 -12
  47. package/templates/table-page.tsx +4 -31
  48. package/templates/table.tsx +7 -10
  49. package/templates/tech-report.tsx +58 -72
  50. package/templates/theme-showcase.tsx +124 -415
@@ -9,26 +9,19 @@
9
9
  import {
10
10
  useCallback,
11
11
  useState,
12
- useMemo,
13
- type CSSProperties,
14
12
  type ReactNode,
15
13
  } from 'react';
16
14
  import {Heading, Text} from '@astryxdesign/core/Text';
17
15
  import {Button} from '@astryxdesign/core/Button';
18
16
  import {Card} from '@astryxdesign/core/Card';
19
- import {Avatar} from '@astryxdesign/core/Avatar';
20
17
  import {Badge} from '@astryxdesign/core/Badge';
21
- import {Token} from '@astryxdesign/core/Token';
22
- import {Banner} from '@astryxdesign/core/Banner';
23
18
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
24
19
  import {TabList, Tab} from '@astryxdesign/core/TabList';
25
20
  import {Selector} from '@astryxdesign/core/Selector';
26
21
  import {HStack, VStack} from '@astryxdesign/core/Stack';
27
22
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
28
23
  import {useMediaQuery} from '@astryxdesign/core/hooks';
29
- import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
30
24
  import {Divider} from '@astryxdesign/core/Divider';
31
- import {Tooltip} from '@astryxdesign/core/Tooltip';
32
25
  import {Table, pixel, proportional} from '@astryxdesign/core/Table';
33
26
  import {Icon} from '@astryxdesign/core/Icon';
34
27
  import {Section} from '@astryxdesign/core/Section';
@@ -37,21 +30,6 @@ import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
37
30
  import {Plus} from 'lucide-react';
38
31
  import {outlinePanel} from 'astryx-dracula/shared/chaptered-doc-config';
39
32
 
40
- // Flush the tab list with the example card's 12px inset (Section padding 3).
41
- const tabListFlush: CSSProperties = {
42
- marginInlineStart: 'calc(var(--spacing-3) * -1)',
43
- };
44
-
45
- // The best-practices table lives in the prose column, where the eye tracks a
46
- // single measure. Table's scroll wrapper bleeds edge-to-edge by reading the
47
- // container padding vars LayoutContent publishes (32px here), which pushes
48
- // its dividers 32px past the measure on both sides at every width. This table
49
- // never overflows, so the bleed buys nothing — zero the vars for this subtree
50
- // so it sits on the measure with the headings above it.
51
- const practicesOnMeasure: CSSProperties = ({
52
- '--container-padding-inline-start': '0px',
53
- '--container-padding-inline-end': '0px',
54
- }) as CSSProperties;
55
33
 
56
34
  const COMPONENT_OUTLINE_ITEMS: OutlineItem[] = [
57
35
  {id: 'usage', label: 'Usage', level: 2},
@@ -64,366 +42,16 @@ const COMPONENT_OUTLINE_OPTIONS = COMPONENT_OUTLINE_ITEMS.map(item => ({
64
42
  label: item.label,
65
43
  }));
66
44
 
67
- // ---------------------------------------------------------------------------
68
- // DialogPreview — stateful dialog preview for component previews
69
- // ---------------------------------------------------------------------------
70
-
71
- function DialogPreview() {
72
- const [isOpen, setIsOpen] = useState(false);
73
- return (
74
- <VStack gap={3}>
75
- <Text type="body" weight="semibold">
76
- Dialog
77
- </Text>
78
- <Button
79
- label="Open Dialog"
80
- variant="primary"
81
- onClick={() => setIsOpen(true)}
82
- />
83
- <Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
84
- <DialogHeader title="Night Dialog" onOpenChange={setIsOpen} />
85
- <Section padding={4}>
86
- <Text type="body">
87
- This is an example dialog. Dialogs are used to require user action
88
- or display important information that needs acknowledgment.
89
- </Text>
90
- </Section>
91
- </Dialog>
92
- </VStack>
93
- );
94
- }
95
-
96
45
  // ---------------------------------------------------------------------------
97
46
  // Data
98
47
  // ---------------------------------------------------------------------------
99
48
 
100
- const COMPONENT_CATEGORIES = [
101
- {
102
- label: 'Core',
103
- items: [
104
- {
105
- key: 'appshell',
106
- name: 'AppShell',
107
- desc: 'AppShell provides a foundational page layout with header, sidebar, and content regions. Use it to establish consistent structure across your application.',
108
- },
109
- {
110
- key: 'avatar',
111
- name: 'Avatar',
112
- desc: 'Avatars represent a person or entity with an image, initials, or icon. They are commonly used in user profiles, comments, and contact lists.',
113
- },
114
- {
115
- key: 'badge',
116
- name: 'Badge',
117
- desc: 'Badges display small counts or status labels. They can be attached to icons, buttons, or list items to surface key information at a glance.',
118
- },
119
- {
120
- key: 'banner',
121
- name: 'Banner',
122
- desc: 'Banners show important, non-modal messages at the top of a page or section. They communicate status, warnings, or promotional information.',
123
- },
124
- {
125
- key: 'button',
126
- name: 'Button',
127
- desc: 'Buttons let people take action. They can be used in forms, dialogs, and toolbars, or as standalone links.',
128
- },
129
- {
130
- key: 'calendar',
131
- name: 'Calendar',
132
- desc: 'Calendar provides a date-picking grid for selecting single dates or date ranges. It integrates with form fields for date input.',
133
- },
134
- {
135
- key: 'dialog',
136
- name: 'Dialog',
137
- desc: 'Dialogs are modal overlays that require user attention or action before continuing. They are used for confirmations, forms, and critical decisions.',
138
- },
139
- {
140
- key: 'dropdownmenu',
141
- name: 'DropdownMenu',
142
- desc: 'DropdownMenu presents a list of actions or options in a floating overlay. It is triggered by a button and supports nested submenus.',
143
- },
144
- {
145
- key: 'empty-state',
146
- name: 'EmptyState',
147
- desc: 'EmptyState provides a placeholder when there is no content to display. It guides users with a message, illustration, and optional call-to-action.',
148
- },
149
- {
150
- key: 'hovercard',
151
- name: 'HoverCard',
152
- desc: 'HoverCard shows a rich preview of content when users hover over a trigger element. It is ideal for previewing profiles, links, or details.',
153
- },
154
- {
155
- key: 'icon',
156
- name: 'Icon',
157
- desc: 'Icons are small visual symbols that represent actions, objects, or concepts. They improve scannability and reinforce meaning alongside text.',
158
- },
159
- {
160
- key: 'kbd',
161
- name: 'Kbd',
162
- desc: 'Kbd renders keyboard shortcut hints in a styled inline element. Use it to show users which key combinations perform specific actions.',
163
- },
164
- {
165
- key: 'link',
166
- name: 'Link',
167
- desc: 'Links provide navigation between pages or to external resources. They follow accessible anchor semantics with visual affordance.',
168
- },
169
- {
170
- key: 'list',
171
- name: 'List',
172
- desc: 'List displays a vertical set of related items. It supports selection, icons, and metadata for building menus, nav lists, and more.',
173
- },
174
- {
175
- key: 'metadatalist',
176
- name: 'MetadataList',
177
- desc: 'MetadataList displays key-value pairs in a structured layout. Use it for detail panels, settings summaries, and record information.',
178
- },
179
- {
180
- key: 'moremenu',
181
- name: 'MoreMenu',
182
- desc: 'MoreMenu provides an overflow menu triggered by an icon button. It collects secondary actions that do not fit in the primary toolbar.',
183
- },
184
- {
185
- key: 'overflowlist',
186
- name: 'OverflowList',
187
- desc: 'OverflowList renders as many items as fit in the available space and collapses the rest into an overflow menu automatically.',
188
- },
189
- {
190
- key: 'pagination',
191
- name: 'Pagination',
192
- desc: 'Pagination lets users navigate through pages of content. It supports page numbers, previous/next controls, and page-size selection.',
193
- },
194
- {
195
- key: 'popover',
196
- name: 'Popover',
197
- desc: 'Popover displays rich content in a floating panel anchored to a trigger element. It is used for forms, filters, and contextual tools.',
198
- },
199
- {
200
- key: 'progressbar',
201
- name: 'ProgressBar',
202
- desc: 'ProgressBar shows the completion status of a task or process. It provides visual feedback for uploads, installations, and multi-step flows.',
203
- },
204
- {
205
- key: 'skeleton',
206
- name: 'Skeleton',
207
- desc: 'Skeleton renders placeholder shapes that mimic content layout while loading. It reduces perceived wait time and prevents layout shifts.',
208
- },
209
- {
210
- key: 'spinner',
211
- name: 'Spinner',
212
- desc: 'Spinner indicates that a process is in progress when the duration is unknown. It draws attention without blocking the interface.',
213
- },
214
- {
215
- key: 'statusdot',
216
- name: 'StatusDot',
217
- desc: 'StatusDot shows a small colored indicator for online, offline, busy, or custom statuses. It is often paired with avatars or list items.',
218
- },
219
- {
220
- key: 'table',
221
- name: 'Table',
222
- desc: 'Table displays structured data in rows and columns with support for sorting, selection, and custom cell rendering.',
223
- },
224
- {
225
- key: 'thumbnail',
226
- name: 'Thumbnail',
227
- desc: 'Thumbnail renders a small image preview with consistent sizing and optional rounded corners. It is used in media lists, cards, and galleries.',
228
- },
229
- {
230
- key: 'timestamp',
231
- name: 'Timestamp',
232
- desc: 'Timestamp formats and displays dates and times with relative or absolute labels. It updates automatically to stay current.',
233
- },
234
- {
235
- key: 'toast',
236
- name: 'Toast',
237
- desc: 'Toasts display brief, non-blocking notifications at the edge of the screen. They auto-dismiss and are used for success, error, or info messages.',
238
- },
239
- {
240
- key: 'togglebutton',
241
- name: 'ToggleButton',
242
- desc: 'ToggleButton is a button that switches between an on and off state. Use it for binary options like bookmarking, favoriting, or muting.',
243
- },
244
- {
245
- key: 'token',
246
- name: 'Token',
247
- desc: 'Tokens display compact metadata labels such as tags, categories, or filters. They can be dismissible and support selection state.',
248
- },
249
- {
250
- key: 'tooltip',
251
- name: 'Tooltip',
252
- desc: 'Tooltips show concise helper text when users hover over or focus an element. They clarify icons, truncated labels, and controls.',
253
- },
254
- {
255
- key: 'treelist',
256
- name: 'TreeList',
257
- desc: 'TreeList renders hierarchical data in an expandable tree structure. It supports multi-level nesting, selection, and lazy loading.',
258
- },
259
- ],
260
- },
261
- {
262
- label: 'Typography',
263
- items: [
264
- {
265
- key: 'heading',
266
- name: 'Heading',
267
- desc: 'Heading renders semantic section titles from h1 through h6. It establishes visual hierarchy and supports multiple weight and size options.',
268
- },
269
- {
270
- key: 'text',
271
- name: 'Text',
272
- desc: 'Text renders body copy, labels, and supporting content with consistent typography. It supports sizes from display down to caption.',
273
- },
274
- ],
275
- },
276
- {
277
- label: 'Layout',
278
- items: [
279
- {
280
- key: 'aspectratio',
281
- name: 'AspectRatio',
282
- desc: 'AspectRatio constrains its child to a specified width-to-height ratio. Use it for responsive images, videos, and embedded media.',
283
- },
284
- {
285
- key: 'card',
286
- name: 'Card',
287
- desc: 'Cards group related content and actions in a contained surface. They can include headers, media, body text, and action bars.',
288
- },
289
- {
290
- key: 'center',
291
- name: 'Center',
292
- desc: 'Center aligns its child horizontally and vertically within the available space. It is useful for empty states, loading screens, and hero sections.',
293
- },
294
- {
295
- key: 'divider',
296
- name: 'Divider',
297
- desc: 'Dividers separate content into distinct sections with a subtle or strong horizontal line. They can optionally include a label.',
298
- },
299
- {
300
- key: 'grid',
301
- name: 'Grid',
302
- desc: 'Grid provides a CSS grid-based layout container with configurable columns, rows, and gap. It simplifies responsive multi-column designs.',
303
- },
304
- {
305
- key: 'layout',
306
- name: 'Layout',
307
- desc: 'Layout provides foundational page-level primitives for header, sidebar, and content regions. It establishes consistent spacing and structure.',
308
- },
309
- {
310
- key: 'section',
311
- name: 'Section',
312
- desc: 'Section wraps a block of content with consistent vertical spacing and an optional heading. It structures pages into logical groups.',
313
- },
314
- {
315
- key: 'stack',
316
- name: 'Stack',
317
- desc: 'Stack arranges child elements in a row or column with consistent gap spacing. It is the primary tool for one-dimensional layout composition.',
318
- },
319
- {
320
- key: 'toolbar',
321
- name: 'Toolbar',
322
- desc: 'Toolbar arranges a row of action buttons and controls in a compact, aligned strip. It is used at the top of panels, editors, and cards.',
323
- },
324
- ],
325
- },
326
- {
327
- label: 'Navigation',
328
- items: [
329
- {
330
- key: 'breadcrumbs',
331
- name: 'Breadcrumbs',
332
- desc: "Breadcrumbs show the user's current location within a navigation hierarchy. They provide quick links back to parent pages.",
333
- },
334
- {
335
- key: 'mobilenav',
336
- name: 'MobileNav',
337
- desc: 'MobileNav provides a responsive navigation menu optimized for small screens. It typically slides in from the edge of the viewport.',
338
- },
339
- {
340
- key: 'sidenav',
341
- name: 'SideNav',
342
- desc: 'SideNav renders a vertical navigation panel with links, sections, and collapsible groups. It is used as the primary nav in dashboard layouts.',
343
- },
344
- {
345
- key: 'tablist',
346
- name: 'TabList',
347
- desc: 'TabList switches between content views using a horizontal row of tabs. Only one tab is active at a time, and content changes without a page reload.',
348
- },
349
- {
350
- key: 'topnav',
351
- name: 'TopNav',
352
- desc: 'TopNav provides an app-level navigation bar across the top of the page. It holds branding, primary links, search, and user actions.',
353
- },
354
- ],
355
- },
356
- {
357
- label: 'Form',
358
- items: [
359
- {
360
- key: 'checkboxinput',
361
- name: 'CheckboxInput',
362
- desc: 'CheckboxInput renders a single checkbox with a label. It is used for boolean opt-in choices like terms acceptance or feature toggles.',
363
- },
364
- {
365
- key: 'selector',
366
- name: 'Selector',
367
- desc: 'Selector lets users pick a single item from a dropdown list. It supports search, grouping, and custom option rendering.',
368
- },
369
- {
370
- key: 'switch',
371
- name: 'Switch',
372
- desc: 'Switch toggles a setting between on and off states with immediate effect. It is used for preferences, feature flags, and real-time controls.',
373
- },
374
- {
375
- key: 'textinput',
376
- name: 'TextInput',
377
- desc: 'TextInput is a single-line text field for short user input like names, emails, and search queries. It supports icons, prefixes, and validation.',
378
- },
379
- {
380
- key: 'typeahead',
381
- name: 'Typeahead',
382
- desc: 'Typeahead provides an autocomplete search input that suggests results as the user types. It supports async data sources and custom rendering.',
383
- },
384
- ],
385
- },
386
- {
387
- label: 'Components',
388
- items: [
389
- {
390
- key: 'codeblock',
391
- name: 'CodeBlock',
392
- desc: 'CodeBlock displays formatted, syntax-highlighted source code. It supports line numbers, copy-to-clipboard, and language detection.',
393
- },
394
- {
395
- key: 'collapsible',
396
- name: 'Collapsible',
397
- desc: 'Collapsible wraps content that can be expanded or collapsed with a trigger. It is used for FAQs, advanced settings, and progressive disclosure.',
398
- },
399
- {
400
- key: 'markdown',
401
- name: 'Markdown',
402
- desc: 'Markdown renders markdown-formatted text into styled HTML. It supports headings, lists, links, code blocks, and inline formatting.',
403
- },
404
- ],
405
- },
406
- {
407
- label: 'Chat',
408
- items: [
409
- {
410
- key: 'chat',
411
- name: 'Chat',
412
- desc: 'Chat provides a conversational message interface with message bubbles, input, and thread support. It is used for AI assistants and messaging UIs.',
413
- },
414
- ],
415
- },
416
- {
417
- label: 'CommandPalette',
418
- items: [
419
- {
420
- key: 'commandpalette',
421
- name: 'CommandPalette',
422
- desc: 'CommandPalette is a keyboard-driven command menu for quick navigation and actions. It is opened with a hotkey and supports fuzzy search.',
423
- },
424
- ],
425
- },
426
- ];
49
+ // This page renders exactly one component's detail. The key is a module
50
+ // constant, not a prop: the only caller passed "button" unconditionally, and
51
+ // the name/docs/previews below index it directly instead of threading a
52
+ // parameter that can never vary.
53
+ // The only caller ever passed 'button': the component name is a literal, not a lookup.
54
+ const ACTIVE_COMPONENT_NAME = 'Button';
427
55
 
428
56
  const COMPONENT_DOCS: Record<
429
57
  string,
@@ -467,111 +95,37 @@ const COMPONENT_DOCS: Record<
467
95
  },
468
96
  };
469
97
 
470
- function getComponentName(key: string): string {
471
- for (const cat of COMPONENT_CATEGORIES) {
472
- const item = cat.items.find(i => i.key === key);
473
- if (item) {
474
- return item.name;
475
- }
476
- }
477
- return key;
478
- }
98
+ const ACTIVE_DOCS = COMPONENT_DOCS['button'];
479
99
 
480
- function getComponentDocs(key: string) {
481
- if (COMPONENT_DOCS[key]) {
482
- return COMPONENT_DOCS[key];
483
- }
484
- const name = getComponentName(key);
485
- let desc = '';
486
- for (const cat of COMPONENT_CATEGORIES) {
487
- const item = cat.items.find(i => i.key === key);
488
- if (item) {
489
- desc = item.desc;
490
- break;
491
- }
492
- }
493
- return {
494
- usage: desc,
495
- bestPractices: [
496
- {
497
- type: 'do' as const,
498
- text: `Use ${name} in the appropriate context to provide the functionality described above.`,
499
- },
500
- {
501
- type: 'do' as const,
502
- text: `Pair ${name} with related components to create cohesive, accessible interfaces.`,
503
- },
504
- {
505
- type: 'dont' as const,
506
- text: `Use ${name} when a simpler alternative achieves the same goal with less complexity.`,
507
- },
508
- ],
509
- examples: [
510
- {
511
- title: `Basic ${name}`,
512
- description: `A simple example of the ${name} component with default settings.`,
513
- code: `<${name} />`,
514
- },
515
- ],
516
- };
517
- }
518
-
519
- const EXAMPLE_PREVIEWS: Record<string, ReactNode[]> = {
520
- button: [
521
- <HStack key="semantics" gap={3} vAlign="center" wrap="wrap">
522
- <Button label="Flat" variant="ghost" />
523
- <Button label="Default" variant="secondary" />
524
- <Button label="Primary" variant="primary" />
525
- <Button label="Destructive" variant="destructive" />
526
- </HStack>,
527
- <Button key="badge" label="Button" variant="secondary" />,
528
- ],
529
- };
100
+ // One preview node per example in ACTIVE_DOCS.examples, index-aligned. The
101
+ // old EXAMPLE_PREVIEWS record carried only this key, so the record wrapper
102
+ // bought nothing; the avatar/badge/card/banner/dialog/text/divider/token/
103
+ // tooltip entries in the old COMPONENT_PREVIEWS map were reachable only
104
+ // through nav keys this page never renders, so they are gone.
105
+ const EXAMPLE_PREVIEW_NODES: ReactNode[] = [
106
+ <HStack key="semantics" gap={3} vAlign="center" wrap="wrap">
107
+ <Button label="Flat" variant="ghost" />
108
+ <Button label="Default" variant="secondary" />
109
+ <Button label="Primary" variant="primary" />
110
+ <Button label="Destructive" variant="destructive" />
111
+ </HStack>,
112
+ <Button key="badge" label="Button" variant="secondary" />,
113
+ ];
530
114
 
531
- const COMPONENT_PREVIEWS: Record<string, ReactNode> = {
532
- button: (
533
- <Button
534
- label="Button"
535
- variant="secondary"
536
- icon={<Icon icon={Plus} />}
537
- endContent={<Badge label="New" variant="cyan" />}
538
- />
539
- ),
540
- avatar: <Avatar name="Vlad" size="lg" />,
541
- badge: <Badge label="Design" variant="yellow" />,
542
- card: (
543
- <Card>
544
- <VStack gap={2}>
545
- <Text type="body" weight="semibold">
546
- Castle Card
547
- </Text>
548
- <Text type="body" color="secondary">
549
- Cards group related content and actions.
550
- </Text>
551
- </VStack>
552
- </Card>
553
- ),
554
- banner: (
555
- <Banner status="info" title="Information">
556
- <Text type="body">This is an informational banner message.</Text>
557
- </Banner>
558
- ),
559
- dialog: <DialogPreview />,
560
- text: <Text type="body">Body text</Text>,
561
- divider: <Divider />,
562
- token: <Token label="Dracula" />,
563
- tooltip: (
564
- <Tooltip content="Primary action">
565
- <Button label="Hover me" variant="primary" />
566
- </Tooltip>
567
- ),
568
- };
115
+ const HERO_PREVIEW = (
116
+ <Button
117
+ label="Button"
118
+ variant="secondary"
119
+ icon={<Icon icon={Plus} />}
120
+ endContent={<Badge label="New" variant="cyan" />}
121
+ />
122
+ );
569
123
 
570
124
  // ---------------------------------------------------------------------------
571
125
  // ComponentDetailView
572
126
  // ---------------------------------------------------------------------------
573
127
 
574
- function ComponentDetailView({activeNav}: {activeNav: string}) {
128
+ function ComponentDetailView() {
575
129
  const [exampleTabs, setExampleTabs] = useState<Record<string, string>>({});
576
130
  const [activeId, setActiveId] = useState<string | undefined>(
577
131
  COMPONENT_OUTLINE_ITEMS[0]?.id,
@@ -587,8 +141,8 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
587
141
  }
588
142
  }, []);
589
143
 
590
- const docs = useMemo(() => getComponentDocs(activeNav), [activeNav]);
591
- const previews = EXAMPLE_PREVIEWS[activeNav] ?? [];
144
+ const docs = ACTIVE_DOCS;
145
+ const previews = EXAMPLE_PREVIEW_NODES;
592
146
 
593
147
  return (
594
148
  <Layout
@@ -612,7 +166,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
612
166
  <LayoutContent isScrollable={false} padding={8}>
613
167
  <VStack gap={8}>
614
168
  <VStack gap={2}>
615
- <Heading level={1} type="display-2">{getComponentName(activeNav)}</Heading>
169
+ <Heading level={1} type="display-2">{ACTIVE_COMPONENT_NAME}</Heading>
616
170
  <Text type="supporting" color="secondary" hasTabularNumbers>
617
171
  March 30, 2026 · Updated 5:40 p.m. PST
618
172
  </Text>
@@ -630,11 +184,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
630
184
 
631
185
  <Card variant="muted" padding={0}>
632
186
  <Center height={360}>
633
- {COMPONENT_PREVIEWS[activeNav] ?? (
634
- <Text type="supporting" color="secondary">
635
- Quiet in the crypt — no preview haunts this shelf yet.
636
- </Text>
637
- )}
187
+ {HERO_PREVIEW}
638
188
  </Center>
639
189
  </Card>
640
190
 
@@ -646,37 +196,35 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
646
196
  <Heading id="best-practices" level={3}>
647
197
  Best practices
648
198
  </Heading>
649
- <div style={practicesOnMeasure}>
650
- <Table
651
- data={docs.bestPractices as Record<string, unknown>[]}
652
- dividers="none"
653
- hasHover
654
- columns={[
655
- {
656
- key: 'type',
657
- header: 'Guidance',
658
- width: pixel(125),
659
- renderCell: (item: Record<string, unknown>) => (
660
- <Badge
661
- label={item.type === 'do' ? 'Do' : "Don't"}
662
- variant={item.type === 'do' ? 'green' : 'red'}
663
- />
664
- ),
665
- },
666
- {
667
- key: 'text',
668
- header: 'Practices',
669
- width: proportional(1),
670
- renderCell: (item: Record<string, unknown>) => (
671
- <Text type="body" textWrap="wrap">
672
- {item.text as string}
673
- </Text>
674
- ),
675
- },
676
- ]}
677
- density="spacious"
678
- />
679
- </div>
199
+ <Table
200
+ data={docs.bestPractices as Record<string, unknown>[]}
201
+ dividers="none"
202
+ hasHover
203
+ columns={[
204
+ {
205
+ key: 'type',
206
+ header: 'Guidance',
207
+ width: pixel(125),
208
+ renderCell: (item: Record<string, unknown>) => (
209
+ <Badge
210
+ label={item.type === 'do' ? 'Do' : "Don't"}
211
+ variant={item.type === 'do' ? 'green' : 'red'}
212
+ />
213
+ ),
214
+ },
215
+ {
216
+ key: 'text',
217
+ header: 'Practices',
218
+ width: proportional(1),
219
+ renderCell: (item: Record<string, unknown>) => (
220
+ <Text type="body" textWrap="wrap">
221
+ {item.text as string}
222
+ </Text>
223
+ ),
224
+ },
225
+ ]}
226
+ density="spacious"
227
+ />
680
228
  </VStack>
681
229
 
682
230
  <Divider />
@@ -692,7 +240,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
692
240
  </VStack>
693
241
  <VStack gap={8}>
694
242
  {docs.examples.map((example, i) => {
695
- const tabKey = `${activeNav}-${i}`;
243
+ const tabKey = `example-${i}`;
696
244
  const activeTab = exampleTabs[tabKey] ?? 'description';
697
245
  return (
698
246
  <Card key={example.title} padding={0}>
@@ -718,8 +266,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
718
266
  [tabKey]: value,
719
267
  }))
720
268
  }
721
- size="sm"
722
- style={tabListFlush}>
269
+ size="sm">
723
270
  <Tab value="description" label="Description" />
724
271
  <Tab value="code" label="Code" />
725
272
  </TabList>
@@ -750,5 +297,5 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
750
297
  // ---------------------------------------------------------------------------
751
298
 
752
299
  export default function DocumentationDesign() {
753
- return <ComponentDetailView activeNav="button" />;
300
+ return <ComponentDetailView />;
754
301
  }