lucent-ui 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
File without changes
File without changes
@@ -39,6 +39,20 @@ export const COMPONENT_MANIFEST = {
39
39
  description: '"code" renders a <pre><code> block with horizontal scroll. ' +
40
40
  '"prompt" renders a single-line truncated span suited to AI prompts.',
41
41
  },
42
+ {
43
+ name: 'minimal',
44
+ type: 'boolean',
45
+ required: false,
46
+ default: 'false',
47
+ description: 'Hides the header bar and shows only a corner copy button overlay.',
48
+ },
49
+ {
50
+ name: 'wrap',
51
+ type: 'boolean',
52
+ required: false,
53
+ default: 'false',
54
+ description: 'When true, wraps long lines instead of scrolling horizontally.',
55
+ },
42
56
  {
43
57
  name: 'helperText',
44
58
  type: 'string',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucent-ui",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "An AI-first React component library with machine-readable manifests.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,18 +30,6 @@
30
30
  "dist-server",
31
31
  "dist-cli"
32
32
  ],
33
- "scripts": {
34
- "dev": "vite --config vite.dev.config.ts",
35
- "build": "vite build",
36
- "build:server": "tsc -p server/tsconfig.json",
37
- "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
38
- "test": "vitest run",
39
- "test:watch": "vitest",
40
- "prepublishOnly": "tsc --noEmit && pnpm build && pnpm build:server && pnpm build:cli",
41
- "changeset": "changeset",
42
- "version-packages": "changeset version",
43
- "release": "pnpm prepublishOnly && changeset publish"
44
- },
45
33
  "keywords": [
46
34
  "react",
47
35
  "component-library",
@@ -56,7 +44,6 @@
56
44
  },
57
45
  "author": "Rozina Szogyenyi",
58
46
  "license": "MIT",
59
- "packageManager": "pnpm@10.30.3",
60
47
  "peerDependencies": {
61
48
  "react": "^18.0.0 || ^19.0.0",
62
49
  "react-dom": "^18.0.0 || ^19.0.0"
@@ -78,5 +65,16 @@
78
65
  "dependencies": {
79
66
  "@modelcontextprotocol/sdk": "^1.27.1",
80
67
  "zod": "^4.3.6"
68
+ },
69
+ "scripts": {
70
+ "dev": "vite --config vite.dev.config.ts",
71
+ "build": "vite build",
72
+ "build:server": "tsc -p server/tsconfig.json",
73
+ "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "changeset": "changeset",
77
+ "version-packages": "changeset version",
78
+ "release": "pnpm prepublishOnly && changeset publish"
81
79
  }
82
- }
80
+ }
@@ -1,18 +0,0 @@
1
- import { RECIPE as ProfileCard } from '../src/manifest/recipes/profile-card.recipe.js';
2
- import { RECIPE as SettingsPanel } from '../src/manifest/recipes/settings-panel.recipe.js';
3
- import { RECIPE as StatsRow } from '../src/manifest/recipes/stats-row.recipe.js';
4
- import { RECIPE as ActionBar } from '../src/manifest/recipes/action-bar.recipe.js';
5
- import { RECIPE as FormLayout } from '../src/manifest/recipes/form-layout.recipe.js';
6
- import { RECIPE as EmptyStateCard } from '../src/manifest/recipes/empty-state-card.recipe.js';
7
- import { RECIPE as CollapsibleCard } from '../src/manifest/recipes/collapsible-card.recipe.js';
8
- import { RECIPE as SearchFilterBar } from '../src/manifest/recipes/search-filter-bar.recipe.js';
9
- export const ALL_RECIPES = [
10
- ProfileCard,
11
- SettingsPanel,
12
- StatsRow,
13
- ActionBar,
14
- FormLayout,
15
- EmptyStateCard,
16
- CollapsibleCard,
17
- SearchFilterBar,
18
- ];
@@ -1,91 +0,0 @@
1
- export const RECIPE = {
2
- id: 'action-bar',
3
- name: 'Action Bar',
4
- description: 'Page-level or card-level header pairing a title with action buttons. Page headers use breadcrumb, large display title, and a divider below; card headers are compact with small text.',
5
- category: 'action',
6
- components: ['text', 'button', 'row', 'stack', 'breadcrumb', 'divider', 'card'],
7
- structure: `
8
- Page header:
9
- Stack gap="4"
10
- ├── Breadcrumb ← navigation context
11
- ├── Row justify="between" align="end"
12
- │ ├── Stack gap="1"
13
- │ │ ├── Text (h1, 3xl, bold, display) ← page title
14
- │ │ └── Text (sm, secondary) ← subtitle
15
- │ └── Row gap="2" ← actions
16
- │ ├── Button (outline, sm)
17
- │ └── Button (primary, sm)
18
- └── Divider
19
-
20
- Card header:
21
- Row justify="between" align="start"
22
- ├── Stack gap="1"
23
- │ ├── Text (xs, secondary, uppercase, tight) ← category label
24
- │ └── Text (md, semibold, tight) ← section title
25
- └── Row gap="1" ← compact actions (top-aligned)
26
- └── Button[] (ghost, xs)
27
- `.trim(),
28
- code: `<Stack gap="4">
29
- <Breadcrumb items={[
30
- { label: 'Home', href: '#' },
31
- { label: 'Projects', href: '#' },
32
- { label: 'Acme Corp' },
33
- ]} />
34
- <Row justify="between" align="end">
35
- <Stack gap="1">
36
- <Text as="h1" size="3xl" weight="bold" family="display">Acme Corp</Text>
37
- <Text size="sm" color="secondary">Last updated 5 minutes ago</Text>
38
- </Stack>
39
- <Row gap="2">
40
- <Button variant="outline" size="sm">Export</Button>
41
- <Button variant="primary" size="sm">New report</Button>
42
- </Row>
43
- </Row>
44
- <Divider />
45
- </Stack>`,
46
- variants: [
47
- {
48
- title: 'Page header — danger zone',
49
- code: `<Stack gap="4">
50
- <Breadcrumb items={[
51
- { label: 'Home', href: '#' },
52
- { label: 'Settings', href: '#' },
53
- { label: 'Danger zone' },
54
- ]} />
55
- <Row justify="between" align="end">
56
- <Stack gap="1">
57
- <Text as="h1" size="3xl" weight="bold" family="display">Danger zone</Text>
58
- <Text size="sm" color="secondary">These actions are irreversible.</Text>
59
- </Stack>
60
- <Button variant="danger" size="sm">Delete project</Button>
61
- </Row>
62
- <Divider />
63
- </Stack>`,
64
- },
65
- {
66
- title: 'Card header (compact with label)',
67
- code: `<Card variant="outline" padding="md" style={{ width: 400 }}>
68
- <Stack gap="4">
69
- <Row justify="between" align="start">
70
- <Stack gap="1">
71
- <Text size="xs" color="secondary" weight="medium" style={{ letterSpacing: 'var(--lucent-letter-spacing-tight)', textTransform: 'uppercase' }}>Activity</Text>
72
- <Text size="md" weight="semibold" style={{ letterSpacing: 'var(--lucent-letter-spacing-base)' }}>Recent activity</Text>
73
- </Stack>
74
- <Row gap="1">
75
- <Button variant="ghost" size="xs">Filter</Button>
76
- <Button variant="ghost" size="xs">Export</Button>
77
- </Row>
78
- </Row>
79
- <Text size="xs" color="secondary">No activity to show yet.</Text>
80
- </Stack>
81
- </Card>`,
82
- },
83
- ],
84
- designNotes: 'Page headers and card headers have very different scales. Page headers use 3xl ' +
85
- 'display font for the title, Breadcrumb for navigation context, and a Divider to ' +
86
- 'separate the header from page content below. align="end" on the Row anchors the ' +
87
- 'buttons to the baseline of the title block so they sit level with the subtitle. ' +
88
- 'Card headers are compact: sm semibold text with xs/ghost buttons that recede ' +
89
- 'visually. The primary action is always rightmost following natural reading order. ' +
90
- 'Both patterns use justify="between" to push title and actions to opposite edges.',
91
- };
@@ -1,100 +0,0 @@
1
- export const RECIPE = {
2
- id: 'collapsible-card',
3
- name: 'Collapsible Card',
4
- description: 'Card with an expandable/collapsible section using smooth height animation, available in all card variants.',
5
- category: 'card',
6
- components: ['card', 'collapsible', 'text'],
7
- structure: `
8
- Card (padding="none", hoverable)
9
- └── Collapsible
10
- ├── trigger: Text (sm, semibold) ← clickable header
11
- └── children ← collapsible body
12
- └── Text (sm, secondary) ← content
13
- `.trim(),
14
- code: `<Card variant="outline" padding="none" hoverable style={{ width: 360 }}>
15
- <Collapsible
16
- trigger={<Text as="span" weight="semibold" size="sm">Details</Text>}
17
- defaultOpen
18
- >
19
- <Text size="sm" color="secondary">
20
- The Collapsible auto-detects its Card parent and bleeds the trigger
21
- full-width. Content inherits the card's padding.
22
- </Text>
23
- </Collapsible>
24
- </Card>`,
25
- variants: [
26
- {
27
- title: 'Ghost variant',
28
- code: `<Card variant="ghost" padding="none" hoverable style={{ width: 360 }}>
29
- <Collapsible trigger={<Text as="span" weight="semibold" size="sm">FAQ item</Text>}>
30
- <Text size="sm" color="secondary">
31
- Transparent container — content floats directly on the page surface.
32
- </Text>
33
- </Collapsible>
34
- </Card>`,
35
- },
36
- {
37
- title: 'Elevated variant',
38
- code: `<Card variant="elevated" padding="none" hoverable style={{ width: 360 }}>
39
- <Collapsible trigger={<Text as="span" weight="semibold" size="sm">Advanced options</Text>}>
40
- <Text size="sm" color="secondary">
41
- Elevated cards cast a shadow — use for prominent collapsible sections.
42
- </Text>
43
- </Collapsible>
44
- </Card>`,
45
- },
46
- {
47
- title: 'Combo (two-tone nested cards)',
48
- code: `<Card variant="filled" padding="none" hoverable style={{ width: 360 }}>
49
- <Collapsible
50
- padded={false}
51
- trigger={<Text as="span" weight="semibold" size="sm">Combo layout</Text>}
52
- >
53
- <Card
54
- variant="elevated"
55
- padding="sm"
56
- style={{ margin: 'var(--lucent-space-1) var(--lucent-space-2) var(--lucent-space-2)' }}
57
- >
58
- <Text size="sm" color="secondary">
59
- Two-tone layout — flat trigger surface, elevated body.
60
- </Text>
61
- </Card>
62
- </Collapsible>
63
- </Card>`,
64
- },
65
- {
66
- title: 'With localStorage persistence',
67
- code: `function CollapsibleSection({ id, title, children }) {
68
- const storageKey = \`collapsible-\${id}\`;
69
- const [open, setOpen] = React.useState(() => {
70
- const saved = localStorage.getItem(storageKey);
71
- return saved !== null ? saved === 'true' : true;
72
- });
73
-
74
- const handleChange = (next) => {
75
- setOpen(next);
76
- localStorage.setItem(storageKey, String(next));
77
- };
78
-
79
- return (
80
- <Card variant="outline" padding="none" hoverable>
81
- <Collapsible
82
- open={open}
83
- onOpenChange={handleChange}
84
- trigger={<Text as="span" weight="semibold" size="sm">{title}</Text>}
85
- >
86
- {children}
87
- </Collapsible>
88
- </Card>
89
- );
90
- }`,
91
- },
92
- ],
93
- designNotes: 'Card uses padding="none" because the Collapsible auto-detects its Card parent ' +
94
- 'via CardPaddingContext and bleeds the trigger full-width while applying card ' +
95
- 'padding to the body content. The hoverable prop gives hover lift feedback ' +
96
- 'without making the card itself interactive — the Collapsible trigger handles ' +
97
- 'clicks. For the combo variant, padded={false} on Collapsible removes its built-in ' +
98
- 'content padding so the nested elevated Card can control its own spacing with ' +
99
- 'custom margin. This creates a two-tone visual: flat trigger + elevated body.',
100
- };
@@ -1,72 +0,0 @@
1
- export const RECIPE = {
2
- id: 'empty-state-card',
3
- name: 'Empty State Card',
4
- description: 'Centered empty state with illustration icon, heading, description, and call-to-action button inside a card.',
5
- category: 'card',
6
- components: ['card', 'empty-state', 'icon', 'button'],
7
- structure: `
8
- Card (outline, padding="lg")
9
- └── EmptyState
10
- ├── illustration: Icon (xl) ← decorative SVG
11
- ├── title: string ← heading
12
- ├── description: string ← explanatory text
13
- └── action: Button (secondary / primary) ← CTA
14
- `.trim(),
15
- code: `<Card variant="outline" padding="lg" style={{ width: 400 }}>
16
- <EmptyState
17
- illustration={
18
- <Icon size="xl">
19
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round">
20
- <circle cx={11} cy={11} r={8} />
21
- <path d="M21 21l-4.35-4.35" />
22
- </svg>
23
- </Icon>
24
- }
25
- title="No results found"
26
- description="Try adjusting your search or filters to find what you're looking for."
27
- action={<Button variant="secondary" size="sm">Clear filters</Button>}
28
- />
29
- </Card>`,
30
- variants: [
31
- {
32
- title: 'Getting started empty state',
33
- code: `<Card variant="elevated" padding="lg" style={{ width: 400 }}>
34
- <EmptyState
35
- illustration={
36
- <Icon size="xl">
37
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round">
38
- <path d="M12 5v14M5 12h14" />
39
- </svg>
40
- </Icon>
41
- }
42
- title="No projects yet"
43
- description="Create your first project to get started."
44
- action={<Button variant="primary" size="sm">Create project</Button>}
45
- />
46
- </Card>`,
47
- },
48
- {
49
- title: 'Error empty state with retry',
50
- code: `<Card variant="outline" padding="lg" style={{ width: 400 }}>
51
- <EmptyState
52
- illustration={
53
- <Icon size="xl">
54
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round">
55
- <circle cx={12} cy={12} r={10} />
56
- <path d="M12 8v4M12 16h.01" />
57
- </svg>
58
- </Icon>
59
- }
60
- title="Something went wrong"
61
- description="We couldn't load your data. Please try again."
62
- action={<Button variant="outline" size="sm">Retry</Button>}
63
- />
64
- </Card>`,
65
- },
66
- ],
67
- designNotes: 'EmptyState handles internal centering and spacing — no need for manual Stack/Row ' +
68
- 'layout inside it. The illustration uses Icon at xl size for visual weight without ' +
69
- 'overwhelming the text. Card variant should match the context: outline for inline ' +
70
- 'empty states (e.g. a table with no rows), elevated for standalone pages. The ' +
71
- 'action button uses size="sm" to avoid competing with the heading for attention.',
72
- };
@@ -1,98 +0,0 @@
1
- export const RECIPE = {
2
- id: 'form-layout',
3
- name: 'Form Layout',
4
- description: 'Stacked form with grouped sections, FormField labels, validation hints, and a submit/cancel footer.',
5
- category: 'form',
6
- components: ['card', 'text', 'input', 'select', 'textarea', 'checkbox', 'button', 'stack', 'row', 'divider', 'form-field'],
7
- structure: `
8
- Card (elevated, padding="lg")
9
- └── Stack as="form" gap="6"
10
- ├── Stack gap="1" ← section header
11
- │ ├── Text (lg, semibold) ← form title
12
- │ └── Text (sm, secondary) ← description
13
- ├── Stack gap="4" ← field group 1
14
- │ ├── Row gap="4" ← side-by-side fields
15
- │ │ ├── FormField (label, required)
16
- │ │ │ └── Input
17
- │ │ └── FormField (label, required)
18
- │ │ └── Input
19
- │ ├── FormField (label)
20
- │ │ └── Input
21
- │ └── FormField (label)
22
- │ └── Select
23
- ├── Divider
24
- ├── Stack gap="4" ← field group 2
25
- │ ├── FormField (label)
26
- │ │ └── Textarea
27
- │ └── Checkbox (contained)
28
- └── Row gap="2" justify="end" ← actions
29
- ├── Button (ghost)
30
- └── Button (primary)
31
- `.trim(),
32
- code: `<Card variant="elevated" padding="lg" style={{ width: 480 }}>
33
- <Stack as="form" gap="6">
34
- <Stack gap="1">
35
- <Text as="h2" size="lg" weight="semibold">Create project</Text>
36
- <Text size="sm" color="secondary">
37
- Fill in the details to set up your new project.
38
- </Text>
39
- </Stack>
40
- <Stack gap="4">
41
- <Row gap="4">
42
- <FormField label="First name" htmlFor="fname" required style={{ flex: 1 }}>
43
- <Input id="fname" placeholder="Jane" />
44
- </FormField>
45
- <FormField label="Last name" htmlFor="lname" required style={{ flex: 1 }}>
46
- <Input id="lname" placeholder="Doe" />
47
- </FormField>
48
- </Row>
49
- <FormField label="Email" htmlFor="email" helperText="We'll never share your email.">
50
- <Input id="email" type="email" placeholder="jane@example.com" />
51
- </FormField>
52
- <FormField label="Role" htmlFor="role">
53
- <Select
54
- id="role"
55
- placeholder="Select a role..."
56
- options={[
57
- { value: 'admin', label: 'Admin' },
58
- { value: 'editor', label: 'Editor' },
59
- { value: 'viewer', label: 'Viewer' },
60
- ]}
61
- />
62
- </FormField>
63
- </Stack>
64
- <Divider />
65
- <Stack gap="4">
66
- <FormField label="Bio" htmlFor="bio">
67
- <Textarea id="bio" placeholder="Tell us about yourself..." rows={3} />
68
- </FormField>
69
- <Checkbox label="I agree to the terms" contained />
70
- </Stack>
71
- <Row gap="2" justify="end">
72
- <Button variant="ghost">Cancel</Button>
73
- <Button variant="primary">Create</Button>
74
- </Row>
75
- </Stack>
76
- </Card>`,
77
- variants: [
78
- {
79
- title: 'Inline form (no card)',
80
- code: `<Stack as="form" gap="4" style={{ maxWidth: 400 }}>
81
- <FormField label="Username" htmlFor="user" required helperText="Letters and numbers only, 3–20 chars.">
82
- <Input id="user" placeholder="yourname" />
83
- </FormField>
84
- <FormField label="Password" htmlFor="pass" required>
85
- <Input id="pass" type="password" />
86
- </FormField>
87
- <Button variant="primary">Sign in</Button>
88
- </Stack>`,
89
- },
90
- ],
91
- designNotes: 'The form uses Stack as="form" to render a semantic <form> element while keeping ' +
92
- 'token-based vertical spacing. gap="6" between sections provides clear visual ' +
93
- 'grouping; gap="4" within a section keeps fields tightly related. Side-by-side ' +
94
- 'fields use Row with flex: 1 on each FormField so they share width equally. ' +
95
- 'The Divider separates logical sections (identity fields vs. profile fields). ' +
96
- 'Actions are right-aligned (justify="end") following the convention that the ' +
97
- 'primary action is the rightmost button.',
98
- };
@@ -1,8 +0,0 @@
1
- export { RECIPE as ProfileCard } from './profile-card.recipe.js';
2
- export { RECIPE as SettingsPanel } from './settings-panel.recipe.js';
3
- export { RECIPE as StatsRow } from './stats-row.recipe.js';
4
- export { RECIPE as ActionBar } from './action-bar.recipe.js';
5
- export { RECIPE as FormLayout } from './form-layout.recipe.js';
6
- export { RECIPE as EmptyStateCard } from './empty-state-card.recipe.js';
7
- export { RECIPE as CollapsibleCard } from './collapsible-card.recipe.js';
8
- export { RECIPE as SearchFilterBar } from './search-filter-bar.recipe.js';
@@ -1,101 +0,0 @@
1
- export const RECIPE = {
2
- id: 'profile-card',
3
- name: 'Profile Card',
4
- description: 'User profile card with avatar, name/role, bio, tag chips, stats row, and action buttons.',
5
- category: 'card',
6
- components: ['card', 'avatar', 'text', 'chip', 'button', 'stack', 'row', 'divider'],
7
- structure: `
8
- Card (elevated, equal padding via style)
9
- └── Stack gap="5"
10
- ├── Row gap="3" align="center" ← avatar + name block
11
- │ ├── Avatar (lg)
12
- │ └── Stack gap="1"
13
- │ ├── Row gap="2" align="center"
14
- │ │ ├── Text (lg, semibold, display) ← full name
15
- │ │ └── Chip (success, sm) ← status badge
16
- │ └── Text (sm, secondary) ← role / subtitle
17
- ├── Text (sm) ← bio paragraph
18
- ├── Row gap="2" wrap ← skill/tag chips
19
- │ └── Chip[] (neutral, borderless, clickable)
20
- ├── Divider
21
- ├── Row gap="6" justify="around" ← stats
22
- │ └── Stack[] gap="0" align="center"
23
- │ ├── Text (2xl, bold, display) ← stat value
24
- │ └── Text (xs, secondary) ← stat label
25
- └── Row gap="3" ← actions
26
- ├── Button (primary, full width)
27
- └── Button (outline, full width)
28
- `.trim(),
29
- code: `<Card variant="elevated" padding="none" style={{ width: 340, padding: 'var(--lucent-space-6)' }}>
30
- <Stack gap="5">
31
- <Row gap="3" align="center">
32
- <Avatar src="/avatars/jane.jpg" alt="Jane Doe" size="lg" />
33
- <Stack gap="1">
34
- <Row gap="2" align="center">
35
- <Text size="lg" weight="semibold" family="display">Jane Doe</Text>
36
- <Chip variant="success" size="sm" dot>Pro</Chip>
37
- </Row>
38
- <Text size="sm" color="secondary">Software Engineer</Text>
39
- </Stack>
40
- </Row>
41
- <Text size="sm">
42
- Building design systems and component libraries.
43
- Passionate about accessible, token-driven UI.
44
- </Text>
45
- <Row gap="2" wrap>
46
- <Chip variant="neutral" borderless onClick={() => {}}>React</Chip>
47
- <Chip variant="neutral" borderless onClick={() => {}}>TypeScript</Chip>
48
- <Chip variant="neutral" borderless onClick={() => {}}>Design Systems</Chip>
49
- </Row>
50
- <Divider />
51
- <Row gap="6" justify="around">
52
- <Stack gap="0" align="center">
53
- <Text size="2xl" weight="bold" family="display">128</Text>
54
- <Text size="xs" color="secondary">Posts</Text>
55
- </Stack>
56
- <Stack gap="0" align="center">
57
- <Text size="2xl" weight="bold" family="display">4.2k</Text>
58
- <Text size="xs" color="secondary">Followers</Text>
59
- </Stack>
60
- <Stack gap="0" align="center">
61
- <Text size="2xl" weight="bold" family="display">312</Text>
62
- <Text size="xs" color="secondary">Following</Text>
63
- </Stack>
64
- </Row>
65
- <Row gap="3">
66
- <Button variant="primary" style={{ flex: 1 }}>Follow</Button>
67
- <Button variant="outline" style={{ flex: 1 }}>Message</Button>
68
- </Row>
69
- </Stack>
70
- </Card>`,
71
- variants: [
72
- {
73
- title: 'Compact collapsible profile',
74
- code: `<Card variant="filled" padding="none" hoverable style={{ width: 280 }}>
75
- <Collapsible
76
- trigger={
77
- <Row gap="3" align="center">
78
- <Avatar alt="Jane Doe" size="md" />
79
- <Stack gap="1">
80
- <Text as="span" size="sm" weight="semibold">Jane Doe</Text>
81
- <Text as="span" size="xs" color="secondary">Software Engineer</Text>
82
- </Stack>
83
- </Row>
84
- }
85
- defaultOpen
86
- >
87
- <Row gap="2" justify="end">
88
- <Button variant="outline" size="sm">Message</Button>
89
- <Button variant="primary" size="sm">Follow</Button>
90
- </Row>
91
- </Collapsible>
92
- </Card>`,
93
- },
94
- ],
95
- designNotes: 'Avatar and name are grouped in a Row with center alignment so the text block ' +
96
- 'vertically centers against the avatar regardless of line count. The name row ' +
97
- 'uses gap="2" for tight coupling between name and status chip. Stats use ' +
98
- 'justify="around" to distribute evenly without fixed widths. Action buttons ' +
99
- 'use flex: 1 to split the row equally. The Divider separates informational ' +
100
- 'content (above) from interactive content (below).',
101
- };