@astryxdesign/cli 0.1.0-canary.797f761 → 0.1.0-canary.7f17ba3

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 XDS" onClick={() => alert('Hi!')} />
91
+ <Button label="Hello Astryx" 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 xstyle',
99
+ title: 'Customize with StyleX',
100
100
  content: [
101
101
  {
102
102
  type: 'prose',
103
- text: 'Every component accepts an `xstyle` prop for StyleX style overrides created via `stylex.create()`.',
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.',
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'],
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'],
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)'],
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/facebookexperimental/xds.git
142
- cd xds/apps/example-nextjs
141
+ code: `git clone https://github.com/facebook/astryx.git
142
+ cd astryx/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
- "xds": "node node_modules/@astryxdesign/cli/bin/astryx.mjs"
160
+ "astryx": "node node_modules/@astryxdesign/cli/bin/astryx.mjs"
161
161
  }`,
162
162
  },
163
163
  {
@@ -22,9 +22,8 @@ export const docs = {
22
22
  type: 'table',
23
23
  headers: ['Approach', 'Use for', 'Example'],
24
24
  rows: [
25
- ['xstyle prop', 'Overriding a specific component', 'xstyle={styles.override}'],
25
+ ['StyleX', 'Component-specific overrides, reusable styles, pseudo-classes, and typed tokens', 'const styles = stylex.create(...); <Button xstyle={styles.save} />'],
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: { ... } })'],
28
27
  ['className', 'Integrating with external CSS or Tailwind on components', 'className="my-card shadow-lg"'],
29
28
  ['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)'"],
30
29
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.0-canary.797f761",
3
+ "version": "0.1.0-canary.7f17ba3",
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.797f761",
58
- "@astryxdesign/lab": "0.1.0-canary.797f761",
59
- "@astryxdesign/theme-neutral": "0.1.0-canary.797f761"
57
+ "@astryxdesign/core": "0.1.0-canary.7f17ba3",
58
+ "@astryxdesign/lab": "0.1.0-canary.7f17ba3",
59
+ "@astryxdesign/theme-neutral": "0.1.0-canary.7f17ba3"
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.797f761",
74
- "@astryxdesign/lab": "0.1.0-canary.797f761",
75
- "@astryxdesign/theme-neutral": "0.1.0-canary.797f761"
73
+ "@astryxdesign/core": "0.1.0-canary.7f17ba3",
74
+ "@astryxdesign/lab": "0.1.0-canary.7f17ba3",
75
+ "@astryxdesign/theme-neutral": "0.1.0-canary.7f17ba3"
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} xds --help for all commands`);
251
+ humanLog(` 3. ${run} astryx --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 xds', 'yarn xds', 'pnpm exec xds').
7
+ * (e.g. 'npx astryx', 'yarn astryx', 'pnpm exec astryx').
8
8
  */
9
9
 
10
10
  import * as fs from 'node:fs';
@@ -3,24 +3,23 @@
3
3
  'use client';
4
4
 
5
5
  import * as stylex from '@stylexjs/stylex';
6
- import {
7
- SideNav,
8
- SideNavHeading,
9
- SideNavItem,
10
- SideNavSection,
11
- } from '@astryxdesign/core/SideNav';
12
- import {Text} from '@astryxdesign/core/Text';
6
+ import {Heading, Text} from '@astryxdesign/core/Text';
13
7
  import {Button} from '@astryxdesign/core/Button';
14
8
  import {Card} from '@astryxdesign/core/Card';
9
+ import {ClickableCard} from '@astryxdesign/core/ClickableCard';
15
10
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
16
- import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
11
+ import {Layout, LayoutContent} from '@astryxdesign/core/Layout';
17
12
  import {Grid} from '@astryxdesign/core/Grid';
18
13
  import {radiusVars} from '@astryxdesign/core/theme/tokens.stylex';
19
14
 
20
15
  const styles = stylex.create({
21
16
  previewCard: {
22
- borderRadius: radiusVars['--radius-container'],
23
- cursor: 'pointer',
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,
24
23
  },
25
24
  });
26
25
 
@@ -213,30 +212,12 @@ const COMPONENT_CATEGORIES = [
213
212
  export default function DocumentationOverviewPage() {
214
213
  return (
215
214
  <Layout
216
- height="fill"
215
+ height="auto"
217
216
  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
- }
236
217
  content={
237
218
  <LayoutContent padding={8}>
238
219
  <VStack gap={10}>
239
- <Card variant="cyan" padding={10}>
220
+ <Card variant="gray" padding={10}>
240
221
  <HStack gap={8} vAlign="center">
241
222
  <StackItem size="fill">
242
223
  <VStack gap={4}>
@@ -246,11 +227,7 @@ export default function DocumentationOverviewPage() {
246
227
  beautiful, accessible products.
247
228
  </Text>
248
229
  <HStack>
249
- <Button
250
- label="Get started"
251
- variant="primary"
252
- size="lg"
253
- />
230
+ <Button label="Get started" variant="primary" size="lg" />
254
231
  </HStack>
255
232
  </VStack>
256
233
  </StackItem>
@@ -260,25 +237,35 @@ export default function DocumentationOverviewPage() {
260
237
 
261
238
  {COMPONENT_CATEGORIES.map(category => (
262
239
  <VStack key={category.label} gap={4}>
263
- <Text type="display-2">{category.label}</Text>
264
- <Grid columns={{minWidth: 260}} gap={8}>
240
+ <Heading level={2}>{category.label}</Heading>
241
+ <Grid
242
+ columns={{minWidth: 260}}
243
+ gap={2}
244
+ xstyle={styles.cardGrid}>
265
245
  {category.items.map(item => (
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>
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>
280
267
  </VStack>
281
- </VStack>
268
+ </ClickableCard>
282
269
  ))}
283
270
  </Grid>
284
271
  </VStack>
@@ -2,14 +2,8 @@
2
2
 
3
3
  'use client';
4
4
 
5
- import {useState, useMemo} from 'react';
5
+ import {useCallback, 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';
13
7
  import {Heading, Text} from '@astryxdesign/core/Text';
14
8
  import {Button} from '@astryxdesign/core/Button';
15
9
  import {IconButton} from '@astryxdesign/core/IconButton';
@@ -20,8 +14,10 @@ import {Token} from '@astryxdesign/core/Token';
20
14
  import {Banner} from '@astryxdesign/core/Banner';
21
15
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
22
16
  import {TabList, Tab} from '@astryxdesign/core/TabList';
17
+ import {Selector} from '@astryxdesign/core/Selector';
23
18
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
24
19
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
20
+ import {useMediaQuery} from '@astryxdesign/core/hooks';
25
21
  import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
26
22
  import {Divider} from '@astryxdesign/core/Divider';
27
23
  import {Tooltip} from '@astryxdesign/core/Tooltip';
@@ -29,6 +25,7 @@ import {Table, pixel} from '@astryxdesign/core/Table';
29
25
  import {Icon} from '@astryxdesign/core/Icon';
30
26
  import {Section} from '@astryxdesign/core/Section';
31
27
  import {Center} from '@astryxdesign/core/Center';
28
+ import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
32
29
  import {
33
30
  ArrowTopRightOnSquareIcon,
34
31
  ArrowsPointingOutIcon,
@@ -37,8 +34,25 @@ import {
37
34
 
38
35
  const styles = stylex.create({
39
36
  tabListFlush: {marginInlineStart: '-12px'},
37
+ outlinePanel: {
38
+ position: 'sticky',
39
+ top: 24,
40
+ alignSelf: 'start',
41
+ paddingBlockStart: 120,
42
+ },
40
43
  });
41
44
 
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
+
42
56
  // ---------------------------------------------------------------------------
43
57
  // DialogPreview — stateful dialog preview for component previews
44
58
  // ---------------------------------------------------------------------------
@@ -493,14 +507,21 @@ function getComponentDocs(key: string) {
493
507
  // ComponentDetailView
494
508
  // ---------------------------------------------------------------------------
495
509
 
496
- function ComponentDetailView({
497
- activeNav,
498
- nav,
499
- }: {
500
- activeNav: string;
501
- nav: React.ReactNode;
502
- }) {
510
+ function ComponentDetailView({activeNav}: {activeNav: string}) {
503
511
  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
+ }, []);
504
525
 
505
526
  const EXAMPLE_PREVIEWS: Record<string, React.ReactNode[]> = {
506
527
  button: [
@@ -556,25 +577,42 @@ function ComponentDetailView({
556
577
 
557
578
  return (
558
579
  <Layout
559
- height="fill"
580
+ height="auto"
560
581
  contentWidth={960}
561
- start={
562
- <LayoutPanel hasDivider padding={0}>
563
- {nav}
564
- </LayoutPanel>
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
+ )
565
595
  }
566
596
  content={
567
- <LayoutContent padding={8}>
597
+ <LayoutContent isScrollable={false} padding={8}>
568
598
  <VStack gap={8}>
569
599
  <VStack gap={2}>
570
600
  <Text type="display-1">{getComponentName(activeNav)}</Text>
571
601
  <Text type="supporting" color="secondary">
572
602
  March 30, 2026 · Updated 5:40 p.m. PST
573
603
  </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
+ )}
574
614
  </VStack>
575
615
 
576
- <Divider />
577
-
578
616
  <Card variant="muted" padding={0}>
579
617
  <Center height={360}>
580
618
  {COMPONENT_PREVIEWS[activeNav] ?? (
@@ -586,13 +624,18 @@ function ComponentDetailView({
586
624
  </Card>
587
625
 
588
626
  <VStack gap={4}>
589
- <Heading level={2}>Usage</Heading>
627
+ <Heading id="usage" level={2}>
628
+ Usage
629
+ </Heading>
590
630
  <Text type="large" weight="normal">
591
631
  {docs.usage}
592
632
  </Text>
593
- <Heading level={3}>Best practices</Heading>
633
+ <Heading id="best-practices" level={3}>
634
+ Best practices
635
+ </Heading>
594
636
  <Table
595
637
  data={docs.bestPractices as Record<string, unknown>[]}
638
+ dividers="none"
596
639
  columns={[
597
640
  {
598
641
  key: 'type',
@@ -616,14 +659,15 @@ function ComponentDetailView({
616
659
  },
617
660
  ]}
618
661
  density="spacious"
619
- dividers="rows"
620
662
  />
621
663
  </VStack>
622
664
 
623
665
  <Divider />
624
666
 
625
667
  <VStack gap={4}>
626
- <Heading level={2}>Examples</Heading>
668
+ <Heading id="examples" level={2}>
669
+ Examples
670
+ </Heading>
627
671
  <Text type="large" weight="normal">
628
672
  Explore common configurations, variations, and states for this
629
673
  component.
@@ -675,7 +719,10 @@ function ComponentDetailView({
675
719
  <TabList
676
720
  value={activeTab}
677
721
  onChange={value =>
678
- setExampleTabs(prev => ({...prev, [tabKey]: value}))
722
+ setExampleTabs(prev => ({
723
+ ...prev,
724
+ [tabKey]: value,
725
+ }))
679
726
  }
680
727
  size="sm"
681
728
  xstyle={styles.tabListFlush}>
@@ -685,7 +732,11 @@ function ComponentDetailView({
685
732
  {activeTab === 'description' ? (
686
733
  <Text type="body">{example.description}</Text>
687
734
  ) : (
688
- <CodeBlock code={example.code} language="tsx" />
735
+ <CodeBlock
736
+ code={example.code}
737
+ language="tsx"
738
+ width="100%"
739
+ />
689
740
  )}
690
741
  </VStack>
691
742
  </Section>
@@ -705,31 +756,5 @@ function ComponentDetailView({
705
756
  // ---------------------------------------------------------------------------
706
757
 
707
758
  export default function DesignDocumentationPage() {
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
- );
759
+ return <ComponentDetailView activeNav="button" />;
735
760
  }
@@ -2,22 +2,21 @@
2
2
 
3
3
  'use client';
4
4
 
5
- import {
6
- SideNav,
7
- SideNavHeading,
8
- SideNavItem,
9
- SideNavSection,
10
- } from '@astryxdesign/core/SideNav';
5
+ import {useCallback, useState} from 'react';
6
+ import * as stylex from '@stylexjs/stylex';
11
7
  import {Heading, Text} from '@astryxdesign/core/Text';
12
8
  import {Button} from '@astryxdesign/core/Button';
13
9
  import {Card} from '@astryxdesign/core/Card';
14
10
  import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
15
11
  import {List, ListItem} from '@astryxdesign/core/List';
16
12
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
13
+ import {Selector} from '@astryxdesign/core/Selector';
17
14
  import {HStack, VStack, StackItem} from '@astryxdesign/core/Stack';
18
15
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
19
16
  import {Divider} from '@astryxdesign/core/Divider';
20
17
  import {Icon} from '@astryxdesign/core/Icon';
18
+ import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
19
+ import {useMediaQuery} from '@astryxdesign/core/hooks';
21
20
  import {
22
21
  SparklesIcon,
23
22
  ClipboardDocumentIcon,
@@ -28,35 +27,75 @@ import {
28
27
  // Main component
29
28
  // ---------------------------------------------------------------------------
30
29
 
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
+
31
51
  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
+
32
66
  return (
33
67
  <Layout
34
- height="fill"
68
+ height="auto"
35
69
  contentWidth={960}
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>
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
+ )
45
80
  }
46
81
  content={
47
- <LayoutContent padding={8}>
82
+ <LayoutContent isScrollable={false} padding={8}>
48
83
  <VStack gap={8}>
49
84
  <VStack gap={2}>
50
- <Text type="display-1">
51
- Getting started with Product Name
52
- </Text>
85
+ <Text type="display-1">Getting started with Product Name</Text>
53
86
  <Text type="supporting" color="secondary">
54
87
  Last updated March 30, 2026
55
88
  </Text>
56
- <Text type="body">
57
- Install the package, configure your theme, and build your first
58
- component in three steps.
59
- </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
+ )}
60
99
  </VStack>
61
100
 
62
101
  <Card>
@@ -64,11 +103,7 @@ export default function TechnicalDocumentationPage() {
64
103
  <HStack gap={2} vAlign="center">
65
104
  <StackItem size="fill">
66
105
  <HStack gap={2} vAlign="center">
67
- <Icon
68
- icon={SparklesIcon}
69
- size="sm"
70
- color="secondary"
71
- />
106
+ <Icon icon={SparklesIcon} size="sm" color="secondary" />
72
107
  <Text type="body" weight="semibold">
73
108
  AI Assistance
74
109
  </Text>
@@ -103,15 +138,17 @@ export default function TechnicalDocumentationPage() {
103
138
  </HStack>
104
139
  <Text type="body" color="secondary">
105
140
  Help me get set up with Product Name. Based on my project, do
106
- the following: 1. Install @astryxdesign/core and the StyleX compiler.
107
- 2. Wrap my app in ThemeProvider. 3. Replace one existing
108
- component with an Astryx equivalent.
141
+ the following: 1. Install @astryxdesign/core and the StyleX
142
+ compiler. 2. Wrap my app in ThemeProvider. 3. Replace one
143
+ existing component with an Astryx equivalent.
109
144
  </Text>
110
145
  </VStack>
111
146
  </Card>
112
147
 
113
148
  <VStack gap={4}>
114
- <Heading level={2}>Prerequisites</Heading>
149
+ <Heading id="prerequisites" level={2}>
150
+ Prerequisites
151
+ </Heading>
115
152
  <List density="compact" listStyle="disc">
116
153
  <ListItem label="Node.js 18+" />
117
154
  <ListItem label="React 18 or 19" />
@@ -122,7 +159,9 @@ export default function TechnicalDocumentationPage() {
122
159
  <Divider />
123
160
 
124
161
  <VStack gap={4}>
125
- <Heading level={2}>Install the package</Heading>
162
+ <Heading id="install-package" level={2}>
163
+ Install the package
164
+ </Heading>
126
165
  <Text type="body">
127
166
  Every project starts with installing the core package. This
128
167
  gives you access to all components, tokens, and utilities.
@@ -131,54 +170,55 @@ export default function TechnicalDocumentationPage() {
131
170
  <Text type="body" weight="bold">
132
171
  Step 1: Install the core package
133
172
  </Text>
134
- <Card padding={0}>
135
- <CodeBlock code="npm install @astryxdesign/core" language="bash" />
136
- </Card>
173
+ <CodeBlock
174
+ code="npm install @astryxdesign/core"
175
+ language="bash"
176
+ width="100%"
177
+ />
137
178
  </VStack>
138
179
  <VStack gap={2}>
139
180
  <Text type="body" weight="bold">
140
181
  Step 2: Add the StyleX compiler
141
182
  </Text>
142
183
  <Text type="body" color="secondary">
143
- Astryx uses StyleX for styling. Add the compiler plugin to your
144
- build configuration.
184
+ Astryx uses StyleX for styling. Add the compiler plugin to
185
+ your build configuration.
145
186
  </Text>
146
- <Card padding={0}>
147
- <CodeBlock
148
- code="npm install @stylexjs/babel-plugin"
149
- language="bash"
150
- />
151
- </Card>
187
+ <CodeBlock
188
+ code="npm install @stylexjs/babel-plugin"
189
+ language="bash"
190
+ width="100%"
191
+ />
152
192
  </VStack>
153
193
  <VStack gap={2}>
154
194
  <Text type="body" weight="bold">
155
195
  Step 3: Import your first component
156
196
  </Text>
157
- <Card padding={0}>
158
- <CodeBlock
159
- code={`import { Button } from '@astryxdesign/core/Button';
197
+ <CodeBlock
198
+ code={`import { Button } from '@astryxdesign/core/Button';
160
199
 
161
200
  export default function App() {
162
201
  return <Button label="Hello Astryx" variant="primary" />;
163
202
  }`}
164
- language="tsx"
165
- />
166
- </Card>
203
+ language="tsx"
204
+ width="100%"
205
+ />
167
206
  </VStack>
168
207
  </VStack>
169
208
 
170
209
  <Divider />
171
210
 
172
211
  <VStack gap={4}>
173
- <Heading level={2}>Configure theming</Heading>
212
+ <Heading id="configure-theming" level={2}>
213
+ Configure theming
214
+ </Heading>
174
215
  <Text type="body">
175
216
  Astryx ships with a default theme that works out of the box. To
176
217
  customize colors, typography, and spacing, wrap your app in a
177
218
  theme provider.
178
219
  </Text>
179
- <Card padding={0}>
180
- <CodeBlock
181
- code={`import { ThemeProvider } from '@astryxdesign/core/Theme';
220
+ <CodeBlock
221
+ code={`import { ThemeProvider } from '@astryxdesign/core/Theme';
182
222
 
183
223
  export default function App({ children }) {
184
224
  return (
@@ -187,9 +227,9 @@ export default function App({ children }) {
187
227
  </ThemeProvider>
188
228
  );
189
229
  }`}
190
- language="tsx"
191
- />
192
- </Card>
230
+ language="tsx"
231
+ width="100%"
232
+ />
193
233
  <Text type="body" color="secondary">
194
234
  See the theming guide for the full list of customizable tokens.
195
235
  </Text>
@@ -198,7 +238,9 @@ export default function App({ children }) {
198
238
  <Divider />
199
239
 
200
240
  <VStack gap={4}>
201
- <Heading level={2}>Next steps</Heading>
241
+ <Heading id="next-steps" level={2}>
242
+ Next steps
243
+ </Heading>
202
244
  <List density="compact" listStyle="disc">
203
245
  <ListItem label="Fundamental concepts — How theming, layout, and composition work" />
204
246
  <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';
8
7
 
9
8
  import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
10
9
  import {ResizeHandle, useResizable} from '@astryxdesign/core/Resizable';
11
10
  import {Text, Heading} from '@astryxdesign/core/Text';
12
11
  import {CodeBlock} from '@astryxdesign/core/CodeBlock';
13
12
  import {colorVars, spacingVars} from '@astryxdesign/core/theme/tokens.stylex';
14
- import {Stack} from '@astryxdesign/core/Layout';
13
+ import {Stack, StackItem} from '@astryxdesign/core/Layout';
14
+ import {useMediaQuery} from '@astryxdesign/core/hooks';
15
15
  import {TabList, Tab} from '@astryxdesign/core/TabList';
16
16
  import {
17
17
  SegmentedControl,
@@ -27,29 +27,14 @@ import type {TreeListItemData} from '@astryxdesign/core/TreeList';
27
27
  import {
28
28
  FolderIcon,
29
29
  DocumentTextIcon,
30
- CodeBracketIcon,
31
30
  MagnifyingGlassIcon,
32
- CommandLineIcon,
33
- ExclamationTriangleIcon,
34
- InformationCircleIcon,
35
- BugAntIcon,
36
- HomeIcon,
37
- FolderOpenIcon,
38
- PuzzlePieceIcon,
39
31
  } 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';
46
32
 
47
33
  const styles = stylex.create({
48
34
  contentFill: {
49
35
  height: '100%',
50
36
  },
51
37
  terminalWrapper: {
52
- flex: 1,
53
38
  minHeight: 0,
54
39
  overflow: 'hidden',
55
40
  display: 'grid',
@@ -69,27 +54,26 @@ const styles = stylex.create({
69
54
  flexShrink: 0,
70
55
  },
71
56
  editorArea: {
72
- flex: 1,
73
57
  overflow: 'auto',
58
+ minHeight: 0,
74
59
  },
75
60
  fileExplorer: {
76
61
  padding: 16,
62
+ minWidth: 0,
77
63
  },
78
- terminalArea: {
64
+ propertiesPanel: {
79
65
  height: '100%',
80
- overflow: 'hidden',
81
66
  },
82
- hideOnMobile: {
83
- display: {
84
- default: 'contents',
85
- '@media (max-width: 768px)': 'none',
86
- },
67
+ propertiesContent: {
68
+ flex: 1,
69
+ minHeight: 0,
87
70
  },
88
- hideSideNav: {
89
- display: {
90
- default: 'flex',
91
- '@media (max-width: 768px)': 'none',
92
- },
71
+ propertyActions: {
72
+ marginTop: 'auto',
73
+ },
74
+ terminalArea: {
75
+ height: '100%',
76
+ overflow: 'hidden',
93
77
  },
94
78
  });
95
79
 
@@ -151,22 +135,23 @@ $ `;
151
135
  function buildFileTree(
152
136
  onFileClick: (name: string) => void,
153
137
  ): TreeListItemData[] {
138
+ const label = (text: string) => <Text maxLines={1}>{text}</Text>;
154
139
  const file = (id: string): TreeListItemData => ({
155
140
  id,
156
- label: id,
141
+ label: label(id),
157
142
  startContent: <Icon icon={DocumentTextIcon} size="xsm" />,
158
143
  onClick: () => onFileClick(id),
159
144
  });
160
145
  return [
161
146
  {
162
147
  id: 'src',
163
- label: 'src',
148
+ label: label('src'),
164
149
  startContent: <Icon icon={FolderIcon} size="xsm" />,
165
150
  isExpanded: true,
166
151
  children: [
167
152
  {
168
153
  id: 'components',
169
- label: 'components',
154
+ label: label('components'),
170
155
  startContent: <Icon icon={FolderIcon} size="xsm" />,
171
156
  isExpanded: true,
172
157
  children: [
@@ -177,14 +162,14 @@ function buildFileTree(
177
162
  },
178
163
  {
179
164
  id: 'pages',
180
- label: 'pages',
165
+ label: label('pages'),
181
166
  startContent: <Icon icon={FolderIcon} size="xsm" />,
182
167
  isExpanded: true,
183
168
  children: [file('index.tsx'), file('about.tsx')],
184
169
  },
185
170
  {
186
171
  id: 'styles',
187
- label: 'styles',
172
+ label: label('styles'),
188
173
  startContent: <Icon icon={FolderIcon} size="xsm" />,
189
174
  isExpanded: true,
190
175
  children: [file('tokens.stylex.ts'), file('theme.ts')],
@@ -216,7 +201,6 @@ const HISTORY_ITEMS = [
216
201
 
217
202
  export default function ResizableWorkspacePage() {
218
203
  const [activeFile, setActiveFile] = useState('Counter.tsx');
219
- const [activeNavItem, setActiveNavItem] = useState('Explorer');
220
204
  const [activeTermTab, setActiveTermTab] = useState('terminal');
221
205
  const [activePropertiesTab, setActivePropertiesTab] = useState('properties');
222
206
  const fileTree = useMemo(() => buildFileTree(setActiveFile), []);
@@ -245,89 +229,48 @@ export default function ResizableWorkspacePage() {
245
229
  collapsedSize: 40,
246
230
  });
247
231
 
232
+ const isMobile = useMediaQuery('(max-width: 768px)');
233
+
248
234
  return (
249
235
  <Layout
250
236
  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
- }
296
237
  content={
297
238
  <LayoutContent padding={0}>
298
239
  <Layout
299
240
  height="fill"
300
241
  start={
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>
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
+ )
331
274
  }
332
275
  content={
333
276
  <LayoutContent padding={0}>
@@ -335,13 +278,13 @@ export default function ResizableWorkspacePage() {
335
278
  height="fill"
336
279
  content={
337
280
  <LayoutContent padding={0}>
338
- <Stack
339
- direction="vertical"
340
- xstyle={styles.contentFill}>
341
- <div {...stylex.props(styles.editorArea)}>
281
+ <Stack direction="vertical" xstyle={styles.contentFill}>
282
+ <StackItem size="fill" xstyle={styles.editorArea}>
342
283
  <CodeBlock
343
284
  code={EDITOR_CODE}
344
285
  language="typescript"
286
+ container="section"
287
+ hasLanguageLabel={false}
345
288
  hasLineNumbers
346
289
  highlightLines={[21]}
347
290
  hasCopyButton={false}
@@ -353,7 +296,7 @@ export default function ResizableWorkspacePage() {
353
296
  borderRadius: 0,
354
297
  }}
355
298
  />
356
- </div>
299
+ </StackItem>
357
300
  <ResizeHandle
358
301
  direction="vertical"
359
302
  hasDivider
@@ -378,43 +321,19 @@ export default function ResizableWorkspacePage() {
378
321
  size="sm"
379
322
  hasDivider={false}
380
323
  xstyle={styles.tabListPadding}>
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
- />
324
+ <Tab label="Terminal" value="terminal" />
325
+ <Tab label="Problems" value="problems" />
326
+ <Tab label="Output" value="output" />
327
+ <Tab label="Debug" value="debug" />
413
328
  </TabList>
414
- <div {...stylex.props(styles.terminalWrapper)}>
329
+ <StackItem
330
+ size="fill"
331
+ xstyle={styles.terminalWrapper}>
415
332
  <CodeBlock
416
333
  code={TERMINAL_OUTPUT}
417
334
  language="bash"
335
+ container="section"
336
+ hasLanguageLabel={false}
418
337
  hasCopyButton={false}
419
338
  size="sm"
420
339
  style={{
@@ -424,7 +343,7 @@ export default function ResizableWorkspacePage() {
424
343
  borderRadius: 0,
425
344
  }}
426
345
  />
427
- </div>
346
+ </StackItem>
428
347
  </Stack>
429
348
  </div>
430
349
  )}
@@ -432,106 +351,118 @@ export default function ResizableWorkspacePage() {
432
351
  </LayoutContent>
433
352
  }
434
353
  end={
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}>
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}>
487
417
  <Button
488
418
  label="Format Document"
489
- size="md"
419
+ size="sm"
490
420
  variant="secondary"
491
421
  />
492
422
  <Button
493
423
  label="Go to Definition"
494
- size="md"
424
+ size="sm"
495
425
  variant="secondary"
496
426
  />
497
427
  <Button
498
428
  label="Find References"
499
- size="md"
429
+ size="sm"
500
430
  variant="secondary"
501
431
  />
502
432
  </Stack>
503
433
  </Stack>
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>
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
+ )
535
466
  }
536
467
  />
537
468
  </LayoutContent>