@astryxdesign/cli 0.1.0-canary.7847c5f → 0.1.0-canary.797f761

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.
@@ -88,7 +88,7 @@ import {VStack} from '@astryxdesign/core/Layout';
88
88
  export default function Page() {
89
89
  return (
90
90
  <VStack gap={2}>
91
- <Button label="Hello Astryx" onClick={() => alert('Hi!')} />
91
+ <Button label="Hello XDS" onClick={() => alert('Hi!')} />
92
92
  </VStack>
93
93
  );
94
94
  }`,
@@ -96,11 +96,11 @@ export default function Page() {
96
96
  ],
97
97
  },
98
98
  {
99
- title: 'Customize with StyleX',
99
+ title: 'Customize with xstyle',
100
100
  content: [
101
101
  {
102
102
  type: 'prose',
103
- text: 'Astryx components support various styling solutions, from plain CSS and `className` to Tailwind and CSS-in-JS. See the [styling docs](/docs/styling) for the full guide. Astryx also has a deep integration with [StyleX](https://stylexjs.com/), an atomic CSS-in-JS library: create styles with `stylex.create()` and pass them to components with the `xstyle` prop.',
103
+ text: 'Every component accepts an `xstyle` prop for StyleX style overrides created via `stylex.create()`.',
104
104
  },
105
105
  {
106
106
  type: 'code',
@@ -127,19 +127,19 @@ const overrides = stylex.create({
127
127
  type: 'table',
128
128
  headers: ['Example', 'Stack', 'Path'],
129
129
  rows: [
130
- ['Next.js', 'Next.js + theme CSS', '[apps/example-nextjs](https://github.com/facebook/astryx/tree/main/apps/example-nextjs)'],
131
- ['Next.js + StyleX', 'Next.js + StyleX for custom styles', '[apps/example-nextjs-stylex](https://github.com/facebook/astryx/tree/main/apps/example-nextjs-stylex)'],
132
- ['Next.js + Tailwind', 'Next.js + Tailwind bridge', '[apps/example-nextjs-tailwind](https://github.com/facebook/astryx/tree/main/apps/example-nextjs-tailwind)'],
133
- ['Next.js Source', 'Next.js importing from source', '[apps/example-nextjs-source](https://github.com/facebook/astryx/tree/main/apps/example-nextjs-source)'],
134
- ['Vite', 'Vite', '[apps/example-vite](https://github.com/facebook/astryx/tree/main/apps/example-vite)'],
130
+ ['Next.js', 'Next.js + theme CSS', 'apps/example-nextjs'],
131
+ ['Next.js + StyleX', 'Next.js + StyleX for custom styles', 'apps/example-nextjs-stylex'],
132
+ ['Next.js + Tailwind', 'Next.js + Tailwind bridge', 'apps/example-nextjs-tailwind'],
133
+ ['Next.js Source', 'Next.js importing from source', 'apps/example-nextjs-source'],
134
+ ['Vite', 'Vite', 'apps/example-vite'],
135
135
  ],
136
136
  },
137
137
  {
138
138
  type: 'code',
139
139
  lang: 'bash',
140
140
  label: 'Clone and run an example',
141
- code: `git clone https://github.com/facebook/astryx.git
142
- cd astryx/apps/example-nextjs
141
+ code: `git clone https://github.com/facebookexperimental/xds.git
142
+ cd xds/apps/example-nextjs
143
143
  pnpm install
144
144
  pnpm dev`,
145
145
  },
@@ -157,7 +157,7 @@ pnpm dev`,
157
157
  lang: 'json',
158
158
  label: 'package.json',
159
159
  code: `"scripts": {
160
- "astryx": "node node_modules/@astryxdesign/cli/bin/astryx.mjs"
160
+ "xds": "node node_modules/@astryxdesign/cli/bin/astryx.mjs"
161
161
  }`,
162
162
  },
163
163
  {
@@ -22,8 +22,9 @@ export const docs = {
22
22
  type: 'table',
23
23
  headers: ['Approach', 'Use for', 'Example'],
24
24
  rows: [
25
- ['StyleX', 'Component-specific overrides, reusable styles, pseudo-classes, and typed tokens', 'const styles = stylex.create(...); <Button xstyle={styles.save} />'],
25
+ ['xstyle prop', 'Overriding a specific component', 'xstyle={styles.override}'],
26
26
  ['Tailwind utilities', 'Layout, wrappers, and utility styling', 'className="flex gap-3 p-4"'],
27
+ ['stylex.create', 'Reusable styles, pseudo-classes, typed tokens', 'stylex.create({ card: { ... } })'],
27
28
  ['className', 'Integrating with external CSS or Tailwind on components', 'className="my-card shadow-lg"'],
28
29
  ['Styling-library token aliases', 'Keeping Panda, Chakra, MUI, Emotion, styled-components, UnoCSS, CSS Modules, or Sass in sync with the system', "colors.surface = 'var(--color-background-surface)'"],
29
30
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.0-canary.7847c5f",
3
+ "version": "0.1.0-canary.797f761",
4
4
  "displayName": "CLI",
5
5
  "description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
6
6
  "author": "Meta Open Source",
@@ -54,9 +54,9 @@
54
54
  "jscodeshift": "^17.3.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@astryxdesign/core": "0.1.0-canary.7847c5f",
58
- "@astryxdesign/lab": "0.1.0-canary.7847c5f",
59
- "@astryxdesign/theme-neutral": "0.1.0-canary.7847c5f"
57
+ "@astryxdesign/core": "0.1.0-canary.797f761",
58
+ "@astryxdesign/lab": "0.1.0-canary.797f761",
59
+ "@astryxdesign/theme-neutral": "0.1.0-canary.797f761"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@astryxdesign/core": {
@@ -70,9 +70,9 @@
70
70
  }
71
71
  },
72
72
  "devDependencies": {
73
- "@astryxdesign/core": "0.1.0-canary.7847c5f",
74
- "@astryxdesign/lab": "0.1.0-canary.7847c5f",
75
- "@astryxdesign/theme-neutral": "0.1.0-canary.7847c5f"
73
+ "@astryxdesign/core": "0.1.0-canary.797f761",
74
+ "@astryxdesign/lab": "0.1.0-canary.797f761",
75
+ "@astryxdesign/theme-neutral": "0.1.0-canary.797f761"
76
76
  },
77
77
  "scripts": {
78
78
  "astryx": "node bin/astryx.mjs",
@@ -248,7 +248,7 @@ export function registerInit(program) {
248
248
  humanLog(' 2. Optionally add a theme:');
249
249
  humanLog(" import { neutralTheme } from '@astryxdesign/theme-neutral'");
250
250
  humanLog(' <Theme theme={neutralTheme}>...</Theme>');
251
- humanLog(` 3. ${run} astryx --help for all commands`);
251
+ humanLog(` 3. ${run} xds --help for all commands`);
252
252
  humanLog('');
253
253
  });
254
254
  }
@@ -4,7 +4,7 @@
4
4
  * @file Detect the project's package manager from lockfiles.
5
5
  *
6
6
  * Returns the correct command prefix for running package binaries
7
- * (e.g. 'npx astryx', 'yarn astryx', 'pnpm exec astryx').
7
+ * (e.g. 'npx xds', 'yarn xds', 'pnpm exec xds').
8
8
  */
9
9
 
10
10
  import * as fs from 'node:fs';
@@ -3,23 +3,24 @@
3
3
  'use client';
4
4
 
5
5
  import * as stylex from '@stylexjs/stylex';
6
- import {Heading, Text} from '@astryxdesign/core/Text';
6
+ import {
7
+ SideNav,
8
+ SideNavHeading,
9
+ SideNavItem,
10
+ SideNavSection,
11
+ } from '@astryxdesign/core/SideNav';
12
+ import {Text} from '@astryxdesign/core/Text';
7
13
  import {Button} from '@astryxdesign/core/Button';
8
14
  import {Card} from '@astryxdesign/core/Card';
9
- import {ClickableCard} from '@astryxdesign/core/ClickableCard';
10
15
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
11
- import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
16
+ import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
12
17
  import {Grid} from '@astryxdesign/core/Grid';
13
18
  import {radiusVars} from '@astryxdesign/core/theme/tokens.stylex';
14
19
 
15
20
  const styles = stylex.create({
16
21
  previewCard: {
17
- borderRadius: radiusVars['--radius-element'],
18
- },
19
- // Negative margin offsets each card's 8px padding so the grid content stays
20
- // visually aligned while giving every card a padded hover/click target.
21
- cardGrid: {
22
- margin: -8,
22
+ borderRadius: radiusVars['--radius-container'],
23
+ cursor: 'pointer',
23
24
  },
24
25
  });
25
26
 
@@ -212,12 +213,30 @@ const COMPONENT_CATEGORIES = [
212
213
  export default function DocumentationOverviewPage() {
213
214
  return (
214
215
  <Layout
215
- height="auto"
216
+ height="fill"
216
217
  contentWidth={1200}
218
+ start={
219
+ <LayoutPanel hasDivider padding={0}>
220
+ <SideNav header={<SideNavHeading heading="Product Name" />}>
221
+ <SideNavSection title="Navigation" isHeaderHidden>
222
+ <SideNavItem label="Home" isSelected />
223
+ <SideNavItem label="Getting started" />
224
+ </SideNavSection>
225
+
226
+ {COMPONENT_CATEGORIES.map(category => (
227
+ <SideNavSection key={category.label} title={category.label}>
228
+ {category.items.map(item => (
229
+ <SideNavItem key={item.key} label={item.name} />
230
+ ))}
231
+ </SideNavSection>
232
+ ))}
233
+ </SideNav>
234
+ </LayoutPanel>
235
+ }
217
236
  content={
218
237
  <LayoutContent padding={8}>
219
238
  <VStack gap={10}>
220
- <Card variant="gray" padding={10}>
239
+ <Card variant="cyan" padding={10}>
221
240
  <HStack gap={8} vAlign="center">
222
241
  <StackItem size="fill">
223
242
  <VStack gap={4}>
@@ -227,7 +246,11 @@ export default function DocumentationOverviewPage() {
227
246
  beautiful, accessible products.
228
247
  </Text>
229
248
  <HStack>
230
- <Button label="Get started" variant="primary" size="lg" />
249
+ <Button
250
+ label="Get started"
251
+ variant="primary"
252
+ size="lg"
253
+ />
231
254
  </HStack>
232
255
  </VStack>
233
256
  </StackItem>
@@ -237,35 +260,25 @@ export default function DocumentationOverviewPage() {
237
260
 
238
261
  {COMPONENT_CATEGORIES.map(category => (
239
262
  <VStack key={category.label} gap={4}>
240
- <Heading level={2}>{category.label}</Heading>
241
- <Grid
242
- columns={{minWidth: 260}}
243
- gap={2}
244
- xstyle={styles.cardGrid}>
263
+ <Text type="display-2">{category.label}</Text>
264
+ <Grid columns={{minWidth: 260}} gap={8}>
245
265
  {category.items.map(item => (
246
- <ClickableCard
247
- key={item.key}
248
- label={`Open ${item.name}`}
249
- onClick={() => {}}
250
- variant="transparent"
251
- padding={2}>
252
- <VStack gap={3}>
253
- <Card
254
- variant="muted"
255
- padding={0}
256
- minHeight={160}
257
- xstyle={styles.previewCard}
258
- />
259
- <VStack gap={0.5}>
260
- <Text type="body" weight="bold">
261
- {item.name}
262
- </Text>
263
- <Text type="body" color="secondary" maxLines={3}>
264
- {item.desc}
265
- </Text>
266
- </VStack>
266
+ <VStack key={item.key} gap={3}>
267
+ <Card
268
+ variant="muted"
269
+ padding={0}
270
+ minHeight={160}
271
+ xstyle={styles.previewCard}
272
+ />
273
+ <VStack gap={0.5}>
274
+ <Text type="body" weight="bold">
275
+ {item.name}
276
+ </Text>
277
+ <Text type="body" color="secondary">
278
+ {item.desc}
279
+ </Text>
267
280
  </VStack>
268
- </ClickableCard>
281
+ </VStack>
269
282
  ))}
270
283
  </Grid>
271
284
  </VStack>
@@ -2,8 +2,14 @@
2
2
 
3
3
  'use client';
4
4
 
5
- import {useCallback, useState, useMemo} from 'react';
5
+ import {useState, useMemo} from 'react';
6
6
  import * as stylex from '@stylexjs/stylex';
7
+ import {
8
+ SideNav,
9
+ SideNavHeading,
10
+ SideNavItem,
11
+ SideNavSection,
12
+ } from '@astryxdesign/core/SideNav';
7
13
  import {Heading, Text} from '@astryxdesign/core/Text';
8
14
  import {Button} from '@astryxdesign/core/Button';
9
15
  import {IconButton} from '@astryxdesign/core/IconButton';
@@ -14,10 +20,8 @@ import {Token} from '@astryxdesign/core/Token';
14
20
  import {Banner} from '@astryxdesign/core/Banner';
15
21
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
16
22
  import {TabList, Tab} from '@astryxdesign/core/TabList';
17
- import {Selector} from '@astryxdesign/core/Selector';
18
23
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
19
24
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
20
- import {useMediaQuery} from '@astryxdesign/core/hooks';
21
25
  import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
22
26
  import {Divider} from '@astryxdesign/core/Divider';
23
27
  import {Tooltip} from '@astryxdesign/core/Tooltip';
@@ -25,7 +29,6 @@ import {Table, pixel} from '@astryxdesign/core/Table';
25
29
  import {Icon} from '@astryxdesign/core/Icon';
26
30
  import {Section} from '@astryxdesign/core/Section';
27
31
  import {Center} from '@astryxdesign/core/Center';
28
- import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
29
32
  import {
30
33
  ArrowTopRightOnSquareIcon,
31
34
  ArrowsPointingOutIcon,
@@ -34,25 +37,8 @@ import {
34
37
 
35
38
  const styles = stylex.create({
36
39
  tabListFlush: {marginInlineStart: '-12px'},
37
- outlinePanel: {
38
- position: 'sticky',
39
- top: 24,
40
- alignSelf: 'start',
41
- paddingBlockStart: 120,
42
- },
43
40
  });
44
41
 
45
- const COMPONENT_OUTLINE_ITEMS: OutlineItem[] = [
46
- {id: 'usage', label: 'Usage', level: 2},
47
- {id: 'best-practices', label: 'Best practices', level: 3},
48
- {id: 'examples', label: 'Examples', level: 2},
49
- ];
50
-
51
- const COMPONENT_OUTLINE_OPTIONS = COMPONENT_OUTLINE_ITEMS.map(item => ({
52
- value: item.id,
53
- label: item.label,
54
- }));
55
-
56
42
  // ---------------------------------------------------------------------------
57
43
  // DialogPreview — stateful dialog preview for component previews
58
44
  // ---------------------------------------------------------------------------
@@ -507,21 +493,14 @@ function getComponentDocs(key: string) {
507
493
  // ComponentDetailView
508
494
  // ---------------------------------------------------------------------------
509
495
 
510
- function ComponentDetailView({activeNav}: {activeNav: string}) {
496
+ function ComponentDetailView({
497
+ activeNav,
498
+ nav,
499
+ }: {
500
+ activeNav: string;
501
+ nav: React.ReactNode;
502
+ }) {
511
503
  const [exampleTabs, setExampleTabs] = useState<Record<string, string>>({});
512
- const [activeId, setActiveId] = useState<string | undefined>(
513
- COMPONENT_OUTLINE_ITEMS[0]?.id,
514
- );
515
- const isMobile = useMediaQuery('(max-width: 768px)');
516
-
517
- const scrollToId = useCallback((id: string) => {
518
- setActiveId(id);
519
- const target = document.getElementById(id);
520
- if (target != null) {
521
- target.scrollIntoView({behavior: 'smooth', block: 'start'});
522
- window.history.pushState(null, '', `#${id}`);
523
- }
524
- }, []);
525
504
 
526
505
  const EXAMPLE_PREVIEWS: Record<string, React.ReactNode[]> = {
527
506
  button: [
@@ -577,42 +556,25 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
577
556
 
578
557
  return (
579
558
  <Layout
580
- height="auto"
559
+ height="fill"
581
560
  contentWidth={960}
582
- end={
583
- isMobile ? undefined : (
584
- <LayoutPanel
585
- isScrollable={false}
586
- label="On this page"
587
- role="complementary"
588
- xstyle={styles.outlinePanel}>
589
- <Outline
590
- items={COMPONENT_OUTLINE_ITEMS}
591
- onActiveIdChange={setActiveId}
592
- />
593
- </LayoutPanel>
594
- )
561
+ start={
562
+ <LayoutPanel hasDivider padding={0}>
563
+ {nav}
564
+ </LayoutPanel>
595
565
  }
596
566
  content={
597
- <LayoutContent isScrollable={false} padding={8}>
567
+ <LayoutContent padding={8}>
598
568
  <VStack gap={8}>
599
569
  <VStack gap={2}>
600
570
  <Text type="display-1">{getComponentName(activeNav)}</Text>
601
571
  <Text type="supporting" color="secondary">
602
572
  March 30, 2026 · Updated 5:40 p.m. PST
603
573
  </Text>
604
- {isMobile && (
605
- <Selector
606
- label="On this page"
607
- isLabelHidden
608
- options={COMPONENT_OUTLINE_OPTIONS}
609
- value={activeId}
610
- onChange={scrollToId}
611
- width="100%"
612
- />
613
- )}
614
574
  </VStack>
615
575
 
576
+ <Divider />
577
+
616
578
  <Card variant="muted" padding={0}>
617
579
  <Center height={360}>
618
580
  {COMPONENT_PREVIEWS[activeNav] ?? (
@@ -624,18 +586,13 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
624
586
  </Card>
625
587
 
626
588
  <VStack gap={4}>
627
- <Heading id="usage" level={2}>
628
- Usage
629
- </Heading>
589
+ <Heading level={2}>Usage</Heading>
630
590
  <Text type="large" weight="normal">
631
591
  {docs.usage}
632
592
  </Text>
633
- <Heading id="best-practices" level={3}>
634
- Best practices
635
- </Heading>
593
+ <Heading level={3}>Best practices</Heading>
636
594
  <Table
637
595
  data={docs.bestPractices as Record<string, unknown>[]}
638
- dividers="none"
639
596
  columns={[
640
597
  {
641
598
  key: 'type',
@@ -659,15 +616,14 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
659
616
  },
660
617
  ]}
661
618
  density="spacious"
619
+ dividers="rows"
662
620
  />
663
621
  </VStack>
664
622
 
665
623
  <Divider />
666
624
 
667
625
  <VStack gap={4}>
668
- <Heading id="examples" level={2}>
669
- Examples
670
- </Heading>
626
+ <Heading level={2}>Examples</Heading>
671
627
  <Text type="large" weight="normal">
672
628
  Explore common configurations, variations, and states for this
673
629
  component.
@@ -719,10 +675,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
719
675
  <TabList
720
676
  value={activeTab}
721
677
  onChange={value =>
722
- setExampleTabs(prev => ({
723
- ...prev,
724
- [tabKey]: value,
725
- }))
678
+ setExampleTabs(prev => ({...prev, [tabKey]: value}))
726
679
  }
727
680
  size="sm"
728
681
  xstyle={styles.tabListFlush}>
@@ -732,11 +685,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
732
685
  {activeTab === 'description' ? (
733
686
  <Text type="body">{example.description}</Text>
734
687
  ) : (
735
- <CodeBlock
736
- code={example.code}
737
- language="tsx"
738
- width="100%"
739
- />
688
+ <CodeBlock code={example.code} language="tsx" />
740
689
  )}
741
690
  </VStack>
742
691
  </Section>
@@ -756,5 +705,31 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
756
705
  // ---------------------------------------------------------------------------
757
706
 
758
707
  export default function DesignDocumentationPage() {
759
- return <ComponentDetailView activeNav="button" />;
708
+ const [activePage, setActivePage] = useState<string>('button');
709
+
710
+ return (
711
+ <ComponentDetailView
712
+ activeNav={activePage}
713
+ nav={
714
+ <SideNav header={<SideNavHeading heading="Product Name" />}>
715
+ {COMPONENT_CATEGORIES.map(category => (
716
+ <SideNavSection key={category.label} title={category.label}>
717
+ {category.items.map(item => (
718
+ <SideNavItem
719
+ key={item.key}
720
+ label={item.name}
721
+ isSelected={activePage === item.key}
722
+ onClick={
723
+ item.key === 'button'
724
+ ? () => setActivePage(item.key)
725
+ : undefined
726
+ }
727
+ />
728
+ ))}
729
+ </SideNavSection>
730
+ ))}
731
+ </SideNav>
732
+ }
733
+ />
734
+ );
760
735
  }
@@ -2,21 +2,22 @@
2
2
 
3
3
  'use client';
4
4
 
5
- import {useCallback, useState} from 'react';
6
- import * as stylex from '@stylexjs/stylex';
5
+ import {
6
+ SideNav,
7
+ SideNavHeading,
8
+ SideNavItem,
9
+ SideNavSection,
10
+ } from '@astryxdesign/core/SideNav';
7
11
  import {Heading, Text} from '@astryxdesign/core/Text';
8
12
  import {Button} from '@astryxdesign/core/Button';
9
13
  import {Card} from '@astryxdesign/core/Card';
10
14
  import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
11
15
  import {List, ListItem} from '@astryxdesign/core/List';
12
16
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
13
- import {Selector} from '@astryxdesign/core/Selector';
14
17
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
15
18
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
16
19
  import {Divider} from '@astryxdesign/core/Divider';
17
20
  import {Icon} from '@astryxdesign/core/Icon';
18
- import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
19
- import {useMediaQuery} from '@astryxdesign/core/hooks';
20
21
  import {
21
22
  SparklesIcon,
22
23
  ClipboardDocumentIcon,
@@ -27,75 +28,35 @@ import {
27
28
  // Main component
28
29
  // ---------------------------------------------------------------------------
29
30
 
30
- const OUTLINE_ITEMS: OutlineItem[] = [
31
- {id: 'prerequisites', label: 'Prerequisites', level: 2},
32
- {id: 'install-package', label: 'Install the package', level: 2},
33
- {id: 'configure-theming', label: 'Configure theming', level: 2},
34
- {id: 'next-steps', label: 'Next steps', level: 2},
35
- ];
36
-
37
- const OUTLINE_OPTIONS = OUTLINE_ITEMS.map(item => ({
38
- value: item.id,
39
- label: item.label,
40
- }));
41
-
42
- const styles = stylex.create({
43
- outlinePanel: {
44
- position: 'sticky',
45
- top: 24,
46
- alignSelf: 'start',
47
- paddingBlockStart: 120,
48
- },
49
- });
50
-
51
31
  export default function TechnicalDocumentationPage() {
52
- const [activeId, setActiveId] = useState<string | undefined>(
53
- OUTLINE_ITEMS[0]?.id,
54
- );
55
- const isMobile = useMediaQuery('(max-width: 768px)');
56
-
57
- const scrollToId = useCallback((id: string) => {
58
- setActiveId(id);
59
- const target = document.getElementById(id);
60
- if (target != null) {
61
- target.scrollIntoView({behavior: 'smooth', block: 'start'});
62
- window.history.pushState(null, '', `#${id}`);
63
- }
64
- }, []);
65
-
66
32
  return (
67
33
  <Layout
68
- height="auto"
34
+ height="fill"
69
35
  contentWidth={960}
70
- end={
71
- isMobile ? undefined : (
72
- <LayoutPanel
73
- isScrollable={false}
74
- label="On this page"
75
- role="complementary"
76
- xstyle={styles.outlinePanel}>
77
- <Outline items={OUTLINE_ITEMS} onActiveIdChange={setActiveId} />
78
- </LayoutPanel>
79
- )
36
+ start={
37
+ <LayoutPanel hasDivider padding={0}>
38
+ <SideNav header={<SideNavHeading heading="Product Name" />}>
39
+ <SideNavSection title="Navigation" isHeaderHidden>
40
+ <SideNavItem label="Home" />
41
+ <SideNavItem label="Getting started" isSelected />
42
+ </SideNavSection>
43
+ </SideNav>
44
+ </LayoutPanel>
80
45
  }
81
46
  content={
82
- <LayoutContent isScrollable={false} padding={8}>
47
+ <LayoutContent padding={8}>
83
48
  <VStack gap={8}>
84
49
  <VStack gap={2}>
85
- <Text type="display-1">Getting started with Product Name</Text>
50
+ <Text type="display-1">
51
+ Getting started with Product Name
52
+ </Text>
86
53
  <Text type="supporting" color="secondary">
87
54
  Last updated March 30, 2026
88
55
  </Text>
89
- {isMobile && (
90
- <Selector
91
- label="On this page"
92
- isLabelHidden
93
- options={OUTLINE_OPTIONS}
94
- value={activeId}
95
- onChange={scrollToId}
96
- width="100%"
97
- />
98
- )}
56
+ <Text type="body">
57
+ Install the package, configure your theme, and build your first
58
+ component in three steps.
59
+ </Text>
99
60
  </VStack>
100
61
 
101
62
  <Card>
@@ -103,7 +64,11 @@ export default function TechnicalDocumentationPage() {
103
64
  <HStack gap={2} vAlign="center">
104
65
  <StackItem size="fill">
105
66
  <HStack gap={2} vAlign="center">
106
- <Icon icon={SparklesIcon} size="sm" color="secondary" />
67
+ <Icon
68
+ icon={SparklesIcon}
69
+ size="sm"
70
+ color="secondary"
71
+ />
107
72
  <Text type="body" weight="semibold">
108
73
  AI Assistance
109
74
  </Text>
@@ -146,9 +111,7 @@ export default function TechnicalDocumentationPage() {
146
111
  </Card>
147
112
 
148
113
  <VStack gap={4}>
149
- <Heading id="prerequisites" level={2}>
150
- Prerequisites
151
- </Heading>
114
+ <Heading level={2}>Prerequisites</Heading>
152
115
  <List density="compact" listStyle="disc">
153
116
  <ListItem label="Node.js 18+" />
154
117
  <ListItem label="React 18 or 19" />
@@ -159,9 +122,7 @@ export default function TechnicalDocumentationPage() {
159
122
  <Divider />
160
123
 
161
124
  <VStack gap={4}>
162
- <Heading id="install-package" level={2}>
163
- Install the package
164
- </Heading>
125
+ <Heading level={2}>Install the package</Heading>
165
126
  <Text type="body">
166
127
  Every project starts with installing the core package. This
167
128
  gives you access to all components, tokens, and utilities.
@@ -170,11 +131,9 @@ export default function TechnicalDocumentationPage() {
170
131
  <Text type="body" weight="bold">
171
132
  Step 1: Install the core package
172
133
  </Text>
173
- <CodeBlock
174
- code="npm install @astryxdesign/core"
175
- language="bash"
176
- width="100%"
177
- />
134
+ <Card padding={0}>
135
+ <CodeBlock code="npm install @astryxdesign/core" language="bash" />
136
+ </Card>
178
137
  </VStack>
179
138
  <VStack gap={2}>
180
139
  <Text type="body" weight="bold">
@@ -184,41 +143,42 @@ export default function TechnicalDocumentationPage() {
184
143
  Astryx uses StyleX for styling. Add the compiler plugin to your
185
144
  build configuration.
186
145
  </Text>
187
- <CodeBlock
188
- code="npm install @stylexjs/babel-plugin"
189
- language="bash"
190
- width="100%"
191
- />
146
+ <Card padding={0}>
147
+ <CodeBlock
148
+ code="npm install @stylexjs/babel-plugin"
149
+ language="bash"
150
+ />
151
+ </Card>
192
152
  </VStack>
193
153
  <VStack gap={2}>
194
154
  <Text type="body" weight="bold">
195
155
  Step 3: Import your first component
196
156
  </Text>
197
- <CodeBlock
198
- code={`import { Button } from '@astryxdesign/core/Button';
157
+ <Card padding={0}>
158
+ <CodeBlock
159
+ code={`import { Button } from '@astryxdesign/core/Button';
199
160
 
200
161
  export default function App() {
201
162
  return <Button label="Hello Astryx" variant="primary" />;
202
163
  }`}
203
- language="tsx"
204
- width="100%"
205
- />
164
+ language="tsx"
165
+ />
166
+ </Card>
206
167
  </VStack>
207
168
  </VStack>
208
169
 
209
170
  <Divider />
210
171
 
211
172
  <VStack gap={4}>
212
- <Heading id="configure-theming" level={2}>
213
- Configure theming
214
- </Heading>
173
+ <Heading level={2}>Configure theming</Heading>
215
174
  <Text type="body">
216
175
  Astryx ships with a default theme that works out of the box. To
217
176
  customize colors, typography, and spacing, wrap your app in a
218
177
  theme provider.
219
178
  </Text>
220
- <CodeBlock
221
- code={`import { ThemeProvider } from '@astryxdesign/core/Theme';
179
+ <Card padding={0}>
180
+ <CodeBlock
181
+ code={`import { ThemeProvider } from '@astryxdesign/core/Theme';
222
182
 
223
183
  export default function App({ children }) {
224
184
  return (
@@ -227,9 +187,9 @@ export default function App({ children }) {
227
187
  </ThemeProvider>
228
188
  );
229
189
  }`}
230
- language="tsx"
231
- width="100%"
232
- />
190
+ language="tsx"
191
+ />
192
+ </Card>
233
193
  <Text type="body" color="secondary">
234
194
  See the theming guide for the full list of customizable tokens.
235
195
  </Text>
@@ -238,9 +198,7 @@ export default function App({ children }) {
238
198
  <Divider />
239
199
 
240
200
  <VStack gap={4}>
241
- <Heading id="next-steps" level={2}>
242
- Next steps
243
- </Heading>
201
+ <Heading level={2}>Next steps</Heading>
244
202
  <List density="compact" listStyle="disc">
245
203
  <ListItem label="Fundamental concepts — How theming, layout, and composition work" />
246
204
  <ListItem label="Component API reference — Props, variants, and examples for every component" />
@@ -4,14 +4,14 @@
4
4
 
5
5
  import {useState, useMemo} from 'react';
6
6
  import * as stylex from '@stylexjs/stylex';
7
+ import {SideNav, SideNavItem, SideNavSection} from '@astryxdesign/core/SideNav';
7
8
 
8
9
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
9
10
  import {ResizeHandle, useResizable} from '@astryxdesign/core/Resizable';
10
11
  import {Text, Heading} from '@astryxdesign/core/Text';
11
12
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
12
13
  import {colorVars, spacingVars} from '@astryxdesign/core/theme/tokens.stylex';
13
- import {Stack, StackItem} from '@astryxdesign/core/Layout';
14
- import {useMediaQuery} from '@astryxdesign/core/hooks';
14
+ import {Stack} from '@astryxdesign/core/Layout';
15
15
  import {TabList, Tab} from '@astryxdesign/core/TabList';
16
16
  import {
17
17
  SegmentedControl,
@@ -27,14 +27,29 @@ import type {TreeListItemData} from '@astryxdesign/core/TreeList';
27
27
  import {
28
28
  FolderIcon,
29
29
  DocumentTextIcon,
30
+ CodeBracketIcon,
30
31
  MagnifyingGlassIcon,
32
+ CommandLineIcon,
33
+ ExclamationTriangleIcon,
34
+ InformationCircleIcon,
35
+ BugAntIcon,
36
+ HomeIcon,
37
+ FolderOpenIcon,
38
+ PuzzlePieceIcon,
31
39
  } from '@heroicons/react/24/outline';
40
+ import {
41
+ HomeIcon as HomeIconSolid,
42
+ FolderOpenIcon as FolderOpenSolid,
43
+ MagnifyingGlassIcon as MagnifyingGlassSolid,
44
+ PuzzlePieceIcon as PuzzlePieceSolid,
45
+ } from '@heroicons/react/24/solid';
32
46
 
33
47
  const styles = stylex.create({
34
48
  contentFill: {
35
49
  height: '100%',
36
50
  },
37
51
  terminalWrapper: {
52
+ flex: 1,
38
53
  minHeight: 0,
39
54
  overflow: 'hidden',
40
55
  display: 'grid',
@@ -54,27 +69,28 @@ const styles = stylex.create({
54
69
  flexShrink: 0,
55
70
  },
56
71
  editorArea: {
72
+ flex: 1,
57
73
  overflow: 'auto',
58
- minHeight: 0,
59
74
  },
60
75
  fileExplorer: {
61
76
  padding: 16,
62
- minWidth: 0,
63
- },
64
- propertiesPanel: {
65
- height: '100%',
66
- },
67
- propertiesContent: {
68
- flex: 1,
69
- minHeight: 0,
70
- },
71
- propertyActions: {
72
- marginTop: 'auto',
73
77
  },
74
78
  terminalArea: {
75
79
  height: '100%',
76
80
  overflow: 'hidden',
77
81
  },
82
+ hideOnMobile: {
83
+ display: {
84
+ default: 'contents',
85
+ '@media (max-width: 768px)': 'none',
86
+ },
87
+ },
88
+ hideSideNav: {
89
+ display: {
90
+ default: 'flex',
91
+ '@media (max-width: 768px)': 'none',
92
+ },
93
+ },
78
94
  });
79
95
 
80
96
  const EDITOR_CODE = `import {useState, useCallback} from 'react';
@@ -135,23 +151,22 @@ $ `;
135
151
  function buildFileTree(
136
152
  onFileClick: (name: string) => void,
137
153
  ): TreeListItemData[] {
138
- const label = (text: string) => <Text maxLines={1}>{text}</Text>;
139
154
  const file = (id: string): TreeListItemData => ({
140
155
  id,
141
- label: label(id),
156
+ label: id,
142
157
  startContent: <Icon icon={DocumentTextIcon} size="xsm" />,
143
158
  onClick: () => onFileClick(id),
144
159
  });
145
160
  return [
146
161
  {
147
162
  id: 'src',
148
- label: label('src'),
163
+ label: 'src',
149
164
  startContent: <Icon icon={FolderIcon} size="xsm" />,
150
165
  isExpanded: true,
151
166
  children: [
152
167
  {
153
168
  id: 'components',
154
- label: label('components'),
169
+ label: 'components',
155
170
  startContent: <Icon icon={FolderIcon} size="xsm" />,
156
171
  isExpanded: true,
157
172
  children: [
@@ -162,14 +177,14 @@ function buildFileTree(
162
177
  },
163
178
  {
164
179
  id: 'pages',
165
- label: label('pages'),
180
+ label: 'pages',
166
181
  startContent: <Icon icon={FolderIcon} size="xsm" />,
167
182
  isExpanded: true,
168
183
  children: [file('index.tsx'), file('about.tsx')],
169
184
  },
170
185
  {
171
186
  id: 'styles',
172
- label: label('styles'),
187
+ label: 'styles',
173
188
  startContent: <Icon icon={FolderIcon} size="xsm" />,
174
189
  isExpanded: true,
175
190
  children: [file('tokens.stylex.ts'), file('theme.ts')],
@@ -201,6 +216,7 @@ const HISTORY_ITEMS = [
201
216
 
202
217
  export default function ResizableWorkspacePage() {
203
218
  const [activeFile, setActiveFile] = useState('Counter.tsx');
219
+ const [activeNavItem, setActiveNavItem] = useState('Explorer');
204
220
  const [activeTermTab, setActiveTermTab] = useState('terminal');
205
221
  const [activePropertiesTab, setActivePropertiesTab] = useState('properties');
206
222
  const fileTree = useMemo(() => buildFileTree(setActiveFile), []);
@@ -229,48 +245,89 @@ export default function ResizableWorkspacePage() {
229
245
  collapsedSize: 40,
230
246
  });
231
247
 
232
- const isMobile = useMediaQuery('(max-width: 768px)');
233
-
234
248
  return (
235
249
  <Layout
236
250
  height="fill"
251
+ start={
252
+ <LayoutPanel hasDivider={false} padding={0}>
253
+ <SideNav
254
+ collapsible={{defaultIsCollapsed: true}}
255
+ resizable
256
+ xstyle={styles.hideSideNav}>
257
+ <SideNavSection title="Navigation" isHeaderHidden>
258
+ <SideNavItem
259
+ label="Home"
260
+ icon={HomeIcon}
261
+ selectedIcon={HomeIconSolid}
262
+ isSelected={activeNavItem === 'Home'}
263
+ onClick={() => setActiveNavItem('Home')}
264
+ />
265
+ <SideNavItem
266
+ label="Explorer"
267
+ icon={FolderOpenIcon}
268
+ selectedIcon={FolderOpenSolid}
269
+ isSelected={activeNavItem === 'Explorer'}
270
+ onClick={() => setActiveNavItem('Explorer')}
271
+ />
272
+ <SideNavItem
273
+ label="Search"
274
+ icon={MagnifyingGlassIcon}
275
+ selectedIcon={MagnifyingGlassSolid}
276
+ isSelected={activeNavItem === 'Search'}
277
+ onClick={() => setActiveNavItem('Search')}
278
+ />
279
+ <SideNavItem
280
+ label="Source Control"
281
+ icon={CodeBracketIcon}
282
+ isSelected={activeNavItem === 'Source Control'}
283
+ onClick={() => setActiveNavItem('Source Control')}
284
+ />
285
+ <SideNavItem
286
+ label="Extensions"
287
+ icon={PuzzlePieceIcon}
288
+ selectedIcon={PuzzlePieceSolid}
289
+ isSelected={activeNavItem === 'Extensions'}
290
+ onClick={() => setActiveNavItem('Extensions')}
291
+ />
292
+ </SideNavSection>
293
+ </SideNav>
294
+ </LayoutPanel>
295
+ }
237
296
  content={
238
297
  <LayoutContent padding={0}>
239
298
  <Layout
240
299
  height="fill"
241
300
  start={
242
- isMobile ? undefined : (
243
- <>
244
- {!startPanel.isCollapsed && (
245
- <LayoutPanel
246
- width={startPanel.size}
247
- hasDivider={false}
248
- padding={0}>
249
- <Stack
250
- direction="vertical"
251
- xstyle={styles.fileExplorer}
252
- gap={2}>
253
- <TextInput
254
- label="Search files"
255
- isLabelHidden
256
- value=""
257
- placeholder="Search"
258
- size="md"
259
- startIcon={MagnifyingGlassIcon}
260
- />
261
- <TreeList items={fileTree} density="compact" />
262
- </Stack>
263
- </LayoutPanel>
264
- )}
265
- <ResizeHandle
266
- direction="horizontal"
267
- hasDivider
268
- isAlwaysVisible={false}
269
- resizable={startPanel.props}
270
- label="Resize file explorer"
271
- />
272
- </>
273
- )
301
+ <div {...stylex.props(styles.hideOnMobile)}>
302
+ {!startPanel.isCollapsed && (
303
+ <LayoutPanel
304
+ width={startPanel.size}
305
+ hasDivider={false}
306
+ padding={0}>
307
+ <Stack
308
+ direction="vertical"
309
+ xstyle={styles.fileExplorer}
310
+ gap={2}>
311
+ <TextInput
312
+ label="Search files"
313
+ isLabelHidden
314
+ value=""
315
+ placeholder="Search"
316
+ size="md"
317
+ startIcon={MagnifyingGlassIcon}
318
+ />
319
+ <TreeList items={fileTree} density="compact" />
320
+ </Stack>
321
+ </LayoutPanel>
322
+ )}
323
+ <ResizeHandle
324
+ direction="horizontal"
325
+ hasDivider
326
+ isAlwaysVisible={false}
327
+ resizable={startPanel.props}
328
+ label="Resize file explorer"
329
+ />
330
+ </div>
274
331
  }
275
332
  content={
276
333
  <LayoutContent padding={0}>
@@ -278,13 +335,13 @@ export default function ResizableWorkspacePage() {
278
335
  height="fill"
279
336
  content={
280
337
  <LayoutContent padding={0}>
281
- <Stack direction="vertical" xstyle={styles.contentFill}>
282
- <StackItem size="fill" xstyle={styles.editorArea}>
338
+ <Stack
339
+ direction="vertical"
340
+ xstyle={styles.contentFill}>
341
+ <div {...stylex.props(styles.editorArea)}>
283
342
  <CodeBlock
284
343
  code={EDITOR_CODE}
285
344
  language="typescript"
286
- container="section"
287
- hasLanguageLabel={false}
288
345
  hasLineNumbers
289
346
  highlightLines={[21]}
290
347
  hasCopyButton={false}
@@ -296,7 +353,7 @@ export default function ResizableWorkspacePage() {
296
353
  borderRadius: 0,
297
354
  }}
298
355
  />
299
- </StackItem>
356
+ </div>
300
357
  <ResizeHandle
301
358
  direction="vertical"
302
359
  hasDivider
@@ -321,19 +378,43 @@ export default function ResizableWorkspacePage() {
321
378
  size="sm"
322
379
  hasDivider={false}
323
380
  xstyle={styles.tabListPadding}>
324
- <Tab label="Terminal" value="terminal" />
325
- <Tab label="Problems" value="problems" />
326
- <Tab label="Output" value="output" />
327
- <Tab label="Debug" value="debug" />
381
+ <Tab
382
+ label="Terminal"
383
+ value="terminal"
384
+ icon={
385
+ <Icon icon={CommandLineIcon} size="sm" />
386
+ }
387
+ />
388
+ <Tab
389
+ label="Problems"
390
+ value="problems"
391
+ icon={
392
+ <Icon
393
+ icon={ExclamationTriangleIcon}
394
+ size="sm"
395
+ />
396
+ }
397
+ />
398
+ <Tab
399
+ label="Output"
400
+ value="output"
401
+ icon={
402
+ <Icon
403
+ icon={InformationCircleIcon}
404
+ size="sm"
405
+ />
406
+ }
407
+ />
408
+ <Tab
409
+ label="Debug"
410
+ value="debug"
411
+ icon={<Icon icon={BugAntIcon} size="sm" />}
412
+ />
328
413
  </TabList>
329
- <StackItem
330
- size="fill"
331
- xstyle={styles.terminalWrapper}>
414
+ <div {...stylex.props(styles.terminalWrapper)}>
332
415
  <CodeBlock
333
416
  code={TERMINAL_OUTPUT}
334
417
  language="bash"
335
- container="section"
336
- hasLanguageLabel={false}
337
418
  hasCopyButton={false}
338
419
  size="sm"
339
420
  style={{
@@ -343,7 +424,7 @@ export default function ResizableWorkspacePage() {
343
424
  borderRadius: 0,
344
425
  }}
345
426
  />
346
- </StackItem>
427
+ </div>
347
428
  </Stack>
348
429
  </div>
349
430
  )}
@@ -351,118 +432,106 @@ export default function ResizableWorkspacePage() {
351
432
  </LayoutContent>
352
433
  }
353
434
  end={
354
- isMobile ? undefined : (
355
- <>
356
- <ResizeHandle
357
- direction="horizontal"
358
- hasDivider
359
- isReversed
360
- isAlwaysVisible={false}
361
- resizable={endPanel.props}
362
- label="Resize properties panel"
363
- />
364
- {!endPanel.isCollapsed && (
365
- <LayoutPanel
366
- width={endPanel.size}
367
- hasDivider={false}
368
- padding={4}>
369
- <Stack
370
- direction="vertical"
371
- gap={3}
372
- xstyle={styles.propertiesPanel}>
373
- <SegmentedControl
374
- label="Properties panel sections"
375
- value={activePropertiesTab}
376
- onChange={setActivePropertiesTab}
377
- size="sm"
378
- layout="fill">
379
- <SegmentedControlItem
380
- label="Properties"
381
- value="properties"
382
- />
383
- <SegmentedControlItem
384
- label="History"
385
- value="history"
386
- />
387
- </SegmentedControl>
388
- {activePropertiesTab === 'properties' ? (
389
- <Stack
390
- direction="vertical"
391
- gap={3}
392
- xstyle={styles.propertiesContent}>
393
- <Stack direction="vertical" gap={1}>
394
- <Heading level={3} maxLines={1}>
395
- {activeFile}
396
- </Heading>
397
- <Text
398
- color="secondary"
399
- type="supporting"
400
- maxLines={1}>
401
- src/components/{activeFile}
402
- </Text>
403
- </Stack>
404
- <MetadataList xstyle={styles.metadataCompact}>
405
- {PROPERTIES.map(prop => (
406
- <MetadataListItem
407
- key={prop.label}
408
- label={prop.label}>
409
- {prop.value}
410
- </MetadataListItem>
411
- ))}
412
- </MetadataList>
413
- <Stack
414
- direction="vertical"
415
- gap={2}
416
- xstyle={styles.propertyActions}>
435
+ <div {...stylex.props(styles.hideOnMobile)}>
436
+ <ResizeHandle
437
+ direction="horizontal"
438
+ hasDivider
439
+ isReversed
440
+ isAlwaysVisible={false}
441
+ resizable={endPanel.props}
442
+ label="Resize properties panel"
443
+ />
444
+ {!endPanel.isCollapsed && (
445
+ <LayoutPanel
446
+ width={endPanel.size}
447
+ hasDivider={false}
448
+ padding={4}>
449
+ <Stack direction="vertical" gap={3}>
450
+ <SegmentedControl
451
+ label="Properties panel sections"
452
+ value={activePropertiesTab}
453
+ onChange={setActivePropertiesTab}
454
+ size="sm"
455
+ layout="fill">
456
+ <SegmentedControlItem
457
+ label="Properties"
458
+ value="properties"
459
+ />
460
+ <SegmentedControlItem
461
+ label="History"
462
+ value="history"
463
+ />
464
+ </SegmentedControl>
465
+ {activePropertiesTab === 'properties' ? (
466
+ <>
467
+ <Stack direction="vertical" gap={1}>
468
+ <Heading level={3}>
469
+ {activeFile}
470
+ </Heading>
471
+ <Text color="secondary" type="supporting">
472
+ src/components/{activeFile}
473
+ </Text>
474
+ </Stack>
475
+ <MetadataList
476
+ xstyle={styles.metadataCompact}>
477
+ {PROPERTIES.map(prop => (
478
+ <MetadataListItem
479
+ key={prop.label}
480
+ label={prop.label}>
481
+ {prop.value}
482
+ </MetadataListItem>
483
+ ))}
484
+ </MetadataList>
485
+ <Stack direction="vertical" gap={2}>
486
+ <Stack direction="vertical" gap={2}>
417
487
  <Button
418
488
  label="Format Document"
419
- size="sm"
489
+ size="md"
420
490
  variant="secondary"
421
491
  />
422
492
  <Button
423
493
  label="Go to Definition"
424
- size="sm"
494
+ size="md"
425
495
  variant="secondary"
426
496
  />
427
497
  <Button
428
498
  label="Find References"
429
- size="sm"
499
+ size="md"
430
500
  variant="secondary"
431
501
  />
432
502
  </Stack>
433
503
  </Stack>
434
- ) : (
435
- <Stack direction="vertical" gap={1}>
436
- <List>
437
- {HISTORY_ITEMS.map(item => (
438
- <ListItem
439
- key={item.label}
440
- label={item.label}
441
- endContent={
442
- <Text
443
- type="supporting"
444
- color="secondary"
445
- maxLines={1}>
446
- {item.time}
447
- </Text>
448
- }
449
- startContent={
450
- <span
451
- {...stylex.props(
452
- styles.historyTimelineDot,
453
- )}
454
- />
455
- }
456
- />
457
- ))}
458
- </List>
459
- </Stack>
460
- )}
461
- </Stack>
462
- </LayoutPanel>
463
- )}
464
- </>
465
- )
504
+ </>
505
+ ) : (
506
+ <Stack direction="vertical" gap={1}>
507
+ <List>
508
+ {HISTORY_ITEMS.map(item => (
509
+ <ListItem
510
+ key={item.label}
511
+ label={item.label}
512
+ endContent={
513
+ <Text
514
+ type="supporting"
515
+ color="secondary">
516
+ {item.time}
517
+ </Text>
518
+ }
519
+ startContent={
520
+ <span
521
+ {...stylex.props(
522
+ styles.historyTimelineDot,
523
+ )}
524
+ />
525
+ }
526
+ />
527
+ ))}
528
+ </List>
529
+ </Stack>
530
+ )}
531
+ </Stack>
532
+ </LayoutPanel>
533
+ )}
534
+ </div>
466
535
  }
467
536
  />
468
537
  </LayoutContent>