@sableui/react 0.18.0 → 0.19.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 (84) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/bin/sable.mjs +108 -14
  3. package/dist/{_contract-CDa9-NdB.d.ts → _contract-DlklV0Im.d.ts} +2 -2
  4. package/dist/blocks/index.d.ts +1 -1
  5. package/dist/components/avatar/avatar.js +16 -7
  6. package/dist/components/avatar/avatar.meta.js +43 -10
  7. package/dist/components/avatar/avatar.variants.js +26 -2
  8. package/dist/components/collapsible/collapsible.js +6 -5
  9. package/dist/components/collapsible/collapsible.meta.js +14 -5
  10. package/dist/components/data-table/data-table.js +132 -45
  11. package/dist/components/data-table/data-table.meta.js +63 -4
  12. package/dist/components/date-picker/date-picker.js +13 -6
  13. package/dist/components/date-picker/date-picker.meta.js +22 -0
  14. package/dist/components/menu/menu.js +2 -0
  15. package/dist/components/menu/menu.meta.js +2 -2
  16. package/dist/components/panel-group/panel-group.js +17 -1
  17. package/dist/components/panel-group/panel-group.meta.js +43 -3
  18. package/dist/components/side-nav/side-nav.variants.js +8 -3
  19. package/dist/components/sidebar/index.js +4 -0
  20. package/dist/components/sidebar/sidebar.js +90 -11
  21. package/dist/components/sidebar/sidebar.meta.js +38 -4
  22. package/dist/components/table/table.js +7 -3
  23. package/dist/components/tabs/tabs.js +10 -2
  24. package/dist/components/tabs/tabs.meta.js +4 -0
  25. package/dist/components/workspace/workspace.js +13 -3
  26. package/dist/components/workspace/workspace.meta.js +109 -1
  27. package/dist/file-hashes.json +53 -49
  28. package/dist/foundations/typography.js +13 -0
  29. package/dist/icon/Icon.js +21 -2
  30. package/dist/icon/index.d.ts +36 -1
  31. package/dist/icon/index.js +5 -0
  32. package/dist/icon/names.js +240 -0
  33. package/dist/icon-sets/reicon.js +19 -1
  34. package/dist/index.d.ts +272 -50
  35. package/dist/index.js +11 -1
  36. package/dist/lib/chart/heat.js +1 -1
  37. package/dist/lib/cn.js +1 -0
  38. package/dist/lib/use-media-query.js +41 -0
  39. package/dist/{meta-DX5r5wQL.d.ts → meta-Dfm9AExe.d.ts} +6 -0
  40. package/dist/patterns/dashboard-page.js +23 -17
  41. package/dist/patterns/index.d.ts +7 -2
  42. package/dist/patterns/index.js +5 -1
  43. package/dist/patterns/tracker-page.js +392 -0
  44. package/dist/patterns/tracker-page.meta.js +40 -0
  45. package/dist/presets/index.d.ts +1 -1
  46. package/dist/styles.css +179 -74
  47. package/dist/{theme-provider-BydfM5Z0.d.ts → theme-provider-BUBealk_.d.ts} +1 -1
  48. package/dist/themes/_contract.js +9 -1
  49. package/dist/themes/atelier/index.d.ts +1 -1
  50. package/dist/themes/ember/index.d.ts +1 -1
  51. package/dist/themes/index.d.ts +2 -2
  52. package/dist/themes/meridian/index.d.ts +1 -1
  53. package/dist/themes/poppy/index.d.ts +1 -1
  54. package/dist/themes/prism/index.d.ts +1 -1
  55. package/dist/themes/sable/index.d.ts +1 -1
  56. package/dist/themes/verdant/index.d.ts +1 -1
  57. package/llms/components/avatar.md +32 -9
  58. package/llms/components/collapsible.md +12 -4
  59. package/llms/components/data-table.md +40 -3
  60. package/llms/components/date-picker.md +15 -1
  61. package/llms/components/menu.md +1 -1
  62. package/llms/components/panel-group.md +34 -2
  63. package/llms/components/sidebar.md +17 -4
  64. package/llms/components/tabs.md +1 -0
  65. package/llms/components/workspace.md +81 -1
  66. package/llms/foundations/typography.md +4 -1
  67. package/llms/patterns/dashboard-page.md +18 -15
  68. package/llms/patterns/tracker-page.md +450 -0
  69. package/llms/reference/token-intent.md +3 -1
  70. package/llms/reference/utilities.md +4 -1
  71. package/llms/tasks/auditing-an-app.md +4 -1
  72. package/llms/tasks/building-a-page.md +3 -2
  73. package/llms/tasks/building-a-tool.md +101 -5
  74. package/llms/tasks/building-with-sable.md +11 -3
  75. package/llms/tasks/creating-a-preset.md +9 -0
  76. package/llms-full.txt +843 -54
  77. package/llms.txt +8 -7
  78. package/package.json +2 -1
  79. package/registry.json +491 -36
  80. package/scripts/adoption.mjs +15 -2
  81. package/scripts/app-css.mjs +129 -0
  82. package/scripts/grade.mjs +161 -2
  83. package/scripts/lint-rules.mjs +126 -0
  84. package/skills/sable/SKILL.md +33 -4
@@ -7,6 +7,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  function Workspace({
8
8
  toolbar,
9
9
  statusBar,
10
+ bottomNav,
10
11
  density = "compact",
11
12
  skipLinkLabel = "Skip to canvas",
12
13
  className,
@@ -24,7 +25,7 @@ function Workspace({
24
25
  {
25
26
  "data-density": density,
26
27
  className: cn(
27
- "grid h-dvh grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden bg-canvas text-canvas-foreground",
28
+ "flex h-dvh flex-col overflow-hidden bg-canvas text-canvas-foreground",
28
29
  className
29
30
  ),
30
31
  ...props,
@@ -38,8 +39,16 @@ function Workspace({
38
39
  }
39
40
  ),
40
41
  toolbar,
41
- /* @__PURE__ */ jsx("div", { className: "grid min-h-0 grid-cols-1", children }),
42
- statusBar
42
+ /* @__PURE__ */ jsx("div", { "data-workspace-body": "", className: "grid min-h-0 flex-1 grid-cols-1 grid-rows-[minmax(0,1fr)]", children }),
43
+ statusBar,
44
+ bottomNav ? /* @__PURE__ */ jsx(
45
+ "div",
46
+ {
47
+ "data-workspace-bottom-nav": "",
48
+ className: "h-[calc(var(--nav-height,56px)+env(safe-area-inset-bottom,0px))] shrink-0 lg:hidden",
49
+ children: bottomNav
50
+ }
51
+ ) : null
43
52
  ]
44
53
  }
45
54
  ) })
@@ -62,6 +71,7 @@ function WorkspaceBar({ className, children, ...props }) {
62
71
  return /* @__PURE__ */ jsx(
63
72
  "header",
64
73
  {
74
+ "data-workspace-bar": "",
65
75
  className: cn(
66
76
  "flex h-(--chrome-bar-height) shrink-0 items-center gap-1 border-b border-hairline bg-panel px-2",
67
77
  className
@@ -12,7 +12,18 @@ var workspaceMeta = {
12
12
  "chrome",
13
13
  "panels",
14
14
  "density",
15
- "viewport"
15
+ "viewport",
16
+ "issue tracker",
17
+ "tracker",
18
+ "inspector",
19
+ "reading view",
20
+ "mobile",
21
+ "responsive",
22
+ "bottom nav",
23
+ "media query",
24
+ "breakpoint",
25
+ "top bar",
26
+ "topbar"
16
27
  ],
17
28
  usage: {
18
29
  description: 'Use Workspace as the outer frame for a tool \u2014 an editor, a console, a canvas app. It owns the viewport, scopes the compact control density, and guarantees a skip link plus a main landmark on the canvas. Reach for AppShell instead for page-register products: it is built around a scrolling document, where a workspace is built around a fixed one. Workspace fills the viewport (h-dvh) by design; to embed one in a page (a docs preview, a modal-sized demo), wrap it in a bounded container and pass className="h-full".',
@@ -29,6 +40,18 @@ var workspaceMeta = {
29
40
  guidance: "do",
30
41
  description: "Keep the density scope at compact unless you have a specific reason; the workspace register's measured control ladder is what makes tool chrome read as tool chrome."
31
42
  },
43
+ {
44
+ guidance: "do",
45
+ description: "Put a WorkspaceBar inside a column when the bar belongs to that column only. An issue tracker's bar (breadcrumb, view tabs, filters) sits above the list, while the sidebar and the inspector beside it run the full height: put the WorkspaceBar first inside the WorkspaceCanvas, not in the `toolbar` slot, which spans every panel. Don't hand-build a fixed-height header for it."
46
+ },
47
+ {
48
+ guidance: "do",
49
+ description: 'Wrap a reading view inside the workspace (a project overview, an update, a doc, release notes) in <Density value="comfortable">. The compact scope maps body text to the 11\u201312px UI tiers, which is right for chrome and too small for paragraphs; the comfortable scope gives prose its page size again while the chrome around it stays compact.'
50
+ },
51
+ {
52
+ guidance: "do",
53
+ description: 'Plan the phone layout with the shell rather than around it: pass a BottomNav to `bottomNav`, and give docked Panels `narrow="sheet"` (an inspector, a navigation rail) or `narrow="hidden"`. Don\'t measure the viewport with a resize listener, render the inspector twice, or subtract the nav\'s height from 100dvh.'
54
+ },
32
55
  {
33
56
  guidance: "dont",
34
57
  description: "Nest a Region or Page inside it to set a width. Region caps are a page-register idea; a workspace is edge-to-edge and its panels are fixed-width chrome outside the content grid."
@@ -66,6 +89,11 @@ var workspaceMeta = {
66
89
  name: "StatusBar",
67
90
  required: false,
68
91
  description: "Bottom chrome for ambient document facts."
92
+ },
93
+ {
94
+ name: "BottomNav",
95
+ required: false,
96
+ description: "Phone navigation in the `bottomNav` slot. Its row is reserved below lg and gone above it, so the body ends where the nav begins."
69
97
  }
70
98
  ]
71
99
  },
@@ -80,6 +108,11 @@ var workspaceMeta = {
80
108
  description: "Top chrome, normally a WorkspaceBar."
81
109
  },
82
110
  { name: "statusBar", type: "ReactNode", description: "Bottom chrome, normally a StatusBar." },
111
+ {
112
+ name: "bottomNav",
113
+ type: "ReactNode",
114
+ description: "Phone navigation, normally a BottomNav. Below lg (BottomNav's own breakpoint) the shell reserves the nav's height as its last row, so the body and the status bar end above the nav instead of under it; above lg the row is gone. Every slot is optional: the body is the shell's one growing row whichever of toolbar, statusBar and bottomNav are present."
115
+ },
83
116
  {
84
117
  name: "density",
85
118
  type: '"compact" | "comfortable"',
@@ -103,6 +136,16 @@ var workspaceMeta = {
103
136
  name: "WorkspaceCanvas",
104
137
  type: "ComponentProps<'main'>",
105
138
  description: "The work surface: renders main#main-content, fills the body, clips its overflow."
139
+ },
140
+ {
141
+ name: "useMediaQuery",
142
+ type: "(query: string, options?: { serverValue?: boolean }) => boolean",
143
+ description: "True while a media query matches, read through useSyncExternalStore: no resize listener, no setState in an effect, no hydration mismatch (the server reports serverValue, default false). Use it to branch structure a class cannot express; style-only changes stay as lg: classes."
144
+ },
145
+ {
146
+ name: "useBelowBreakpoint",
147
+ type: '(breakpoint?: "sm" | "md" | "lg" | "xl" | "2xl", options?: { serverValue?: boolean }) => boolean',
148
+ description: "True below a named breakpoint (default lg), flipping at the width where that breakpoint's classes start to apply (Tailwind's sm 40 / md 48 / lg 64 / xl 80 / 2xl 96rem). Panel's `narrow` uses it; reach for it instead of a hand-rolled window.innerWidth check."
106
149
  }
107
150
  ],
108
151
  a11y: [
@@ -164,6 +207,71 @@ var workspaceMeta = {
164
207
  <div className="p-3 text-ui-num text-muted-foreground">log output\u2026</div>
165
208
  </WorkspaceCanvas>
166
209
  </PanelGroup>
210
+ </Workspace>`
211
+ },
212
+ {
213
+ label: "Issue tracker: column bar, docked inspector, phone sheet",
214
+ code: `function Tracker() {
215
+ const [selected, setSelected] = useState<string | null>("AES-104")
216
+ return (
217
+ <Workspace
218
+ bottomNav={
219
+ <BottomNav>
220
+ <BottomNavItem showLabel label="Issues" icon={<Icon name="list" />} active />
221
+ <BottomNavItem showLabel label="Inbox" icon={<Icon name="inbox" />} />
222
+ </BottomNav>
223
+ }
224
+ statusBar={<StatusBar><span>12 issues</span></StatusBar>}
225
+ >
226
+ <PanelGroup>
227
+ <Panel label="Navigation" width={220} narrow="hidden">
228
+ <PanelSection title="Workspace">
229
+ <PropertyRow label="Team"><Input defaultValue="Studio" /></PropertyRow>
230
+ </PanelSection>
231
+ </Panel>
232
+ <WorkspaceCanvas className="flex flex-col">
233
+ <WorkspaceBar>
234
+ <Breadcrumbs>
235
+ <BreadcrumbItem><BreadcrumbLink href="#">Studio</BreadcrumbLink></BreadcrumbItem>
236
+ <BreadcrumbSeparator />
237
+ <BreadcrumbItem><BreadcrumbPage>Digital flagship</BreadcrumbPage></BreadcrumbItem>
238
+ </Breadcrumbs>
239
+ </WorkspaceBar>
240
+ <div className="min-h-0 flex-1 overflow-y-auto scrollbar-quiet">
241
+ <Button size="xs" variant="ghost" onClick={() => setSelected("AES-104")}>AES-104 Refine product page</Button>
242
+ </div>
243
+ </WorkspaceCanvas>
244
+ <Panel
245
+ label="Issue details"
246
+ width={320}
247
+ narrow="sheet"
248
+ open={selected !== null}
249
+ onOpenChange={(open) => !open && setSelected(null)}
250
+ >
251
+ <PanelSection title={selected ?? ""}>
252
+ <PropertyRow label="Status"><Input defaultValue="In progress" /></PropertyRow>
253
+ </PanelSection>
254
+ </Panel>
255
+ </PanelGroup>
256
+ </Workspace>
257
+ )
258
+ }`
259
+ },
260
+ {
261
+ label: "Reading view inside a workspace",
262
+ code: `<Workspace>
263
+ <PanelGroup>
264
+ <WorkspaceCanvas className="overflow-y-auto scrollbar-quiet">
265
+ <Density value="comfortable">
266
+ <article className="max-w-prose p-6">
267
+ <h2 className="text-heading-3">Project overview</h2>
268
+ <p className="mt-3 text-body-md text-muted-foreground">
269
+ Paragraphs keep their page size here, while the chrome around the canvas stays compact.
270
+ </p>
271
+ </article>
272
+ </Density>
273
+ </WorkspaceCanvas>
274
+ </PanelGroup>
167
275
  </Workspace>`
168
276
  },
169
277
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "dist/_contract-CDa9-NdB.d.ts": "062fb08b461ea845",
2
+ "dist/_contract-DlklV0Im.d.ts": "62902c1191994acb",
3
3
  "dist/activity-DT8VK9fx.d.ts": "528ef87bbc766d76",
4
4
  "dist/adapters/github.d.ts": "7e30b303e1daa370",
5
5
  "dist/adapters/github.js": "3a2c4593e2ad1e18",
@@ -29,7 +29,7 @@
29
29
  "dist/blocks/goal-progress-block.meta.js": "3e5b376bf58bdb4b",
30
30
  "dist/blocks/hero-block.js": "ac74e80d4d7d2ad9",
31
31
  "dist/blocks/hero-block.meta.js": "2dd2e5be2472a563",
32
- "dist/blocks/index.d.ts": "5f4a38dda1460f56",
32
+ "dist/blocks/index.d.ts": "6ce6abc6734dbd72",
33
33
  "dist/blocks/index.js": "690417c222dc4d2a",
34
34
  "dist/blocks/invite-team-block.js": "be4df15518252535",
35
35
  "dist/blocks/invite-team-block.meta.js": "fd57b632f0f32276",
@@ -112,9 +112,9 @@
112
112
  "dist/components/attachment/attachment.meta.js": "bd6839318c806693",
113
113
  "dist/components/attachment/attachment.variants.js": "6435e477e7fab33e",
114
114
  "dist/components/attachment/index.js": "133c4711cb724f18",
115
- "dist/components/avatar/avatar.js": "4d6e3af8d615054f",
116
- "dist/components/avatar/avatar.meta.js": "7f60515562e66607",
117
- "dist/components/avatar/avatar.variants.js": "55007c724a59bf1f",
115
+ "dist/components/avatar/avatar.js": "07b29a9c1a0c83a8",
116
+ "dist/components/avatar/avatar.meta.js": "22eee8ca57dbc0fd",
117
+ "dist/components/avatar/avatar.variants.js": "97468d4f1b308d70",
118
118
  "dist/components/avatar/index.js": "12b7af50f0f54b67",
119
119
  "dist/components/badge/badge.js": "d4244f501b1ed90f",
120
120
  "dist/components/badge/badge.meta.js": "4cccd768874c1ad8",
@@ -178,8 +178,8 @@
178
178
  "dist/components/code-block/code-block.js": "b66aed579438bca7",
179
179
  "dist/components/code-block/code-block.meta.js": "d7e869100f45e650",
180
180
  "dist/components/code-block/index.js": "e40982950359204b",
181
- "dist/components/collapsible/collapsible.js": "fecb96bdc056e7bb",
182
- "dist/components/collapsible/collapsible.meta.js": "5d677ef03ee2f7e9",
181
+ "dist/components/collapsible/collapsible.js": "d7c1dc37baa02104",
182
+ "dist/components/collapsible/collapsible.meta.js": "dae49ec2983fb629",
183
183
  "dist/components/collapsible/index.js": "072c3578300f205e",
184
184
  "dist/components/color-field/color-field.js": "05aa083444586cfb",
185
185
  "dist/components/color-field/color-field.meta.js": "a5815df99fdca4a2",
@@ -201,11 +201,11 @@
201
201
  "dist/components/context-menu/context-menu.js": "9a5f899f1fd5aa84",
202
202
  "dist/components/context-menu/context-menu.meta.js": "6915409a31be6ef0",
203
203
  "dist/components/context-menu/index.js": "b4a609ee13e4bdd5",
204
- "dist/components/data-table/data-table.js": "be6e1011624d580b",
205
- "dist/components/data-table/data-table.meta.js": "e8df5d8910630c0a",
204
+ "dist/components/data-table/data-table.js": "11d62363b90271a9",
205
+ "dist/components/data-table/data-table.meta.js": "cdca28cc23d28c45",
206
206
  "dist/components/data-table/index.js": "181835d5e3b6c366",
207
- "dist/components/date-picker/date-picker.js": "7dd1d41202bede97",
208
- "dist/components/date-picker/date-picker.meta.js": "fbd9d0e31affe527",
207
+ "dist/components/date-picker/date-picker.js": "974fb092a642138d",
208
+ "dist/components/date-picker/date-picker.meta.js": "bb48e03dbf11b074",
209
209
  "dist/components/date-picker/index.js": "44e1227972891934",
210
210
  "dist/components/date-tile/date-tile.js": "7458bf733bc64ab2",
211
211
  "dist/components/date-tile/date-tile.meta.js": "0bc0081c8cb73535",
@@ -286,8 +286,8 @@
286
286
  "dist/components/media-tile/media-tile.meta.js": "a1323b8f1f945e58",
287
287
  "dist/components/media-tile/media-tile.variants.js": "e22c08004b4a55ad",
288
288
  "dist/components/menu/index.js": "c368589e92d3a189",
289
- "dist/components/menu/menu.js": "d31a3cd0d737447d",
290
- "dist/components/menu/menu.meta.js": "533a215b9ff28d85",
289
+ "dist/components/menu/menu.js": "162648914d1182eb",
290
+ "dist/components/menu/menu.meta.js": "3587ac2db429fe37",
291
291
  "dist/components/menubar/index.js": "4e0c7717fa9f2a4d",
292
292
  "dist/components/menubar/menubar.js": "7177568e95c22fd1",
293
293
  "dist/components/menubar/menubar.meta.js": "2d313681a64a7bfd",
@@ -338,8 +338,8 @@
338
338
  "dist/components/pagination/pagination.js": "1220c9e09285601e",
339
339
  "dist/components/pagination/pagination.meta.js": "d4b190da016cf9c8",
340
340
  "dist/components/panel-group/index.js": "8589d6450207f608",
341
- "dist/components/panel-group/panel-group.js": "c720c80c554d89b6",
342
- "dist/components/panel-group/panel-group.meta.js": "b699a78c93e63911",
341
+ "dist/components/panel-group/panel-group.js": "bee8719d8826c020",
342
+ "dist/components/panel-group/panel-group.meta.js": "c2c80330b0b36724",
343
343
  "dist/components/panel-section/index.js": "055470e0a68862e6",
344
344
  "dist/components/panel-section/panel-section.js": "04b12c7e507083df",
345
345
  "dist/components/panel-section/panel-section.meta.js": "148a7bbe63d7e58f",
@@ -419,10 +419,10 @@
419
419
  "dist/components/side-nav/index.js": "fa4e5f5e7232de1e",
420
420
  "dist/components/side-nav/side-nav.js": "4327344d1e8ce8bc",
421
421
  "dist/components/side-nav/side-nav.meta.js": "82637ce04cbd2b88",
422
- "dist/components/side-nav/side-nav.variants.js": "54086fe23a4f679f",
423
- "dist/components/sidebar/index.js": "eac0c8742dcaae4d",
424
- "dist/components/sidebar/sidebar.js": "5a2ce3bc89528fbf",
425
- "dist/components/sidebar/sidebar.meta.js": "84718479850faceb",
422
+ "dist/components/side-nav/side-nav.variants.js": "0b94d077067544d1",
423
+ "dist/components/sidebar/index.js": "570bd667f5f64241",
424
+ "dist/components/sidebar/sidebar.js": "afe8fcfb35f05d5f",
425
+ "dist/components/sidebar/sidebar.meta.js": "28cad3311519b366",
426
426
  "dist/components/skeleton/index.js": "4e01c345b14d42f1",
427
427
  "dist/components/skeleton/skeleton.js": "5ca6d75e81a048ee",
428
428
  "dist/components/skeleton/skeleton.meta.js": "9b7014b97385d773",
@@ -453,11 +453,11 @@
453
453
  "dist/components/switch/switch.js": "5f7b31327500f893",
454
454
  "dist/components/switch/switch.meta.js": "a18e8a74430cb644",
455
455
  "dist/components/table/index.js": "690a2dcf36ac85f6",
456
- "dist/components/table/table.js": "b84e3ad4a2c01c07",
456
+ "dist/components/table/table.js": "cd8aa7b71696532b",
457
457
  "dist/components/table/table.meta.js": "161e701f7e719851",
458
458
  "dist/components/tabs/index.js": "a48e84d7f04203f9",
459
- "dist/components/tabs/tabs.js": "26a15a1cda1d7a39",
460
- "dist/components/tabs/tabs.meta.js": "9622b77b090361a0",
459
+ "dist/components/tabs/tabs.js": "8dee06138a436780",
460
+ "dist/components/tabs/tabs.meta.js": "74c1da8d70dcb31a",
461
461
  "dist/components/textarea/index.js": "09897d89dd4083dd",
462
462
  "dist/components/textarea/textarea.js": "f6b0a934cacdb35b",
463
463
  "dist/components/textarea/textarea.meta.js": "2ae567e1d704c52d",
@@ -495,8 +495,8 @@
495
495
  "dist/components/video/video.js": "d916305d298d6f30",
496
496
  "dist/components/video/video.meta.js": "3c3035022b1f5d90",
497
497
  "dist/components/workspace/index.js": "bace33b15a746d71",
498
- "dist/components/workspace/workspace.js": "1d676d32bc632e30",
499
- "dist/components/workspace/workspace.meta.js": "b2f9dc7be0d6a6be",
498
+ "dist/components/workspace/workspace.js": "54d938b4954b309e",
499
+ "dist/components/workspace/workspace.meta.js": "173277771d6e38f3",
500
500
  "dist/foundations/accessibility.js": "3d5a26dac7b1d821",
501
501
  "dist/foundations/index.d.ts": "34f9553827230412",
502
502
  "dist/foundations/index.js": "37e83ca8063a4282",
@@ -506,20 +506,21 @@
506
506
  "dist/foundations/spacing.js": "49fd401256835447",
507
507
  "dist/foundations/surfaces.js": "2791d0b950ba2c42",
508
508
  "dist/foundations/types.js": "e3b0c44298fc1c14",
509
- "dist/foundations/typography.js": "a682867ef56c208c",
510
- "dist/icon/Icon.js": "bb0869322d3a61b2",
509
+ "dist/foundations/typography.js": "d1a324eed0feb558",
510
+ "dist/icon/Icon.js": "68d5200af511744d",
511
511
  "dist/icon/IconProvider.js": "35a60f03b55f92ae",
512
512
  "dist/icon/compose.js": "d9d4445aa33f867d",
513
- "dist/icon/index.d.ts": "88a0a84a2313196d",
514
- "dist/icon/index.js": "12fcd896bfc51d3e",
513
+ "dist/icon/index.d.ts": "044dbd3a068471b0",
514
+ "dist/icon/index.js": "51f70bcb1219b9a6",
515
+ "dist/icon/names.js": "1fec5b3136d91fa1",
515
516
  "dist/icon/registry.js": "e3b0c44298fc1c14",
516
517
  "dist/icon-sets/brands.d.ts": "c413ccbdd078d8b4",
517
518
  "dist/icon-sets/brands.generated.js": "901e198df0563b0b",
518
519
  "dist/icon-sets/brands.js": "9a37d8739f14f2a9",
519
520
  "dist/icon-sets/reicon.d.ts": "52f6e41dc8137b4e",
520
- "dist/icon-sets/reicon.js": "9cac6fee5ad95a62",
521
- "dist/index.d.ts": "ad94845a460a1df4",
522
- "dist/index.js": "fe1322803fff6cf8",
521
+ "dist/icon-sets/reicon.js": "6d3302cc2ed5a58e",
522
+ "dist/index.d.ts": "e5a512f3a1ad8bb5",
523
+ "dist/index.js": "ca9bc8552d36f795",
523
524
  "dist/ios/chrome.css": "a7ca9e3735d8658e",
524
525
  "dist/ios/chrome.js": "37091fef7b68bd28",
525
526
  "dist/ios/frame.css": "dd2703c944ce575a",
@@ -564,12 +565,12 @@
564
565
  "dist/lib/chart/dim.js": "e67e46858379174a",
565
566
  "dist/lib/chart/format.js": "f0256bbe226a0967",
566
567
  "dist/lib/chart/geometry.js": "10acd4c2626b0969",
567
- "dist/lib/chart/heat.js": "ca6d47e6d28d4428",
568
+ "dist/lib/chart/heat.js": "2de024b5f50cbaff",
568
569
  "dist/lib/chart/motion.js": "8edb50cf54b30a1a",
569
570
  "dist/lib/chart/scale.js": "b4b329322bfdd17a",
570
571
  "dist/lib/chart/types.js": "5665ade26e2b21a7",
571
572
  "dist/lib/chart/use-arrival.js": "9c38f37cf12146f9",
572
- "dist/lib/cn.js": "d16165e3f2f3c488",
573
+ "dist/lib/cn.js": "35c2afae2a2e5753",
573
574
  "dist/lib/component-guidance.js": "add930d690472d31",
574
575
  "dist/lib/density.js": "304cdaf6733fbad1",
575
576
  "dist/lib/field-shell.js": "cd47989e26b4f1ce",
@@ -596,10 +597,11 @@
596
597
  "dist/lib/use-edge-fade.js": "392e8c33f072fd21",
597
598
  "dist/lib/use-isomorphic-layout-effect.js": "8b93c47e9f069454",
598
599
  "dist/lib/use-match-highlight.js": "4ab41f3a82446ebf",
600
+ "dist/lib/use-media-query.js": "9998712dac227f59",
599
601
  "dist/lib/use-resize-observer.js": "4f08e280d64be22c",
600
602
  "dist/lib/use-scroll-activity.js": "793394a8c952e697",
601
603
  "dist/lib/use-smooth-wheel.js": "735e0e004c7204fb",
602
- "dist/meta-DX5r5wQL.d.ts": "b15f63123088709a",
604
+ "dist/meta-Dfm9AExe.d.ts": "cbc99ba174d4aae3",
603
605
  "dist/motion/Burst.js": "cede90200e958908",
604
606
  "dist/motion/MotionProvider.js": "624f5502828e7fd0",
605
607
  "dist/motion/Presence.js": "21fc6f1c68207718",
@@ -618,48 +620,50 @@
618
620
  "dist/patterns/brand-landing-page.meta.js": "4483ca25391e53cd",
619
621
  "dist/patterns/chat-panel.js": "f4597a28521c26df",
620
622
  "dist/patterns/chat-panel.meta.js": "a435a08964f40f79",
621
- "dist/patterns/dashboard-page.js": "82a56a2d61381ff0",
623
+ "dist/patterns/dashboard-page.js": "0b34d8ed44b4e29d",
622
624
  "dist/patterns/dashboard-page.meta.js": "afa1ab16823686d0",
623
625
  "dist/patterns/feed-page.js": "6c34ebf590eb51df",
624
626
  "dist/patterns/feed-page.meta.js": "d87a18be1d0eebca",
625
- "dist/patterns/index.d.ts": "07bf94356dd329a8",
626
- "dist/patterns/index.js": "3ff5fabb1afbf8dc",
627
+ "dist/patterns/index.d.ts": "31c319650574425f",
628
+ "dist/patterns/index.js": "e6d7654a7cd9141b",
627
629
  "dist/patterns/landing-page.js": "d27227dbb74021bf",
628
630
  "dist/patterns/landing-page.meta.js": "95d34273ee999eea",
629
631
  "dist/patterns/list-detail-page.js": "7c124da100befd14",
630
632
  "dist/patterns/list-detail-page.meta.js": "df01b058381bd67a",
631
633
  "dist/patterns/settings-page.js": "61277b5e00a76244",
632
634
  "dist/patterns/settings-page.meta.js": "68d34a8d2ecf417a",
633
- "dist/presets/index.d.ts": "454f6f809cc08d6a",
635
+ "dist/patterns/tracker-page.js": "7d92a8f05f6aa044",
636
+ "dist/patterns/tracker-page.meta.js": "e0fe9b7416b1ace0",
637
+ "dist/presets/index.d.ts": "87fa4c7ffd7c0da8",
634
638
  "dist/presets/index.js": "fe9c4f8231bf18b2",
635
639
  "dist/registry-CyL28OzJ.d.ts": "b068fc89571517d8",
636
- "dist/styles.css": "d02c108604a72bb5",
637
- "dist/theme-provider-BydfM5Z0.d.ts": "71808ed18b268496",
638
- "dist/themes/_contract.js": "f566700602557a22",
640
+ "dist/styles.css": "f42ee1b4773f49b0",
641
+ "dist/theme-provider-BUBealk_.d.ts": "3b8c1368c3a39296",
642
+ "dist/themes/_contract.js": "c37c09de82106fe8",
639
643
  "dist/themes/atelier/atelier.css": "ca476ee9fe980307",
640
- "dist/themes/atelier/index.d.ts": "941ec7d11a9ea429",
644
+ "dist/themes/atelier/index.d.ts": "1e0aefad2c32bb56",
641
645
  "dist/themes/atelier/index.js": "909453609097c7f7",
642
646
  "dist/themes/contrast.js": "65b057ac2613a364",
643
647
  "dist/themes/ember/ember.css": "ca5e2e60ecb52464",
644
- "dist/themes/ember/index.d.ts": "74d05c575e106131",
648
+ "dist/themes/ember/index.d.ts": "266ea5636ed7bbc9",
645
649
  "dist/themes/ember/index.js": "cffa33f1bef32147",
646
- "dist/themes/index.d.ts": "8ee47b37d061d88a",
650
+ "dist/themes/index.d.ts": "73e82f4cf28dd622",
647
651
  "dist/themes/index.js": "7f33a54fb5c24748",
648
- "dist/themes/meridian/index.d.ts": "f108ee2888fcd002",
652
+ "dist/themes/meridian/index.d.ts": "8f3d193836383ddb",
649
653
  "dist/themes/meridian/index.js": "56462cce4dca41ca",
650
654
  "dist/themes/meridian/meridian.css": "a76f7d8f8105d19d",
651
- "dist/themes/poppy/index.d.ts": "40caf47c32b61be8",
655
+ "dist/themes/poppy/index.d.ts": "c8323cd277c07f2d",
652
656
  "dist/themes/poppy/index.js": "f2e535806e5a2bf7",
653
657
  "dist/themes/poppy/poppy.css": "da551a8b7bdd30c1",
654
- "dist/themes/prism/index.d.ts": "ce69006adb495d25",
658
+ "dist/themes/prism/index.d.ts": "68650a1e59d78cf6",
655
659
  "dist/themes/prism/index.js": "ec2cee6b1265ffa1",
656
660
  "dist/themes/prism/prism.css": "51c7bfbf7dfab6ca",
657
661
  "dist/themes/registry.js": "933de6f608db253f",
658
- "dist/themes/sable/index.d.ts": "c2c7e2009c0fca7e",
662
+ "dist/themes/sable/index.d.ts": "1613fafeb268466e",
659
663
  "dist/themes/sable/index.js": "28eaf45ae57577b6",
660
664
  "dist/themes/sable/sable.css": "b0cfb24b9519f20f",
661
665
  "dist/themes/theme-provider.js": "05ad5f1f820e7dd5",
662
- "dist/themes/verdant/index.d.ts": "1832116ef671f45f",
666
+ "dist/themes/verdant/index.d.ts": "0c887c715f9b1b14",
663
667
  "dist/themes/verdant/index.js": "f7427c153f1fa913",
664
668
  "dist/themes/verdant/verdant.css": "84746574d124472f",
665
669
  "dist/themes.css": "994aa4e048ed1cd9"
@@ -73,6 +73,14 @@ var typography = {
73
73
  rule: "text-wrap: balance on headings, pretty on ledes, neither on long prose.",
74
74
  why: "balance evens short headlines; pretty only refuses a one-word last line. Both cost layout work that long prose does not repay."
75
75
  },
76
+ {
77
+ rule: "To make all the type smaller or larger, set the base: --type-base: 14 (a unitless px number; 16 is the default). Never retune tiers one by one for it.",
78
+ why: 'Every tier is authored on a 16px base and keeps its ratio to it, so one number rebuilds the whole scale, headings to captions, and the baseline step and spacing follow the body. Shrinking tiers individually breaks the ratios between them. Set it on :root, a theme scope or a preset (tokens.shared["type-base"]) so the rhythm follows too.'
79
+ },
80
+ {
81
+ rule: `Inside a Workspace, a panel's title is text-ui-title (14px \xB7 600); compact text-title-lg resolves to it. A reading view inside the tool (an overview, a document, a long form) is wrapped in <Density value="comfortable"> and gets the page tiers back.`,
82
+ why: "The compact register remaps page tiers onto the 10-12px chrome ramp, which is right for chrome and wrong for prose: an issue overview set in a tool rendered its paragraphs at 11px. The remap stops at a comfortable boundary, so the wrapper is the whole fix."
83
+ },
76
84
  {
77
85
  rule: "A mark attached to a line of type (a dot, a bullet, a timeline marker, a leading icon) sits in a box one line tall (h-[1lh] items-center) and is sized in em.",
78
86
  why: "1lh is the line-height of the text beside it, so the mark centres on that line in every tier, density and theme. A px size with a mt-[3px] nudge is right for one font at one size and wrong everywhere else."
@@ -106,6 +114,11 @@ var typography = {
106
114
  instead: "Ragged-right at body-md; body-xs only for dense data.",
107
115
  why: "Justification opens rivers on the web, which has no hyphenation control; 12px prose fails people with low vision."
108
116
  },
117
+ {
118
+ never: "Changing the root font-size (html { font-size: 14px }) to make a product denser.",
119
+ instead: '--type-base: 14, with Workspace or <Density value="compact"> for denser controls.',
120
+ why: "The root also sizes every rem the app wrote: widths, insets, media queries. --type-base moves only the type and the rhythm built on it, and density moves the controls, so each can be chosen on its own."
121
+ },
109
122
  {
110
123
  never: "A new font family, or raw font-feature-settings / font-variation-settings.",
111
124
  instead: "The theme's sans, mono and optional display; font-variant-numeric and font-weight.",
package/dist/icon/Icon.js CHANGED
@@ -1,11 +1,16 @@
1
1
  // src/icon/Icon.tsx
2
2
  import { useIconSet } from "./IconProvider.js";
3
+ import { nearestIconNames, resolveIconAlias } from "./names.js";
3
4
  import { Fragment, jsx } from "react/jsx-runtime";
4
5
  var MISSING_ICON_PROVIDER = (
5
6
  // design-lint-disable-next-line self-import -- the warning text names the consumer's import, not an import
6
7
  'Sable: <Icon> has no icon set, so icons render nothing. Wrap your app in <IconProvider set={reiconIconSet}> from "@sableui/react/icons/reicon".'
7
8
  );
8
9
  var warned = false;
10
+ var missingWarned = /* @__PURE__ */ new Set();
11
+ function missingIconMessage(name) {
12
+ return `Sable: <Icon name="${name}"> resolved to nothing in the active icon set, so it renders nothing. Nearest contract names: ${nearestIconNames(name).join(", ")}. The full list is registry.json \u2192 iconContract.names; an app's own names go in a set composed with composeIconSets.`;
13
+ }
9
14
  function isDevelopment() {
10
15
  try {
11
16
  return process.env.NODE_ENV !== "production";
@@ -22,9 +27,23 @@ function Icon({ name, variant, spin, className, title }) {
22
27
  }
23
28
  return null;
24
29
  }
25
- return /* @__PURE__ */ jsx(Fragment, { children: set({ name, variant: variant ?? defaultVariant, spin, className, title }) });
30
+ const props = { variant: variant ?? defaultVariant, spin, className, title };
31
+ let rendered = set({ name, ...props });
32
+ if (rendered == null) {
33
+ const alias = resolveIconAlias(name);
34
+ if (alias) rendered = set({ name: alias, ...props });
35
+ }
36
+ if (rendered == null) {
37
+ if (!missingWarned.has(name) && isDevelopment()) {
38
+ missingWarned.add(name);
39
+ console.warn(missingIconMessage(name));
40
+ }
41
+ return null;
42
+ }
43
+ return /* @__PURE__ */ jsx(Fragment, { children: rendered });
26
44
  }
27
45
  export {
28
46
  Icon,
29
- MISSING_ICON_PROVIDER
47
+ MISSING_ICON_PROVIDER,
48
+ missingIconMessage
30
49
  };
@@ -21,6 +21,13 @@ interface IconProps {
21
21
  * There is no built-in fallback: an app that brings its own icons ships none
22
22
  * of Reicon's artwork. Without a provider, icons render nothing, and a
23
23
  * development build says so once per page.
24
+ *
25
+ * A name the set does not have is tried once more as another library's
26
+ * spelling (`check-circle`, `settings`, `x` → the contract's `circle-check`,
27
+ * `gear`, `xmark`; see ICON_ALIASES). Still nothing, and a development build
28
+ * names the nearest contract names, once per name: a missing glyph renders as
29
+ * an empty gap that reads like a layout choice, and through an app's own
30
+ * wrapper (`<Glyph name={n} />`) no lint can see the literal.
24
31
  */
25
32
  declare function Icon({ name, variant, spin, className, title }: IconProps): react.JSX.Element | null;
26
33
 
@@ -44,6 +51,34 @@ declare function Icon({ name, variant, spin, className, title }: IconProps): rea
44
51
  */
45
52
  declare function composeIconSets(...sets: IconSet[]): IconSet;
46
53
 
54
+ /**
55
+ * The icon contract: every semantic name an app can pass to `<Icon name>` and
56
+ * expect a glyph from Sable's bundled set, plus the names other icon libraries
57
+ * use for the same glyphs.
58
+ *
59
+ * `ICON_NAMES` is the same list as `registry.json → iconContract.names`
60
+ * (asserted in reicon.test.tsx), kept here so `<Icon>` can point at the nearest
61
+ * real name when one resolves to nothing — the core graph never imports an icon
62
+ * set, so it cannot ask Reicon what it has.
63
+ */
64
+ declare const ICON_NAMES: readonly ["heart", "comment", "share", "gem", "image-stack", "home", "bell", "gear", "user", "search", "plus", "minus", "check", "xmark", "chevron-down", "chevron-up", "chevron-left", "chevron-right", "arrow-up", "arrow-up-right", "expand", "eye", "eye-off", "external", "spinner", "ellipsis", "info", "warning", "circle-check", "calendar", "copy", "file", "folder", "sun", "moon", "desktop", "lock", "list", "bars", "grid", "layout", "book", "component", "sparkles", "play", "pause", "next", "previous", "arrow-down", "arrow-left", "arrow-right", "clock", "send", "trash", "edit", "download", "upload", "link", "inbox", "mail", "logout", "star", "refresh", "filter", "bold", "italic", "eyedropper", "stop", "key", "wallet", "thumbs-up", "thumbs-down", "shuffle", "repeat", "repeat-one", "volume", "volume-low", "volume-off", "queue", "music", "tag", "paperclip", "chart", "flag", "at-sign", "replay", "motion", "emoji", "briefcase", "layers", "microphone", "video", "broadcast", "newspaper", "palette", "gamepad", "users", "archive", "kanban", "sliders", "help", "sidebar", "chevron-up-down"];
65
+ type IconName = (typeof ICON_NAMES)[number];
66
+ /**
67
+ * Other libraries' names for glyphs the contract already has. Agents and
68
+ * people reach for Lucide's and Heroicons' spellings first (`check-circle`,
69
+ * `settings`, `x`, `more-horizontal`, `cog-6-tooth`); `<Icon>` resolves these
70
+ * to the contract name before it gives up, so a guessed name still draws.
71
+ * Write the contract name in new code — lint says which one.
72
+ */
73
+ declare const ICON_ALIASES: Readonly<Record<string, IconName>>;
74
+ /** The contract name an alias stands for, or `undefined` when it is not one. */
75
+ declare function resolveIconAlias(name: string): IconName | undefined;
76
+ /**
77
+ * Up to three contract names closest to `name`: ones that share a word with it
78
+ * first (`user-avatar` → user), then by edit distance.
79
+ */
80
+ declare function nearestIconNames(name: string, limit?: number): string[];
81
+
47
82
  interface IconContextValue {
48
83
  set: IconSet | null;
49
84
  defaultVariant: IconVariant;
@@ -65,4 +100,4 @@ interface IconProviderProps {
65
100
  declare function IconProvider({ set, defaultVariant, children, }: IconProviderProps): react.JSX.Element;
66
101
  declare function useIconSet(): IconContextValue;
67
102
 
68
- export { Icon, type IconContextValue, type IconProps, IconProvider, type IconProviderProps, IconSet, IconVariant, composeIconSets, useIconSet };
103
+ export { ICON_ALIASES, ICON_NAMES, Icon, type IconContextValue, type IconName, type IconProps, IconProvider, type IconProviderProps, IconSet, IconVariant, composeIconSets, nearestIconNames, resolveIconAlias, useIconSet };
@@ -1,13 +1,18 @@
1
1
  // src/icon/index.ts
2
2
  import { Icon } from "./Icon.js";
3
3
  import { composeIconSets } from "./compose.js";
4
+ import { ICON_ALIASES, ICON_NAMES, nearestIconNames, resolveIconAlias } from "./names.js";
4
5
  import {
5
6
  IconProvider,
6
7
  useIconSet
7
8
  } from "./IconProvider.js";
8
9
  export {
10
+ ICON_ALIASES,
11
+ ICON_NAMES,
9
12
  Icon,
10
13
  IconProvider,
11
14
  composeIconSets,
15
+ nearestIconNames,
16
+ resolveIconAlias,
12
17
  useIconSet
13
18
  };