lucent-ui 0.22.0 → 0.23.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.
@@ -0,0 +1,43 @@
1
+ export const COMPONENT_MANIFEST = {
2
+ id: 'button-group',
3
+ name: 'ButtonGroup',
4
+ tier: 'atom',
5
+ domain: 'neutral',
6
+ specVersion: '0.1',
7
+ description: 'A layout wrapper that visually groups multiple Button or SplitButton children into a related strip.',
8
+ designIntent: 'Use ButtonGroup for peer actions that belong together visually — toolbars, action bars, ' +
9
+ 'toggle groups. Unlike SegmentedControl (single-select with indicator), ButtonGroup has no ' +
10
+ 'built-in selection state; each child handles its own behaviour. ' +
11
+ 'Items are separated by a small token-based gap with subtle inner corner radius ' +
12
+ 'so the group reads as a unit while each button retains its own border and hover states.',
13
+ props: [
14
+ { name: 'children', type: 'ReactNode', required: true, description: 'Button or SplitButton elements to group.' },
15
+ { name: 'style', type: 'object', required: false, description: 'Style overrides for the wrapper div.' },
16
+ ],
17
+ usageExamples: [
18
+ {
19
+ title: 'Toolbar',
20
+ code: `<ButtonGroup>\n <Button variant="outline" leftIcon={<Icon name="bold" />} />\n <Button variant="outline" leftIcon={<Icon name="italic" />} />\n <Button variant="outline" leftIcon={<Icon name="underline" />} />\n</ButtonGroup>`,
21
+ },
22
+ {
23
+ title: 'Action pair',
24
+ code: `<ButtonGroup>\n <Button variant="primary">Save</Button>\n <Button variant="outline">Cancel</Button>\n</ButtonGroup>`,
25
+ },
26
+ {
27
+ title: 'With SplitButton',
28
+ code: `<ButtonGroup>\n <SplitButton onClick={deploy} menuItems={[{ label: 'Staging', onSelect: staging }]}>Deploy</SplitButton>\n <Button variant="outline">Logs</Button>\n</ButtonGroup>`,
29
+ },
30
+ ],
31
+ compositionGraph: [
32
+ { componentId: 'button', componentName: 'Button', role: 'Grouped action item', required: false },
33
+ { componentId: 'split-button', componentName: 'SplitButton', role: 'Grouped split action item', required: false },
34
+ ],
35
+ accessibility: {
36
+ role: 'group',
37
+ ariaAttributes: [],
38
+ keyboardInteractions: [
39
+ 'Tab moves focus between buttons in the group',
40
+ ],
41
+ notes: 'Uses role="group" on the wrapper. Individual button accessibility is inherited from the children.',
42
+ },
43
+ };
@@ -0,0 +1,75 @@
1
+ export const COMPONENT_MANIFEST = {
2
+ id: 'split-button',
3
+ name: 'SplitButton',
4
+ tier: 'atom',
5
+ domain: 'neutral',
6
+ specVersion: '0.1',
7
+ description: 'A compound button pairing a primary action with a chevron dropdown for secondary actions.',
8
+ designIntent: 'Use SplitButton when there is one dominant action alongside a small set of related alternatives ' +
9
+ '(e.g. "Save" + "Save as draft", "Deploy" + "Deploy to staging"). ' +
10
+ 'Each half is a fully independent button separated by a small token-based gap, with a subtle ' +
11
+ 'inner corner radius (radius-sm) so the pair reads as a unit without sharing a border. ' +
12
+ 'Hover lift and press ring apply independently per half. ' +
13
+ 'Ghost variants use tighter inner padding to keep the halves visually close. ' +
14
+ 'The chevron half opens a Menu molecule; all dropdown keyboard navigation is inherited.',
15
+ props: [
16
+ { name: 'children', type: 'ReactNode', required: true, description: 'Label content for the primary action button.' },
17
+ { name: 'onClick', type: 'function', required: true, description: 'Handler fired when the primary (left) half is clicked.' },
18
+ { name: 'menuItems', type: 'array', required: true, description: 'Array of { label, onSelect, disabled?, danger?, icon? } for the dropdown.' },
19
+ { name: 'variant', type: 'enum', required: false, default: 'primary', description: 'Visual variant applied to both halves.', enumValues: ['primary', 'secondary', 'outline', 'ghost', 'danger', 'danger-outline', 'danger-ghost'] },
20
+ { name: 'size', type: 'enum', required: false, default: 'md', description: 'Size applied to both halves.', enumValues: ['2xs', 'xs', 'sm', 'md', 'lg'] },
21
+ { name: 'bordered', type: 'boolean', required: false, default: 'true', description: 'If false, removes the border on both halves.' },
22
+ { name: 'menuPlacement', type: 'enum', required: false, default: 'bottom-end', description: 'Dropdown placement relative to the chevron trigger.', enumValues: ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'right'] },
23
+ { name: 'disabled', type: 'boolean', required: false, description: 'Disables both halves.' },
24
+ { name: 'loading', type: 'boolean', required: false, default: 'false', description: 'Shows a spinner in the primary half and disables both.' },
25
+ { name: 'leftIcon', type: 'ReactNode', required: false, description: 'Icon rendered before the label in the primary half.' },
26
+ { name: 'style', type: 'object', required: false, description: 'Style overrides for the wrapper div.' },
27
+ ],
28
+ usageExamples: [
29
+ {
30
+ title: 'Basic',
31
+ code: `<SplitButton onClick={handleSave} menuItems={[{ label: 'Save as draft', onSelect: handleDraft }]}>Save</SplitButton>`,
32
+ },
33
+ {
34
+ title: 'Outline with leftIcon',
35
+ code: `<SplitButton variant="outline" leftIcon={<Icon name="deploy" />} onClick={deploy} menuItems={[{ label: 'Deploy to staging', onSelect: deployStaging }, { label: 'Rollback', onSelect: rollback, danger: true }]}>Deploy</SplitButton>`,
36
+ },
37
+ {
38
+ title: 'Menu items with icons',
39
+ code: `<SplitButton onClick={handleSave} menuItems={[{ label: 'Save as draft', onSelect: handleDraft, icon: <Icon name="file" /> }, { label: 'Export', onSelect: handleExport, icon: <Icon name="download" /> }]}>Save</SplitButton>`,
40
+ },
41
+ {
42
+ title: 'Small ghost bordered (issue #90 use case)',
43
+ code: `<SplitButton variant="ghost" size="2xs" bordered onClick={toggle} menuItems={[{ label: 'Reset layout', onSelect: reset }]}>Collapse all</SplitButton>`,
44
+ },
45
+ {
46
+ title: 'Danger outline with disabled item',
47
+ code: `<SplitButton variant="danger-outline" onClick={handleDelete} menuItems={[{ label: 'Move to trash', onSelect: trash }, { label: 'Delete forever', onSelect: nuke, danger: true }, { label: 'Archive (unavailable)', onSelect: archive, disabled: true }]}>Delete</SplitButton>`,
48
+ },
49
+ {
50
+ title: 'Disabled',
51
+ code: `<SplitButton disabled onClick={handleSave} menuItems={[{ label: 'Option', onSelect: fn }]}>Save</SplitButton>`,
52
+ },
53
+ {
54
+ title: 'Loading',
55
+ code: `<SplitButton loading onClick={handleSave} menuItems={[{ label: 'Option', onSelect: fn }]}>Saving...</SplitButton>`,
56
+ },
57
+ ],
58
+ compositionGraph: [
59
+ { componentId: 'menu', componentName: 'Menu', role: 'Dropdown container for secondary actions', required: true },
60
+ { componentId: 'menu-item', componentName: 'MenuItem', role: 'Individual dropdown action', required: true },
61
+ ],
62
+ accessibility: {
63
+ role: 'group',
64
+ ariaAttributes: ['aria-label', 'aria-haspopup', 'aria-expanded', 'aria-busy'],
65
+ keyboardInteractions: [
66
+ 'Enter/Space on primary button fires onClick',
67
+ 'Enter/Space/ArrowDown on chevron opens dropdown',
68
+ 'ArrowDown/ArrowUp cycles dropdown items',
69
+ 'Home/End jumps to first/last item',
70
+ 'Escape closes dropdown and returns focus to chevron',
71
+ 'Tab closes dropdown',
72
+ ],
73
+ notes: 'The wrapper uses role="group" with aria-label derived from children. The chevron button carries aria-haspopup="menu" and aria-expanded via Menu. Primary button has aria-busy when loading.',
74
+ },
75
+ };
@@ -7,11 +7,12 @@ export const ButtonManifest = {
7
7
  description: 'A clickable control that triggers an action. The primary interactive primitive in Lucent UI.',
8
8
  designIntent: 'Buttons communicate available actions. Variant conveys hierarchy: use "primary" for the ' +
9
9
  'single most important action in a view, "secondary" for supporting actions, "ghost" for ' +
10
- 'low-emphasis actions in dense UIs, "outline" for bordered buttons with no fill, and "danger" exclusively for destructive or irreversible ' +
10
+ 'low-emphasis actions in dense UIs, "outline" for bordered buttons with transparent background, and "danger" exclusively for destructive or irreversible ' +
11
11
  'operations. Use "danger-ghost" for low-emphasis destructive actions (red text, no fill) and ' +
12
- '"danger-outline" for bordered destructive buttons. Size should match surrounding content density — prefer "md" as the default, ' +
12
+ '"danger-outline" for bordered destructive buttons (also transparent background). Size should match surrounding content density — prefer "md" as the default, ' +
13
13
  '"sm" for toolbars or tables, "xs" for compact UIs like customizer panels, and "2xs" for ' +
14
- 'ultra-dense inline controls (~22px height) such as table-inline actions or toolbar icon triggers.',
14
+ 'ultra-dense inline controls (~22px height) such as table-inline actions or toolbar icon triggers. ' +
15
+ 'Icon-only buttons (leftIcon/rightIcon without children) automatically render as square with aspect-ratio: 1.',
15
16
  props: [
16
17
  {
17
18
  name: 'variant',
@@ -44,8 +45,8 @@ export const ButtonManifest = {
44
45
  {
45
46
  name: 'children',
46
47
  type: 'ReactNode',
47
- required: true,
48
- description: 'Button label or content.',
48
+ required: false,
49
+ description: 'Button label or content. Omit for icon-only buttons (provide leftIcon or rightIcon instead).',
49
50
  },
50
51
  {
51
52
  name: 'disabled',
@@ -161,6 +162,11 @@ export const ButtonManifest = {
161
162
  title: 'Dense inline action',
162
163
  code: `<Button variant="ghost" size="2xs" leftIcon={<RefreshIcon />}>Retry</Button>`,
163
164
  },
165
+ {
166
+ title: 'Icon-only (square)',
167
+ code: `<Button variant="outline" size="2xs" leftIcon={<CloseIcon />} aria-label="Close" />`,
168
+ description: 'Omitting children auto-sizes the button as a square via aspect-ratio: 1.',
169
+ },
164
170
  ],
165
171
  compositionGraph: [],
166
172
  accessibility: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucent-ui",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "An AI-first React component library with machine-readable manifests.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",