@sableui/react 0.6.2 → 0.7.1

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 (642) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +315 -0
  3. package/CONTRIBUTING.md +16 -10
  4. package/DESIGN.md +3 -3
  5. package/README.md +3 -3
  6. package/bin/sable-lint.mjs +18 -0
  7. package/bin/sable.mjs +470 -62
  8. package/dist/blocks/api-keys-block.js +44 -0
  9. package/dist/blocks/api-keys-block.meta.js +21 -0
  10. package/dist/blocks/chat-thread-block.js +17 -0
  11. package/dist/blocks/chat-thread-block.meta.js +20 -0
  12. package/dist/blocks/command-palette-block.js +24 -0
  13. package/dist/blocks/command-palette-block.meta.js +21 -0
  14. package/dist/blocks/cta-band-block.js +27 -0
  15. package/dist/blocks/cta-band-block.meta.js +20 -0
  16. package/dist/blocks/danger-zone-block.js +39 -0
  17. package/dist/blocks/danger-zone-block.meta.js +21 -0
  18. package/dist/blocks/device-comparison-block.js +69 -0
  19. package/dist/blocks/device-comparison-block.meta.js +21 -0
  20. package/dist/blocks/event-card-block.js +40 -0
  21. package/dist/blocks/event-card-block.meta.js +21 -0
  22. package/dist/blocks/faq-block.js +38 -0
  23. package/dist/blocks/faq-block.meta.js +20 -0
  24. package/dist/blocks/feature-grid-block.js +38 -0
  25. package/dist/blocks/feature-grid-block.meta.js +21 -0
  26. package/dist/blocks/feedback-form-block.js +30 -0
  27. package/dist/blocks/feedback-form-block.meta.js +21 -0
  28. package/dist/blocks/goal-progress-block.js +24 -0
  29. package/dist/blocks/goal-progress-block.meta.js +21 -0
  30. package/dist/blocks/hero-block.js +33 -0
  31. package/dist/blocks/hero-block.meta.js +21 -0
  32. package/dist/blocks/index.js +162 -1964
  33. package/dist/blocks/invite-team-block.js +36 -0
  34. package/dist/blocks/invite-team-block.meta.js +20 -0
  35. package/dist/blocks/invoice-history-block.js +41 -0
  36. package/dist/blocks/invoice-history-block.meta.js +21 -0
  37. package/dist/blocks/kpi-sparkline-block.js +29 -0
  38. package/dist/blocks/kpi-sparkline-block.meta.js +20 -0
  39. package/dist/blocks/media-player-block.js +68 -0
  40. package/dist/blocks/media-player-block.meta.js +21 -0
  41. package/dist/blocks/newsletter-block.js +35 -0
  42. package/dist/blocks/newsletter-block.meta.js +21 -0
  43. package/dist/blocks/not-found-block.js +27 -0
  44. package/dist/blocks/not-found-block.meta.js +20 -0
  45. package/dist/blocks/notification-list-block.js +32 -0
  46. package/dist/blocks/notification-list-block.meta.js +21 -0
  47. package/dist/blocks/notification-settings-block.js +25 -0
  48. package/dist/blocks/notification-settings-block.meta.js +20 -0
  49. package/dist/blocks/order-tracking-block.js +26 -0
  50. package/dist/blocks/order-tracking-block.meta.js +20 -0
  51. package/dist/blocks/pricing-card-block.js +46 -0
  52. package/dist/blocks/pricing-card-block.meta.js +21 -0
  53. package/dist/blocks/pricing-table-block.js +88 -0
  54. package/dist/blocks/pricing-table-block.meta.js +20 -0
  55. package/dist/blocks/product-card-block.js +26 -0
  56. package/dist/blocks/product-card-block.meta.js +21 -0
  57. package/dist/blocks/profile-card-block.js +58 -0
  58. package/dist/blocks/profile-card-block.meta.js +21 -0
  59. package/dist/blocks/profile-form-block.js +70 -0
  60. package/dist/blocks/profile-form-block.meta.js +21 -0
  61. package/dist/blocks/revenue-breakdown-block.js +38 -0
  62. package/dist/blocks/revenue-breakdown-block.meta.js +21 -0
  63. package/dist/blocks/sign-in-block.js +38 -0
  64. package/dist/blocks/sign-in-block.meta.js +21 -0
  65. package/dist/blocks/sign-up-block.js +48 -0
  66. package/dist/blocks/sign-up-block.meta.js +21 -0
  67. package/dist/blocks/site-footer-block.js +54 -0
  68. package/dist/blocks/site-footer-block.meta.js +20 -0
  69. package/dist/blocks/stat-row-block.js +28 -0
  70. package/dist/blocks/stat-row-block.meta.js +21 -0
  71. package/dist/blocks/steps-block.js +37 -0
  72. package/dist/blocks/steps-block.meta.js +20 -0
  73. package/dist/blocks/task-list-block.js +52 -0
  74. package/dist/blocks/task-list-block.meta.js +21 -0
  75. package/dist/blocks/team-members-block.js +49 -0
  76. package/dist/blocks/team-members-block.meta.js +21 -0
  77. package/dist/blocks/testimonial-block.js +49 -0
  78. package/dist/blocks/testimonial-block.meta.js +20 -0
  79. package/dist/blocks/traffic-sources-block.js +39 -0
  80. package/dist/blocks/traffic-sources-block.meta.js +21 -0
  81. package/dist/blocks/traffic-trend-block.js +61 -0
  82. package/dist/blocks/traffic-trend-block.meta.js +21 -0
  83. package/dist/blocks/upload-list-block.js +39 -0
  84. package/dist/blocks/upload-list-block.meta.js +21 -0
  85. package/dist/blocks/verify-code-block.js +28 -0
  86. package/dist/blocks/verify-code-block.meta.js +21 -0
  87. package/dist/blocks/wallet-block.js +48 -0
  88. package/dist/blocks/wallet-block.meta.js +21 -0
  89. package/dist/components/accordion/accordion.js +63 -0
  90. package/dist/components/accordion/accordion.meta.js +155 -0
  91. package/dist/components/accordion/index.js +15 -0
  92. package/dist/components/app-header/app-header.js +30 -0
  93. package/dist/components/app-header/app-header.meta.js +88 -0
  94. package/dist/components/app-header/index.js +7 -0
  95. package/dist/components/app-shell/app-shell-dock.js +37 -0
  96. package/dist/components/app-shell/app-shell.js +67 -0
  97. package/dist/components/app-shell/app-shell.meta.js +176 -0
  98. package/dist/components/app-shell/index.js +7 -0
  99. package/dist/components/area-chart/area-chart.js +10 -0
  100. package/dist/components/area-chart/area-chart.meta.js +218 -0
  101. package/dist/components/area-chart/index.js +7 -0
  102. package/dist/components/attachment/attachment.js +176 -0
  103. package/dist/components/attachment/attachment.meta.js +179 -0
  104. package/dist/components/attachment/attachment.variants.js +47 -0
  105. package/dist/components/attachment/index.js +7 -0
  106. package/dist/components/avatar/avatar.js +192 -0
  107. package/dist/components/avatar/avatar.meta.js +233 -0
  108. package/dist/components/avatar/avatar.variants.js +44 -0
  109. package/dist/components/avatar/index.js +22 -0
  110. package/dist/components/badge/badge.js +26 -0
  111. package/dist/components/badge/badge.meta.js +171 -0
  112. package/dist/components/badge/badge.variants.js +103 -0
  113. package/dist/components/badge/index.js +11 -0
  114. package/dist/components/banner/banner.js +76 -0
  115. package/dist/components/banner/banner.meta.js +172 -0
  116. package/dist/components/banner/banner.variants.js +44 -0
  117. package/dist/components/banner/index.js +9 -0
  118. package/dist/components/bar-chart/bar-chart.js +10 -0
  119. package/dist/components/bar-chart/bar-chart.meta.js +245 -0
  120. package/dist/components/bar-chart/index.js +7 -0
  121. package/dist/components/bottom-nav/bottom-nav.js +63 -0
  122. package/dist/components/bottom-nav/bottom-nav.meta.js +95 -0
  123. package/dist/components/bottom-nav/index.js +11 -0
  124. package/dist/components/breadcrumbs/breadcrumbs.js +90 -0
  125. package/dist/components/breadcrumbs/breadcrumbs.meta.js +111 -0
  126. package/dist/components/breadcrumbs/index.js +17 -0
  127. package/dist/components/browser/browser.js +39 -0
  128. package/dist/components/browser/browser.meta.js +80 -0
  129. package/dist/components/browser/index.js +7 -0
  130. package/dist/components/bubble/bubble.js +110 -0
  131. package/dist/components/bubble/bubble.meta.js +158 -0
  132. package/dist/components/bubble/bubble.variants.js +90 -0
  133. package/dist/components/bubble/index.js +13 -0
  134. package/dist/components/button/button.js +87 -0
  135. package/dist/components/button/button.meta.js +302 -0
  136. package/dist/components/button/button.variants.js +137 -0
  137. package/dist/components/button/index.js +9 -0
  138. package/dist/components/calendar/calendar.js +89 -0
  139. package/dist/components/calendar/calendar.meta.js +136 -0
  140. package/dist/components/calendar/index.js +7 -0
  141. package/dist/components/card/card.js +203 -0
  142. package/dist/components/card/card.meta.js +212 -0
  143. package/dist/components/card/index.js +23 -0
  144. package/dist/components/carousel/carousel.js +428 -0
  145. package/dist/components/carousel/carousel.meta.js +100 -0
  146. package/dist/components/carousel/index.js +7 -0
  147. package/dist/components/chat-composer/chat-composer.js +174 -0
  148. package/dist/components/chat-composer/chat-composer.meta.js +241 -0
  149. package/dist/components/chat-composer/chat-composer.variants.js +17 -0
  150. package/dist/components/chat-composer/index.js +7 -0
  151. package/dist/components/checkbox/checkbox-mark.js +28 -0
  152. package/dist/components/checkbox/checkbox.js +30 -0
  153. package/dist/components/checkbox/checkbox.meta.js +127 -0
  154. package/dist/components/checkbox/index.js +7 -0
  155. package/dist/components/checkbox-card/checkbox-card.js +94 -0
  156. package/dist/components/checkbox-card/checkbox-card.meta.js +154 -0
  157. package/dist/components/checkbox-card/checkbox-card.variants.js +43 -0
  158. package/dist/components/checkbox-card/index.js +11 -0
  159. package/dist/components/code/code.js +19 -0
  160. package/dist/components/code/code.meta.js +71 -0
  161. package/dist/components/code/index.js +7 -0
  162. package/dist/components/code-block/code-block.js +84 -0
  163. package/dist/components/code-block/code-block.meta.js +103 -0
  164. package/dist/components/code-block/index.js +8 -0
  165. package/dist/components/collapsible/collapsible.js +38 -0
  166. package/dist/components/collapsible/collapsible.meta.js +117 -0
  167. package/dist/components/collapsible/index.js +15 -0
  168. package/dist/components/color-field/color-field.js +293 -0
  169. package/dist/components/color-field/color-field.meta.js +142 -0
  170. package/dist/components/color-field/color.js +69 -0
  171. package/dist/components/color-field/index.js +15 -0
  172. package/dist/components/combobox/combobox.js +486 -0
  173. package/dist/components/combobox/combobox.meta.js +217 -0
  174. package/dist/components/combobox/index.js +37 -0
  175. package/dist/components/command-menu/command-menu-trigger.js +122 -0
  176. package/dist/components/command-menu/command-menu.js +433 -0
  177. package/dist/components/command-menu/command-menu.meta.js +279 -0
  178. package/dist/components/command-menu/index.js +21 -0
  179. package/dist/components/context-card/context-card.js +62 -0
  180. package/dist/components/context-card/context-card.meta.js +106 -0
  181. package/dist/components/context-card/index.js +17 -0
  182. package/dist/components/context-menu/context-menu.js +92 -0
  183. package/dist/components/context-menu/context-menu.meta.js +138 -0
  184. package/dist/components/context-menu/index.js +23 -0
  185. package/dist/components/data-table/data-table.js +142 -0
  186. package/dist/components/data-table/data-table.meta.js +146 -0
  187. package/dist/components/data-table/index.js +9 -0
  188. package/dist/components/date-picker/date-picker.js +196 -0
  189. package/dist/components/date-picker/date-picker.meta.js +164 -0
  190. package/dist/components/date-picker/index.js +7 -0
  191. package/dist/components/date-tile/date-tile.js +29 -0
  192. package/dist/components/date-tile/date-tile.meta.js +72 -0
  193. package/dist/components/date-tile/index.js +7 -0
  194. package/dist/components/destructive-action-modal/destructive-action-modal.js +130 -0
  195. package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +136 -0
  196. package/dist/components/destructive-action-modal/index.js +9 -0
  197. package/dist/components/dialog/dialog.js +207 -0
  198. package/dist/components/dialog/dialog.meta.js +239 -0
  199. package/dist/components/dialog/index.js +45 -0
  200. package/dist/components/disclosure-picker/disclosure-picker.js +417 -0
  201. package/dist/components/disclosure-picker/disclosure-picker.meta.js +213 -0
  202. package/dist/components/disclosure-picker/index.js +21 -0
  203. package/dist/components/empty-state/empty-state.js +52 -0
  204. package/dist/components/empty-state/empty-state.meta.js +158 -0
  205. package/dist/components/empty-state/index.js +7 -0
  206. package/dist/components/entity/entity.js +70 -0
  207. package/dist/components/entity/entity.meta.js +108 -0
  208. package/dist/components/entity/entity.variants.js +16 -0
  209. package/dist/components/entity/index.js +7 -0
  210. package/dist/components/feedback/feedback.js +93 -0
  211. package/dist/components/feedback/feedback.meta.js +109 -0
  212. package/dist/components/feedback/index.js +7 -0
  213. package/dist/components/fieldset/fieldset.js +80 -0
  214. package/dist/components/fieldset/fieldset.meta.js +160 -0
  215. package/dist/components/fieldset/index.js +17 -0
  216. package/dist/components/file-dropzone/file-dropzone.js +193 -0
  217. package/dist/components/file-dropzone/file-dropzone.meta.js +210 -0
  218. package/dist/components/file-dropzone/file-dropzone.variants.js +26 -0
  219. package/dist/components/file-dropzone/index.js +13 -0
  220. package/dist/components/font-picker/font-picker.js +77 -0
  221. package/dist/components/font-picker/font-picker.meta.js +119 -0
  222. package/dist/components/font-picker/index.js +7 -0
  223. package/dist/components/form/form.js +145 -0
  224. package/dist/components/form/form.meta.js +142 -0
  225. package/dist/components/form/index.js +11 -0
  226. package/dist/components/gliding-highlight/gliding-highlight.js +50 -0
  227. package/dist/components/gliding-highlight/gliding-highlight.meta.js +135 -0
  228. package/dist/components/gliding-highlight/index.js +9 -0
  229. package/dist/components/gradient-art/gradient-art.js +168 -0
  230. package/dist/components/gradient-art/gradient-art.meta.js +81 -0
  231. package/dist/components/gradient-art/index.js +7 -0
  232. package/dist/components/gradient-art/renderer.js +336 -0
  233. package/dist/components/inline-status/index.js +7 -0
  234. package/dist/components/inline-status/inline-status.js +33 -0
  235. package/dist/components/inline-status/inline-status.meta.js +83 -0
  236. package/dist/components/inline-status/inline-status.variants.js +18 -0
  237. package/dist/components/input/index.js +7 -0
  238. package/dist/components/input/input.js +33 -0
  239. package/dist/components/input/input.meta.js +155 -0
  240. package/dist/components/kbd/index.js +8 -0
  241. package/dist/components/kbd/kbd.js +54 -0
  242. package/dist/components/kbd/kbd.meta.js +95 -0
  243. package/dist/components/label/index.js +7 -0
  244. package/dist/components/label/label.js +51 -0
  245. package/dist/components/label/label.meta.js +98 -0
  246. package/dist/components/line-chart/index.js +7 -0
  247. package/dist/components/line-chart/line-chart.js +10 -0
  248. package/dist/components/line-chart/line-chart.meta.js +222 -0
  249. package/dist/components/marker/index.js +7 -0
  250. package/dist/components/marker/marker.js +39 -0
  251. package/dist/components/marker/marker.meta.js +132 -0
  252. package/dist/components/marker/marker.variants.js +25 -0
  253. package/dist/components/marquee/index.js +7 -0
  254. package/dist/components/marquee/marquee.js +146 -0
  255. package/dist/components/marquee/marquee.meta.js +106 -0
  256. package/dist/components/media-image/index.js +7 -0
  257. package/dist/components/media-image/media-image.js +56 -0
  258. package/dist/components/media-image/media-image.meta.js +92 -0
  259. package/dist/components/media-tile/index.js +7 -0
  260. package/dist/components/media-tile/media-tile.js +135 -0
  261. package/dist/components/media-tile/media-tile.meta.js +133 -0
  262. package/dist/components/media-tile/media-tile.variants.js +48 -0
  263. package/dist/components/menu/index.js +33 -0
  264. package/dist/components/menu/menu.js +168 -0
  265. package/dist/components/menu/menu.meta.js +176 -0
  266. package/dist/components/message/index.js +7 -0
  267. package/dist/components/message/message.js +83 -0
  268. package/dist/components/message/message.meta.js +150 -0
  269. package/dist/components/message/message.variants.js +41 -0
  270. package/dist/components/message-scroller/index.js +9 -0
  271. package/dist/components/message-scroller/message-scroller.js +300 -0
  272. package/dist/components/message-scroller/message-scroller.meta.js +172 -0
  273. package/dist/components/meter/index.js +20 -0
  274. package/dist/components/meter/meter.js +112 -0
  275. package/dist/components/meter/meter.meta.js +227 -0
  276. package/dist/components/meter/meter.variants.js +61 -0
  277. package/dist/components/middle-truncate/index.js +7 -0
  278. package/dist/components/middle-truncate/middle-truncate.js +28 -0
  279. package/dist/components/middle-truncate/middle-truncate.meta.js +77 -0
  280. package/dist/components/note/index.js +9 -0
  281. package/dist/components/note/note.js +73 -0
  282. package/dist/components/note/note.meta.js +112 -0
  283. package/dist/components/note/note.variants.js +22 -0
  284. package/dist/components/notification-badge/index.js +15 -0
  285. package/dist/components/notification-badge/notification-badge.js +48 -0
  286. package/dist/components/notification-badge/notification-badge.meta.js +107 -0
  287. package/dist/components/notification-badge/notification-badge.variants.js +57 -0
  288. package/dist/components/number-field/index.js +7 -0
  289. package/dist/components/number-field/number-field.js +70 -0
  290. package/dist/components/number-field/number-field.meta.js +109 -0
  291. package/dist/components/otp-field/index.js +7 -0
  292. package/dist/components/otp-field/otp-field.js +70 -0
  293. package/dist/components/otp-field/otp-field.meta.js +197 -0
  294. package/dist/components/overflow-menu/index.js +7 -0
  295. package/dist/components/overflow-menu/overflow-menu.js +26 -0
  296. package/dist/components/overflow-menu/overflow-menu.meta.js +100 -0
  297. package/dist/components/page-header/index.js +7 -0
  298. package/dist/components/page-header/page-header.js +34 -0
  299. package/dist/components/page-header/page-header.meta.js +104 -0
  300. package/dist/components/page-loader/index.js +7 -0
  301. package/dist/components/page-loader/page-loader.js +37 -0
  302. package/dist/components/page-loader/page-loader.meta.js +85 -0
  303. package/dist/components/pagination/index.js +7 -0
  304. package/dist/components/pagination/pagination.js +105 -0
  305. package/dist/components/pagination/pagination.meta.js +141 -0
  306. package/dist/components/panel-group/index.js +10 -0
  307. package/dist/components/panel-group/panel-group.js +168 -0
  308. package/dist/components/panel-group/panel-group.meta.js +153 -0
  309. package/dist/components/panel-section/index.js +9 -0
  310. package/dist/components/panel-section/panel-section.js +115 -0
  311. package/dist/components/panel-section/panel-section.meta.js +156 -0
  312. package/dist/components/phone/index.js +7 -0
  313. package/dist/components/phone/phone.js +50 -0
  314. package/dist/{chunk-KFZ43RKU.js → components/phone/phone.meta.js} +3 -50
  315. package/dist/{chunk-NEQNXI2G.js → components/phone/status-icons.js} +4 -4
  316. package/dist/components/pie-chart/index.js +7 -0
  317. package/dist/components/pie-chart/pie-chart.js +191 -0
  318. package/dist/components/pie-chart/pie-chart.meta.js +210 -0
  319. package/dist/components/pill/index.js +9 -0
  320. package/dist/components/pill/pill.js +24 -0
  321. package/dist/components/pill/pill.meta.js +108 -0
  322. package/dist/components/pill/pill.variants.js +65 -0
  323. package/dist/components/popover/index.js +23 -0
  324. package/dist/components/popover/popover.js +100 -0
  325. package/dist/components/popover/popover.meta.js +136 -0
  326. package/dist/components/progress/index.js +19 -0
  327. package/dist/components/progress/progress.js +70 -0
  328. package/dist/components/progress/progress.meta.js +171 -0
  329. package/dist/components/progress/progress.variants.js +17 -0
  330. package/dist/components/radar-chart/index.js +7 -0
  331. package/dist/components/radar-chart/radar-chart.js +161 -0
  332. package/dist/components/radar-chart/radar-chart.meta.js +204 -0
  333. package/dist/components/radial-chart/index.js +7 -0
  334. package/dist/components/radial-chart/radial-chart.js +175 -0
  335. package/dist/components/radial-chart/radial-chart.meta.js +216 -0
  336. package/dist/components/radio/index.js +8 -0
  337. package/dist/components/radio/radio.js +39 -0
  338. package/dist/components/radio/radio.meta.js +104 -0
  339. package/dist/components/radio-card/index.js +7 -0
  340. package/dist/components/radio-card/radio-card.js +81 -0
  341. package/dist/components/radio-card/radio-card.meta.js +144 -0
  342. package/dist/components/radio-card/radio-card.variants.js +43 -0
  343. package/dist/components/relative-time/index.js +7 -0
  344. package/dist/components/relative-time/relative-time.js +47 -0
  345. package/dist/components/relative-time/relative-time.meta.js +77 -0
  346. package/dist/components/scroller/index.js +7 -0
  347. package/dist/components/scroller/scroller.js +111 -0
  348. package/dist/components/scroller/scroller.meta.js +118 -0
  349. package/dist/components/scrub-field/index.js +7 -0
  350. package/dist/components/scrub-field/scrub-field.js +124 -0
  351. package/dist/components/scrub-field/scrub-field.meta.js +139 -0
  352. package/dist/components/search-input/index.js +7 -0
  353. package/dist/components/search-input/search-input.js +91 -0
  354. package/dist/components/search-input/search-input.meta.js +129 -0
  355. package/dist/components/section-header/index.js +7 -0
  356. package/dist/components/section-header/section-header.js +39 -0
  357. package/dist/components/section-header/section-header.meta.js +174 -0
  358. package/dist/components/segmented-bar/index.js +14 -0
  359. package/dist/components/segmented-bar/segmented-bar.js +26 -0
  360. package/dist/components/segmented-bar/segmented-bar.meta.js +108 -0
  361. package/dist/components/segmented-bar/segmented-bar.variants.js +39 -0
  362. package/dist/components/select/index.js +25 -0
  363. package/dist/components/select/select.js +191 -0
  364. package/dist/components/select/select.meta.js +189 -0
  365. package/dist/components/separator/index.js +7 -0
  366. package/dist/components/separator/separator.js +42 -0
  367. package/dist/components/separator/separator.meta.js +84 -0
  368. package/dist/components/settings-menu/index.js +11 -0
  369. package/dist/components/settings-menu/settings-menu.js +101 -0
  370. package/dist/components/settings-menu/settings-menu.meta.js +177 -0
  371. package/dist/components/settings-menu/settings-menu.variants.js +15 -0
  372. package/dist/components/sheet/index.js +29 -0
  373. package/dist/components/sheet/sheet.js +121 -0
  374. package/dist/components/sheet/sheet.meta.js +167 -0
  375. package/dist/components/sheet/sheet.variants.js +21 -0
  376. package/dist/components/show-more/index.js +7 -0
  377. package/dist/components/show-more/show-more.js +87 -0
  378. package/dist/components/show-more/show-more.meta.js +79 -0
  379. package/dist/components/side-nav/index.js +13 -0
  380. package/dist/components/side-nav/side-nav.js +75 -0
  381. package/dist/components/side-nav/side-nav.meta.js +139 -0
  382. package/dist/components/side-nav/side-nav.variants.js +34 -0
  383. package/dist/components/sidebar/index.js +21 -0
  384. package/dist/components/sidebar/sidebar.js +196 -0
  385. package/dist/components/sidebar/sidebar.meta.js +118 -0
  386. package/dist/components/skeleton/index.js +7 -0
  387. package/dist/components/skeleton/skeleton.js +16 -0
  388. package/dist/components/skeleton/skeleton.meta.js +90 -0
  389. package/dist/components/slider/index.js +21 -0
  390. package/dist/components/slider/slider.js +115 -0
  391. package/dist/components/slider/slider.meta.js +214 -0
  392. package/dist/components/sparkline/index.js +7 -0
  393. package/dist/components/sparkline/sparkline.js +84 -0
  394. package/dist/components/sparkline/sparkline.meta.js +147 -0
  395. package/dist/components/spinner/index.js +7 -0
  396. package/dist/components/spinner/spinner.js +59 -0
  397. package/dist/components/spinner/spinner.meta.js +118 -0
  398. package/dist/components/status-bar/index.js +9 -0
  399. package/dist/components/status-bar/status-bar.js +40 -0
  400. package/dist/components/status-bar/status-bar.meta.js +117 -0
  401. package/dist/components/status-dot/index.js +9 -0
  402. package/dist/components/status-dot/status-dot.js +40 -0
  403. package/dist/components/status-dot/status-dot.meta.js +113 -0
  404. package/dist/components/status-dot/status-dot.variants.js +28 -0
  405. package/dist/components/stepper/index.js +17 -0
  406. package/dist/components/stepper/stepper.js +161 -0
  407. package/dist/components/stepper/stepper.meta.js +190 -0
  408. package/dist/components/stepper/stepper.variants.js +72 -0
  409. package/dist/components/streaming-text/index.js +7 -0
  410. package/dist/components/streaming-text/streaming-text.js +55 -0
  411. package/dist/components/streaming-text/streaming-text.meta.js +147 -0
  412. package/dist/components/switch/index.js +7 -0
  413. package/dist/components/switch/switch.js +26 -0
  414. package/dist/components/switch/switch.meta.js +133 -0
  415. package/dist/components/table/index.js +23 -0
  416. package/dist/components/table/table.js +117 -0
  417. package/dist/components/table/table.meta.js +143 -0
  418. package/dist/components/tabs/index.js +17 -0
  419. package/dist/components/tabs/tabs.js +58 -0
  420. package/dist/components/tabs/tabs.meta.js +120 -0
  421. package/dist/components/textarea/index.js +7 -0
  422. package/dist/components/textarea/textarea.js +38 -0
  423. package/dist/components/textarea/textarea.meta.js +117 -0
  424. package/dist/components/theme-selector/index.js +11 -0
  425. package/dist/components/theme-selector/theme-selector.js +145 -0
  426. package/dist/components/theme-selector/theme-selector.meta.js +181 -0
  427. package/dist/components/theme-switcher/index.js +7 -0
  428. package/dist/components/theme-switcher/theme-switcher.js +106 -0
  429. package/dist/components/theme-switcher/theme-switcher.meta.js +99 -0
  430. package/dist/components/thinking-orb/index.js +9 -0
  431. package/dist/components/thinking-orb/thinking-orb.js +126 -0
  432. package/dist/components/thinking-orb/thinking-orb.meta.js +142 -0
  433. package/dist/components/thinking-orb/thinking-orb.render.js +252 -0
  434. package/dist/components/toast/index.js +29 -0
  435. package/dist/components/toast/toast.js +254 -0
  436. package/dist/components/toast/toast.meta.js +261 -0
  437. package/dist/components/toggle/index.js +13 -0
  438. package/dist/components/toggle/toggle.js +51 -0
  439. package/dist/components/toggle/toggle.meta.js +129 -0
  440. package/dist/components/toolbar/index.js +27 -0
  441. package/dist/components/toolbar/toolbar.js +80 -0
  442. package/dist/components/toolbar/toolbar.meta.js +272 -0
  443. package/dist/components/toolbar/toolbar.variants.js +16 -0
  444. package/dist/components/tooltip/index.js +21 -0
  445. package/dist/components/tooltip/tooltip.js +74 -0
  446. package/dist/components/tooltip/tooltip.meta.js +156 -0
  447. package/dist/components/tree/index.js +12 -0
  448. package/dist/components/tree/tree.js +364 -0
  449. package/dist/components/tree/tree.meta.js +334 -0
  450. package/dist/components/video/index.js +7 -0
  451. package/dist/components/video/video.js +59 -0
  452. package/dist/components/video/video.meta.js +81 -0
  453. package/dist/components/workspace/index.js +9 -0
  454. package/dist/components/workspace/workspace.js +79 -0
  455. package/dist/components/workspace/workspace.meta.js +183 -0
  456. package/dist/foundations/accessibility.js +107 -0
  457. package/dist/foundations/index.js +25 -792
  458. package/dist/foundations/motion.js +135 -0
  459. package/dist/foundations/scrolling.js +62 -0
  460. package/dist/foundations/shape.js +113 -0
  461. package/dist/foundations/spacing.js +121 -0
  462. package/dist/foundations/surfaces.js +107 -0
  463. package/dist/foundations/types.js +0 -0
  464. package/dist/foundations/typography.js +152 -0
  465. package/dist/icon/Icon.js +30 -0
  466. package/dist/icon/IconProvider.js +27 -0
  467. package/dist/icon/compose.js +13 -0
  468. package/dist/icon/index.d.ts +13 -5
  469. package/dist/icon/index.js +13 -2
  470. package/dist/icon/registry.js +0 -0
  471. package/dist/icon-sets/brands.generated.js +123 -0
  472. package/dist/icon-sets/brands.js +10 -123
  473. package/dist/icon-sets/reicon.d.ts +3 -1
  474. package/dist/icon-sets/reicon.js +209 -1
  475. package/dist/index.d.ts +618 -34
  476. package/dist/index.js +211 -11351
  477. package/dist/ios/chrome.js +36 -0
  478. package/dist/ios/index.js +19 -315
  479. package/dist/ios/liquid-glass.js +103 -0
  480. package/dist/ios/native-app.js +46 -0
  481. package/dist/ios/navigation.js +63 -0
  482. package/dist/ios/phone-frame.js +64 -0
  483. package/dist/ios/preview-theme.js +83 -0
  484. package/dist/ios/preview.js +10 -137
  485. package/dist/ios/sheet.js +85 -0
  486. package/dist/ios/theme.js +19 -0
  487. package/dist/layout/AspectBox.js +27 -0
  488. package/dist/layout/Bleed.js +15 -0
  489. package/dist/layout/Center.js +21 -0
  490. package/dist/layout/Columns.js +70 -0
  491. package/dist/layout/Grid.js +50 -0
  492. package/dist/layout/GridOverlay.js +41 -0
  493. package/dist/layout/Page.js +11 -0
  494. package/dist/layout/Region.js +39 -0
  495. package/dist/layout/Row.js +45 -0
  496. package/dist/layout/SafeArea.js +26 -0
  497. package/dist/layout/Spacer.js +9 -0
  498. package/dist/layout/SplitPane.js +55 -0
  499. package/dist/layout/Stack.js +42 -0
  500. package/dist/layout/_responsive.js +16 -0
  501. package/dist/layout/_safeArea.js +18 -0
  502. package/dist/layout/index.d.ts +30 -8
  503. package/dist/layout/index.js +14 -2
  504. package/dist/lib/anchored-scroll.js +7 -0
  505. package/dist/lib/bleed.js +7 -0
  506. package/dist/lib/chart/cartesian.js +365 -0
  507. package/dist/lib/chart/chart-frame.js +163 -0
  508. package/dist/lib/chart/chart-legend.js +13 -0
  509. package/dist/lib/chart/chart-tooltip.js +38 -0
  510. package/dist/lib/chart/format.js +24 -0
  511. package/dist/lib/chart/geometry.js +114 -0
  512. package/dist/lib/chart/motion.js +34 -0
  513. package/dist/lib/chart/scale.js +72 -0
  514. package/dist/lib/chart/types.js +45 -0
  515. package/dist/lib/chart/use-arrival.js +41 -0
  516. package/dist/{chunk-SRQWQUTW.js → lib/cn.js} +5 -5
  517. package/dist/lib/component-guidance.js +215 -0
  518. package/dist/lib/density.js +54 -0
  519. package/dist/lib/field-shell.js +11 -0
  520. package/dist/lib/glyphs.js +9 -0
  521. package/dist/lib/heading-row.js +36 -0
  522. package/dist/lib/highlight-glide.js +84 -0
  523. package/dist/lib/hit-target.js +5 -0
  524. package/dist/lib/meta.js +0 -0
  525. package/dist/lib/portal-theme-scope.js +74 -0
  526. package/dist/{chunk-FKZXAFIY.js → lib/presentation.js} +8 -3
  527. package/dist/lib/refs.js +14 -0
  528. package/dist/lib/scrub-gesture.js +50 -0
  529. package/dist/lib/segmented.js +13 -0
  530. package/dist/lib/selection-card.js +41 -0
  531. package/dist/lib/status-tone.js +13 -0
  532. package/dist/lib/taxonomy.js +214 -0
  533. package/dist/lib/trim-label.js +20 -0
  534. package/dist/lib/use-copy.js +27 -0
  535. package/dist/lib/use-edge-fade.js +32 -0
  536. package/dist/lib/use-isomorphic-layout-effect.js +8 -0
  537. package/dist/lib/use-resize-observer.js +18 -0
  538. package/dist/lib/use-scroll-activity.js +52 -0
  539. package/dist/lib/use-wheel-to-horizontal.js +65 -0
  540. package/dist/motion/MotionProvider.js +75 -0
  541. package/dist/motion/Presence.js +112 -0
  542. package/dist/motion/Reveal.js +42 -0
  543. package/dist/motion/TextReveal.js +61 -0
  544. package/dist/motion/index.js +24 -0
  545. package/dist/motion/useInView.js +36 -0
  546. package/dist/motion/useReducedMotion.js +25 -0
  547. package/dist/motion/viewTransition.js +47 -0
  548. package/dist/patterns/auth-page.js +90 -0
  549. package/dist/patterns/auth-page.meta.js +27 -0
  550. package/dist/patterns/brand-landing-page.js +341 -0
  551. package/dist/patterns/brand-landing-page.meta.js +28 -0
  552. package/dist/patterns/chat-panel.js +185 -0
  553. package/dist/patterns/chat-panel.meta.js +31 -0
  554. package/dist/patterns/dashboard-page.js +332 -0
  555. package/dist/patterns/dashboard-page.meta.js +29 -0
  556. package/dist/patterns/feed-page.js +205 -0
  557. package/dist/patterns/feed-page.meta.js +25 -0
  558. package/dist/patterns/index.js +34 -2140
  559. package/dist/patterns/landing-page.js +356 -0
  560. package/dist/patterns/landing-page.meta.js +26 -0
  561. package/dist/patterns/list-detail-page.js +373 -0
  562. package/dist/patterns/list-detail-page.meta.js +24 -0
  563. package/dist/patterns/settings-page.js +268 -0
  564. package/dist/patterns/settings-page.meta.js +27 -0
  565. package/dist/presets/index.d.ts +7 -1
  566. package/dist/presets/index.js +53 -185
  567. package/dist/styles.css +118 -3
  568. package/dist/{theme-provider-kQszHYji.d.ts → theme-provider-S0NVJs2Y.d.ts} +6 -1
  569. package/dist/{chunk-NETUFI34.js → themes/_contract.js} +14 -2
  570. package/dist/themes/atelier/index.js +17 -1
  571. package/dist/themes/contrast.js +288 -0
  572. package/dist/themes/ember/ember.css +6 -6
  573. package/dist/themes/ember/index.js +16 -1
  574. package/dist/themes/index.d.ts +1 -1
  575. package/dist/themes/index.js +3 -8
  576. package/dist/themes/meridian/index.js +16 -1
  577. package/dist/themes/poppy/index.js +17 -1
  578. package/dist/themes/prism/index.js +17 -1
  579. package/dist/themes/prism/prism.css +2 -2
  580. package/dist/themes/registry.js +59 -0
  581. package/dist/themes/sable/index.js +16 -1
  582. package/dist/{chunk-EFLC4NA3.js → themes/theme-provider.js} +38 -54
  583. package/dist/themes/verdant/index.js +17 -1
  584. package/llms/blocks/media-player-block.md +20 -7
  585. package/llms/blocks/product-card-block.md +6 -4
  586. package/llms/blocks/task-list-block.md +1 -1
  587. package/llms/blocks/traffic-sources-block.md +1 -1
  588. package/llms/blocks/traffic-trend-block.md +5 -3
  589. package/llms/blocks/verify-code-block.md +1 -1
  590. package/llms/components/app-shell.md +5 -2
  591. package/llms/components/avatar.md +25 -3
  592. package/llms/components/button.md +20 -1
  593. package/llms/components/card.md +1 -1
  594. package/llms/components/disclosure-picker.md +168 -0
  595. package/llms/components/font-picker.md +104 -0
  596. package/llms/components/media-image.md +98 -0
  597. package/llms/components/media-tile.md +143 -0
  598. package/llms/components/page-header.md +6 -5
  599. package/llms/components/pie-chart.md +2 -2
  600. package/llms/components/radial-chart.md +1 -1
  601. package/llms/components/section-header.md +144 -0
  602. package/llms/components/sheet.md +10 -0
  603. package/llms/components/sidebar.md +4 -3
  604. package/llms/components/slider.md +14 -2
  605. package/llms/components/theme-selector.md +44 -4
  606. package/llms/components/tree.md +3 -3
  607. package/llms/layout/grid.md +24 -3
  608. package/llms/layout/region.md +16 -3
  609. package/llms/patterns/dashboard-page.md +3 -6
  610. package/llms/patterns/feed-page.md +4 -4
  611. package/llms/patterns/list-detail-page.md +1 -0
  612. package/llms/patterns/settings-page.md +2 -5
  613. package/llms/reference/utilities.md +84 -4
  614. package/llms/tasks/building-a-page.md +34 -3
  615. package/llms/tasks/converting-an-app.md +14 -4
  616. package/llms/tasks/creating-a-preset.md +4 -1
  617. package/llms/tasks/reviewing-a-design.md +19 -0
  618. package/llms/tasks/using-presets.md +19 -0
  619. package/llms-full.txt +1066 -78
  620. package/llms.txt +26 -9
  621. package/package.json +1 -1
  622. package/registry.json +2342 -164
  623. package/scripts/agent-setup.mjs +90 -0
  624. package/scripts/design-presets.mjs +59 -6
  625. package/scripts/grade.mjs +195 -29
  626. package/scripts/lint-rules.mjs +486 -10
  627. package/scripts/review-evidence.mjs +28 -1
  628. package/skills/sable/SKILL.md +67 -24
  629. package/targets/ios.json +45 -0
  630. package/dist/chunk-3IWZYBGH.js +0 -398
  631. package/dist/chunk-7MG4SHYX.js +0 -15
  632. package/dist/chunk-7QSDYBXO.js +0 -7421
  633. package/dist/chunk-ADLLDWLW.js +0 -15
  634. package/dist/chunk-C4Z3CF54.js +0 -15
  635. package/dist/chunk-EDXLM7YL.js +0 -5078
  636. package/dist/chunk-EHH4MHO6.js +0 -14
  637. package/dist/chunk-K7RUHUGC.js +0 -14
  638. package/dist/chunk-KO3NAEBF.js +0 -15
  639. package/dist/chunk-L4IEIQQA.js +0 -14
  640. package/dist/chunk-QJ6FWTUG.js +0 -40
  641. package/dist/chunk-VVW4UCDI.js +0 -4908
  642. package/dist/chunk-XAZGMY73.js +0 -187
package/AGENTS.md CHANGED
@@ -32,7 +32,7 @@ library gap to report, not a reason for a native control.
32
32
 
33
33
  These are short on purpose. Each links to a foundations doc with the full vocabulary, the reasoning, and before/after examples; `sable-lint` catches most violations.
34
34
 
35
- 0. **Compose from Sable components; never rebuild one.** Before writing any interactive or recognisable element — a button, a field, a toggle, tabs, a menu, a badge, a card, a search box, a dialog — run `npx sable search "<what you need>"` and use what it returns. A raw `<button>`, `<input>` or `<div>` dressed in a component's classes is a reinvention, and it is a bug even when every class is a semantic token: it misses the component's states, density, focus and keyboard contract, and drifts the moment the component changes. A link that looks like a button is `<Button render={<a href=… />}>`. If nothing fits, compose from the nearest component's parts; if it genuinely does not exist, say so in your summary — that is a library gap to report, not a license to invent. The rules below are for styling what you compose *around* components, not for re-creating them. `sable-lint` flags the common cases (`hand-rolled-control`, `lookalike-control`).
35
+ 0. **Compose from Sable components; never rebuild one.** Before writing any interactive or recognisable element — a button, a field, a toggle, tabs, a menu, a badge, a card, a search box, a dialog — run `npx sable search "<what you need>"` and use what it returns. A raw `<button>`, `<input>` or `<div>` dressed in a component's classes is a reinvention, and it is a bug even when every class is a semantic token: it misses the component's states, density, focus and keyboard contract, and drifts the moment the component changes. A link that looks like a button is `<Button render={<a href=… />}>`. If nothing fits, compose from the nearest component's parts; if it genuinely does not exist, say so in your summary — that is a library gap to report, not a license to invent. The rules below are for styling what you compose *around* components, not for re-creating them. `sable-lint` flags the common cases (`hand-rolled-control`, `lookalike-control`), and the bigger assemblies built from utilities and tokens: a raw `grid` of mapped items is `<Grid cols minItemWidth>` (`lookalike-grid`), a link or button wearing a card's radius, surface and padding is `<Card render={<Link … />}>` (`lookalike-card`), a round fixed-size box holding a picture, GradientArt or initials is `<Avatar>` (`lookalike-avatar`), and an `aspect-*` + `overflow-hidden` frame around media is `<AspectBox ratio>` (`lookalike-media-frame`). `sable check` also lists every app-owned surface (a card fill, a bordered padded box, a media frame, a fixed-size circle) for your reuse inventory: each one composes the component named beside it, or the inventory says why the app owns it.
36
36
 
37
37
  **The same goes for everything below a component. Resolve in this order, and stop at the first that fits:** (1) a **component** → (2) a Sable **utility or recipe** — relative surfaces (`sable-surface*`, `sable-well`, `sable-surface-interactive`), state variants (`sable-selected:`, `sable-highlighted:`…), geometry (`rounded-concentric-*`, `popup-inset`, `rounded-popup-item`, `popup-bleed`), scroll policies, `motion-*` recipes → (3) a **semantic token** → (4) hand-written CSS, last, and reported as a gap. `npx sable search "<what you need>"` searches all of these; `registry.json → utilities` lists the utilities with intent and what each replaces. Mixing ink into a surface by hand (`color-mix(… var(--foreground) …)`) is the tell that you skipped step 2 — that is what the relative-surface utilities compute, in every theme and mode.
38
38
 
@@ -61,7 +61,7 @@ The machine-readable version of this section is the `consume` block in `registry
61
61
 
62
62
  `styles.css` carries **Sable only** — the default theme, and the layout floor every other theme inherits (`--page-inset`, `--region-*`, `--rhythm-unit` on a bare `:root`). The bundled **alternates are opt-in**, because they are `[data-sable-theme]`-scoped and an app that never sets the attribute ships CSS that can never match: 5.5 KB gzip of permanently-dead weight, which is exactly how a real app's build audit found this. Using one? `@import "@sableui/react/theme/prism.css";`. Offering the whole set through a `<ThemeSelector>`? `@import "@sableui/react/themes.css";`. **Symptom of forgetting:** `data-sable-theme` is set, nothing matches, and the page silently renders as the Sable theme — no error, just the wrong theme.
63
63
  4. **Fonts:** load Figtree (sans) + DM Mono (mono) — the theme declares the stacks, the host provides the files. Apply `antialiased` (`-webkit-font-smoothing: antialiased` / `-moz-osx-font-smoothing: grayscale`) **once on the app root**: Sable's type scale was drawn against smoothed text, and without it macOS renders every weight heavier than designed. The package deliberately doesn't set it — a library has no business styling the host's whole page — so it has to happen here.
64
- 5. **Providers (all optional):** components render with **Reicon** by default, so `<IconProvider set={…}>` is only needed to replace it with another icon package or extend it using `composeIconSets(appExtras, reiconIconSet)` (list the added names in `.sable/icons.json` as `{ "names": [...] }` so `sable grade` accepts them); `<MotionProvider>` is optional too. Use `<SableThemeProvider>` + `useSableTheme()` when the app should expose user-selectable themes; pair `themeId` / `setThemeId` with `<ThemeSelector>` and `preference` / `setPreference` with `<ThemeSwitcher>`. The provider stores `light` / `dark` / `system`, toggles the `dark` class on `<html>`, stores the active design theme, and writes `data-sable-theme` for non-default themes. Apps with their own state layer can call `applySableColorMode()` / `applySableTheme()` instead. You only need a Tailwind `@custom-variant dark` if **your** app code uses `dark:` utilities (Sable ships none).
64
+ 5. **Providers:** `<IconProvider>` is **required** — wrap the app once in `<IconProvider set={reiconIconSet}>` with `import { reiconIconSet } from "@sableui/react/icons/reicon"` (without it every `<Icon>` renders nothing; development builds warn once). Pass another icon package's set instead to replace Reicon, or extend it using `composeIconSets(appExtras, reiconIconSet)` (list the added names in `.sable/icons.json` as `{ "names": [...] }` so `sable grade` accepts them); `<MotionProvider>` is optional too. Use `<SableThemeProvider>` + `useSableTheme()` when the app should expose user-selectable themes; pair `themeId` / `setThemeId` with `<ThemeSelector>` and `preference` / `setPreference` with `<ThemeSwitcher>`. The provider stores `light` / `dark` / `system`, toggles the `dark` class on `<html>`, stores the active design theme, and writes `data-sable-theme` for non-default themes. Apps with their own state layer can call `applySableColorMode()` / `applySableTheme()` instead. You only need a Tailwind `@custom-variant dark` if **your** app code uses `dark:` utilities (Sable ships none).
65
65
  6. Import from the root barrel: `import { Button } from "@sableui/react"`. Page shells live on the `patterns` subpath (`import { SettingsPage } from "@sableui/react/patterns"`) — preview one, then copy its source from `registry.json → patterns` when the route needs to diverge. Blocks — section-sized compositions such as a sign-in card or a pricing card — live on the `blocks` subpath (`import { SignInBlock } from "@sableui/react/blocks"`) and ship the same way (`registry.json → blocks`); copy one and edit its content constants. A block's props are only `className`, `framed` and its primary action's callback, never content.
66
66
 
67
67
  7. **Check the wiring:** `npx sable doctor`. Every check it runs corresponds to a way Sable fails *silently* — a second React (blank page, "invalid hook call"), a missing `@source` (Tailwind emits none of the package's classes, everything renders unstyled), or `data-sable-theme` set with no theme CSS imported (every theme renders as the Sable theme and the switcher looks broken). Exits 1 on any failure; `--json` for the structured report.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,320 @@
1
1
  # What's new
2
2
 
3
+ ## 0.7.1
4
+
5
+ Fixes from building a customer-support app on 0.7.0 with an app-owned preset,
6
+ plus guidance for choosing a page width. Nothing to migrate.
7
+
8
+ - **A table in a flush card keeps the card's corners.** `Card` now clips to its
9
+ rounded corners when it holds a `CardContent flush`, as it already did for a
10
+ `CardList`. The hover fill of an edge-to-edge `Table` or `DataTable` row and a
11
+ `TableFooter`'s tint no longer poke square corners out past the card's.
12
+ - **Only body rows hover.** `TableRow` lifts on hover inside `TableBody`; header
13
+ and footer rows stay still, since nothing there responds to a click.
14
+ - **SideNav and Sidebar icons stay level with their labels at any spacing
15
+ step.** The icon box was sized in spacing steps around an icon sized in type,
16
+ so a theme with a step other than 4px shrank the box and dropped every icon
17
+ about 2px below its label. The box is now sized in the icon's own unit and
18
+ centred with flex.
19
+ - **A donut's number holds the centre.** In `PieChart` and a full-ring
20
+ `RadialChart`, `centerLabel`'s value sits on the centre and its caption hangs
21
+ below, instead of the pair splitting the centre between them.
22
+ - **`centerLabel.value` takes a number.** It is written in full up to 9,999 and
23
+ shortened from 10,000 on (`10.2K`, `102.1K`, `1.2M`), in the chart's
24
+ `locale`. A string still shows as written, for units.
25
+ - **Presets can set page widths.** `page-inset` and the role caps
26
+ (`region-form`, `-feed`, `-content`, `-wide`, `-full`) are preset tokens, so a
27
+ product's widths live in its preset rather than app CSS. They are opt-in:
28
+ `sable preset --template` and Canvas exports leave them out, so a preset never
29
+ overrides widths an app already sets. `PAGE_WIDTH_TOKENS` lists them.
30
+ - **Guidance for choosing a page width.** `sable docs building-a-page` has a
31
+ table of which `max` to use by what the person does on the page, and when to
32
+ go fluid. The Region docs say `full` is the largest cap (edge to edge is
33
+ `bleed`) and show a fluid workspace; the old "Fluid dashboard" example was
34
+ capped and is retitled. `reviewing-a-design` asks for a 1920px capture of a
35
+ capped or fluid route, where the choice shows, and the skill's brief records
36
+ each route's width.
37
+ - **A pasted preset gets applied.** Canvas's "Copy for agent" message now asks
38
+ the agent to apply the preset and build the app, not only save it. The skill
39
+ treats a preset supplied for an existing app as a restyle, `sable docs
40
+ using-presets` has an "Apply to an existing app" section, and a passing
41
+ `sable preset --check` lists the steps that make an app use the preset.
42
+ - **`TrafficTrendBlock` fits a narrow card.** Below 24rem its range toggles
43
+ move under the title instead of squeezing it.
44
+
45
+ ## 0.7.0
46
+
47
+ A dogfooding release. An agent built a music streaming app on Sable with
48
+ Sable's own skill, and this release closes the gaps it hit:
49
+ - media tiles, an image fallback and a persistent player dock;
50
+ - section headers, portrait avatars and in-place pickers;
51
+ - labelled sliders and focusable text links.
52
+ The package is also much smaller per import, `sable check` runs the whole
53
+ finish check in one command, and lint now catches hand-assembled lookalikes of
54
+ Sable components.
55
+
56
+ ### Upgrading from 0.6
57
+
58
+ - **Icons are opt-in.** Wrap the app once in `<IconProvider set={reiconIconSet}>`,
59
+ with `reiconIconSet` from `@sableui/react/icons/reicon`. Without it, every
60
+ `<Icon>` (including those inside Sable components) renders nothing, and
61
+ `sable doctor` reports it.
62
+ - **`SableThemeProvider` defaults to `"system"`.** Pass
63
+ `defaultPreference="dark"` to keep the old first-visit mode.
64
+ - **`Grid minItemWidth` keeps its empty columns (`auto-fill`).** Pass
65
+ `track="fit"` where a fixed set should stretch across the row.
66
+ - **Button's side padding is a variable.** A `px-*` className on a Button no
67
+ longer matches its optical alignment (`bleed`). Set `--button-px` instead.
68
+
69
+ ### Everything in this release
70
+
71
+ - **`SableThemeProvider` follows the visitor's OS on a first visit.**
72
+ `defaultPreference` now defaults to `"system"` instead of `"dark"`. A stored
73
+ choice still wins. **Behaviour change:** an app that relied on the implicit
74
+ dark default should pass `defaultPreference="dark"` (or its preset's
75
+ `defaultMode`).
76
+ - **`Slider` names the slider, not its wrapper.** `aria-label` used to land on
77
+ the Root's `role="group"`, so screen readers announced an unnamed slider. It
78
+ now goes to each thumb's input. New `getAriaLabel` (per thumb, for ranges)
79
+ and `getAriaValueText` (e.g. a seek bar's "1:23 of 3:45").
80
+ - **AppShell `dock` slot** for persistent chrome such as a now-playing or call
81
+ bar. It is full width and sticky to the bottom, above `bottomNav` on mobile,
82
+ and the app Sidebar shortens to clear it.
83
+ - **`link-underline` carries a focus-visible outline** in `--ring`, so plain
84
+ text links meet the focus floor without app CSS.
85
+ - **Media icons:** `shuffle`, `repeat`, `repeat-one`, `volume`, `volume-low`,
86
+ `volume-off`, `queue`, `music`.
87
+ - **`--theme-art-brand` hook.** Ember's and Prism's art (and the default art
88
+ ground) key off it, falling back to `--primary`. Set it wherever the primary
89
+ action stops being the brand, such as a neutral black/white primary, so the
90
+ art keeps the brand colour.
91
+ - **`sable lint`'s `static-interactive` rule reads variants, not substrings.**
92
+ A capability query like `[@media(hover:none)]:opacity-100` is no longer
93
+ mistaken for a hover style.
94
+ - **Breaking: icons are opt-in.** `<Icon>` no longer falls back to Reicon, so
95
+ an app that brings its own set ships none of Reicon's artwork (about 190 KB
96
+ minified). Without an `IconProvider`, icons render nothing and a development
97
+ build warns once. `reiconIconSet`, `reiconIconNames` and the deprecated
98
+ `fallbackIconSet` moved off the root (and `/icons`) barrel to
99
+ `@sableui/react/icons/reicon`. **Migrate:** wrap the app once in
100
+ `<IconProvider set={reiconIconSet}>` with
101
+ `import { reiconIconSet } from "@sableui/react/icons/reicon"`.
102
+ - **Smaller bundles: the package ships one module per source file.** `dist`
103
+ keeps the `src` layout instead of a few large bundles, so a bundler drops
104
+ every component an app does not import. Importing only `Button` used to pull
105
+ in about 70% of the library under esbuild; the import paths and exports are
106
+ unchanged. Each module also keeps its `"use client"` directive.
107
+ - **GradientArt loads its WebGL renderer on first paint.** The shader code
108
+ (about 43 KB minified) is a separate chunk, so a plain `Avatar` no longer
109
+ ships it, and art still shows its CSS stand-in until the shader fades in.
110
+ - **`OtpField` resolves Base UI's OTP parts when it first renders**, so apps
111
+ that never use it no longer carry Base UI's OTP Field.
112
+ - **`sable check`: one command to run before calling a build done.** It runs
113
+ `sable doctor`, `sable lint` and `sable grade` on the given paths (default
114
+ `src`), prints one pass/fail summary, exits 1 if any fails, and then lists the
115
+ review steps no tool can verify (rendered design, journey, motion, reduced
116
+ motion, reuse inventory, lessons). `--json` returns `{ type: "check", data }`.
117
+ The skill's finish checklist now opens the skill and starts with it.
118
+ - **`sable setup --agent` offers a Claude Code Stop hook** that runs
119
+ `sable check --stop-hook` before Claude can end a task: on failure it exits 2
120
+ with the findings on stderr, so Claude keeps working (once; a second
121
+ consecutive block lets it stop). Setup prints the exact `.claude/settings.json`
122
+ entry and writes it only with `--hook`, merging into existing settings. The
123
+ command is one plain invocation with no `cd`, `&&` or `${…}`, so it runs under
124
+ Git Bash and PowerShell 5.1 alike; `sable check --stop-hook` locates the
125
+ project from `CLAUDE_PROJECT_DIR` (plus `--root <dir>` for a workspace
126
+ package, reached with the package manager's workspace flag).
127
+ - **`sable grade` judges each import against the subpath it came from.** It
128
+ checked every `@sableui/react/*` import against the root barrel, so correct
129
+ imports from `/blocks`, `/presets` or `/icons/reicon` read as hallucinations
130
+ (25 in Sable's own docs app). Each subpath's surface is now read from the
131
+ package `exports`, an unpublished subpath is reported as such, and
132
+ `reiconIconSet` imported from the root is caught. Props and icon names are
133
+ judged only on components imported from Sable (aliases followed), not on an
134
+ app's own `PageHeader` or `Icon`, and not on JSX inside template-literal code
135
+ samples. Media attributes such as `autoPlay` are accepted.
136
+ - **`sable doctor` checks icons.** An app that imports Sable with no
137
+ `<IconProvider>` fails with the fix, since every icon would render nothing.
138
+ - **Global flags work on every command.** `sable learn --search "…" --dense`
139
+ failed with "Unknown flag: --dense"; `--dense` and `--detail` are now taken out
140
+ before a command hands off to its script, and `--json --dense` prints that
141
+ script's JSON on one line. A switch no longer swallows the next word
142
+ (`sable component --dense Button`), and a bad `--detail` value is an error on
143
+ every command.
144
+ - **Every utility in styles.css is retrievable.** `sable utility
145
+ motion-icon-swap` answered ERR_UNKNOWN_UTILITY: the 24 `motion-*`/`animate-*`
146
+ recipes were only names in `tokens.motion.recipes`. They now have full entries
147
+ (family `motion`), and `sable utility text-heading-1` resolves the paired type
148
+ tiers. A test asks the CLI for every `@utility` and `@custom-variant`, with an
149
+ explicit `INTERNAL_UTILITIES` list for deliberate exceptions.
150
+ - **Providers are in the catalog.** New `sable provider [Name]` and
151
+ `registry.json → providers` cover SableThemeProvider, IconProvider,
152
+ MotionProvider, ToastProvider, TooltipProvider, CodeHighlightProvider,
153
+ DensityProvider and PresentationProvider: whether each is required, where it
154
+ goes, props and examples. `sable component SableThemeProvider` answers with the
155
+ provider, and search finds them. The build fails on an exported `*Provider`
156
+ documented nowhere.
157
+ - **`sable docs AGENTS` serves the package's AGENTS.md** (also README,
158
+ CONTRIBUTING, DESIGN, CHANGELOG and llms), the file llms.txt points agents to
159
+ first. An unknown doc now lists the package and task guide names.
160
+ - **`sable lint --json`** returns `{ type: "lint", data: { violations, … } }`.
161
+ The skill already told agents to pass `--json`; it printed text.
162
+ - **`sable preset --check` warns when a preset is newer than the installed
163
+ Sable.** A preset authored with a later `sableVersion` used to validate
164
+ silently. It still validates, but now prints both versions and the upgrade
165
+ command for the project's package manager; `--json` carries it as
166
+ `warnings: [{ code: "WARN_PRESET_NEWER_SABLE", authoredWith, installed,
167
+ suggestion }]`. `--css` reports the same warning.
168
+ - **`sable grade --template` shows what an artifact looks like.** It now starts
169
+ with one example screenshot and one example report artifact (they fail
170
+ validation until replaced with real captures), and `sable docs
171
+ reviewing-a-design` lists every artifact field with its meaning.
172
+ - **`SheetList`**: edge-to-edge rows in a Sheet (a queue, notifications, a
173
+ picker), the Sheet equivalent of `CardList`. Entity rows line up with
174
+ `SheetHeader` instead of sitting 12px in, with no `--entity-px` override. The
175
+ Sheet's padding is now the `--sheet-p` variable.
176
+ - **`SidebarBrand`**: the brand row on the same grid as `SidebarItem`, with the
177
+ mark in the icon slot and the name on the label column. It collapses to the
178
+ mark. The dashboard and settings patterns use it.
179
+ - **GradientArt gives each seed its own variant of the palette.** The palette
180
+ turns by up to ±20° of hue and ±0.04 lightness, so presets whose art is one
181
+ colour family (Ember, brand-derived palettes) still tell artists and covers
182
+ apart. The same seed still paints the same art.
183
+ - **Selected table rows paint their fill on the cells**, each overlapping the
184
+ next by 1px, to rule out hairline seams at fractional column widths.
185
+ - **`MediaPlayerBlock`'s seek bar runs in seconds.** The readouts follow the
186
+ drag, and the slider announces "1:24 of 3:40".
187
+ - **`static-interactive` explains display-only hovers.** When a hover only
188
+ flips `display`, visibility or an underline, the hint points at an opacity
189
+ swap or `motion-icon-swap` instead of `motion-interactive`.
190
+ - **ThemeSelector draws each theme as a miniature of itself.** Each tile is
191
+ the theme's background, "Aa" in its display face, a card, and a primary
192
+ button whose corners scale from the theme's real radii, so it shows shape as
193
+ well as colour. Four of six themes used to come out as the same
194
+ white-and-black strip. Names sit in the app's face, so every option is the
195
+ same height. The selected tile takes an outline in the app's own ink, and
196
+ list rows carry a small tile. A light preview is now pinned with `.light`, so
197
+ a dark page no longer paints it dark. The docs' Customize panel uses this
198
+ component instead of its own preset tiles.
199
+ - **Segmented controls follow the theme's control shape.** ThemeSwitcher and
200
+ ToggleGroup `appearance="segmented"` take `--button-radius`, the same corners
201
+ as Button: square in Meridian, 12px in Poppy, a capsule in Sable.
202
+ - **`sable lint` catches assemblies, not just controls.** Four new
203
+ consumer rules (new in 0.7.0, so `sable-lint --since 0.6.2` lists them) find
204
+ pieces built from Sable's utilities and tokens, which every token rule passes:
205
+ `lookalike-grid` (a raw `grid` over mapped or repeated items, or an
206
+ auto-fill `minmax()` track → `Grid`, with the `cols` map spelled out),
207
+ `lookalike-card` (an `<a>`, `<button>` or router `<Link>` with a card's
208
+ radius, surface and padding → `Card render={…}`), `lookalike-avatar` (a
209
+ round fixed-size box holding an image, GradientArt or initials → `Avatar`)
210
+ and `lookalike-media-frame` (`aspect-*` + `overflow-hidden` around media →
211
+ `AspectBox`). Each needs a signal beyond the classes, so a form grid, a text
212
+ link, a status dot or an empty ratio box is left alone. The docs site's own
213
+ item grids now compose `Grid`.
214
+ - **`sable grade` and `sable check` list app-owned surfaces.** Elements with a
215
+ card fill, a bordered padded box, a media frame or a fixed-size circle are
216
+ listed with the component to consider (`inventory` in `--json`). The list is
217
+ never scored and never fails a check; it is the starting point for the reuse
218
+ inventory.
219
+ - **`MediaTile`: a cover-first tile** for albums, videos, podcasts, galleries
220
+ and shelves. Square, `video` or `poster` cover, a title and subtitle that
221
+ can each be links (`href` / `render={<Link />}`, `subtitleHref` /
222
+ `subtitleRender`), an `action` over the cover's corner and a `badge` on it.
223
+ The whole tile follows the title's link without wrapping anything in an
224
+ `<a>`, so the artist link and the play button stay separate controls; Tab
225
+ goes title → subtitle → action.
226
+ - **`MediaImage`: an image with a fallback.** It fills its box, rests on a
227
+ muted surface while loading, fades the image in, and paints GradientArt
228
+ seeded by the item when there is no `src` or it fails to load. It uses
229
+ Base UI's image-load tracking, the same Avatar uses, so a failure before
230
+ hydration is still caught. `MediaPlayerBlock` and `ProductCardBlock` use it
231
+ for their artwork; add the image URL to their content constant.
232
+ - **`reveal-on-hover` utility** for row and tile actions that wait for hover.
233
+ It hides the action only on devices that can hover, shows it on hover or
234
+ focus within the nearest `group` (or `data-reveal-scope`) and while its menu
235
+ is open, and fades on the motion tokens. The action stays in the tab order
236
+ and the accessibility tree, and touch devices always show it. `sable lint`'s
237
+ `static-interactive` rule accepts it as a recipe and now suggests it for a
238
+ hand-written `group-hover:opacity-*` reveal.
239
+ - **`stretched-link` utility.** One link covers its whole item while the
240
+ item's other controls stay clickable, and its focus ring goes around the
241
+ item (`--stretched-link-radius`).
242
+ - **`SectionHeader`** heads each section below the page title: an h2 by
243
+ default (`titleAs` for h3/h4), an optional description, and actions on the
244
+ title row, such as a "View all" link or two small buttons. The actions drop
245
+ beneath the title only when the row runs out of room. `size` sets the look
246
+ apart from the level (`md` text-heading-3, `sm` text-heading-4), and the
247
+ title always carries an id (`titleId`, or a generated one) for
248
+ `<section aria-labelledby>`. It shares PageHeader's title row and has no outer
249
+ spacing, so the section's gap sets the rhythm. PageHeader's docs now point a
250
+ second heading to it.
251
+ - **Avatar `xl` (96px) and `2xl` (160px)** for portraits: profile headers,
252
+ artist heroes and roster tiles. The initials move to `text-heading-2` /
253
+ `text-display-lg`, about a third of the circle as at row sizes. A `status`
254
+ indicator and its cut-out ring scale with the portrait, and the photo fades
255
+ in over the resting surface. AvatarGroup stays at `sm`–`lg`.
256
+ - **Breaking: `Grid minItemWidth` keeps empty columns (`auto-fill`).** With one
257
+ or two items, `auto-fit` collapsed the empty tracks and stretched each item
258
+ across the row, so a single search result rendered as a full-width cover.
259
+ New `track` prop: `"fill"` (default) keeps items at their size whatever their
260
+ count; `"fit"` restores the old stretch for a fixed set designed to span the
261
+ row. **Migrate:** add `track="fit"` to a grid of a known, fixed number of
262
+ cards that should share the row (a KPI row, plan tiers).
263
+ - **New `DisclosurePicker`:** a property row that names the current value and
264
+ opens its list in place, beneath the row, instead of in a popup, for a tool
265
+ panel where the choice is made while looking at what it changes. Parts:
266
+ `DisclosurePickerTrigger` (optional `label` caption and `media`, the value,
267
+ a chevron that flips), `DisclosurePickerPanel`, `DisclosurePickerList` (a
268
+ listbox: arrows move, Enter/Space/click pick and close),
269
+ `DisclosurePickerOption` (optional `media` and `note`) and
270
+ `DisclosurePickerEmpty`, plus `useDisclosurePicker()` for a list it does not
271
+ own, such as a Tree.
272
+ Search is opt-in (`search` on the panel), for lists past about a dozen
273
+ options. Keyboard opens move focus to the search field or the chosen option;
274
+ Escape and picks return it to the row. `size="xs"` sits on the
275
+ property-grid rung.
276
+ - **New `FontPicker`,** built on DisclosurePicker: the row names the family in
277
+ its own face, and the list draws each family in itself. Fonts and loading
278
+ come from the app (`fonts`, `loadFont(font, "preview" | "use")`); Sable
279
+ bundles no catalog. Search is on by default; pass `searchable={false}` for a
280
+ short list.
281
+ - **Button `bleed` — optical alignment for borderless buttons.** A ghost or
282
+ link button carries padding you can't see, so at the start or end of a row
283
+ its label sat ~12px inside the edge the row shares. `bleed="start" | "end" |
284
+ "both"` pulls the box out by exactly its own padding (the size's new
285
+ `--button-px`, which the padding now reads; the icon side's optical padding
286
+ when an icon sits there; an icon-only size's inset to its glyph), so the
287
+ label lands on the edge while the hit area and hover fill keep their full
288
+ size. Logical sides, so RTL mirrors. A no-op on variants with a visible edge
289
+ (primary, secondary, outline, destructive, floating, field, on-color,
290
+ on-color-outline). SectionHeader and PageHeader actions, PageHeader's back
291
+ control and CardAction now align their outermost ghost/link Button
292
+ automatically (it renders `data-bleed="auto"`; `bleed="none"` opts out);
293
+ when a header's actions wrap beneath the title, the first one aligns to the
294
+ start edge. Applied in the dashboard, feed and list-detail patterns and the
295
+ task-list and verify-code blocks (the feed's hand-set `-ms-3` is gone).
296
+ **Migrate:** replace `-ms-3` / `-me-3` nudges on ghost buttons with `bleed`;
297
+ to retune a button's padding set `[--button-px:…]` rather than `px-*`.
298
+ - **`DisclosurePicker` commits on Enter, not on every arrow key.** Its list
299
+ was a radio group, whose checked state follows focus, so each arrow press
300
+ called `onValueChange`: in the docs Customize panel the first ArrowDown on
301
+ the preset row switched presets (or opened "discard changes?"), and on a font
302
+ row loaded every family passed. The list is now a `role="listbox"` of
303
+ `role="option"` rows: ArrowUp/ArrowDown, Home/End and type-ahead move focus
304
+ (roving tabindex) and apply nothing; Enter, Space or a click picks, applies
305
+ and closes; Escape closes with the value untouched. `aria-selected` marks the
306
+ committed value. `onValueChange` fires once per pick, and not when the pick
307
+ is the current value. ArrowUp from the first option returns to the search
308
+ field. New on `DisclosurePickerOption`: `media` (a leading tile),
309
+ `disabled`, `textValue` (what type-ahead matches). Changed from the
310
+ unreleased radio version: the list takes div props instead of RadioGroup
311
+ props, options are no longer
312
+ `role="radio"`, and `DisclosurePickerEmpty` belongs in place of the list,
313
+ not inside it. **FontPicker** inherits it: arrows walk the families, Enter
314
+ picks and loads one. ThemeSelector's `layout="list"` stays a radio group
315
+ (instant switching is standard there); its guidance now says to use a
316
+ DisclosurePicker list with `ThemePreview` media when a switch has a cost.
317
+
3
318
  ## 0.6.2
4
319
 
5
320
  - **`llms.txt` names the default theme correctly.** Its summary still called
package/CONTRIBUTING.md CHANGED
@@ -11,7 +11,7 @@ The checks are the specification. Every rule below names the gate that enforces
11
11
  1. **Components reference semantic tokens only.** Use `bg-card`, `text-muted-foreground`, `bg-tone-edition`, `text-primary`, etc. — never raw palette steps (`--purple-heart-700`) and never inline `oklch()`/hex. For text/icons on a tone fill use the theme-adaptive `text-tone-*-foreground` ink (no `dark:` variant); for scrims, media letterbox, and pills on imagery use `bg-overlay`/`text-overlay-foreground`. Palette names are theme-internal; a component that names a palette breaks themeability — and `design-lint`'s `palette-ref` rule now has **zero** sanctioned exceptions.
12
12
  2. **Paired typography utilities, never decomposed.** Use `text-heading-1`, `text-body-md`, `text-label-lg`, … Each bundles size + weight + line-height + tracking. Do **not** combine `text-{size}` + `font-{weight}` + `tracking-{x}` by hand. (Icons are the exception — size them with `text-{size}` scales, never type utilities.)
13
13
  3. **Base UI is behavior; `components/` is the public API.** Each component wraps `@base-ui/react` directly inside its own folder (use the **`render` prop** for composition; read state from `data-[state=…]` attributes) and owns 100% of the styling. There is no separate primitives tier. Consumers import only from the package root barrel.
14
- 4. **Semantic icons in components.** Use `<Icon name="…">`; Reicon is the default. Concrete icon-package imports belong only in `src/icon-sets/`. Apps may override or extend the set through `<IconProvider>`.
14
+ 4. **Semantic icons in components.** Use `<Icon name="…">`. Concrete icon-package imports belong only in `src/icon-sets/`, which nothing in the core graph imports. Apps supply the set through `<IconProvider>` (Reicon from `@sableui/react/icons/reicon`, or their own).
15
15
  5. **Every scroll container declares a scrollbar policy.** An `overflow-*-auto|scroll` utility must be paired with `scrollbar-quiet` (a 4px thumb, invisible at rest, fading in while the region scrolls) or `scrollbar-hidden` (no bar — only when something else carries the affordance, and you should be able to name it). Left bare, the platform draws its own: on Windows that means stepper arrows hard against the padding edge, poking out of any rounded corner. Vertical scrolling takes a bar; **horizontal takes none** — an edge fade says "there is more this way" better than a rail does, which is why `Scroller` splits by axis. New scroll container? Reach for `useScrollActivity` so the bar appears on touch drags and programmatic scrolls, which hover cannot see. Enforced by `design-lint`'s `unmanaged-overflow` **and** by `build-registry` over every registry example. Full rationale: `DESIGN.md → Scrolling`.
16
16
  6. **Tailwind v4 packaging discipline.** `src/styles.css` must **not** `@import "tailwindcss"` (the consumer owns that). It carries only token blocks, `@theme inline` mappings, `@utility` typography, layout vars, and `@source`. `@source` points at `src` (never the gitignored `dist`).
17
17
 
@@ -139,19 +139,25 @@ The manifest's `fonts` field is `{ sans: string; mono: string; display?: string
139
139
  ## Icon contract
140
140
 
141
141
  Components reference semantic names through `<Icon name="…" />`. Reicon (MIT)
142
- is the default, installed as a runtime dependency and kept external to the build.
143
- Concrete icon imports belong only in `src/icon-sets/`; components never import
144
- an icon package or duplicate its SVG artwork.
145
-
146
- - **Use the default:** render components without an icon provider.
142
+ is the bundled set, installed as a runtime dependency and kept external to the
143
+ build. It is opt-in: `<Icon>` has no fallback, so an app that brings its own set
144
+ ships none of Reicon's artwork. Concrete icon imports belong only in
145
+ `src/icon-sets/`; components never import an icon package or duplicate its SVG
146
+ artwork, and nothing in the core graph imports `src/icon-sets/`.
147
+
148
+ - **Use Reicon:** `import { reiconIconSet } from "@sableui/react/icons/reicon"` and
149
+ mount `<IconProvider set={reiconIconSet}>` once at the app root. Without a
150
+ provider, icons render nothing and a development build warns once.
147
151
  - **Replace the package:** pass your own `IconSet` to `<IconProvider set={mySet}>`.
148
152
  Map semantic names to that package's components; preserve className, variant,
149
153
  spin and accessible labels. Return `null` for unknown names.
150
- - **Extend Reicon:** import `composeIconSets` and `reiconIconSet` from Sable and
151
- use `<IconProvider set={composeIconSets(appExtras, reiconIconSet)}>`.
154
+ - **Extend Reicon:** import `composeIconSets` from Sable and `reiconIconSet` from
155
+ `@sableui/react/icons/reicon`, then use
156
+ `<IconProvider set={composeIconSets(appExtras, reiconIconSet)}>`.
152
157
  The first set returning a non-null node wins.
153
- - `@sableui/react/icons/reicon` remains an alternative import for the same adapter.
154
- `fallbackIconSet` is a deprecated alias of Reicon, with no separate artwork.
158
+ - `@sableui/react/icons/reicon` is the only import path for the adapter; the root
159
+ barrel does not re-export it. `fallbackIconSet` is a deprecated alias of Reicon
160
+ on the same subpath, with no separate artwork.
155
161
  - Brand marks are a separate additive set at `@sableui/react/icons/brands`.
156
162
  The 23 marks are generated from licensed `reicon-brands` npm artwork by
157
163
  `pnpm icons:brands`, normalized to the 24-unit grid, and inherit currentColor.
package/DESIGN.md CHANGED
@@ -329,7 +329,7 @@ The same 4px scale governs **sizes**, so controls, icons, and avatars all land o
329
329
 
330
330
  - **Controls** — `sm` h-8 (32, compact / chips) · **`md` h-10 (40) — the default** (Input, Button, Select, the field row) · `lg` h-12 (48, prominent / touch-first) · `xl` h-14 (56, hero / mobile CTA).
331
331
  - **Icons** — `xs` 14 · `sm` 16 · **`md` 20 — default UI glyph** · `lg` 24 · `xl` 32. Icons are em-sized with `text-{size}` (never type utilities) and inherit text color.
332
- - **Avatars / media** — `sm` 24 · `md` 32 · `lg` 40 · `xl` 48, then up the scale (`size-16`, `size-24`).
332
+ - **Avatars** — `sm` 32 · **`md` 40 — default** · `lg` 48 (rows with a title and subtitle) · `xl` 96 (a profile header beside a name) · `2xl` 160 (an artist or profile hero, a roster portrait). `xl` doubles `lg` and `2xl` is ×1.67 again — steps far enough apart that two neighbours never read as the same size. Media beyond that is an `AspectBox`, not an avatar.
333
333
  - **Touch targets** — interactive elements clear **44×44px** (`size-11`, WCAG 2.5.5). A visually smaller control (a 32px icon button) keeps the 44px *hit* area via padding or hit-slop, not a bigger glyph. The `hit-target` utility is the sanctioned implementation: a centered 44px `::after`, so the hit area grows without the layout moving. Never on an `<input>` — replaced elements don't render pseudo-elements reliably; wrap it in a label instead.
334
334
 
335
335
  **Borderless controls need roughly twice the clearance of bordered ones.** A bordered control declares where it ends; a borderless one has nothing but the gap to say so, and two ghost buttons 12px apart read as one wide target. Budget around 24px between adjacent borderless controls where 12px would be right for bordered ones.
@@ -369,7 +369,7 @@ Sable's layout foundation is deliberately fluid: **edge-to-edge, with a fluid pa
369
369
 
370
370
  (`--region-ultrawide` 1800 is legacy; prefer `full`.)
371
371
 
372
- **Primitives** (in `src/layout/`): `Region` (fluid page region: `inset`, opt-in `max` as a named region *or any CSS length*, per-edge `safeArea`) · `Page` (route shell — `Region` + a vertically-rhythmic `Stack`) · `Grid` (CSS Grid; explicit `cols` or `auto-fit minmax` so cells breathe) · `Columns`/`Col` (track + spans) · `Bleed` (escapes a capped/inset ancestor to the **content-area** edge, via container-query units, so it stops at a fixed sidebar) · `SplitPane` (responsive two-pane, side-by-side above a breakpoint, stacked below) · `Stack` (vertical rhythm) · `Row` (horizontal flex) · `Spacer` · `Center` (optional prose cap) · `AspectBox` (reserves space, no layout shift) · `SafeArea` · `GridOverlay` (structure visualizer for docs/dev).
372
+ **Primitives** (in `src/layout/`): `Region` (fluid page region: `inset`, opt-in `max` as a named region *or any CSS length*, per-edge `safeArea`) · `Page` (route shell — `Region` + a vertically-rhythmic `Stack`) · `Grid` (CSS Grid; explicit `cols` or `minItemWidth` cells that breathe — `auto-fill` by default, so a sparse row keeps its empty columns and one result stays one cell wide; `track="fit"` stretches a fixed set across the row) · `Columns`/`Col` (track + spans) · `Bleed` (escapes a capped/inset ancestor to the **content-area** edge, via container-query units, so it stops at a fixed sidebar) · `SplitPane` (responsive two-pane, side-by-side above a breakpoint, stacked below) · `Stack` (vertical rhythm) · `Row` (horizontal flex) · `Spacer` · `Center` (optional prose cap) · `AspectBox` (reserves space, no layout shift) · `SafeArea` · `GridOverlay` (structure visualizer for docs/dev).
373
373
 
374
374
  `Columns count`, `Col span`/`start`, and `Grid cols` accept either a number **or a responsive breakpoint map** (`{ base, sm, md, lg, xl, "2xl" }`) — resolved mobile-first by the stylesheet, so a grid can thin to fewer columns on small screens without the consumer's Tailwind scanning a class.
375
375
 
@@ -528,7 +528,7 @@ Not every removal is a regression; check for an **equivalent replacement** befor
528
528
  - Pure `#000`/`#fff` (use `zinc-950`/`zinc-50`, or `overlay`/`overlay-foreground` for scrims). *(CI-enforced: `raw-black-white`.)*
529
529
  - In the Sable theme, purple/brand for primary CTAs (brand = editions + selection only). A different theme chooses its action palette through the semantic pair.
530
530
  - Gradient text, side-stripe borders, nested cards, mixed radii.
531
- - Concrete icon-package imports scattered through components. Reicon supplies the default through the shared adapter; IconProvider allows replacement.
531
+ - Concrete icon-package imports scattered through components. Reicon is an opt-in set behind the shared adapter; the app's IconProvider supplies it or a replacement.
532
532
  - A global page max-width (use opt-in region caps).
533
533
  - Off-scale spacing or sizing (`p-[15px]`, `h-[37px]`) — snap to the 4px scale. In a component, a fixed `rem`/`px` padding or gap also escapes the density control. *(CI-enforced in the package: `fixed-spacing`.)*
534
534
  - Restyling a library component with override classes to get a look it lacks (`<Button className="rounded-full bg-…">`, `<MenuItem className="text-destructive">`, `pl-6` on a table cell). Use or add a real variant. *(CI-enforced: `component-restyle`, `destructive-item-ink`, `table-edge-padding`.)*
package/README.md CHANGED
@@ -21,7 +21,7 @@ in Sable.
21
21
 
22
22
  - **Behavior:** [Base UI](https://base-ui.com) (`@base-ui/react`) — accessible primitives, isolated behind our own component APIs.
23
23
  - **Styling:** Tailwind CSS v4 + semantic CSS-variable tokens; variants via `class-variance-authority`.
24
- - **Icons:** Reicon by default, with a pluggable adapter for any icon package.
24
+ - **Icons:** opt-in through `IconProvider` — Reicon's set ships ready to mount, or plug in any icon package. Apps pay only for the set they choose.
25
25
  - **Motion:** a token-driven, zero-dependency motion layer (recipes, policy, browser-native primitives).
26
26
  - **Themeable:** components reference semantic tokens only; a theme is a CSS variable set.
27
27
 
@@ -98,7 +98,7 @@ function ThemeControls() {
98
98
  )
99
99
  }
100
100
 
101
- <IconProvider set={reiconIconSet}> {/* optional — omit for Reicon, or pass another icon set */}
101
+ <IconProvider set={reiconIconSet}> {/* required — Reicon, or pass another icon set */}
102
102
  <MotionProvider> {/* optional — enables the motion on/off toggle */}
103
103
  <SableThemeProvider> {/* optional, recommended when users can switch themes */}
104
104
  <App />
@@ -107,7 +107,7 @@ function ThemeControls() {
107
107
  </IconProvider>
108
108
  ```
109
109
 
110
- Components render Reicon icons out of the box. To replace Reicon, mount `<IconProvider set={yourIconSet}>`; to add domain icons, use `composeIconSets(appExtras, reiconIconSet)`. Both helpers and the `IconSet` type are exported from Sable. A set returns `null` for names it does not handle. List the names your extras add in `.sable/icons.json` (`{ "names": [...] }`) so `sable grade` counts them as icons the app supplies, not off-contract ones. `fallbackIconSet` remains a deprecated alias of Reicon for compatibility. `SableThemeProvider` is the preferred route for user-facing theme switching: it stores the user's color preference (`light` / `dark` / `system`), toggles the **`.dark` class**, stores the active design theme, and writes **`data-sable-theme`** for non-default themes. If your app already owns this state, use `applySableColorMode()` / `applySableTheme()` or the exported `SABLE_THEME_ATTRIBUTE` constant directly. You do **not** need a Tailwind `@custom-variant dark` for Sable itself — add one (`@custom-variant dark (&:where(.dark, .dark *));`) only if **your own** app code uses `dark:` utilities (Sable ships none).
110
+ Icons are opt-in: mount `<IconProvider set={reiconIconSet}>` once at the app root, with `reiconIconSet` from `@sableui/react/icons/reicon` (its only import path). Without a provider every `<Icon>` renders nothing, and a development build warns once. An app that passes its own set ships none of Reicon's artwork. To add domain icons, use `composeIconSets(appExtras, reiconIconSet)`; `composeIconSets` and the `IconSet` type are exported from the root. A set returns `null` for names it does not handle. List the names your extras add in `.sable/icons.json` (`{ "names": [...] }`) so `sable grade` counts them as icons the app supplies, not off-contract ones. `fallbackIconSet` remains a deprecated alias of Reicon, exported from `@sableui/react/icons/reicon`. `SableThemeProvider` is the preferred route for user-facing theme switching: it stores the user's color preference (`light` / `dark` / `system`), toggles the **`.dark` class**, stores the active design theme, and writes **`data-sable-theme`** for non-default themes. If your app already owns this state, use `applySableColorMode()` / `applySableTheme()` or the exported `SABLE_THEME_ATTRIBUTE` constant directly. You do **not** need a Tailwind `@custom-variant dark` for Sable itself — add one (`@custom-variant dark (&:where(.dark, .dark *));`) only if **your own** app code uses `dark:` utilities (Sable ships none).
111
111
 
112
112
  **6. Use it:**
113
113
 
@@ -58,6 +58,10 @@ const rules = makeRules({ semanticTokens, motionTokens, labelContrast: await lab
58
58
 
59
59
  /* ── Args. ─────────────────────────────────────────────────────────────────── */
60
60
  const args = process.argv.slice(2)
61
+ /* --json: agents and `sable check` read the result instead of scraping the
62
+ report. The skill told agents to run `sable lint <paths> --json` while this
63
+ printed the text report regardless. */
64
+ const JSON_OUT = args.includes("--json")
61
65
  if (args.includes("--help") || args.includes("-h")) {
62
66
  console.log(
63
67
  [
@@ -67,6 +71,7 @@ if (args.includes("--help") || args.includes("-h")) {
67
71
  " sable-lint --ignore <glob> skip matching paths (repeatable)",
68
72
  " sable-lint --rules list the rules",
69
73
  " sable-lint --since <ver> what the contract added since that version",
74
+ ' sable-lint --json print { type: "lint", data } instead of the report',
70
75
  "",
71
76
  `Contract: @sableui/react ${REGISTRY.version} (${semanticTokens.size} semantic tokens)`,
72
77
  "",
@@ -131,6 +136,13 @@ const targets = args.filter((a, i) => !a.startsWith("-") && args[i - 1] !== "--i
131
136
  const dirs = (targets.length ? targets : ["src"]).map((d) => resolve(CWD, d))
132
137
  const missing = dirs.filter((d) => !existsSync(d))
133
138
  if (missing.length) {
139
+ if (JSON_OUT) {
140
+ const message = `Not found: ${missing.map((d) => relative(CWD, d)).join(", ")}`
141
+ process.stdout.write(
142
+ `${JSON.stringify({ type: "error", code: "ERR_LINT_INPUT", message, suggestions: ["Pass the directories to lint, e.g. sable lint app components"] })}\n`,
143
+ )
144
+ process.exit(1)
145
+ }
134
146
  console.error(
135
147
  `[sable-lint] Not found: ${missing.map((d) => relative(CWD, d)).join(", ")}\n` +
136
148
  "Pass the directories to lint, e.g. `sable-lint app components`.",
@@ -191,6 +203,12 @@ for (const dir of dirs) {
191
203
  }
192
204
  }
193
205
 
206
+ if (JSON_OUT) {
207
+ const data = { version: REGISTRY.version, filesScanned, failed: violations.length > 0, violations }
208
+ process.stdout.write(`${JSON.stringify({ type: "lint", data }, null, 1)}\n`)
209
+ process.exit(violations.length ? 1 : 0)
210
+ }
211
+
194
212
  process.exit(
195
213
  report(
196
214
  "sable-lint",