lucent-ui 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/index.cjs +102 -45
  2. package/dist/index.d.ts +143 -0
  3. package/dist/index.js +2578 -1885
  4. package/dist-cli/cli/entry.js +0 -0
  5. package/dist-cli/cli/index.js +0 -0
  6. package/dist-server/server/index.js +29 -29
  7. package/dist-server/server/pattern-registry.js +18 -0
  8. package/dist-server/server/registry.js +8 -0
  9. package/dist-server/src/components/molecules/Card/Card.manifest.js +2 -2
  10. package/dist-server/src/components/molecules/Collapsible/Collapsible.manifest.js +4 -4
  11. package/dist-server/src/components/molecules/FilterDateRange/FilterDateRange.manifest.js +46 -0
  12. package/dist-server/src/components/molecules/FilterMultiSelect/FilterMultiSelect.manifest.js +66 -0
  13. package/dist-server/src/components/molecules/FilterSearch/FilterSearch.manifest.js +37 -0
  14. package/dist-server/src/components/molecules/FilterSelect/FilterSelect.manifest.js +58 -0
  15. package/dist-server/src/components/molecules/Stepper/Stepper.manifest.js +115 -0
  16. package/dist-server/src/components/molecules/Timeline/Timeline.manifest.js +23 -9
  17. package/dist-server/src/manifest/{recipes/action-bar.recipe.js → patterns/action-bar.pattern.js} +1 -1
  18. package/dist-server/src/manifest/{recipes/collapsible-card.recipe.js → patterns/collapsible-card.pattern.js} +1 -1
  19. package/dist-server/src/manifest/patterns/dashboard-header.pattern.js +98 -0
  20. package/dist-server/src/manifest/{recipes/empty-state-card.recipe.js → patterns/empty-state-card.pattern.js} +1 -1
  21. package/dist-server/src/manifest/{recipes/form-layout.recipe.js → patterns/form-layout.pattern.js} +1 -1
  22. package/dist-server/src/manifest/patterns/index.js +12 -0
  23. package/dist-server/src/manifest/patterns/notification-feed.pattern.js +91 -0
  24. package/dist-server/src/manifest/patterns/onboarding-flow.pattern.js +107 -0
  25. package/dist-server/src/manifest/patterns/pricing-table.pattern.js +108 -0
  26. package/dist-server/src/manifest/{recipes/profile-card.recipe.js → patterns/profile-card.pattern.js} +1 -1
  27. package/dist-server/src/manifest/patterns/search-filter-bar.pattern.js +122 -0
  28. package/dist-server/src/manifest/{recipes/settings-panel.recipe.js → patterns/settings-panel.pattern.js} +1 -1
  29. package/dist-server/src/manifest/{recipes/stats-row.recipe.js → patterns/stats-row.pattern.js} +1 -1
  30. package/package.json +13 -15
  31. package/dist-server/server/recipe-registry.js +0 -18
  32. package/dist-server/src/manifest/recipes/index.js +0 -8
  33. package/dist-server/src/manifest/recipes/search-filter-bar.recipe.js +0 -197
  34. package/dist-server/src/manifest/validate.test.js +0 -28
@@ -4,19 +4,20 @@ export const COMPONENT_MANIFEST = {
4
4
  tier: 'molecule',
5
5
  domain: 'neutral',
6
6
  specVersion: '1.0',
7
- description: 'A vertical ordered event list with status-colored dots, connector lines, optional dates, and custom icons.',
7
+ description: 'A vertical activity feed with filled status dots, inline timestamps, optional nested content blocks, and custom icons.',
8
8
  designIntent: 'Timeline renders as a semantic <ol> with each event as a <li>, preserving document order for assistive technologies. ' +
9
- 'The dot and connector are rendered in a fixed-width left column so content in the right column can wrap freely without misaligning the spine. ' +
10
- 'The connector line is omitted on the last item there is nothing to connect to. ' +
9
+ 'Dots are compact (20px) and filled with the status color, with white iconography for high contrast this mirrors modern activity-feed patterns. ' +
10
+ 'Title and date sit inline (date follows title) to keep the eye on a single reading line, rather than splitting attention across the row. ' +
11
+ 'Each item supports an optional content slot for embedded blocks — comment cards, review notes, nested detail panels — placed below the title/description. ' +
12
+ 'The connector line is thin (1.5px) and omitted on the last item. ' +
11
13
  'Status colors follow the same semantic token set as Alert and Badge so danger/success/warning/info carry consistent meaning across the design system. ' +
12
- 'Default status (no explicit icon) renders a plain dot; success/danger/warning get built-in iconography inside the dot. ' +
13
14
  'Custom icons slot in via the icon prop to handle domain-specific event types (e.g. a deploy icon, a payment icon).',
14
15
  props: [
15
16
  {
16
17
  name: 'items',
17
18
  type: 'array',
18
19
  required: true,
19
- description: 'Array of TimelineItem objects. Each has id, title, optional description, optional date string, optional status, and optional icon.',
20
+ description: 'Array of TimelineItem objects. Each has id, title, optional description, optional content (ReactNode for embedded blocks), optional date string, optional status, and optional icon.',
20
21
  },
21
22
  {
22
23
  name: 'style',
@@ -38,18 +39,31 @@ export const COMPONENT_MANIFEST = {
38
39
  />`,
39
40
  },
40
41
  {
41
- title: 'With custom icons',
42
+ title: 'Activity feed with nested content',
42
43
  code: `<Timeline
43
44
  items={[
44
- { id: 'deploy', title: 'v1.2.0 deployed', status: 'success', icon: <RocketIcon />, date: '2h ago' },
45
- { id: 'review', title: 'PR #47 merged', icon: <GitMergeIcon />, date: '3h ago' },
46
- { id: 'alert', title: 'Error rate spike', status: 'warning', icon: <AlertIcon />, date: '4h ago' },
45
+ { id: '1', title: 'Submitted for review', date: 'Mar 22', status: 'success' },
46
+ {
47
+ id: '2',
48
+ title: 'Changes requested',
49
+ date: 'Mar 25',
50
+ status: 'warning',
51
+ content: (
52
+ <Card variant="outline" padding="sm" radius="md">
53
+ <Text size="sm" weight="semibold">Oliver Brown</Text>
54
+ <Text size="sm" color="secondary">Please update the error handling.</Text>
55
+ </Card>
56
+ ),
57
+ },
58
+ { id: '3', title: 'Resubmitted', date: 'Mar 28', status: 'info' },
59
+ { id: '4', title: 'Approved', date: 'Mar 29', status: 'success' },
47
60
  ]}
48
61
  />`,
49
62
  },
50
63
  ],
51
64
  compositionGraph: [
52
65
  { componentId: 'text', componentName: 'Text', role: 'Event title, description, and date label', required: true },
66
+ { componentId: 'card', componentName: 'Card', role: 'Container for nested content blocks in the content slot', required: false },
53
67
  ],
54
68
  accessibility: {
55
69
  role: 'list',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'action-bar',
3
3
  name: 'Action Bar',
4
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.',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'collapsible-card',
3
3
  name: 'Collapsible Card',
4
4
  description: 'Card with an expandable/collapsible section using smooth height animation, available in all card variants.',
@@ -0,0 +1,98 @@
1
+ export const PATTERN = {
2
+ id: 'dashboard-header',
3
+ name: 'Dashboard Header',
4
+ description: 'Full dashboard header combining breadcrumb navigation, page title with action buttons, and a row of KPI stat cards with trend indicators.',
5
+ category: 'dashboard',
6
+ components: ['text', 'button', 'chip', 'card', 'stack', 'row', 'breadcrumb', 'divider'],
7
+ structure: `
8
+ Stack gap="5"
9
+ ├── Stack gap="4" ← action bar
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"
16
+ │ │ ├── Button (outline, sm, leftIcon) ← Export
17
+ │ │ └── Button (primary, sm, leftIcon) ← New report
18
+ │ └── Divider
19
+ └── Row gap="3" wrap ← stats row
20
+ └── Card[] (outline, padding="md", flex=1)
21
+ └── Stack gap="3"
22
+ ├── Text (xs, secondary, medium) ← metric label
23
+ ├── Text (2xl, bold, display) ← value
24
+ └── Row gap="2" align="center"
25
+ ├── Chip (success/danger, sm, borderless) ← trend
26
+ └── Text (xs, secondary) ← comparison
27
+ `.trim(),
28
+ code: `<Stack gap="5" style={{ maxWidth: 860 }}>
29
+ <Stack gap="4">
30
+ <Breadcrumb items={[
31
+ { label: 'Home', href: '#' },
32
+ { label: 'Analytics', href: '#' },
33
+ { label: 'Overview' },
34
+ ]} />
35
+ <Row justify="between" align="end">
36
+ <Stack gap="1">
37
+ <Text as="h1" size="3xl" weight="bold" family="display">Analytics Overview</Text>
38
+ <Text size="sm" color="secondary">Last updated 5 minutes ago</Text>
39
+ </Stack>
40
+ <Row gap="2">
41
+ <Button variant="outline" size="sm" leftIcon={<DownloadIcon />}>Export</Button>
42
+ <Button variant="primary" size="sm" leftIcon={<PlusIcon />}>New report</Button>
43
+ </Row>
44
+ </Row>
45
+ <Divider />
46
+ </Stack>
47
+ <Row gap="3" wrap>
48
+ <Card variant="outline" padding="md" style={{ flex: 1, minWidth: 180 }}>
49
+ <Stack gap="3">
50
+ <Text size="xs" color="secondary" weight="medium">Total Visitors</Text>
51
+ <Text size="2xl" weight="bold" family="display">24.8k</Text>
52
+ <Row gap="2" align="center">
53
+ <Chip variant="success" size="sm" borderless>+18%</Chip>
54
+ <Text size="xs" color="secondary">20.9k last month</Text>
55
+ </Row>
56
+ </Stack>
57
+ </Card>
58
+ <Card variant="outline" padding="md" style={{ flex: 1, minWidth: 180 }}>
59
+ <Stack gap="3">
60
+ <Text size="xs" color="secondary" weight="medium">Conversion Rate</Text>
61
+ <Text size="2xl" weight="bold" family="display">3.24%</Text>
62
+ <Row gap="2" align="center">
63
+ <Chip variant="success" size="sm" borderless>+0.4%</Chip>
64
+ <Text size="xs" color="secondary">2.84% last month</Text>
65
+ </Row>
66
+ </Stack>
67
+ </Card>
68
+ <Card variant="outline" padding="md" style={{ flex: 1, minWidth: 180 }}>
69
+ <Stack gap="3">
70
+ <Text size="xs" color="secondary" weight="medium">Avg. Session</Text>
71
+ <Text size="2xl" weight="bold" family="display">4m 32s</Text>
72
+ <Row gap="2" align="center">
73
+ <Chip variant="danger" size="sm" borderless>-12%</Chip>
74
+ <Text size="xs" color="secondary">5m 08s last month</Text>
75
+ </Row>
76
+ </Stack>
77
+ </Card>
78
+ <Card variant="outline" padding="md" style={{ flex: 1, minWidth: 180 }}>
79
+ <Stack gap="3">
80
+ <Text size="xs" color="secondary" weight="medium">Revenue</Text>
81
+ <Text size="2xl" weight="bold" family="display">$48.2k</Text>
82
+ <Row gap="2" align="center">
83
+ <Chip variant="success" size="sm" borderless>+24%</Chip>
84
+ <Text size="xs" color="secondary">$38.9k last month</Text>
85
+ </Row>
86
+ </Stack>
87
+ </Card>
88
+ </Row>
89
+ </Stack>`,
90
+ designNotes: 'This composition combines two common dashboard patterns: the ActionBar and StatsRow. ' +
91
+ 'The breadcrumb provides navigation context above the title. align="end" on the ' +
92
+ 'title row anchors the action buttons to the baseline of the subtitle. Button ' +
93
+ 'leftIcon props add visual affordance to Export (download icon) and New report ' +
94
+ '(plus icon). The Divider separates the header chrome from the metric content below. ' +
95
+ 'Stat cards use flex: 1 with minWidth: 180 for responsive equal sizing. Display font ' +
96
+ 'on metric values creates instant visual hierarchy. Trend chips use success/danger ' +
97
+ 'variants with borderless styling so they read as inline metadata, not interactive tags.',
98
+ };
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'empty-state-card',
3
3
  name: 'Empty State Card',
4
4
  description: 'Centered empty state with illustration icon, heading, description, and call-to-action button inside a card.',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'form-layout',
3
3
  name: 'Form Layout',
4
4
  description: 'Stacked form with grouped sections, FormField labels, validation hints, and a submit/cancel footer.',
@@ -0,0 +1,12 @@
1
+ export { PATTERN as ProfileCard } from './profile-card.pattern.js';
2
+ export { PATTERN as SettingsPanel } from './settings-panel.pattern.js';
3
+ export { PATTERN as StatsRow } from './stats-row.pattern.js';
4
+ export { PATTERN as ActionBar } from './action-bar.pattern.js';
5
+ export { PATTERN as FormLayout } from './form-layout.pattern.js';
6
+ export { PATTERN as EmptyStateCard } from './empty-state-card.pattern.js';
7
+ export { PATTERN as CollapsibleCard } from './collapsible-card.pattern.js';
8
+ export { PATTERN as SearchFilterBar } from './search-filter-bar.pattern.js';
9
+ export { PATTERN as PricingTable } from './pricing-table.pattern.js';
10
+ export { PATTERN as NotificationFeed } from './notification-feed.pattern.js';
11
+ export { PATTERN as OnboardingFlow } from './onboarding-flow.pattern.js';
12
+ export { PATTERN as DashboardHeader } from './dashboard-header.pattern.js';
@@ -0,0 +1,91 @@
1
+ export const PATTERN = {
2
+ id: 'notification-feed',
3
+ name: 'Notification Feed',
4
+ description: 'Scrollable list of notification items with avatar, message, timestamp, type chip, and contextual action buttons. Unread items are visually distinct.',
5
+ category: 'card',
6
+ components: ['card', 'avatar', 'text', 'chip', 'button', 'stack', 'row'],
7
+ structure: `
8
+ Card (elevated, padding via style)
9
+ └── Stack gap="3"
10
+ ├── Row justify="between" align="center" ← header
11
+ │ ├── Row gap="2" align="center"
12
+ │ │ ├── Text (lg, semibold) ← title
13
+ │ │ └── Chip (accent, sm) ← unread count
14
+ │ └── Button (ghost, xs) ← mark all read
15
+ └── Stack gap="2" ← notification list
16
+ └── Card[] (filled for unread, ghost for read)
17
+ └── Row gap="3" align="start" justify="between"
18
+ ├── Row gap="3" align="start"
19
+ │ ├── Avatar (sm)
20
+ │ └── Stack gap="1"
21
+ │ ├── Text (sm) ← message with bold name + target
22
+ │ └── Row gap="2"
23
+ │ ├── Chip (type-colored, sm, borderless)
24
+ │ └── Text (xs, secondary) ← timestamp
25
+ └── Row gap="1" ← actions
26
+ ├── Button? (primary/outline, xs) ← contextual
27
+ └── Button (ghost, xs) ← dismiss
28
+ `.trim(),
29
+ code: `<Card variant="elevated" padding="none" style={{ width: 520, padding: 'var(--lucent-space-4)' }}>
30
+ <Stack gap="3">
31
+ <Row justify="between" align="center" style={{ padding: '0 var(--lucent-space-2)' }}>
32
+ <Row gap="2" align="center">
33
+ <Text size="lg" weight="semibold">Notifications</Text>
34
+ <Chip variant="accent" size="sm">3 new</Chip>
35
+ </Row>
36
+ <Button variant="ghost" size="xs">Mark all read</Button>
37
+ </Row>
38
+ <Stack gap="2">
39
+ <Card variant="filled" padding="md" style={{ borderLeft: '3px solid var(--lucent-accent-default)' }}>
40
+ <Row gap="3" align="start" justify="between">
41
+ <Row gap="3" align="start" style={{ flex: 1, minWidth: 0 }}>
42
+ <Avatar alt="Alice Chen" size="sm" />
43
+ <Stack gap="1" style={{ flex: 1, minWidth: 0 }}>
44
+ <Text size="sm">
45
+ <Text as="span" size="sm" weight="semibold">Alice Chen</Text>
46
+ {' '}mentioned you in{' '}
47
+ <Text as="span" size="sm" weight="medium">Design system roadmap</Text>
48
+ </Text>
49
+ <Row gap="2" align="center">
50
+ <Chip variant="accent" size="sm" borderless>Mention</Chip>
51
+ <Text size="xs" color="secondary">2 min ago</Text>
52
+ </Row>
53
+ </Stack>
54
+ </Row>
55
+ <Button variant="ghost" size="xs">Dismiss</Button>
56
+ </Row>
57
+ </Card>
58
+ <Card variant="ghost" padding="md">
59
+ <Row gap="3" align="start" justify="between">
60
+ <Row gap="3" align="start" style={{ flex: 1, minWidth: 0 }}>
61
+ <Avatar alt="Carol Park" size="sm" />
62
+ <Stack gap="1" style={{ flex: 1, minWidth: 0 }}>
63
+ <Text size="sm">
64
+ <Text as="span" size="sm" weight="semibold">Carol Park</Text>
65
+ {' '}requested your review on{' '}
66
+ <Text as="span" size="sm" weight="medium">PR #139: Add Slider atom</Text>
67
+ </Text>
68
+ <Row gap="2" align="center">
69
+ <Chip variant="warning" size="sm" borderless>Review</Chip>
70
+ <Text size="xs" color="secondary">1 hour ago</Text>
71
+ </Row>
72
+ </Stack>
73
+ </Row>
74
+ <Row gap="1">
75
+ <Button variant="primary" size="xs">Review</Button>
76
+ <Button variant="ghost" size="xs">Dismiss</Button>
77
+ </Row>
78
+ </Row>
79
+ </Card>
80
+ </Stack>
81
+ </Stack>
82
+ </Card>`,
83
+ designNotes: 'Unread notifications use Card variant="filled" with an accent left border to create ' +
84
+ 'a visual distinction from read items (variant="ghost"). The accent border is a ' +
85
+ 'familiar "unread indicator" pattern. Each notification item is a self-contained Row ' +
86
+ 'with the avatar and message on the left (flex: 1, minWidth: 0 for text truncation) ' +
87
+ 'and contextual actions on the right. Type chips (mention, comment, review, invite) ' +
88
+ 'use semantic variant colors (accent, info, warning, success) with borderless styling ' +
89
+ 'so they read as metadata, not interactive elements. Action buttons are contextual: ' +
90
+ 'review requests get a primary "Review" button, invites get "Accept", others just "Dismiss".',
91
+ };
@@ -0,0 +1,107 @@
1
+ export const PATTERN = {
2
+ id: 'onboarding-flow',
3
+ name: 'Onboarding Flow',
4
+ description: 'Multi-step onboarding form with step indicator, progress bar, form fields, and back/next navigation. Steps: Profile → Preferences → Confirmation.',
5
+ category: 'form',
6
+ components: ['card', 'text', 'button', 'progress', 'form-field', 'input', 'select', 'checkbox', 'avatar', 'chip', 'stack', 'row', 'divider'],
7
+ structure: `
8
+ Card (elevated, padding="lg")
9
+ └── Stack gap="6"
10
+ ├── Stack gap="1" ← header
11
+ │ ├── Text (lg, semibold, display) ← title
12
+ │ └── Text (sm, secondary) ← step counter
13
+ ├── Progress (sm) ← progress bar
14
+ ├── StepIndicator ← custom step circles
15
+ │ ├── Row justify="between"
16
+ │ │ └── circle[] + connector lines
17
+ │ └── Row justify="between"
18
+ │ └── Text[] (xs, step labels)
19
+ ├── Divider
20
+ ├── Step content (conditional)
21
+ │ ├── Step 0: Stack of FormFields (name, email, role Select)
22
+ │ ├── Step 1: FormFields (team size, use case) + Checkbox
23
+ │ └── Step 2: Avatar + name + email + role Chip (confirmation)
24
+ └── Row justify="between" ← navigation
25
+ ├── Button (ghost, sm) ← Back (disabled on step 0)
26
+ └── Button (primary, sm) ← Continue / Complete
27
+ `.trim(),
28
+ code: `const [step, setStep] = useState(0);
29
+ const STEPS = ['Profile', 'Preferences', 'Confirm'];
30
+
31
+ <Card variant="elevated" padding="lg" style={{ width: 440 }}>
32
+ <Stack gap="6">
33
+ <Stack gap="1">
34
+ <Text size="lg" weight="semibold" family="display">Create your account</Text>
35
+ <Text size="sm" color="secondary">Step {step + 1} of {STEPS.length}</Text>
36
+ </Stack>
37
+ <Progress value={((step + 1) / STEPS.length) * 100} size="sm" />
38
+ <Divider />
39
+ {step === 0 && (
40
+ <Stack gap="4">
41
+ <FormField label="Full name" htmlFor="onb-name" required>
42
+ <Input id="onb-name" placeholder="Jane Doe" />
43
+ </FormField>
44
+ <FormField label="Email address" htmlFor="onb-email" required>
45
+ <Input id="onb-email" type="email" placeholder="jane@example.com" />
46
+ </FormField>
47
+ <FormField label="Role" htmlFor="onb-role">
48
+ <Select
49
+ id="onb-role"
50
+ defaultValue="engineer"
51
+ options={[
52
+ { value: 'engineer', label: 'Engineer' },
53
+ { value: 'designer', label: 'Designer' },
54
+ { value: 'product', label: 'Product Manager' },
55
+ { value: 'other', label: 'Other' },
56
+ ]}
57
+ />
58
+ </FormField>
59
+ </Stack>
60
+ )}
61
+ {step === 1 && (
62
+ <Stack gap="4">
63
+ <FormField label="Team size" htmlFor="onb-team">
64
+ <Select
65
+ id="onb-team"
66
+ defaultValue="small"
67
+ options={[
68
+ { value: 'solo', label: 'Just me' },
69
+ { value: 'small', label: '2–10 people' },
70
+ { value: 'medium', label: '11–50 people' },
71
+ { value: 'large', label: '50+ people' },
72
+ ]}
73
+ />
74
+ </FormField>
75
+ <Checkbox label="I agree to the terms of service" />
76
+ </Stack>
77
+ )}
78
+ {step === 2 && (
79
+ <Stack gap="4" align="center">
80
+ <Avatar alt="Jane Doe" size="xl" />
81
+ <Stack gap="1" align="center">
82
+ <Text size="lg" weight="semibold" family="display">Jane Doe</Text>
83
+ <Text size="sm" color="secondary">jane@example.com</Text>
84
+ </Stack>
85
+ <Chip variant="accent" size="sm">Engineer</Chip>
86
+ <Text size="sm" color="secondary" align="center">
87
+ Everything looks good. Click "Complete" to finish setup.
88
+ </Text>
89
+ </Stack>
90
+ )}
91
+ <Row justify="between">
92
+ <Button variant="ghost" size="sm" onClick={() => setStep(s => s - 1)} disabled={step === 0}>Back</Button>
93
+ <Button variant="primary" size="sm" onClick={() => setStep(s => Math.min(s + 1, 2))}>
94
+ {step === 2 ? 'Complete' : 'Continue'}
95
+ </Button>
96
+ </Row>
97
+ </Stack>
98
+ </Card>`,
99
+ designNotes: 'The Progress bar gives an at-a-glance sense of completion. The step indicator uses ' +
100
+ 'accent-colored circles for completed/current steps and neutral borders for future ' +
101
+ 'steps, with connector lines between them. Each step is a conditional render block ' +
102
+ 'so the form content swaps without layout shift (the Card maintains constant width). ' +
103
+ 'The confirmation step centers content with Avatar + name for a personal touch. ' +
104
+ 'Back button uses ghost variant and is disabled on step 0 to prevent confusion. ' +
105
+ 'justify="between" on the navigation row pushes Back and Continue to opposite edges, ' +
106
+ 'giving Back room to breathe so users don\'t accidentally go backwards.',
107
+ };
@@ -0,0 +1,108 @@
1
+ export const PATTERN = {
2
+ id: 'pricing-table',
3
+ name: 'Pricing Table',
4
+ description: 'Side-by-side pricing tier cards with feature lists and CTA buttons. Middle tier highlighted with accent border and primary button.',
5
+ category: 'card',
6
+ components: ['card', 'text', 'button', 'chip', 'stack', 'row', 'divider'],
7
+ structure: `
8
+ Row gap="4" wrap align="stretch"
9
+ ├── Card (outline, flex=1) ← free tier
10
+ │ └── Stack gap="5" (full height)
11
+ │ ├── Stack gap="2" ← tier header
12
+ │ │ ├── Text (sm, secondary) ← tier name
13
+ │ │ ├── Row align="baseline"
14
+ │ │ │ ├── Text (3xl, bold, display) ← price
15
+ │ │ │ └── Text (sm, secondary) ← period
16
+ │ │ └── Text (xs, secondary) ← tagline
17
+ │ ├── Divider
18
+ │ ├── Stack gap="3" (flex=1) ← feature list
19
+ │ │ └── Row[] gap="2" align="center"
20
+ │ │ ├── ✓ icon (success color)
21
+ │ │ └── Text (sm)
22
+ │ └── Button (outline, full width)
23
+ ├── Card (elevated, accent border, flex=1) ← pro tier (highlighted)
24
+ │ └── (same structure + Chip "Popular")
25
+ │ └── Button (primary, full width)
26
+ └── Card (outline, flex=1) ← enterprise tier
27
+ └── (same structure)
28
+ └── Button (outline, full width)
29
+ `.trim(),
30
+ code: `<Row gap="4" wrap align="stretch">
31
+ <Card variant="outline" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320 }}>
32
+ <Stack gap="5" style={{ height: '100%' }}>
33
+ <Stack gap="2">
34
+ <Text size="sm" weight="medium" color="secondary">Free</Text>
35
+ <Row gap="1" align="baseline">
36
+ <Text size="3xl" weight="bold" family="display">$0</Text>
37
+ <Text size="sm" color="secondary">/ month</Text>
38
+ </Row>
39
+ <Text size="xs" color="secondary">For individuals getting started.</Text>
40
+ </Stack>
41
+ <Divider />
42
+ <Stack gap="3" style={{ flex: 1 }}>
43
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Up to 3 projects</Text></Row>
44
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">1 GB storage</Text></Row>
45
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Community support</Text></Row>
46
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Basic analytics</Text></Row>
47
+ </Stack>
48
+ <Button variant="outline" style={{ width: '100%' }}>Get started</Button>
49
+ </Stack>
50
+ </Card>
51
+ <Card variant="elevated" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320, border: '2px solid var(--lucent-accent-default)' }}>
52
+ <Stack gap="5" style={{ height: '100%' }}>
53
+ <Stack gap="2">
54
+ <Row gap="2" align="center">
55
+ <Text size="sm" weight="medium" color="secondary">Pro</Text>
56
+ <Chip variant="accent" size="sm">Popular</Chip>
57
+ </Row>
58
+ <Row gap="1" align="baseline">
59
+ <Text size="3xl" weight="bold" family="display">$29</Text>
60
+ <Text size="sm" color="secondary">/ month</Text>
61
+ </Row>
62
+ <Text size="xs" color="secondary">For professionals and small teams.</Text>
63
+ </Stack>
64
+ <Divider />
65
+ <Stack gap="3" style={{ flex: 1 }}>
66
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Unlimited projects</Text></Row>
67
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">50 GB storage</Text></Row>
68
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Priority support</Text></Row>
69
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Advanced analytics</Text></Row>
70
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Custom domains</Text></Row>
71
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Team collaboration</Text></Row>
72
+ </Stack>
73
+ <Button variant="primary" style={{ width: '100%' }}>Upgrade to Pro</Button>
74
+ </Stack>
75
+ </Card>
76
+ <Card variant="outline" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320 }}>
77
+ <Stack gap="5" style={{ height: '100%' }}>
78
+ <Stack gap="2">
79
+ <Text size="sm" weight="medium" color="secondary">Enterprise</Text>
80
+ <Row gap="1" align="baseline">
81
+ <Text size="3xl" weight="bold" family="display">$99</Text>
82
+ <Text size="sm" color="secondary">/ month</Text>
83
+ </Row>
84
+ <Text size="xs" color="secondary">For organizations at scale.</Text>
85
+ </Stack>
86
+ <Divider />
87
+ <Stack gap="3" style={{ flex: 1 }}>
88
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Everything in Pro</Text></Row>
89
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Unlimited storage</Text></Row>
90
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">24/7 dedicated support</Text></Row>
91
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">SSO & SAML</Text></Row>
92
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Audit logs</Text></Row>
93
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">SLA guarantee</Text></Row>
94
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Custom integrations</Text></Row>
95
+ </Stack>
96
+ <Button variant="outline" style={{ width: '100%' }}>Contact sales</Button>
97
+ </Stack>
98
+ </Card>
99
+ </Row>`,
100
+ designNotes: 'Three tiers in a Row with flex: 1 and minWidth/maxWidth constraints ensure equal ' +
101
+ 'sizing that wraps gracefully on narrow viewports. align="stretch" makes all cards ' +
102
+ 'equal height. The Pro tier is visually distinguished with an accent border and ' +
103
+ 'elevated variant so it draws the eye first. Each card uses Stack with height: 100% ' +
104
+ 'and the feature list has flex: 1 to push the CTA button to the bottom regardless ' +
105
+ 'of feature count. Price uses display font at 3xl for maximum impact; align="baseline" ' +
106
+ 'on the Row anchors the period text to the price baseline. The "Popular" Chip reinforces ' +
107
+ 'the recommended tier. CheckIcon uses success color for positive feature connotation.',
108
+ };
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'profile-card',
3
3
  name: 'Profile Card',
4
4
  description: 'User profile card with avatar, name/role, bio, tag chips, stats row, and action buttons.',