lucent-ui 0.14.1 → 0.15.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.
@@ -4,21 +4,64 @@ export const COMPONENT_MANIFEST = {
4
4
  tier: 'molecule',
5
5
  domain: 'neutral',
6
6
  specVersion: '0.1',
7
- description: 'A surface container with optional header, body, and footer slots, configurable padding, shadow, and radius. Includes a CardBleed sub-component for edge-to-edge content.',
8
- designIntent: 'Card provides a consistent elevated surface for grouping related content. The header and footer slots ' +
9
- 'are separated from the body by a border-default divider, giving visual structure without requiring ' +
10
- 'the consumer to manage spacing. Padding, shadow, and radius are all configurable to accommodate ' +
11
- 'flat/ghost cards, modal-like surfaces, and compact data-dense layouts. The overflow: hidden ensures ' +
12
- 'children respect the border-radius without needing additional clipping.\n\n' +
13
- 'CardBleed is a companion component that allows specific children within the Card body to stretch ' +
14
- 'edge-to-edge, cancelling the horizontal padding via negative margins. Text inside CardBleed stays ' +
15
- 'aligned with the rest of the card content thanks to matching inner padding. Use it for dividers, ' +
16
- 'full-width lists, or bordered sections that need to reach the card edges.\n\n' +
17
- 'Token rule: Card uses surface for its background. Never use bgBase or bgSubtle on a Card — ' +
18
- 'those tokens are reserved for the page canvas (body, sidebar, layout regions). Content nested ' +
19
- 'inside a Card that needs a tinted fill (e.g. a footer, inset panel, or disabled input) should use ' +
20
- 'surfaceSecondary.',
7
+ description: 'A surface container with five elevation variants that form a visual importance hierarchy. ' +
8
+ 'Supports optional header, body, and footer slots with configurable padding, shadow, and radius. ' +
9
+ 'Includes a CardBleed sub-component for edge-to-edge content.',
10
+ designIntent: 'Card provides a configurable surface with an explicit elevation hierarchy. Each variant maps to ' +
11
+ 'a distinct level of visual prominence, giving consumers a single prop to express how much attention ' +
12
+ 'a surface should command relative to its surroundings.\n\n' +
13
+ '## Elevation hierarchy (lowest highest)\n\n' +
14
+ '1. **ghost** transparent background, no border, no shadow.\n' +
15
+ ' The card is invisible as a container content floats directly against the page or parent surface. ' +
16
+ 'Use for logical groupings that shouldn\'t compete visually: sidebar sections, form regions, or ' +
17
+ 'layout slots where structure exists conceptually but not visually. Header/footer dividers still ' +
18
+ 'render if those slots are used, providing minimal internal structure.\n\n' +
19
+ '2. **outline** (default) transparent background with `border-default` border, no shadow.\n' +
20
+ ' Like ghost, the card inherits its container\'s background — the border alone defines the boundary. ' +
21
+ 'This is the workhorse variant for lists of items, form sections, data panels, ' +
22
+ 'and any content that needs a visible container without drawing excessive attention. Header and footer ' +
23
+ 'slots are separated from the body by matching `border-default` dividers.\n\n' +
24
+ '3. **filled** — semi-transparent tinted background, no border, no shadow.\n' +
25
+ ' Differentiates the card from its surroundings by darkening (light mode) or lightening (dark mode) ' +
26
+ 'whatever surface it sits on. Uses `color-mix(in srgb, textPrimary 6%, transparent)` so the tint is ' +
27
+ 'always relative to the container — never a fixed color. Use for secondary content areas, inset panels, ' +
28
+ 'summary blocks, or anywhere a border would feel heavy but the card needs to be visually distinct. ' +
29
+ 'Effective for nesting (e.g., a filled card inside an elevated card creates a recessed region).\n\n' +
30
+ '4. **elevated** — `surface` background with medium shadow, no border.\n' +
31
+ ' The card lifts off the page through depth. The shadow creates a physical metaphor: this content ' +
32
+ 'sits above the surface it rests on. Use for primary content areas, feature highlights, pricing cards, ' +
33
+ 'or any surface that should feel physically elevated. The lack of border keeps the silhouette soft — ' +
34
+ 'the shadow alone defines the boundary.\n\n' +
35
+ '5. **combo** — transparent wrapper with an elevated body inset.\n' +
36
+ ' The header and footer are flat — they blend into the page background as if they were part of it. ' +
37
+ 'Only the body section is elevated: it gets `surface` background, border-radius, and shadow, appearing ' +
38
+ 'as a raised card sitting between the flat header/footer regions. This draws the eye to the primary ' +
39
+ 'content while keeping supporting info (header) and actions (footer) visually subordinate — they frame ' +
40
+ 'the elevated body without competing with it. No dividers are rendered — the elevation change IS the ' +
41
+ 'visual separator. Use for detail panels, profile cards, or settings forms where the body content is ' +
42
+ 'the focal point.\n\n' +
43
+ '## Shadow override\n' +
44
+ 'The `shadow` prop overrides whatever shadow the variant implies. This allows fine-tuning without ' +
45
+ 'changing the variant. For example, `variant="elevated" shadow="lg"` gives an elevated card with extra ' +
46
+ 'depth, while `variant="outline" shadow="none"` gives a flat bordered card.\n\n' +
47
+ '## Token rules\n' +
48
+ '- `elevated` and `combo` body use `surface` (the primary component surface — white in light mode).\n' +
49
+ '- `filled` uses a semi-transparent tint of `textPrimary` — contextually darker/lighter than its container.\n' +
50
+ '- `combo` wrapper is transparent (header/footer blend with page); only the body is elevated with `surface`.\n' +
51
+ '- `ghost` and `outline` use `transparent` — they inherit from whatever they\'re placed on. ' +
52
+ 'The border is the only visual differentiator for `outline`.\n' +
53
+ '- Never use `bgBase` or `bgSubtle` on a Card — those tokens are reserved for the page canvas.\n' +
54
+ '- Content nested inside a Card that needs a tinted fill should use `surfaceSecondary`.',
21
55
  props: [
56
+ {
57
+ name: 'variant',
58
+ type: 'enum',
59
+ required: false,
60
+ default: 'outline',
61
+ description: 'The elevation variant. Controls background color, border, and default shadow. ' +
62
+ 'Ordered from lowest to highest visual prominence: ghost → outline → filled → elevated → combo.',
63
+ enumValues: ['ghost', 'outline', 'filled', 'elevated', 'combo'],
64
+ },
22
65
  {
23
66
  name: 'children',
24
67
  type: 'ReactNode',
@@ -29,13 +72,15 @@ export const COMPONENT_MANIFEST = {
29
72
  name: 'header',
30
73
  type: 'ReactNode',
31
74
  required: false,
32
- description: 'Content rendered in the header slot, separated from the body by a divider.',
75
+ description: 'Content rendered in the header slot. Separated from the body by a divider in all variants except combo, ' +
76
+ 'where the background-color change provides the separation.',
33
77
  },
34
78
  {
35
79
  name: 'footer',
36
80
  type: 'ReactNode',
37
81
  required: false,
38
- description: 'Content rendered in the footer slot, separated from the body by a divider.',
82
+ description: 'Content rendered in the footer slot. Separated from the body by a divider in all variants except combo, ' +
83
+ 'where the background-color change provides the separation.',
39
84
  },
40
85
  {
41
86
  name: 'padding',
@@ -49,8 +94,8 @@ export const COMPONENT_MANIFEST = {
49
94
  name: 'shadow',
50
95
  type: 'enum',
51
96
  required: false,
52
- default: 'sm',
53
- description: 'Box shadow elevation.',
97
+ description: 'Box shadow elevation. When omitted, uses the variant\'s default: ghost=none, outline=none, filled=none, elevated=md, combo=md. ' +
98
+ 'When set explicitly, overrides the variant\'s default.',
54
99
  enumValues: ['none', 'sm', 'md', 'lg'],
55
100
  },
56
101
  {
@@ -67,27 +112,102 @@ export const COMPONENT_MANIFEST = {
67
112
  required: false,
68
113
  description: 'Inline style overrides for the card wrapper.',
69
114
  },
115
+ {
116
+ name: 'onClick',
117
+ type: 'function',
118
+ required: false,
119
+ description: 'Click handler. When provided, the card renders as a <button> with hover lift, focus ring, ' +
120
+ 'and active press states matching the Button component.',
121
+ },
122
+ {
123
+ name: 'href',
124
+ type: 'string',
125
+ required: false,
126
+ description: 'Link URL. When provided, the card renders as an <a>. Takes precedence over onClick for the element type, ' +
127
+ 'but onClick is still attached as a handler.',
128
+ },
129
+ {
130
+ name: 'target',
131
+ type: 'string',
132
+ required: false,
133
+ description: 'Passed to <a> when href is set (e.g. "_blank").',
134
+ },
135
+ {
136
+ name: 'rel',
137
+ type: 'string',
138
+ required: false,
139
+ description: 'Passed to <a> when href is set (e.g. "noopener noreferrer").',
140
+ },
141
+ {
142
+ name: 'disabled',
143
+ type: 'boolean',
144
+ required: false,
145
+ description: 'Disables interactive behavior. Reduces opacity, removes hover/focus/active states, ' +
146
+ 'and sets cursor to not-allowed. Only applies when onClick or href is set.',
147
+ },
148
+ {
149
+ name: 'status',
150
+ type: 'enum',
151
+ required: false,
152
+ description: 'Adds a 3px colored accent bar on the left edge of the card. Uses the corresponding status ' +
153
+ 'token (successDefault, warningDefault, dangerDefault, infoDefault). Works with all variants.',
154
+ enumValues: ['success', 'warning', 'danger', 'info'],
155
+ },
156
+ {
157
+ name: 'selected',
158
+ type: 'boolean',
159
+ required: false,
160
+ description: 'Adds an inset accent ring and subtle background tint to indicate selection. Used for card grids ' +
161
+ 'where cards act as radio/checkbox options. Pairs with onClick for toggle behavior. ' +
162
+ 'Sets aria-pressed on interactive cards. Disabled takes precedence — ring is hidden when disabled.',
163
+ },
164
+ {
165
+ name: 'media',
166
+ type: 'ReactNode',
167
+ required: false,
168
+ description: 'Full-bleed content rendered at the top of the card (before header). No padding is applied. ' +
169
+ 'Use for hero images, illustrations, or any edge-to-edge top content. The card\'s overflow:hidden ' +
170
+ 'clips media to the border-radius.',
171
+ },
70
172
  ],
71
173
  usageExamples: [
72
174
  {
73
- title: 'Simple card',
74
- code: `<Card>
75
- <Text>Some content here.</Text>
175
+ title: 'Ghost — invisible container',
176
+ code: `<Card variant="ghost">
177
+ <Text>Content sits directly on the page background.</Text>
76
178
  </Card>`,
77
179
  },
78
180
  {
79
- title: 'With header and footer',
181
+ title: 'Outline bordered card (default)',
80
182
  code: `<Card
81
183
  header={<Text weight="semibold">Card title</Text>}
82
184
  footer={<Button variant="primary">Save</Button>}
83
185
  >
84
- <Text color="secondary">Card body content goes here.</Text>
186
+ <Text color="secondary">The standard card treatment with dividers.</Text>
187
+ </Card>`,
188
+ },
189
+ {
190
+ title: 'Filled — color-differentiated region',
191
+ code: `<Card variant="filled" padding="lg">
192
+ <Text weight="semibold">Summary</Text>
193
+ <Text color="secondary" size="sm">A tinted panel that stands out through background color alone.</Text>
85
194
  </Card>`,
86
195
  },
87
196
  {
88
- title: 'Flat variant',
89
- code: `<Card shadow="none" radius="sm" padding="sm">
90
- <Text size="sm">Compact flat card</Text>
197
+ title: 'Elevated — shadow-lifted surface',
198
+ code: `<Card variant="elevated">
199
+ <Text weight="semibold">Featured</Text>
200
+ <Text color="secondary">This card floats above the page with shadow depth.</Text>
201
+ </Card>`,
202
+ },
203
+ {
204
+ title: 'Combo — two-tone card with header and footer',
205
+ code: `<Card
206
+ variant="combo"
207
+ header={<Text weight="semibold">Profile</Text>}
208
+ footer={<Button variant="primary">Update</Button>}
209
+ >
210
+ <Text>The bright body is framed by the muted header and footer.</Text>
91
211
  </Card>`,
92
212
  },
93
213
  {
@@ -97,13 +217,58 @@ export const COMPONENT_MANIFEST = {
97
217
  <CardBleed style={{ borderTop: '1px solid var(--lucent-border-default)', marginTop: 'var(--lucent-space-4)' }}>
98
218
  <Text color="secondary">This section stretches to the card edges.</Text>
99
219
  </CardBleed>
220
+ </Card>`,
221
+ },
222
+ {
223
+ title: 'Clickable card',
224
+ code: `<Card variant="elevated" onClick={() => navigate('/detail')}>
225
+ <Text weight="semibold">Dashboard tile</Text>
226
+ <Text color="secondary" size="sm">Click to view details</Text>
227
+ </Card>`,
228
+ },
229
+ {
230
+ title: 'Link card',
231
+ code: `<Card variant="elevated" href="/docs/getting-started" target="_blank" rel="noopener noreferrer">
232
+ <Text weight="semibold">Documentation</Text>
233
+ <Text color="secondary" size="sm">Opens in a new tab</Text>
234
+ </Card>`,
235
+ },
236
+ {
237
+ title: 'Status accent',
238
+ code: `<Card status="danger">
239
+ <Text weight="semibold">Payment failed</Text>
240
+ <Text color="secondary" size="sm">Check your card details and try again.</Text>
241
+ </Card>`,
242
+ },
243
+ {
244
+ title: 'Selectable card',
245
+ code: `<Card
246
+ variant="elevated"
247
+ selected={isSelected}
248
+ onClick={() => setIsSelected(!isSelected)}
249
+ >
250
+ <Text weight="semibold">Pro plan</Text>
251
+ <Text color="secondary" size="sm">$29/month</Text>
252
+ </Card>`,
253
+ },
254
+ {
255
+ title: 'Media card with hero image',
256
+ code: `<Card
257
+ variant="elevated"
258
+ media={<img src="/hero.jpg" alt="Hero" style={{ width: '100%', display: 'block' }} />}
259
+ >
260
+ <Text weight="semibold">Article title</Text>
261
+ <Text color="secondary" size="sm">A card with a full-bleed hero image.</Text>
100
262
  </Card>`,
101
263
  },
102
264
  ],
103
265
  compositionGraph: [],
104
266
  accessibility: {
105
- notes: 'Card has no implicit ARIA role. If the card represents a landmark, wrap it in a <section> or <article> ' +
106
- 'and provide an aria-label. For interactive cards (clickable), make the wrapper a <button> or <a> and ' +
107
- 'ensure focus styles are visible.',
267
+ notes: 'Non-interactive cards have no implicit ARIA role wrap in <section> or <article> if needed. ' +
268
+ 'Interactive cards with onClick render as <button> with focus ring. ' +
269
+ 'Interactive cards with href render as <a> with focus ring. ' +
270
+ 'Selected cards set aria-pressed on the interactive element. ' +
271
+ 'The status accent bar is aria-hidden (decorative). ' +
272
+ 'Media slot images should include alt text.',
108
273
  },
109
274
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucent-ui",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "An AI-first React component library with machine-readable manifests.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",