@astryxdesign/cli 0.1.8-canary.35b07f5 → 0.1.8-canary.3bb5896

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 (24) hide show
  1. package/docs/elevation.doc.mjs +79 -1
  2. package/package.json +9 -9
  3. package/templates/blocks/components/Banner/BannerFloating.doc.mjs +14 -0
  4. package/templates/blocks/components/Banner/BannerFloating.tsx +16 -0
  5. package/templates/blocks/components/Button/ButtonFloating.doc.mjs +14 -0
  6. package/templates/blocks/components/Button/ButtonFloating.tsx +37 -0
  7. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.doc.mjs +14 -0
  8. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.tsx +23 -0
  9. package/templates/blocks/components/Card/CardElevations.doc.mjs +14 -0
  10. package/templates/blocks/components/Card/CardElevations.tsx +32 -0
  11. package/templates/blocks/components/Card/ClickableCardElevated.doc.mjs +14 -0
  12. package/templates/blocks/components/Card/ClickableCardElevated.tsx +21 -0
  13. package/templates/blocks/components/Card/SelectableCardElevated.doc.mjs +14 -0
  14. package/templates/blocks/components/Card/SelectableCardElevated.tsx +37 -0
  15. package/templates/blocks/components/ChatComposer/ChatComposerFlat.doc.mjs +14 -0
  16. package/templates/blocks/components/ChatComposer/ChatComposerFlat.tsx +75 -0
  17. package/templates/blocks/components/IconButton/IconButtonFloating.doc.mjs +14 -0
  18. package/templates/blocks/components/IconButton/IconButtonFloating.tsx +37 -0
  19. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.doc.mjs +14 -0
  20. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.tsx +39 -0
  21. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.doc.mjs +14 -0
  22. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.tsx +44 -0
  23. package/templates/blocks/components/Thumbnail/ThumbnailElevated.doc.mjs +14 -0
  24. package/templates/blocks/components/Thumbnail/ThumbnailElevated.tsx +31 -0
@@ -36,10 +36,85 @@ export const docs = {
36
36
  },
37
37
  ],
38
38
  },
39
+ {
40
+ title: 'Choosing a level',
41
+ category: 'foundations',
42
+ content: [
43
+ {
44
+ type: 'prose',
45
+ text: 'Pick the level by how far the surface sits from the page, not by how much shadow you want. Elevation encodes stacking order: a higher level means the surface is layered over more of the UI. Use exactly one level per surface, and only raise a surface above `none` when it actually sits above other content.',
46
+ },
47
+ {
48
+ type: 'table',
49
+ headers: ['Level', 'When to use', 'Examples'],
50
+ rows: [
51
+ [
52
+ 'none',
53
+ 'The component is flat and embedded in the surface — it is part of the page, not layered above it. This is the default for every surface except ChatComposer.',
54
+ 'A Card in a grid, an inline Banner, a standard Button',
55
+ ],
56
+ [
57
+ 'low',
58
+ 'The component is in the normal page flow but should read as distinct from the background. Use for emphasis or to separate the component from the surface behind it — the component still sits on the page, it is not floating over other content.',
59
+ 'A raised Card that needs emphasis, a ChatComposer, a resting Thumbnail',
60
+ ],
61
+ [
62
+ 'med',
63
+ 'The component sits over other content on the same page — it floats above nearby elements but not the whole screen.',
64
+ 'A Popover, a floating Banner, a floating action Button',
65
+ ],
66
+ [
67
+ 'high',
68
+ 'The component is placed over the entire UI — it is the topmost layer and typically has a backdrop or takes focus from everything else.',
69
+ 'A modal Dialog, a full-screen overlay surface',
70
+ ],
71
+ ],
72
+ },
73
+ {
74
+ type: 'prose',
75
+ text: 'If two surfaces overlap, the one on top takes the higher level. If a surface does not overlap anything, it is `none` or `low` — never `med` or `high`.',
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ title: 'The elevation prop',
81
+ category: 'foundations',
82
+ content: [
83
+ {
84
+ type: 'prose',
85
+ text: 'Configurable surfaces expose a single `elevation` prop instead of asking consumers to hand-write a box-shadow. It takes the graded enum `none | low | med | high`, narrowed per component to the steps that surface needs: Card, ClickableCard, SelectableCard, Button, IconButton, ButtonGroup, Thumbnail, and Banner expose the full scale, while ChatComposer exposes only `none | low`. `none` is a flat literal (`box-shadow: none`); the other levels map to the `--shadow-*` tokens above, so a surface stays theme-agnostic.',
86
+ },
87
+ {
88
+ type: 'prose',
89
+ text: 'Prop defaults preserve current appearance: every surface defaults to `none` except ChatComposer, which defaults to `low` to keep its raised look. Set `elevation="none"` to flatten it — the flat composer draws a border with the same rest / hover / focus treatment as a text input. Thumbnail keeps its existing hover-only shadow at the `none` default.',
90
+ },
91
+ {
92
+ type: 'code',
93
+ lang: 'tsx',
94
+ label: 'Raising a surface with the elevation prop',
95
+ code: `// Flat by default — raise only when the surface needs to float.
96
+ <Card elevation="low">Raised card</Card>
97
+
98
+ // A floating action button.
99
+ <IconButton icon={<Icon icon="add" />} label="New" variant="primary" elevation="med" />
100
+
101
+ // Flatten the composer (defaults to 'low').
102
+ <ChatComposer elevation="none" onSubmit={handleSubmit} />`,
103
+ },
104
+ {
105
+ type: 'prose',
106
+ text: 'Always float — no prop: intrinsic overlays (Dialog, Popover, Tooltip, Toast, HoverCard, DropdownMenu, and the components that compose them) bake their elevation in and never expose the prop. Flow content (inputs, Text, layout) is never elevated; input fields use inset rings, which are a separate concept from elevation.',
107
+ },
108
+ ],
109
+ },
39
110
  {
40
111
  title: 'Usage',
41
112
  category: 'foundations',
42
113
  content: [
114
+ {
115
+ type: 'prose',
116
+ text: 'When building a custom surface, read elevation from the token scale rather than a hand-rolled shadow — the same tokens the elevation prop resolves to.',
117
+ },
43
118
  {
44
119
  type: 'code',
45
120
  lang: 'tsx',
@@ -68,7 +143,8 @@ const styles = stylex.create({
68
143
  type: 'list',
69
144
  style: 'do',
70
145
  items: [
71
- 'Match elevation to interaction context: low for tooltips, med for dropdowns, high for dialogs.',
146
+ 'Reach for the `elevation` prop on a configurable surface before writing any custom shadow.',
147
+ 'Choose the level by how far the surface sits from the page: `none` when flat/embedded, `low` when in-flow but distinct, `med` when over page content, `high` when over the whole UI. See "Choosing a level".',
72
148
  'Use inset shadows for input focus/selection states; they compose better than outlines.',
73
149
  ],
74
150
  },
@@ -76,6 +152,8 @@ const styles = stylex.create({
76
152
  type: 'list',
77
153
  style: 'dont',
78
154
  items: [
155
+ 'Hand-write a `box-shadow` in app code — set the `elevation` prop or read `shadowVars` instead.',
156
+ 'Raise a surface that does not sit above other content — a non-overlapping surface is `none` or `low`, never `med` or `high`.',
79
157
  'Stack multiple elevation levels on the same element.',
80
158
  'Use elevation shadows for decorative borders. Use --color-border tokens instead.',
81
159
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.8-canary.35b07f5",
3
+ "version": "0.1.8-canary.3bb5896",
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",
@@ -79,10 +79,10 @@
79
79
  "zod": "^4.4.3"
80
80
  },
81
81
  "peerDependencies": {
82
- "@astryxdesign/charts": "0.1.8-canary.35b07f5",
83
- "@astryxdesign/core": "0.1.8-canary.35b07f5",
84
- "@astryxdesign/lab": "0.1.8-canary.35b07f5",
85
- "@astryxdesign/theme-neutral": "0.1.8-canary.35b07f5",
82
+ "@astryxdesign/charts": "0.1.8-canary.3bb5896",
83
+ "@astryxdesign/core": "0.1.8-canary.3bb5896",
84
+ "@astryxdesign/lab": "0.1.8-canary.3bb5896",
85
+ "@astryxdesign/theme-neutral": "0.1.8-canary.3bb5896",
86
86
  "gpt-tokenizer": "^3.4.0"
87
87
  },
88
88
  "peerDependenciesMeta": {
@@ -100,10 +100,10 @@
100
100
  }
101
101
  },
102
102
  "devDependencies": {
103
- "@astryxdesign/charts": "0.1.8-canary.35b07f5",
104
- "@astryxdesign/core": "0.1.8-canary.35b07f5",
105
- "@astryxdesign/lab": "0.1.8-canary.35b07f5",
106
- "@astryxdesign/theme-neutral": "0.1.8-canary.35b07f5",
103
+ "@astryxdesign/charts": "0.1.8-canary.3bb5896",
104
+ "@astryxdesign/core": "0.1.8-canary.3bb5896",
105
+ "@astryxdesign/lab": "0.1.8-canary.3bb5896",
106
+ "@astryxdesign/theme-neutral": "0.1.8-canary.3bb5896",
107
107
  "gpt-tokenizer": "^3.4.0"
108
108
  },
109
109
  "scripts": {
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'Banner',
7
+ name: 'Banner — Floating',
8
+ displayName: 'Banner — Floating',
9
+ description:
10
+ 'A floating banner raised with `elevation="med"`. Banners are inline by default; raise one when it should read as an overlay above content.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Banner'],
14
+ };
@@ -0,0 +1,16 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Banner} from '@astryxdesign/core/Banner';
6
+
7
+ export default function BannerFloating() {
8
+ return (
9
+ <Banner
10
+ status="info"
11
+ title="You have unsaved changes"
12
+ description="A raised banner reads as an overlay floating above the page."
13
+ elevation="med"
14
+ />
15
+ );
16
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'Button',
7
+ name: 'Button — Floating',
8
+ displayName: 'Button — Floating',
9
+ description:
10
+ 'A floating action button raised with `elevation="med"`. Use elevation for buttons that hover above content, like a FAB.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Button', 'Icon', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,37 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Button} from '@astryxdesign/core/Button';
6
+ import {Icon} from '@astryxdesign/core/Icon';
7
+ import {Stack} from '@astryxdesign/core/Layout';
8
+ import {Text} from '@astryxdesign/core/Text';
9
+ import {PlusIcon} from '@heroicons/react/24/outline';
10
+
11
+ const VARIANTS = [
12
+ {variant: 'primary' as const, label: 'Primary'},
13
+ {variant: 'secondary' as const, label: 'Secondary'},
14
+ {variant: 'ghost' as const, label: 'Ghost'},
15
+ {variant: 'destructive' as const, label: 'Destructive'},
16
+ ];
17
+
18
+ export default function ButtonFloating() {
19
+ return (
20
+ <Stack direction="vertical" gap={4}>
21
+ <Text type="supporting" color="secondary">
22
+ Floating action buttons — raised above content with `elevation="med"`
23
+ </Text>
24
+ <Stack direction="horizontal" gap={3} vAlign="center">
25
+ {VARIANTS.map(({variant, label}) => (
26
+ <Button
27
+ key={variant}
28
+ label={label}
29
+ variant={variant}
30
+ icon={<Icon icon={PlusIcon} />}
31
+ elevation="med"
32
+ />
33
+ ))}
34
+ </Stack>
35
+ </Stack>
36
+ );
37
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'ButtonGroup',
7
+ name: 'ButtonGroup — Floating',
8
+ displayName: 'ButtonGroup — Floating',
9
+ description:
10
+ 'A grouped action bar raised with `elevation="med"`. The connected buttons share one surface, so the shadow lifts them as a unit.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['ButtonGroup', 'Button', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,23 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {ButtonGroup} from '@astryxdesign/core/ButtonGroup';
6
+ import {Button} from '@astryxdesign/core/Button';
7
+ import {Stack} from '@astryxdesign/core/Layout';
8
+ import {Text} from '@astryxdesign/core/Text';
9
+
10
+ export default function ButtonGroupFloating() {
11
+ return (
12
+ <Stack direction="vertical" gap={3} hAlign="start">
13
+ <Text type="supporting" color="secondary">
14
+ The whole group shares one raised surface — a floating action bar
15
+ </Text>
16
+ <ButtonGroup label="Zoom controls" elevation="med">
17
+ <Button label="Zoom out" />
18
+ <Button label="Reset" />
19
+ <Button label="Zoom in" />
20
+ </ButtonGroup>
21
+ </Stack>
22
+ );
23
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'Card',
7
+ name: 'Card — Elevations',
8
+ displayName: 'Card — Elevations',
9
+ description:
10
+ 'The four elevation levels side by side. Cards are flat by default; raise a card with `elevation` only when it needs to float above surrounding content.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Card', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,32 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Card} from '@astryxdesign/core/Card';
6
+ import {Stack} from '@astryxdesign/core/Layout';
7
+ import {Grid} from '@astryxdesign/core/Grid';
8
+ import {Text, Heading} from '@astryxdesign/core/Text';
9
+
10
+ const LEVELS = [
11
+ {elevation: 'none' as const, caption: 'Flat — sits on the page (default)'},
12
+ {elevation: 'low' as const, caption: 'Low — lifts off slightly'},
13
+ {elevation: 'med' as const, caption: 'Med — clearly raised'},
14
+ {elevation: 'high' as const, caption: 'High — floats well above'},
15
+ ];
16
+
17
+ export default function CardElevations() {
18
+ return (
19
+ <Grid columns={2} gap={4} width={520}>
20
+ {LEVELS.map(({elevation, caption}) => (
21
+ <Card key={elevation} elevation={elevation}>
22
+ <Stack direction="vertical" gap={1}>
23
+ <Heading level={4}>elevation=&quot;{elevation}&quot;</Heading>
24
+ <Text type="supporting" color="secondary">
25
+ {caption}
26
+ </Text>
27
+ </Stack>
28
+ </Card>
29
+ ))}
30
+ </Grid>
31
+ );
32
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'ClickableCard',
7
+ name: 'ClickableCard — Elevated',
8
+ displayName: 'Clickable Card — Elevated',
9
+ description:
10
+ 'A clickable card raised with `elevation="med"` so the shadow signals the whole surface is interactive.',
11
+ isReady: true,
12
+ aspectRatio: 1,
13
+ componentsUsed: ['ClickableCard', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {ClickableCard} from '@astryxdesign/core/ClickableCard';
6
+ import {Stack} from '@astryxdesign/core/Layout';
7
+ import {Text, Heading} from '@astryxdesign/core/Text';
8
+
9
+ export default function ClickableCardElevated() {
10
+ return (
11
+ <ClickableCard label="Open report" href="#" elevation="med" width={320}>
12
+ <Stack direction="vertical" gap={2}>
13
+ <Heading level={4}>Quarterly report</Heading>
14
+ <Text type="body" color="secondary">
15
+ A raised shadow signals the whole card is clickable, lifting it above
16
+ the surrounding content.
17
+ </Text>
18
+ </Stack>
19
+ </ClickableCard>
20
+ );
21
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'SelectableCard',
7
+ name: 'SelectableCard — Elevated',
8
+ displayName: 'Selectable Card — Elevated',
9
+ description:
10
+ 'Raised selectable cards with `elevation="low"`. The inset selection ring composes on top of the shadow, so a selected card keeps its elevation.',
11
+ isReady: true,
12
+ aspectRatio: 2,
13
+ componentsUsed: ['SelectableCard', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,37 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {useState} from 'react';
6
+ import {SelectableCard} from '@astryxdesign/core/SelectableCard';
7
+ import {Grid} from '@astryxdesign/core/Grid';
8
+ import {Text} from '@astryxdesign/core/Text';
9
+
10
+ const PLANS = [
11
+ {id: 'starter', name: 'Starter'},
12
+ {id: 'pro', name: 'Pro'},
13
+ ];
14
+
15
+ export default function SelectableCardElevated() {
16
+ const [selected, setSelected] = useState('pro');
17
+
18
+ return (
19
+ <Grid columns={2} gap={3} width={420}>
20
+ {PLANS.map(plan => (
21
+ <SelectableCard
22
+ key={plan.id}
23
+ label={plan.name}
24
+ isSelected={selected === plan.id}
25
+ onChange={() => setSelected(plan.id)}
26
+ elevation="low">
27
+ <Text type="body" weight="bold">
28
+ {plan.name}
29
+ </Text>
30
+ <Text type="supporting" color="secondary">
31
+ The resting shadow stays put — the selection ring layers on top.
32
+ </Text>
33
+ </SelectableCard>
34
+ ))}
35
+ </Grid>
36
+ );
37
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'ChatComposer',
7
+ name: 'ChatComposer — Flat',
8
+ displayName: 'ChatComposer — Flat',
9
+ description:
10
+ 'The composer is raised by default (`elevation="low"`). Set `elevation="none"` to flatten it — it then draws a border with the same rest / hover / focus treatment as a text input.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['ChatComposer', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,75 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {ChatComposer} from '@astryxdesign/core/Chat';
6
+ import {Button} from '@astryxdesign/core/Button';
7
+ import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
8
+ import {Icon} from '@astryxdesign/core/Icon';
9
+ import {Stack} from '@astryxdesign/core/Layout';
10
+ import {Text} from '@astryxdesign/core/Text';
11
+ import {
12
+ Cog6ToothIcon,
13
+ MicrophoneIcon,
14
+ SparklesIcon,
15
+ } from '@heroicons/react/24/outline';
16
+
17
+ export default function ChatComposerFlat() {
18
+ return (
19
+ <Stack direction="vertical" gap={2} style={{width: '100%', maxWidth: 450}}>
20
+ <Text type="supporting" color="secondary">
21
+ elevation=&quot;none&quot; — flat, with a text-input-style border and
22
+ focus ring
23
+ </Text>
24
+ <ChatComposer
25
+ elevation="none"
26
+ onSubmit={value => {
27
+ console.log('Sent:', value);
28
+ }}
29
+ footerActions={
30
+ <>
31
+ <DropdownMenu
32
+ button={{
33
+ label: 'Auto',
34
+ variant: 'ghost',
35
+ size: 'md',
36
+ icon: <Icon icon={SparklesIcon} size="sm" />,
37
+ children: 'Auto',
38
+ }}
39
+ menuWidth={200}
40
+ items={[
41
+ {label: 'Auto', onClick: () => {}},
42
+ {label: 'Model A', onClick: () => {}},
43
+ {label: 'Model B', onClick: () => {}},
44
+ ]}
45
+ />
46
+ <DropdownMenu
47
+ button={{
48
+ label: 'Settings',
49
+ variant: 'ghost',
50
+ size: 'md',
51
+ icon: <Icon icon={Cog6ToothIcon} size="sm" />,
52
+ children: 'Settings',
53
+ }}
54
+ menuWidth={200}
55
+ items={[
56
+ {label: 'Preferences', onClick: () => {}},
57
+ {label: 'Keyboard shortcuts', onClick: () => {}},
58
+ {label: 'About', onClick: () => {}},
59
+ ]}
60
+ />
61
+ </>
62
+ }
63
+ sendActions={
64
+ <Button
65
+ label="Microphone"
66
+ variant="ghost"
67
+ size="md"
68
+ icon={<Icon icon={MicrophoneIcon} />}
69
+ isIconOnly
70
+ />
71
+ }
72
+ />
73
+ </Stack>
74
+ );
75
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'IconButton',
7
+ name: 'IconButton — Floating',
8
+ displayName: 'IconButton — Floating',
9
+ description:
10
+ 'A floating action button (FAB) — the most common floating-button shape is icon-only. Raised here with `elevation="high"`.',
11
+ isReady: true,
12
+ aspectRatio: 4 / 3,
13
+ componentsUsed: ['IconButton', 'Icon', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,37 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {IconButton} from '@astryxdesign/core/IconButton';
6
+ import {Icon} from '@astryxdesign/core/Icon';
7
+ import {Stack} from '@astryxdesign/core/Layout';
8
+ import {Text} from '@astryxdesign/core/Text';
9
+ import {PlusIcon} from '@heroicons/react/24/outline';
10
+
11
+ const VARIANTS = [
12
+ {variant: 'primary' as const, label: 'Primary'},
13
+ {variant: 'secondary' as const, label: 'Secondary'},
14
+ {variant: 'ghost' as const, label: 'Ghost'},
15
+ {variant: 'destructive' as const, label: 'Destructive'},
16
+ ];
17
+
18
+ export default function IconButtonFloating() {
19
+ return (
20
+ <Stack direction="vertical" gap={4}>
21
+ <Text type="supporting" color="secondary">
22
+ FABs are usually icon-only — raise one with `elevation="high"`
23
+ </Text>
24
+ <Stack direction="horizontal" gap={3} vAlign="center">
25
+ {VARIANTS.map(({variant, label}) => (
26
+ <IconButton
27
+ key={variant}
28
+ label={label}
29
+ variant={variant}
30
+ icon={<Icon icon={PlusIcon} />}
31
+ elevation="high"
32
+ />
33
+ ))}
34
+ </Stack>
35
+ </Stack>
36
+ );
37
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'OverflowList',
7
+ name: 'OverflowList — Capped Toolbar',
8
+ displayName: 'OverflowList — Capped Toolbar',
9
+ description:
10
+ 'maxVisibleItems caps the row at three actions even when more would fit; the rest move to a dropdown',
11
+ isReady: true,
12
+ aspectRatio: 4 / 3,
13
+ componentsUsed: ['OverflowList', 'Button', 'DropdownMenu', 'Card', 'Center'],
14
+ };
@@ -0,0 +1,39 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {OverflowList} from '@astryxdesign/core/OverflowList';
6
+ import {Button} from '@astryxdesign/core/Button';
7
+ import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
8
+ import {Card} from '@astryxdesign/core/Card';
9
+ import {Center} from '@astryxdesign/core/Center';
10
+
11
+ const actions = ['Save', 'Edit', 'Duplicate', 'Share', 'Archive', 'Delete'];
12
+
13
+ export default function OverflowListCappedToolbar() {
14
+ return (
15
+ <Center width={420}>
16
+ <Card padding={2}>
17
+ <OverflowList
18
+ gap={2}
19
+ maxVisibleItems={3}
20
+ overflowRenderer={overflowItems => (
21
+ <DropdownMenu
22
+ button={{
23
+ label: `+${overflowItems.length}`,
24
+ variant: 'ghost',
25
+ size: 'sm',
26
+ }}
27
+ items={overflowItems.map(({index}) => ({
28
+ label: actions[index],
29
+ }))}
30
+ />
31
+ )}>
32
+ {actions.map(action => (
33
+ <Button key={action} label={action} size="sm" />
34
+ ))}
35
+ </OverflowList>
36
+ </Card>
37
+ </Center>
38
+ );
39
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'OverflowList',
7
+ name: 'OverflowList — Multi-row Tags',
8
+ displayName: 'OverflowList — Multi-row Tags',
9
+ description:
10
+ 'Tags wrap onto up to two rows with maxRows, then collapse the rest into a count badge',
11
+ isReady: true,
12
+ aspectRatio: 4 / 3,
13
+ componentsUsed: ['OverflowList', 'Badge', 'Card'],
14
+ };
@@ -0,0 +1,44 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {OverflowList} from '@astryxdesign/core/OverflowList';
6
+ import {Badge} from '@astryxdesign/core/Badge';
7
+ import {Card} from '@astryxdesign/core/Card';
8
+
9
+ const tags = [
10
+ 'React',
11
+ 'TypeScript',
12
+ 'StyleX',
13
+ 'Storybook',
14
+ 'Vitest',
15
+ 'Playwright',
16
+ 'ESLint',
17
+ 'Prettier',
18
+ 'Vite',
19
+ 'pnpm',
20
+ ];
21
+
22
+ export default function OverflowListMultiRowTags() {
23
+ return (
24
+ <Card
25
+ padding={2}
26
+ style={{
27
+ resize: 'horizontal',
28
+ overflow: 'hidden',
29
+ minWidth: 120,
30
+ width: 260,
31
+ }}>
32
+ <OverflowList
33
+ gap={1}
34
+ maxRows={2}
35
+ overflowRenderer={overflowItems => (
36
+ <Badge variant="neutral" label={`+${overflowItems.length}`} />
37
+ )}>
38
+ {tags.map(tag => (
39
+ <Badge key={tag} variant="info" label={tag} />
40
+ ))}
41
+ </OverflowList>
42
+ </Card>
43
+ );
44
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'Thumbnail',
7
+ name: 'Thumbnail — Elevated',
8
+ displayName: 'Thumbnail — Elevated',
9
+ description:
10
+ 'Image tiles raised at rest with `elevation`. The default keeps the existing hover-only shadow; a set level lifts the tile off the page.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Thumbnail', 'Layout', 'Text'],
14
+ };
@@ -0,0 +1,31 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
+ import {Stack} from '@astryxdesign/core/Layout';
7
+ import {Text} from '@astryxdesign/core/Text';
8
+
9
+ // Demo imagery is inlined as same-origin data URIs so the example is
10
+ // self-contained. Thumbnail's remove-button overlay uses useImageMode, which
11
+ // FETCHES the image to sample pixels (APCA) for contrast — a cross-origin CDN
12
+ // URL without CORS headers cannot be sampled, so contrast detection fails
13
+ // silently in hosted previews.
14
+ const NIGHT_FOREST =
15
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX18+H08uDy8d/y8N/v7t/m5NXl49Tk4tPc3NDX1cnW1cnW1cjW1MjV1MjV1MfR0cXIyLzEw7m8vLC3t68wM0QvM0MvM0IvMkIuMkEtMEErLz8iJjkhJTggJTcgJDcfJDYfIzYeIjUcITMbHzIWHiwYGzIXGjEWGjAWGTAVGS8VGC8UGC4UGC0UFy4TFy0TFywTFi0SFiwRFSsQFSkNFR4QFCoPFCkPFCgPEykOEygOEigNEicNEScMESYIDw8ECgr+gzZIAAABqElEQVR42uWQ61aCQBRGD5GylNBMKS9FZgNdNE0cyUvB+79VM8MoF9Fgplau2j/8nO8c9hqAD0ngDwjeJYH1lrfBWoCtYDboNu/Zv7qQYNap1bpCN1gxFh1dNe5WAsCScWqoWttbChAKvHZZ0W6XQoIFpW8ooFmLCG2Rl1BgaZAU5Ac8yg0RlC9nngCRQDFuhQRzinWmkCuYk3lxQoFrlskVqlcTMcEFvYIKoFbNfmEFuIypWaGGkt66tqZuEbjAHZl6SQVF1WqNvpDAHVktXTvR9JbgDZii1+tZI7cYMJXkCASvksB4L5VxHmAsibzgRRIYbjgfCgFDSeBZkh8W1HMIniSBR0mOQPCQA3RgBk4M5DhNZwdEcbKgLXy5iDiZg5gApRYbqTql2FZgMxAKeEWSNUFYx3s2SfU28HPWItrpySSwE2cEdqrYtxgmxgHGScFmHrAGk8SxczIxZnNC1McFOGshShyb4yxB9sI3CXDGnH0MLsD7F5LvvptEgH12Zg1Pn2WuHsjvrwt8308t+nwxT38sgiAE8/RTeagH+vx/F8SeF0Ja8Ak14ia/LgmpZQAAAABJRU5ErkJggg==';
16
+ const GOLDEN_SUNSET =
17
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAkFBMVEX/+uv/7sj6tFr3sVn1r1nzrVjxq1jvqVftp1frpVbpo1bnoVXln1XjnVThm1TfmVPdl1PblVLZk1LXkVLVj1HTjVHRi1DPiVDNh0/LhU/Jg07HgU7Ff03DfU3Be0y/eUy9d0u7dUu5c0u2cEq0bkqybEmwakmuaEisZkiqZEeoYkemYEakXkaiXEVgLjQ8HCjaJlMkAAABI0lEQVR42u3Mx3qDMBSE0Z/03nsvdhxjcnn/twsYjISR5MUkO5+V9N2ZYUPEpogtEdsidkTsitgTsS/iQMShiCMRxyJORJyKOBNxLuJCxKWIKxHXIm5E3Iq4E3Ev4kHEo4gnEc8iXkS8inhLymrJBO8J2UIiw0dc5sRDfEbVRWr1o/pbMMWox9yz6zcLIzMbBTD2VaHu7fr1gjXGA3w5baj5+H0w6x09TDpdaP7rDZhn0sd3y89Y9fcHbPnoYdroZ2w6dQNmg6OHfG4QyrsBC8gdZpVQxtqF4M1mjepFURThTLNgMYsePxaX2WqY6D8GSm2grK2slbGBciHZ9hOE2smNpTuxenhieCdeH06E7qTqvYnImWTbbUSPlKL1wHrgTwZ+AWq+3H5MpRkwAAAAAElFTkSuQmCC';
18
+
19
+ export default function ThumbnailElevated() {
20
+ return (
21
+ <Stack direction="vertical" gap={3}>
22
+ <Text type="supporting" color="secondary">
23
+ Raised tiles — `elevation` lifts an image off the page at rest
24
+ </Text>
25
+ <Stack direction="horizontal" gap={3} vAlign="center">
26
+ <Thumbnail src={NIGHT_FOREST} alt="Forest at night" elevation="low" />
27
+ <Thumbnail src={GOLDEN_SUNSET} alt="Golden sunset" elevation="high" />
28
+ </Stack>
29
+ </Stack>
30
+ );
31
+ }