@cueplusplus/ui 0.5.0 → 0.7.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 (69) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/chrome/_drag.js +59 -0
  3. package/dist/chrome/_status-bar-item.js +85 -0
  4. package/dist/chrome/_status-bar.d.ts +15 -0
  5. package/dist/chrome/_status-bar.js +171 -0
  6. package/dist/chrome/app-shell.d.ts +69 -1
  7. package/dist/chrome/app-shell.js +38 -11
  8. package/dist/chrome/index.d.ts +3 -2
  9. package/dist/chrome/status-bar.d.ts +87 -4
  10. package/dist/chrome/status-bar.js +80 -16
  11. package/dist/chrome/title-bar.d.ts +11 -1
  12. package/dist/chrome/title-bar.js +1 -3
  13. package/dist/date/_segments.js +15 -4
  14. package/dist/forms/_chassis.d.ts +104 -4
  15. package/dist/forms/_chassis.js +104 -4
  16. package/dist/forms/input-group.d.ts +8 -3
  17. package/dist/forms/input-group.js +8 -3
  18. package/dist/forms/number-field.d.ts +3 -2
  19. package/dist/forms/number-field.js +3 -2
  20. package/dist/index.d.ts +8 -3
  21. package/dist/index.js +5 -1
  22. package/dist/instruments/_data-row.d.ts +68 -0
  23. package/dist/instruments/_data-row.js +139 -0
  24. package/dist/instruments/data-row.d.ts +94 -0
  25. package/dist/instruments/data-row.js +151 -0
  26. package/dist/instruments/data-tree.d.ts +126 -0
  27. package/dist/instruments/data-tree.js +303 -0
  28. package/dist/instruments/index.d.ts +6 -2
  29. package/dist/instruments/index.js +5 -1
  30. package/dist/instruments/ledger.d.ts +105 -0
  31. package/dist/instruments/ledger.js +114 -0
  32. package/dist/instruments/table.d.ts +108 -2
  33. package/dist/instruments/table.js +165 -39
  34. package/dist/theming/_presets.js +3 -3
  35. package/manifest/components/app-shell.json +45 -4
  36. package/manifest/components/autocomplete.json +2 -0
  37. package/manifest/components/color-field.json +2 -0
  38. package/manifest/components/color-picker.json +2 -0
  39. package/manifest/components/combobox.json +2 -0
  40. package/manifest/components/composer.json +2 -0
  41. package/manifest/components/data-row.json +208 -0
  42. package/manifest/components/data-tree.json +151 -0
  43. package/manifest/components/date-field.json +2 -0
  44. package/manifest/components/date-picker.json +2 -0
  45. package/manifest/components/date-range-picker.json +2 -0
  46. package/manifest/components/env-var-input.json +2 -0
  47. package/manifest/components/input-group.json +4 -2
  48. package/manifest/components/input.json +3 -1
  49. package/manifest/components/ledger.json +187 -0
  50. package/manifest/components/multi-select.json +2 -0
  51. package/manifest/components/musical-time-input.json +2 -0
  52. package/manifest/components/number-field.json +3 -1
  53. package/manifest/components/otp-field.json +2 -0
  54. package/manifest/components/password-input.json +2 -0
  55. package/manifest/components/scrub-input.json +2 -0
  56. package/manifest/components/search-input.json +2 -0
  57. package/manifest/components/select.json +1 -0
  58. package/manifest/components/status-bar.json +146 -12
  59. package/manifest/components/table-scroll-region.json +6 -0
  60. package/manifest/components/table.json +113 -5
  61. package/manifest/components/tags-input.json +2 -0
  62. package/manifest/components/textarea.json +3 -1
  63. package/manifest/components/time-field.json +2 -0
  64. package/manifest/components/title-bar.json +1 -1
  65. package/manifest/components/toggle-group.json +2 -0
  66. package/manifest/components/toggle.json +2 -0
  67. package/manifest/manifest.json +72 -33
  68. package/manifest/tokens.json +1 -1
  69. package/package.json +2 -2
@@ -0,0 +1,187 @@
1
+ {
2
+ "name": "Ledger",
3
+ "slug": "ledger",
4
+ "group": "instruments",
5
+ "importPath": "@cueplusplus/ui",
6
+ "peerDependencies": [],
7
+ "clientOnly": false,
8
+ "description": "A grouped run of dense rows under sticky headings: the ledger idiom.\n\nThe flattest of the three surfaces this package ships over one row model, and\nthe one that does the least: headings, slugs, rows. **Nothing here collapses.**\nA ledger with a disclosure on it would be a tree with worse semantics, so the\ncomponent emits no `aria-expanded` anywhere, and a test holds it to that —\ncollapse belongs to `DataTree`, and grouping-with-columns belongs to a\ngrouped `Table`.\n\nWhat it does own is the document outline. Tiers and groups are real `<h2>` /\n`<h3>` elements at caller-chosen levels, because in a list of two hundred\nrows the heading list *is* how a screen-reader user navigates.\n\nStatic markup — no `\"use client\"`. The rows inside it may be anything;\n`DataRow` is the intended tenant.",
9
+ "props": [],
10
+ "typeReferences": [],
11
+ "subcomponents": [
12
+ "Root",
13
+ "Tier",
14
+ "Group",
15
+ "Empty"
16
+ ],
17
+ "parts": [
18
+ {
19
+ "name": "Root",
20
+ "usage": "Ledger.Root",
21
+ "description": "The ledger surface: the type contract, and the container the columns shed\nagainst.\n\nDeliberately unframed. A ledger is a continuous document of rows, so the\nhairlines belong to the rows and there is no border, radius or card around\nany part of it — a per-group card would turn one list into a stack of little\ntables and break the top-to-bottom scan the whole idiom exists for.",
22
+ "props": [
23
+ {
24
+ "name": "containment",
25
+ "type": "boolean",
26
+ "required": false,
27
+ "defaultValue": "true",
28
+ "description": "Make the ledger a size container, so a caller's `@container` rules shed\ncolumns against the *pane* rather than the viewport. Defaults to `true`.\n\nThis is the difference between a list that sheds a column when the window\nnarrows and one that also sheds it when a detail drawer opens beside it and\ntakes half the pane — which is the same visual problem and, without a\ncontainer, an entirely separate piece of code. Switch it off only when an\nancestor is already the container the rules name; nesting containers moves\nwhich one a bare `@container` query resolves against."
29
+ }
30
+ ],
31
+ "typeReferences": [
32
+ "React.ComponentPropsWithoutRef<\"div\">"
33
+ ]
34
+ },
35
+ {
36
+ "name": "Tier",
37
+ "usage": "Ledger.Tier",
38
+ "description": "One named run of the ledger, with its own sticky heading.\n\nThe heading paints its own ground (`bg-bg`) because a sticky element with a\ntransparent background is a sticky element you can read rows straight through.",
39
+ "props": [
40
+ {
41
+ "name": "heading",
42
+ "type": "ReactNode",
43
+ "required": false,
44
+ "defaultValue": null,
45
+ "description": "The tier's name. Omitted, the tier draws no heading and reserves no space\nfor one — which is how a single unnamed run of rows uses the same component\nas a grouped one."
46
+ },
47
+ {
48
+ "name": "count",
49
+ "type": "number",
50
+ "required": false,
51
+ "defaultValue": null,
52
+ "description": "How many rows the tier holds, printed beside its name."
53
+ },
54
+ {
55
+ "name": "sticky",
56
+ "type": "boolean",
57
+ "required": false,
58
+ "defaultValue": "true",
59
+ "description": "Dock the heading to the top of the scroll container while the tier's rows\nscroll past. Defaults to `true`."
60
+ },
61
+ {
62
+ "name": "headingLevel",
63
+ "type": "4 | 2 | 3",
64
+ "required": false,
65
+ "defaultValue": "2",
66
+ "description": "Which heading element the tier's name is. Defaults to `2`.\n\nThe outline is the navigation: a screen-reader user moves through a long\nledger by heading, so the levels have to describe the real nesting of the\npage the ledger sits in, not the ledger's own idea of itself."
67
+ },
68
+ {
69
+ "name": "note",
70
+ "type": "ReactNode",
71
+ "required": false,
72
+ "defaultValue": null,
73
+ "description": "A qualifier printed after the name — a date, a source, a state."
74
+ }
75
+ ],
76
+ "typeReferences": [
77
+ "React.ComponentPropsWithoutRef<\"section\">"
78
+ ]
79
+ },
80
+ {
81
+ "name": "Group",
82
+ "usage": "Ledger.Group",
83
+ "description": "A run of rows under a slug in the left gutter.\n\nThe gutter is a grid track whose width is the custom property\n`--cue-ledger-gutter` (default `7rem`), so a `@container` rule can narrow it\nat the same breakpoint it sheds a column, without this component knowing\nanything about the caller's breakpoints.\n\nThe slug is `self-start`, which is the load-bearing half of making it stick:\na stretched grid item is exactly as tall as the group, and an element as tall\nas its own scroll range has nowhere to slide to.",
84
+ "props": [
85
+ {
86
+ "name": "label",
87
+ "type": "ReactNode",
88
+ "required": false,
89
+ "defaultValue": null,
90
+ "description": "The group's slug, printed in the left gutter.\n\nOmitted, the group is a plain block: no gutter, no heading, no indent. A\ngrouped list that has one unnamed run in it should not pay a 7rem empty\ncolumn for it, and an empty gutter reads as a missing label rather than as\nan absent one."
91
+ },
92
+ {
93
+ "name": "headingLevel",
94
+ "type": "4 | 3",
95
+ "required": false,
96
+ "defaultValue": "3",
97
+ "description": "Which heading element the slug is. Defaults to `3`."
98
+ },
99
+ {
100
+ "name": "sticky",
101
+ "type": "boolean",
102
+ "required": false,
103
+ "defaultValue": "true",
104
+ "description": "Dock the slug under the tier heading while the group's rows scroll past.\nDefaults to `true`.\n\nThe offset is a custom property, `--cue-ledger-slug-top` (default `2.5rem`):\nhow far down the slug docks depends on how tall the tier heading above it\nrenders, which is a function of the density and the type the *caller*\nconfigured. Set it on the ledger — `className=\"[--cue-ledger-slug-top:2rem]\"` —\nrather than measuring at runtime."
105
+ }
106
+ ],
107
+ "typeReferences": [
108
+ "React.ComponentPropsWithoutRef<\"div\">"
109
+ ]
110
+ },
111
+ {
112
+ "name": "Empty",
113
+ "usage": "Ledger.Empty",
114
+ "description": "The line that stands where a group's rows would be.\n\nA slot rather than an `empty` prop on the group, because there are two\ndifferent emptinesses and only the caller knows which one this is: a place\nsomebody made that has nothing in it yet (\"Nothing yet.\") and a filter that\nemptied a place that does have things in it (\"Nothing matches …\"). A prop\nwould have to guess, and guessing conflates them — which is precisely the bug\nthis slot exists to make impossible. For a whole surface with nothing in it\nand no heading left to keep, use `EmptyState` instead.",
115
+ "props": [],
116
+ "typeReferences": [
117
+ "React.ComponentPropsWithoutRef<\"p\">"
118
+ ]
119
+ }
120
+ ],
121
+ "variants": {},
122
+ "defaultVariants": {},
123
+ "tokensUsed": [
124
+ "--cue-bg",
125
+ "--cue-data-row-cols",
126
+ "--cue-fg",
127
+ "--cue-fg-subtle",
128
+ "--cue-font-mono",
129
+ "--cue-ledger-gutter",
130
+ "--cue-ledger-slug-top",
131
+ "--cue-pad-row-x",
132
+ "--cue-pad-row-y",
133
+ "--cue-space-2",
134
+ "--cue-space-3",
135
+ "--cue-text-label",
136
+ "--cue-text-ui"
137
+ ],
138
+ "summary": "Grouped dense rows under sticky tier headings and gutter slugs, with no disclosure anywhere.",
139
+ "examples": [
140
+ {
141
+ "title": "Tiers, groups, rows",
142
+ "code": "<Ledger.Root className=\"[--cue-data-row-cols:1fr_8rem_auto] [--cue-ledger-slug-top:1.75rem]\">\n <Ledger.Tier heading=\"Patch\" count={5} note=\"polled 18:04\">\n <Ledger.Group label=\"inputs\">\n {inputs.map((device) => (\n <DataRow.Root key={device.id} interactive onActivate={() => open(device)}>\n …\n </DataRow.Root>\n ))}\n </Ledger.Group>\n <Ledger.Group label=\"spare\">\n <Ledger.Empty>Nothing yet.</Ledger.Empty>\n </Ledger.Group>\n </Ledger.Tier>\n</Ledger.Root>",
143
+ "language": "tsx"
144
+ },
145
+ {
146
+ "title": "One unnamed group",
147
+ "code": "<Ledger.Tier>\n <Ledger.Group>\n <DataRow.Root>…</DataRow.Root>\n </Ledger.Group>\n</Ledger.Tier>",
148
+ "language": "tsx"
149
+ },
150
+ {
151
+ "title": "Usage",
152
+ "code": "<Ledger.Root className=\"[--cue-data-row-cols:1fr_8rem_auto]\">\n <Ledger.Tier heading=\"Ours\" count={12}>\n <Ledger.Group label=\"released\">\n <DataRow.Root interactive onActivate={open}>…</DataRow.Root>\n </Ledger.Group>\n <Ledger.Group label=\"lab\">\n <Ledger.Empty>Nothing yet.</Ledger.Empty>\n </Ledger.Group>\n </Ledger.Tier>\n</Ledger.Root>",
153
+ "language": "tsx"
154
+ }
155
+ ],
156
+ "status": "stable",
157
+ "url": "/docs/components/ledger",
158
+ "mdUrl": "/docs/components/ledger.md",
159
+ "jsonUrl": "/r/components/ledger.json",
160
+ "whenToUse": [
161
+ "A long list that is grouped but never folded: a corpus by source, a patch by direction, a run log by day.",
162
+ "A surface whose groups should be reachable by heading, since the tiers and slugs are real `h2`/`h3` elements."
163
+ ],
164
+ "whenNotToUse": [
165
+ "A hierarchy the reader folds. Use `DataTree`, which owns collapse and the tree semantics that go with it.",
166
+ "Rows meant to be compared down a named column. Use a grouped `Table`, where the columns are named once in a `thead`.",
167
+ "A handful of rows in a panel. Use `Row` inside `Panel`; a tier heading over three rows is furniture with nothing to organise."
168
+ ],
169
+ "commonMistakes": [
170
+ "Adding a toggle to a tier. A ledger that folds is a tree with worse semantics — the component emits no `aria-expanded`, and a test holds it to that.",
171
+ "Framing each group in a `Panel`. A card per group turns one list into a stack of little tables and breaks the top-to-bottom scan.",
172
+ "Leaving `--cue-ledger-slug-top` at its default after changing the density or the heading type, which docks the slug over the tier heading instead of under it.",
173
+ "Passing an `empty` string as a prop. `Ledger.Empty` takes the words because \"this place is empty\" and \"your filter emptied it\" are different sentences."
174
+ ],
175
+ "specimens": [
176
+ {
177
+ "title": "Ledger",
178
+ "group": "instruments",
179
+ "components": [
180
+ "Ledger"
181
+ ],
182
+ "code": "<Panel className=\"w-full\">\n <div className=\"max-h-[18rem] overflow-y-auto\">\n <Ledger.Root className={PATCH_TRACKS}>\n <Ledger.Tier heading=\"Patch\" count={PATCH_DEVICES.length} note=\"polled 18:04\">\n {PATCH_GROUPS.map((group) => (\n <Ledger.Group key={group.id} label={group.label}>\n {group.rows.map((device) => (\n <DataRow.Root key={device.id} interactive>\n <PatchRowSlots device={device} />\n </DataRow.Root>\n ))}\n </Ledger.Group>\n ))}\n <Ledger.Group label=\"spare\">\n <Ledger.Empty>Nothing yet.</Ledger.Empty>\n </Ledger.Group>\n </Ledger.Tier>\n </Ledger.Root>\n </div>\n</Panel>",
183
+ "note": "The same rows as tiers and slugs: sticky headings, a gutter for the group name, and no disclosure anywhere — the ledger does not collapse.",
184
+ "interaction": "the tier heading and the group slug dock as the rows scroll under them; scroll the pane to see it."
185
+ }
186
+ ]
187
+ }
@@ -137,6 +137,7 @@
137
137
  "--cue-accent-soft",
138
138
  "--cue-border",
139
139
  "--cue-border-overlay",
140
+ "--cue-border-strong",
140
141
  "--cue-chip-h",
141
142
  "--cue-control-lg",
142
143
  "--cue-control-md",
@@ -145,6 +146,7 @@
145
146
  "--cue-fg",
146
147
  "--cue-fg-muted",
147
148
  "--cue-fg-subtle",
149
+ "--cue-focus",
148
150
  "--cue-font-mono",
149
151
  "--cue-icon-sm",
150
152
  "--cue-radius-control",
@@ -67,12 +67,14 @@
67
67
  "tokensUsed": [
68
68
  "--cue-accent",
69
69
  "--cue-border",
70
+ "--cue-border-strong",
70
71
  "--cue-control-lg",
71
72
  "--cue-control-md",
72
73
  "--cue-control-sm",
73
74
  "--cue-danger",
74
75
  "--cue-fg",
75
76
  "--cue-fg-subtle",
77
+ "--cue-focus",
76
78
  "--cue-font-mono",
77
79
  "--cue-radius-control",
78
80
  "--cue-space-2",
@@ -5,7 +5,7 @@
5
5
  "importPath": "@cueplusplus/ui",
6
6
  "peerDependencies": [],
7
7
  "clientOnly": true,
8
- "description": "A numeric control with explicit − / + affordances and no native spinners.\n\nNative `<input type=\"number\">` spinners are unusable at console density — a\npair of 4px arrows that only appear on hover — so this is a text input with\n`inputMode=\"numeric\"` flanked by real buttons, which is also what makes it\ntouch-usable. Arrow keys step, Shift steps by `largeStep`, Alt by `smallStep`,\nand values typed out of range are clamped to `min`/`max` on blur.\n\nThe group owns the rim; the input and both buttons render bare inside it, so\nthe whole cluster reads as one field and focus feedback moves to\n`focus-within`.",
8
+ "description": "A numeric control with explicit − / + affordances and no native spinners.\n\nNative `<input type=\"number\">` spinners are unusable at console density — a\npair of 4px arrows that only appear on hover — so this is a text input with\n`inputMode=\"numeric\"` flanked by real buttons, which is also what makes it\ntouch-usable. Arrow keys step, Shift steps by `largeStep`, Alt by `smallStep`,\nand values typed out of range are clamped to `min`/`max` on blur.\n\nThe group owns the rim; the input and both buttons render bare inside it, so\nthe whole cluster reads as one field. Focus feedback follows the group's\nsplit: the rim strengthens on `:focus-within`, and the ring waits for\n`:has(:focus-visible)` so a click on a stepper does not paint one.",
9
9
  "props": [
10
10
  {
11
11
  "name": "className",
@@ -60,6 +60,7 @@
60
60
  "tokensUsed": [
61
61
  "--cue-accent",
62
62
  "--cue-border",
63
+ "--cue-border-strong",
63
64
  "--cue-control-lg",
64
65
  "--cue-control-md",
65
66
  "--cue-control-sm",
@@ -67,6 +68,7 @@
67
68
  "--cue-fg",
68
69
  "--cue-fg-muted",
69
70
  "--cue-fg-subtle",
71
+ "--cue-focus",
70
72
  "--cue-font-mono",
71
73
  "--cue-radius-control",
72
74
  "--cue-space-2",
@@ -81,12 +81,14 @@
81
81
  "tokensUsed": [
82
82
  "--cue-accent",
83
83
  "--cue-border",
84
+ "--cue-border-strong",
84
85
  "--cue-control-lg",
85
86
  "--cue-control-md",
86
87
  "--cue-control-sm",
87
88
  "--cue-danger",
88
89
  "--cue-fg",
89
90
  "--cue-fg-subtle",
91
+ "--cue-focus",
90
92
  "--cue-font-mono",
91
93
  "--cue-radius-control",
92
94
  "--cue-space-1",
@@ -74,12 +74,14 @@
74
74
  "tokensUsed": [
75
75
  "--cue-accent",
76
76
  "--cue-border",
77
+ "--cue-border-strong",
77
78
  "--cue-control-lg",
78
79
  "--cue-control-md",
79
80
  "--cue-control-sm",
80
81
  "--cue-danger",
81
82
  "--cue-fg",
82
83
  "--cue-fg-subtle",
84
+ "--cue-focus",
83
85
  "--cue-font-mono",
84
86
  "--cue-icon-md",
85
87
  "--cue-radius-control",
@@ -144,12 +144,14 @@
144
144
  "tokensUsed": [
145
145
  "--cue-accent",
146
146
  "--cue-border",
147
+ "--cue-border-strong",
147
148
  "--cue-control-lg",
148
149
  "--cue-control-md",
149
150
  "--cue-control-sm",
150
151
  "--cue-danger",
151
152
  "--cue-fg",
152
153
  "--cue-fg-subtle",
154
+ "--cue-focus",
153
155
  "--cue-font-mono",
154
156
  "--cue-radius-control",
155
157
  "--cue-space-2",
@@ -88,12 +88,14 @@
88
88
  "tokensUsed": [
89
89
  "--cue-accent",
90
90
  "--cue-border",
91
+ "--cue-border-strong",
91
92
  "--cue-control-lg",
92
93
  "--cue-control-md",
93
94
  "--cue-control-sm",
94
95
  "--cue-danger",
95
96
  "--cue-fg",
96
97
  "--cue-fg-subtle",
98
+ "--cue-focus",
97
99
  "--cue-font-mono",
98
100
  "--cue-icon-md",
99
101
  "--cue-icon-sm",
@@ -138,6 +138,7 @@
138
138
  "--cue-fg",
139
139
  "--cue-fg-muted",
140
140
  "--cue-fg-subtle",
141
+ "--cue-focus",
141
142
  "--cue-font-mono",
142
143
  "--cue-icon-md",
143
144
  "--cue-icon-sm",
@@ -5,11 +5,100 @@
5
5
  "importPath": "@cueplusplus/ui",
6
6
  "peerDependencies": [],
7
7
  "clientOnly": false,
8
- "description": "The quiet strip along the bottom of a window.\n\nThe smallest type in the system in the subtlest ink: a status bar is read\nwhen you go looking for it and ignored otherwise, so it must never compete\nwith the work above it. Height comes from `--cue-chrome-statusbar`.\n\nNot a live region — a status bar changes constantly (frame counters, DMX\noutput), and announcing every change would make the app unusable with a\nscreen reader. Put anything that genuinely must be announced in a `Toast`.\n\nStatic markup — no `\"use client\"`.",
8
+ "description": "The quiet strip along the bottom of a window.\n\nThe smallest type in the system in the subtlest ink: a status bar is read\nwhen you go looking for it and ignored otherwise, so it must never compete\nwith the work above it. Height comes from `--cue-chrome-statusbar`, and the\nbar clips rather than wrapping — one rung of the frame, always, whatever it\nis asked to hold.\n\nItems come in two kinds and the difference is deliberate. A plain\n`StatusBar.Item` is a **reading**: a `<div>`, inert by construction, with no\nhover, no focus and nothing to click — which is what most of a status bar is.\n`interactive` (or a `render`) makes one a **control**, and only then does it\ngrow the button chassis, the full-height hover fill and the inset focus ring.\n`StatusBar.Group` fuses adjacent items into one segmented control.\n\nNot a live region — a status bar changes constantly (frame counters, DMX\noutput), and announcing every change would make the app unusable with a\nscreen reader. Put anything that genuinely must be announced in a `Toast`.\n\nStatic markup — no `\"use client\"`. A bar of readings renders on the server\nand ships no JavaScript at all; the interactive item is the one part that\ncrosses the boundary, and it crosses it alone (`./_status-bar-item.tsx`).",
9
9
  "props": [],
10
- "typeReferences": [],
11
- "subcomponents": [],
12
- "parts": [],
10
+ "typeReferences": [
11
+ "React.ComponentPropsWithoutRef<\"div\">"
12
+ ],
13
+ "subcomponents": [
14
+ "Item",
15
+ "Group"
16
+ ],
17
+ "parts": [
18
+ {
19
+ "name": "Item",
20
+ "usage": "StatusBar.Item",
21
+ "description": "One reading in the bar: a mode, a count, a connection state — or, given\n`interactive`, one control in it.",
22
+ "props": [
23
+ {
24
+ "name": "tone",
25
+ "type": "\"accent\" | \"danger\" | \"busy\" | \"neutral\" | \"ok\" | \"warn\" | \"info\"",
26
+ "required": false,
27
+ "defaultValue": "neutral",
28
+ "description": "Ink for this item. Defaults to the bar's own subtle grey."
29
+ },
30
+ {
31
+ "name": "push",
32
+ "type": "boolean",
33
+ "required": false,
34
+ "defaultValue": "false",
35
+ "description": "Start the trailing cluster here: pushes this item and everything after it\nto the far edge. Cheaper and more robust than a spacer element, which a\ncaller has to remember to keep in the right place."
36
+ },
37
+ {
38
+ "name": "icon",
39
+ "type": "ElementType<any, keyof IntrinsicElements>",
40
+ "required": false,
41
+ "defaultValue": null,
42
+ "description": "Optional leading icon (e.g. a lucide icon), drawn `aria-hidden`."
43
+ },
44
+ {
45
+ "name": "interactive",
46
+ "type": "boolean",
47
+ "required": false,
48
+ "defaultValue": "false",
49
+ "description": "Make the item a real control: a `<button type=\"button\">` with hover, press\nand keyboard-focus feedback. Defaults to `false`.\n\nA native button, not a `<div>` with an `onClick` — keyboard order is DOM\norder, `Enter` and `Space` work because the platform makes them work, and\nassistive technology is told what the thing is by the element rather than\nby a role attribute that has to be kept honest by hand.\n\n**An icon-only interactive item needs an `aria-label`.** There is no text\nfor a screen reader to read and the icon is drawn `aria-hidden`, so without\none the control announces as \"button\" and nothing else."
50
+ },
51
+ {
52
+ "name": "prominence",
53
+ "type": "\"quiet\" | \"filled\"",
54
+ "required": false,
55
+ "defaultValue": "quiet",
56
+ "description": "How loudly the item states itself. Defaults to `\"quiet\"`.\n\n`quiet` is a reading: tone ink on the bar's own ground. `filled` is a chip\n— a full-height tone wash behind tone ink — for the small set of states\nthat earn a colour of their own: a warning, an error, the remote you are\nattached to. It is loud deliberately, and a bar of five filled items is a\nbar nobody reads."
57
+ },
58
+ {
59
+ "name": "render",
60
+ "type": "RenderProp<Record<string, unknown>>",
61
+ "required": false,
62
+ "defaultValue": null,
63
+ "description": "Replace the rendered element (Base UI render prop) — `render={<a href=\"…\" />}`\nfor a status item that navigates.\n\nImplies {@link StatusBarItemProps.interactive}: whatever is rendered gets\nthe control chassis, because an element a reader can click has to look like\none whether it is a button or a link."
64
+ },
65
+ {
66
+ "name": "disabled",
67
+ "type": "boolean",
68
+ "required": false,
69
+ "defaultValue": null,
70
+ "description": "Grey the control out, take it out of the pointer's reach and announce it as\ndisabled. Defaults to `false`. Ignored by a static item, which is a reading\nand not a control.\n\nHonoured whatever {@link StatusBarItemProps.render} renders, but by two\nroutes: the `disabled` attribute where it means something — the default\n`<button>`, a form control, a component that takes a `disabled` prop of its\nown — and `aria-disabled` plus `data-disabled` everywhere, which is all an\nanchor gets. There is no such thing as a disabled link; a link that should\nnot be followed should not be a link, and this states the intent rather\nthan pretending to enforce it."
71
+ }
72
+ ],
73
+ "typeReferences": [
74
+ "React.ComponentPropsWithoutRef<\"div\">"
75
+ ]
76
+ },
77
+ {
78
+ "name": "Group",
79
+ "usage": "StatusBar.Group",
80
+ "description": "Items that belong to each other, one pixel apart.\n\nThe bar spaces its readings at `--cue-space-4` because they are unrelated\nfacts. A branch name and its ahead/behind count are not unrelated facts; they\nare one control that happens to have two halves, and the gap between them is\nwhat says so. This is VS Code's `compact` mechanic — the seam collapses, and\nhovering either half grounds both while the half under the cursor goes one\nrung further — expressed as a wrapper rather than as an id-reference between\ntwo items, because a design system's items do not have ids to point at.\n\nA `role=\"group\"` rather than nothing: the members are announced as a set, and\nthe DOM order they are announced in is the order they are read in. Give it an\n`aria-label` when the members only make sense together.",
81
+ "props": [
82
+ {
83
+ "name": "push",
84
+ "type": "boolean",
85
+ "required": false,
86
+ "defaultValue": "false",
87
+ "description": "Start the trailing cluster at this group: pushes it and everything after it\nto the far edge, exactly as `push` does on a single item."
88
+ },
89
+ {
90
+ "name": "aria-label",
91
+ "type": "string",
92
+ "required": false,
93
+ "defaultValue": null,
94
+ "description": "What the cluster is called. Optional, and worth giving whenever the members\nonly make sense together — \"Repository\", \"Connection\" — because a `group`\nwith no name is a landmark a screen reader reads straight past."
95
+ }
96
+ ],
97
+ "typeReferences": [
98
+ "React.ComponentPropsWithoutRef<\"div\">"
99
+ ]
100
+ }
101
+ ],
13
102
  "variants": {},
14
103
  "defaultVariants": {},
15
104
  "tokensUsed": [
@@ -18,6 +107,7 @@
18
107
  "--cue-busy",
19
108
  "--cue-chrome-statusbar",
20
109
  "--cue-danger",
110
+ "--cue-fg",
21
111
  "--cue-fg-subtle",
22
112
  "--cue-font-mono",
23
113
  "--cue-icon-sm",
@@ -25,23 +115,68 @@
25
115
  "--cue-ok",
26
116
  "--cue-pad-row-x",
27
117
  "--cue-space-2",
118
+ "--cue-space-3",
28
119
  "--cue-space-4",
29
120
  "--cue-surface-1",
121
+ "--cue-surface-2",
122
+ "--cue-surface-3",
30
123
  "--cue-text-label",
31
124
  "--cue-warn"
32
125
  ],
33
- "summary": "The quiet strip along the bottom of a window.",
126
+ "summary": "The quiet strip along the bottom of a window: readings by default, controls where the reading is worth clicking.",
34
127
  "examples": [
35
128
  {
36
- "title": "Usage",
129
+ "title": "Readings and controls",
37
130
  "code": "<StatusBar>\n <StatusBar.Item icon={Plug} tone=\"ok\">Art-Net connected</StatusBar.Item>\n <StatusBar.Item push>512 channels</StatusBar.Item>\n</StatusBar>",
38
131
  "language": "tsx"
132
+ },
133
+ {
134
+ "title": "Readings and controls",
135
+ "code": "<StatusBar.Item interactive icon={GitBranch} onClick={openBranchPicker}>act-2</StatusBar.Item>\n<StatusBar.Item render={<a href=\"/runs/42\" />}>run 42</StatusBar.Item>",
136
+ "language": "tsx"
137
+ },
138
+ {
139
+ "title": "`push`, and the two clusters",
140
+ "code": "<StatusBar>\n <StatusBar.Item>universe 1</StatusBar.Item>\n <StatusBar.Item tone=\"ok\">44 Hz</StatusBar.Item>\n <StatusBar.Item push>10.0.0.42</StatusBar.Item>\n <StatusBar.Item tone=\"info\">art-net</StatusBar.Item>\n</StatusBar>",
141
+ "language": "tsx"
142
+ },
143
+ {
144
+ "title": "Groups: items that belong to each other",
145
+ "code": "<StatusBar.Group aria-label=\"Show file\">\n <StatusBar.Item interactive icon={GitBranch}>act-2</StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>2</StatusBar.Item>\n</StatusBar.Group>",
146
+ "language": "tsx"
147
+ },
148
+ {
149
+ "title": "`prominence`, and how rarely to reach for it",
150
+ "code": "<StatusBar.Item prominence=\"filled\" tone=\"danger\" icon={TriangleAlert}>3 faults</StatusBar.Item>",
151
+ "language": "tsx"
152
+ },
153
+ {
154
+ "title": "Usage",
155
+ "code": "<StatusBar>\n <StatusBar.Item icon={Plug} tone=\"ok\">Art-Net connected</StatusBar.Item>\n <StatusBar.Item push>512 channels</StatusBar.Item>\n</StatusBar>\n// The VS Code shape: a clickable cluster left, a loud state right.\n<StatusBar>\n <StatusBar.Group aria-label=\"Repository\">\n <StatusBar.Item interactive icon={GitBranch}>main</StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>2</StatusBar.Item>\n </StatusBar.Group>\n <StatusBar.Item push interactive prominence=\"filled\" tone=\"danger\">3 faults</StatusBar.Item>\n</StatusBar>",
156
+ "language": "tsx"
39
157
  }
40
158
  ],
41
- "status": "todo-docs",
159
+ "status": "stable",
42
160
  "url": "/docs/components/status-bar",
43
161
  "mdUrl": "/docs/components/status-bar.md",
44
162
  "jsonUrl": "/r/components/status-bar.json",
163
+ "whenToUse": [
164
+ "Ambient state a reader goes looking for rather than is interrupted by: a connection, a frame rate, a cursor position, a branch name.",
165
+ "A state that is also a destination. `interactive` makes an item a real button, `render={<a href=\"…\" />}` makes it a link, and `StatusBar.Group` fuses two of them into one control."
166
+ ],
167
+ "whenNotToUse": [
168
+ "Anything that must be noticed. A status bar is deliberately the quietest thing on screen and nothing in it is announced — urgent messages are `Toast`'s job.",
169
+ "A row of actions. That is `Toolbar`, which is sized for controls and has the keyboard model to match.",
170
+ "The end of a document — an address, fine print, a sitemap. That is `Footer`, which scrolls away with the content; the bar is part of the frame and never leaves the screen."
171
+ ],
172
+ "commonMistakes": [
173
+ "Making every item interactive. Most of a status bar is a reading, and a bar where everything lights up under the pointer is a bar where nothing means anything.",
174
+ "Filling more than one or two items. `prominence=\"filled\"` is the loudest thing the bar can do; a bar of five filled chips is a bar nobody reads.",
175
+ "An icon-only interactive item with no `aria-label`. The icon is drawn `aria-hidden`, so the control announces as \"button\" and nothing else.",
176
+ "Wrapping related items in a plain `<div>` to close the gap. `StatusBar.Group` closes the seam *and* neutralises the interactive item's negative margin, which a bare div does not.",
177
+ "Reaching for `aria-live` to make a reading announce itself. The bar changes constantly, and a live region here makes the app unusable with a screen reader.",
178
+ "Fixing the height with `h-6` instead of letting `--cue-chrome-statusbar` set it, which stops tracking density."
179
+ ],
45
180
  "specimens": [
46
181
  {
47
182
  "title": "StatusBar",
@@ -49,10 +184,9 @@
49
184
  "components": [
50
185
  "StatusBar"
51
186
  ],
52
- "code": "<div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n <StatusBar>\n <StatusBar.Item>universe 1</StatusBar.Item>\n <StatusBar.Item tone=\"ok\">44 Hz</StatusBar.Item>\n <StatusBar.Item tone=\"warn\">2 conflicts</StatusBar.Item>\n <StatusBar.Item push>10.0.0.42</StatusBar.Item>\n <StatusBar.Item tone=\"info\">art-net</StatusBar.Item>\n </StatusBar>\n</div>",
53
- "note": "The bottom strip: tone-inked items, and a `push` item that takes the remaining space.",
54
- "interaction": "none by default; items are plain readouts."
187
+ "code": "<div className=\"flex w-full flex-col gap-(--cue-space-4)\">\n <div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n <StatusBar>\n <StatusBar.Item>universe 1</StatusBar.Item>\n <StatusBar.Item tone=\"ok\">44 Hz</StatusBar.Item>\n <StatusBar.Item tone=\"warn\">2 conflicts</StatusBar.Item>\n <StatusBar.Item push>10.0.0.42</StatusBar.Item>\n <StatusBar.Item tone=\"info\">art-net</StatusBar.Item>\n </StatusBar>\n </div>\n\n <div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n <StatusBar>\n <StatusBar.Group aria-label=\"Show file\">\n <StatusBar.Item interactive icon={GitBranch}>\n act-2\n </StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>\n 2\n </StatusBar.Item>\n </StatusBar.Group>\n <StatusBar.Item interactive tone=\"ok\">\n 44 Hz\n </StatusBar.Item>\n <StatusBar.Item interactive icon={Radio} aria-label=\"Art-Net output\" />\n <StatusBar.Item push interactive prominence=\"filled\" tone=\"warn\" icon={TriangleAlert}>\n 2 conflicts\n </StatusBar.Item>\n </StatusBar>\n </div>\n</div>",
188
+ "note": "The bottom strip: tone-inked items, and a `push` item that takes the remaining space. Drawn twice — the readings-only bar it has always been, and the VS Code shape beneath it, where some of the items are controls: a compact Group whose two halves read as one, an icon-only control that carries its name in aria-label, and a filled item for the one state that has earned a colour.",
189
+ "interaction": "the plain items are inert by construction — no hover, no focus, nothing to click. The controls take a full-height fill on hover and a darker one while pressed, Tab reaches them in DOM order and Enter fires them, and the focus ring is drawn inside the item so it never paints across the bar. Hovering either half of the Group grounds both, and the half under the pointer goes one rung further."
55
190
  }
56
- ],
57
- "todo": true
191
+ ]
58
192
  }
@@ -24,14 +24,20 @@
24
24
  "defaultVariants": {},
25
25
  "tokensUsed": [
26
26
  "--cue-accent",
27
+ "--cue-accent-soft",
28
+ "--cue-bg",
27
29
  "--cue-border",
28
30
  "--cue-fg",
29
31
  "--cue-fg-subtle",
30
32
  "--cue-font-mono",
33
+ "--cue-hair-strong",
31
34
  "--cue-pad-row-x",
32
35
  "--cue-pad-row-y",
36
+ "--cue-row-hover",
37
+ "--cue-space-2",
33
38
  "--cue-space-3",
34
39
  "--cue-surface-1",
40
+ "--cue-table-group-top",
35
41
  "--cue-text-label",
36
42
  "--cue-text-ui"
37
43
  ],