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