@sableui/react 0.0.0-stage → 0.1.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 (247) hide show
  1. package/AGENTS.md +157 -0
  2. package/CHANGELOG.md +55 -0
  3. package/CONTRIBUTING.md +221 -0
  4. package/DESIGN.md +548 -0
  5. package/LICENSE +21 -0
  6. package/README.md +194 -2
  7. package/THIRD_PARTY_NOTICES.md +44 -0
  8. package/bin/sable-lint.mjs +0 -0
  9. package/bin/sable.mjs +1223 -0
  10. package/dist/_contract-CYScOdf5.d.ts +150 -0
  11. package/dist/chunk-3IWZYBGH.js +398 -0
  12. package/dist/chunk-5Z5KLOAU.js +167 -0
  13. package/dist/chunk-7MG4SHYX.js +15 -0
  14. package/dist/chunk-ADLLDWLW.js +15 -0
  15. package/dist/chunk-EP55KSYA.js +14 -0
  16. package/dist/chunk-J23ELQQZ.js +9396 -0
  17. package/dist/chunk-JI7WJP2L.js +286 -0
  18. package/dist/chunk-K7RUHUGC.js +14 -0
  19. package/dist/chunk-KO3NAEBF.js +15 -0
  20. package/dist/chunk-L4IEIQQA.js +14 -0
  21. package/dist/chunk-L6Y46C62.js +270 -0
  22. package/dist/chunk-NEQNXI2G.js +25 -0
  23. package/dist/chunk-PAOZBZME.js +40 -0
  24. package/dist/chunk-RWP4XBSK.js +139 -0
  25. package/dist/chunk-SRQWQUTW.js +63 -0
  26. package/dist/chunk-YXBOYWBN.js +15 -0
  27. package/dist/foundations/index.d.ts +88 -0
  28. package/dist/foundations/index.js +792 -0
  29. package/dist/icon/index.d.ts +60 -0
  30. package/dist/icon/index.js +2 -0
  31. package/dist/icon-sets/brands.d.ts +44 -0
  32. package/dist/icon-sets/brands.js +147 -0
  33. package/dist/icon-sets/reicon.d.ts +8 -0
  34. package/dist/icon-sets/reicon.js +1 -0
  35. package/dist/index.d.ts +7013 -0
  36. package/dist/index.js +15462 -0
  37. package/dist/ios/chrome.css +83 -0
  38. package/dist/ios/frame.css +5 -0
  39. package/dist/ios/index.d.ts +70 -0
  40. package/dist/ios/index.js +313 -0
  41. package/dist/ios/preview.d.ts +64 -0
  42. package/dist/ios/preview.js +133 -0
  43. package/dist/ios/styles.css +77 -0
  44. package/dist/layout/index.d.ts +280 -0
  45. package/dist/layout/index.js +2 -0
  46. package/dist/meta-6dUzgC6M.d.ts +205 -0
  47. package/dist/patterns/index.d.ts +36 -0
  48. package/dist/patterns/index.js +934 -0
  49. package/dist/presets/index.d.ts +37 -0
  50. package/dist/presets/index.js +117 -0
  51. package/dist/registry-CyL28OzJ.d.ts +28 -0
  52. package/dist/styles.css +1893 -0
  53. package/dist/theme-provider-BWcizhAt.d.ts +105 -0
  54. package/dist/themes/atelier/atelier.css +213 -0
  55. package/dist/themes/atelier/index.d.ts +9 -0
  56. package/dist/themes/atelier/index.js +1 -0
  57. package/dist/themes/base/base.css +443 -0
  58. package/dist/themes/base/index.d.ts +9 -0
  59. package/dist/themes/base/index.js +1 -0
  60. package/dist/themes/ember/ember.css +270 -0
  61. package/dist/themes/ember/index.d.ts +9 -0
  62. package/dist/themes/ember/index.js +1 -0
  63. package/dist/themes/index.d.ts +10 -0
  64. package/dist/themes/index.js +8 -0
  65. package/dist/themes/meridian/index.d.ts +9 -0
  66. package/dist/themes/meridian/index.js +1 -0
  67. package/dist/themes/meridian/meridian.css +255 -0
  68. package/dist/themes/poppy/index.d.ts +6 -0
  69. package/dist/themes/poppy/index.js +1 -0
  70. package/dist/themes/poppy/poppy.css +174 -0
  71. package/dist/themes/prism/index.d.ts +6 -0
  72. package/dist/themes/prism/index.js +1 -0
  73. package/dist/themes/prism/prism.css +183 -0
  74. package/dist/themes/verdant/index.d.ts +9 -0
  75. package/dist/themes/verdant/index.js +1 -0
  76. package/dist/themes/verdant/verdant.css +210 -0
  77. package/dist/themes.css +30 -0
  78. package/llms/components/accordion.md +93 -0
  79. package/llms/components/app-header.md +70 -0
  80. package/llms/components/app-shell.md +117 -0
  81. package/llms/components/attachment.md +105 -0
  82. package/llms/components/avatar.md +141 -0
  83. package/llms/components/badge.md +117 -0
  84. package/llms/components/banner.md +119 -0
  85. package/llms/components/bottom-nav.md +80 -0
  86. package/llms/components/breadcrumbs.md +75 -0
  87. package/llms/components/browser.md +67 -0
  88. package/llms/components/bubble.md +119 -0
  89. package/llms/components/button.md +199 -0
  90. package/llms/components/calendar.md +75 -0
  91. package/llms/components/card.md +135 -0
  92. package/llms/components/carousel.md +87 -0
  93. package/llms/components/chat-composer.md +126 -0
  94. package/llms/components/checkbox-card.md +98 -0
  95. package/llms/components/checkbox.md +90 -0
  96. package/llms/components/code-block.md +81 -0
  97. package/llms/components/code.md +62 -0
  98. package/llms/components/collapsible.md +83 -0
  99. package/llms/components/color-field.md +100 -0
  100. package/llms/components/combobox.md +119 -0
  101. package/llms/components/command-menu.md +158 -0
  102. package/llms/components/context-card.md +73 -0
  103. package/llms/components/context-menu.md +79 -0
  104. package/llms/components/data-table.md +99 -0
  105. package/llms/components/date-picker.md +97 -0
  106. package/llms/components/description.md +96 -0
  107. package/llms/components/destructive-action-modal.md +76 -0
  108. package/llms/components/dialog.md +127 -0
  109. package/llms/components/dots-menu.md +64 -0
  110. package/llms/components/empty-state.md +113 -0
  111. package/llms/components/entity.md +67 -0
  112. package/llms/components/feedback.md +63 -0
  113. package/llms/components/fieldset.md +106 -0
  114. package/llms/components/file-dropzone.md +103 -0
  115. package/llms/components/form.md +111 -0
  116. package/llms/components/gliding-highlight.md +84 -0
  117. package/llms/components/inline-status.md +62 -0
  118. package/llms/components/input.md +127 -0
  119. package/llms/components/kbd.md +57 -0
  120. package/llms/components/label.md +73 -0
  121. package/llms/components/marker.md +99 -0
  122. package/llms/components/marquee.md +66 -0
  123. package/llms/components/menu.md +122 -0
  124. package/llms/components/message-scroller.md +110 -0
  125. package/llms/components/message.md +114 -0
  126. package/llms/components/meter.md +124 -0
  127. package/llms/components/middle-truncate.md +62 -0
  128. package/llms/components/note.md +81 -0
  129. package/llms/components/notification-badge.md +67 -0
  130. package/llms/components/number-field.md +70 -0
  131. package/llms/components/otp-field.md +101 -0
  132. package/llms/components/page-header.md +76 -0
  133. package/llms/components/page-loader.md +67 -0
  134. package/llms/components/pagination.md +92 -0
  135. package/llms/components/panel-group.md +120 -0
  136. package/llms/components/panel-section.md +122 -0
  137. package/llms/components/phone.md +70 -0
  138. package/llms/components/pill.md +73 -0
  139. package/llms/components/popover.md +79 -0
  140. package/llms/components/progress.md +98 -0
  141. package/llms/components/radio-card.md +90 -0
  142. package/llms/components/radio.md +86 -0
  143. package/llms/components/relative-time.md +60 -0
  144. package/llms/components/scroller.md +88 -0
  145. package/llms/components/scrub-field.md +97 -0
  146. package/llms/components/search-input.md +84 -0
  147. package/llms/components/select.md +106 -0
  148. package/llms/components/separator.md +70 -0
  149. package/llms/components/settings-menu.md +139 -0
  150. package/llms/components/sheet.md +97 -0
  151. package/llms/components/show-more.md +59 -0
  152. package/llms/components/side-nav.md +106 -0
  153. package/llms/components/sidebar.md +87 -0
  154. package/llms/components/skeleton.md +62 -0
  155. package/llms/components/slider.md +114 -0
  156. package/llms/components/spinner.md +87 -0
  157. package/llms/components/status-bar.md +91 -0
  158. package/llms/components/status-dot.md +70 -0
  159. package/llms/components/stepper.md +125 -0
  160. package/llms/components/streaming-text.md +86 -0
  161. package/llms/components/switch.md +87 -0
  162. package/llms/components/table.md +112 -0
  163. package/llms/components/tabs.md +84 -0
  164. package/llms/components/textarea.md +88 -0
  165. package/llms/components/theme-selector.md +95 -0
  166. package/llms/components/theme-switcher.md +71 -0
  167. package/llms/components/thinking-orb.md +91 -0
  168. package/llms/components/toast.md +130 -0
  169. package/llms/components/toggle.md +95 -0
  170. package/llms/components/toolbar.md +168 -0
  171. package/llms/components/tooltip.md +93 -0
  172. package/llms/components/tree.md +227 -0
  173. package/llms/components/video.md +66 -0
  174. package/llms/components/workspace.md +138 -0
  175. package/llms/foundations/accessibility.md +60 -0
  176. package/llms/foundations/motion.md +96 -0
  177. package/llms/foundations/scrolling.md +41 -0
  178. package/llms/foundations/shape.md +73 -0
  179. package/llms/foundations/spacing.md +82 -0
  180. package/llms/foundations/surfaces.md +69 -0
  181. package/llms/foundations/typography.md +120 -0
  182. package/llms/layout/aspect-box.md +37 -0
  183. package/llms/layout/bleed.md +31 -0
  184. package/llms/layout/center.md +37 -0
  185. package/llms/layout/columns.md +51 -0
  186. package/llms/layout/grid-overlay.md +40 -0
  187. package/llms/layout/grid.md +47 -0
  188. package/llms/layout/page.md +39 -0
  189. package/llms/layout/region.md +49 -0
  190. package/llms/layout/row.md +40 -0
  191. package/llms/layout/safe-area.md +37 -0
  192. package/llms/layout/spacer.md +39 -0
  193. package/llms/layout/split-pane.md +41 -0
  194. package/llms/layout/stack.md +39 -0
  195. package/llms/patterns/auth-page.md +68 -0
  196. package/llms/patterns/brand-landing-page.md +423 -0
  197. package/llms/patterns/chat-panel.md +210 -0
  198. package/llms/patterns/dashboard-page.md +170 -0
  199. package/llms/patterns/feed-page.md +103 -0
  200. package/llms/patterns/landing-page.md +122 -0
  201. package/llms/patterns/list-detail-page.md +100 -0
  202. package/llms/patterns/settings-page.md +121 -0
  203. package/llms/reference/token-intent.md +324 -0
  204. package/llms/reference/utilities.md +129 -0
  205. package/llms/tasks/adding-a-component.md +113 -0
  206. package/llms/tasks/auditing-an-app.md +216 -0
  207. package/llms/tasks/building-a-page.md +109 -0
  208. package/llms/tasks/building-a-tool.md +141 -0
  209. package/llms/tasks/building-an-ios-prototype.md +105 -0
  210. package/llms/tasks/building-with-sable.md +73 -0
  211. package/llms/tasks/choosing-a-component.md +109 -0
  212. package/llms/tasks/converting-an-app.md +139 -0
  213. package/llms/tasks/creating-a-preset.md +43 -0
  214. package/llms/tasks/creating-design-assets.md +96 -0
  215. package/llms/tasks/designing-a-surface.md +115 -0
  216. package/llms/tasks/developing-taste.md +157 -0
  217. package/llms/tasks/getting-started.md +63 -0
  218. package/llms/tasks/learning-from-builds.md +74 -0
  219. package/llms/tasks/reviewing-a-design.md +114 -0
  220. package/llms/tasks/reviewing-a-journey.md +84 -0
  221. package/llms/tasks/reviewing-motion.md +92 -0
  222. package/llms/tasks/using-presets.md +87 -0
  223. package/llms/taste/asset-plan.template.json +53 -0
  224. package/llms/taste/composition-follows-content.md +20 -0
  225. package/llms/taste/hierarchy-is-a-relationship.md +22 -0
  226. package/llms/taste/lessons.json +57 -0
  227. package/llms/taste/motion-needs-a-reason.md +21 -0
  228. package/llms/taste/surface-states-travel-together.md +20 -0
  229. package/llms-full.txt +14906 -0
  230. package/llms.txt +405 -0
  231. package/package.json +185 -3
  232. package/registry.json +18812 -0
  233. package/scripts/agent-setup.mjs +54 -0
  234. package/scripts/design-atlas.mjs +221 -0
  235. package/scripts/design-directions.mjs +123 -0
  236. package/scripts/design-memory.mjs +166 -0
  237. package/scripts/design-preferences.mjs +227 -0
  238. package/scripts/design-presets.mjs +48 -0
  239. package/scripts/grade.mjs +509 -0
  240. package/scripts/lint-rules.mjs +1065 -0
  241. package/scripts/review-evidence.mjs +120 -0
  242. package/scripts/skeleton.mjs +248 -0
  243. package/skills/entry/SKILL.md +34 -0
  244. package/skills/sable/SKILL.md +187 -0
  245. package/skills/sable/agents/openai.yaml +6 -0
  246. package/targets/ios.json +982 -0
  247. package/third-party/reicon-LICENSE.txt +20 -0
@@ -0,0 +1,105 @@
1
+ # Task: building an iOS app prototype
2
+
3
+ An iOS prototype is a clickable iPhone app that runs in the browser, for demos,
4
+ presentations and user testing. It is not a native app and not for production.
5
+
6
+ ## The rule: Sable first
7
+
8
+ **Every screen is built from Sable components, in the chosen preset.** Rows,
9
+ lists, inputs, switches, sliders, search, selects, buttons, forms, toasts,
10
+ empty states, cards and all content are Sable — exactly as on the web.
11
+
12
+ The Sable iOS adapter (`@sableui/react/ios`) supplies only the iOS **system
13
+ chrome** Sable has no equivalent for:
14
+
15
+ | Chrome | Adapter |
16
+ | --- | --- |
17
+ | App frame, status bar, safe areas | `NativeApp` |
18
+ | A screen | `IosPage` |
19
+ | Navigation bar: title, large title, back, bar actions | `IosNavbar`, `IosBackLink`, `IosIconButton` |
20
+ | Tab bar | `IosTabbar`, `IosTab` |
21
+ | Sheet presentation (detents, drag, Escape) | `IosSheet` (its content is Sable) |
22
+ | Push and pop between screens | `NavigationStack`, `useNavigation` |
23
+
24
+ **Never import from `konsta`.** Konsta is the adapter's optional peer, not a
25
+ component library for the app; `sable-lint` flags a direct import
26
+ (`native-ui-import`). If a screen needs something Sable does not have, report
27
+ it as a library gap; do not reach for a native control or hand-build one.
28
+
29
+ `sable target ios --component <Name> --json` gives the decision for any
30
+ component: `preserve` (use the Sable component — almost everything),
31
+ `replace` (an adapter chrome component), `contextual` (Button and PageHeader:
32
+ the adapter only in the navigation bar, Sable everywhere else), `recompose`
33
+ (desktop patterns rebuilt from Sable parts for a phone, e.g. a table becomes
34
+ Entity rows in a Card) or `redesign` (desktop workspace tools).
35
+
36
+ ## How iOS screens look in Sable
37
+
38
+ - **Grouped settings:** a section heading (`text-title-sm text-muted-foreground`)
39
+ over a `Card` with `CardList` and `Entity` rows; a `Switch`, value text or
40
+ chevron in each row's `action`.
41
+ - **Forms:** `Form` with `Field` around `Input`, `Textarea`, `Select`, `Slider`.
42
+ - **Lists that drill down:** interactive `Entity` rows in a `Card` that call
43
+ `useNavigation().push(id, <NextScreen />)`.
44
+ - **Search:** `SearchInput` on the page margin.
45
+ - **Feedback:** `toast()` inside `ToastProvider`; `EmptyState` for no results.
46
+ - Align content to the native page margin with `px-(--ios-inset)` so it lines
47
+ up with large titles. The adapter spaces a screen's first content below its
48
+ title.
49
+
50
+ ## Setup
51
+
52
+ 1. Install `konsta` in the consuming project only when an iOS prototype is
53
+ requested (`pnpm add konsta`). It is the adapter's optional peer.
54
+ 2. Give the prototype its own HTML entry, separate from the web app, with one
55
+ stylesheet:
56
+
57
+ ```css
58
+ @import "tailwindcss";
59
+ @import "@sableui/react/styles.css";
60
+ @import "@sableui/react/theme/verdant.css"; /* the preset, or themes.css */
61
+ @import "@sableui/react/ios/styles.css";
62
+ @source "../node_modules/@sableui/react/dist";
63
+ ```
64
+
65
+ Set the preset with `data-sable-theme` on `<html>`. The adapter maps the
66
+ preset onto the chrome (background, ink, accent, fonts), so chrome and
67
+ content follow the same preset and light or dark mode. It pins Tailwind's
68
+ spacing and radii on the chrome only, so a preset's density and corners
69
+ reach your Sable content and the chrome keeps iOS geometry.
70
+ 3. Compose: `NativeApp` at the root (`mode` sets light or dark, also on
71
+ `<html>` so portaled Sable popups follow); one `NavigationStack` per tab,
72
+ each holding an `IosPage` with an `IosNavbar` and Sable content; an
73
+ `IosTabbar` of `IosTab`s with Sable `Icon`s; `IosSheet` around Sable forms.
74
+ Keep the app's `IconProvider`.
75
+ 4. Frame it on a host page with `PhoneFrame` from `@sableui/react/ios/preview`
76
+ and `@sableui/react/ios/frame.css` (Sable's Phone around an iframe at
77
+ 402×874, scaled as a whole device). To follow a host's live look, call
78
+ `useIosPreviewBridge(frameRef, { mode })` on the host and
79
+ `useIosPreviewTheme()` in the prototype.
80
+ 5. Verify light and dark, keyboard focus, reduced motion, back and edge swipe,
81
+ independent tabs, sheet drag, Escape and cancel, form validation and save,
82
+ and empty-state recovery. Run `sable-lint` over the prototype: it is a Sable
83
+ app and the same rules apply.
84
+
85
+ ## Geometry hooks
86
+
87
+ `--ios-inset` (16px page margin), `--ios-safe-top` (62px), `--ios-header-height`,
88
+ `--ios-tabbar-bottom`, `--ios-content-bottom` and `--ios-motion`, for the
89
+ standard 402×874 phone. Reduced transparency removes the glass blur.
90
+
91
+ ## Engineering handoff
92
+
93
+ The prototype's colors, fonts, radii and spacing are the preset's tokens.
94
+ Engineers take styles from the preset export (Customize → Export, or
95
+ `sable preset`), not code from the prototype.
96
+
97
+ Platform: iOS 27 on a 6.3" iPhone Pro. Chrome by Konsta UI 5.5 (MIT) through
98
+ the adapter.
99
+
100
+ ```sh
101
+ sable target ios --json
102
+ sable target ios --component Switch --json # preserve: Sable Switch
103
+ sable target ios --component BottomNav --json # replace: IosTabbar + IosTab
104
+ sable target ios --component Button --json # contextual: Sable except in the nav bar
105
+ ```
@@ -0,0 +1,73 @@
1
+ # Task: build an idea with Sable
2
+
3
+ Start with the product the user wants: a portfolio, shop, booking service, app,
4
+ or another concrete task. A preset supplies visual decisions; the brief supplies
5
+ content, information architecture, and behavior. Inspiration is optional.
6
+
7
+ ## One workflow
8
+
9
+ 1. Install and wire Sable using [getting-started.md](./getting-started.md).
10
+ Run `pnpm exec sable setup --agent` to connect the package-owned skill.
11
+ 2. Choose a bundled preset, customize one in Canvas, or use `inspire` to develop
12
+ an original preset from references. See [creating-a-preset.md](./creating-a-preset.md).
13
+ Starting directly from an idea is equally valid.
14
+ 3. Put a downloaded preset in `.sable/presets/<id>.json` in the target project.
15
+ Ask the agent to build your idea using that exact file. Read
16
+ [using-presets.md](./using-presets.md) for validation, CSS, and fonts.
17
+ 4. Build from Sable's installed components, patterns, utilities, and tokens.
18
+ Inspect and operate the real result, then repair observed problems.
19
+
20
+ Examples in Codex:
21
+
22
+ ```text
23
+ $sable build a portfolio for a furniture designer
24
+ $sable inspire these references into a warm, compact shop preset
25
+ $sable build a ceramics shop using .sable/presets/clay.json
26
+ $sable audit our checkout and report prioritized findings
27
+ $sable polish our checkout and repair the reported issues
28
+ ```
29
+
30
+ Claude Code and Cursor use `/sable` with the same modes. `create` is a `build`
31
+ alias. These are agent instructions; the shell CLI serves catalog resources and
32
+ validates records. Other agents can read the package's `skills/sable/SKILL.md`.
33
+
34
+ `inspire` produces a preset and component preview. `audit` evaluates an existing
35
+ interface and reports by default. A reference URL does not request a cloned site.
36
+ Develop alternative rendered directions when requested or when a consequential
37
+ choice needs comparison. A three-design comparison is not a mandatory build step.
38
+
39
+ ## Required library use
40
+
41
+ Read the installed package's `AGENTS.md` and `registry.json` consume block. Search
42
+ for each recognizable element with `sable search "<need>" --json`; inspect the
43
+ matching `component <Name> --props` and `--examples` before implementation.
44
+ Use Sable's composed anatomy and supported variants. A hand-built button, field,
45
+ menu, dialog, table, or card that imitates a shipped component violates the contract.
46
+
47
+ Resolve styling through component variants, documented utilities/recipes, then
48
+ semantic tokens. App-owned CSS handles uncovered content and composition; it
49
+ does not replace component appearance or behavior. Inspect a relevant pattern's
50
+ skeleton; compose an original layout if its structure does not fit the brief.
51
+ Read [choosing-a-component.md](./choosing-a-component.md) and
52
+ [designing-a-surface.md](./designing-a-surface.md).
53
+
54
+ For a real library gap, record the unsuccessful search and missing capability.
55
+ In the Sable repository, add the capability to the package under `CONTRIBUTING.md`
56
+ before consuming it. In another project, report the gap and preserve the package;
57
+ implement genuinely app-specific structure without creating parallel controls.
58
+
59
+ ## Finish the working product
60
+
61
+ Run `sable doctor`, `sable lint <source paths> --json`, and the project's build/type
62
+ checks. Inspect desktop/mobile layouts, operate the primary task and recovery
63
+ path, and check keyboard focus, fonts, reduced motion, and meaningful states.
64
+ Use [reviewing-a-design.md](./reviewing-a-design.md),
65
+ [reviewing-a-journey.md](./reviewing-a-journey.md), and
66
+ [reviewing-motion.md](./reviewing-motion.md) as applicable.
67
+
68
+ Passing static checks does not establish visual quality. Review composition,
69
+ hierarchy, rhythm, imagery, and brief fit in the actual product. Deliver the
70
+ working result, preset/revision used, checks, and any material limitations.
71
+ Include a reuse inventory: imported Sable components and parts, retrieved token
72
+ groups/utilities, and the reason for each app-owned UI/CSS addition. Inspect source
73
+ ownership; valid classes on a hand-built control still violate the contract.
@@ -0,0 +1,109 @@
1
+ # Task: choosing between similar components
2
+
3
+ Sable has several components that look alike and do different jobs. Choosing
4
+ the wrong one usually still renders, so nothing fails. It shows up later as
5
+ a focus trap where none belonged, a menu that cannot hold a form, or a status
6
+ that a screen reader never announces. Use the tables below before you compose.
7
+ For each component, `npx sable component <Name> --detail full` has the full
8
+ contract.
9
+
10
+ The question to ask is always **what the user is doing**, not what it looks like.
11
+
12
+ ## Surfaces that open over the page
13
+
14
+ | The user is… | Use | Not |
15
+ |---|---|---|
16
+ | Making a focused decision or filling a short form, and the page should wait | `Dialog` | `Sheet` (too much room), `Popover` (no focus trap) |
17
+ | Confirming something irreversible | `DestructiveActionModal` | a `Dialog` you style red: it already handles the async confirm, the spinner and the verb-first labels |
18
+ | Working through a long or secondary task beside the page context (filters, details, an edit form) | `Sheet` | `Dialog` (cramps long content) |
19
+ | Viewing media full-bleed | `Dialog` with `DialogContent size="fullscreen"` | `Dialog` at its default size (500px cap) |
20
+ | Adjusting a few controls anchored to a trigger, without leaving the flow | `Popover` | `Menu` (cannot hold inputs) |
21
+ | Reading a rich preview of a link or a name on hover or focus | `ContextCard` | `Tooltip` (plain text only), `Popover` (needs a click) |
22
+ | Reading a short label for an icon or control | `Tooltip` | anything that holds interactive content: tooltips are not reachable by touch |
23
+ | Jumping anywhere or running a command by name | `CommandMenu` | `Combobox` (one field's value, not app-wide actions) |
24
+ | Sending a quick rating and message | `Feedback` | a hand-built `Popover` + `Textarea` |
25
+
26
+ ## Lists of actions
27
+
28
+ | The user is… | Use | Not |
29
+ |---|---|---|
30
+ | Choosing one action from a list under a button | `Menu` | `Select` (that picks a *value*) |
31
+ | Reaching secondary actions for a row or card | `DotsMenu` | a bare `Menu` with a hand-made icon trigger |
32
+ | Acting on the thing under the pointer (right-click or long press) | `ContextMenu`, always mirrored in a visible `DotsMenu` | `ContextMenu` alone: it is undiscoverable |
33
+ | Changing a few settings that each show their current value | `SettingsMenu` | a `Menu` of toggles that hides each current state |
34
+ | Running a main action that has close variations | a `Button` beside an icon `Button` that opens a `Menu` | a hand-fused split control |
35
+
36
+ ## Picking a value
37
+
38
+ | The user is… | Use | Not |
39
+ |---|---|---|
40
+ | Picking one of 2–5 visible options | `Radio` (`RadioGroup`) | `Select`: hiding five options behind a click costs a click |
41
+ | Picking one option that needs a title and description | `RadioCard` (in a `RadioGroup`) | `Radio` with hand-styled cards |
42
+ | Picking several options that need a title and description | `CheckboxCard` | `Checkbox` with hand-styled cards |
43
+ | Picking one of many known options | `Select` | `Combobox`, unless the list is long enough to need typing |
44
+ | Picking one of a long or searchable list | `Combobox` | `Select` (no filtering) |
45
+ | Picking several from a long list | `Combobox multiple` with `ComboboxChips` | a column of `Checkbox`es past about eight items |
46
+ | Turning a setting on or off with an immediate effect | `Switch` | `Checkbox` (that is a form value applied on submit) |
47
+ | Agreeing to something, or ticking a form value submitted later | `Checkbox` | `Switch` |
48
+ | Switching a view mode, or pressing a formatting toggle | `Toggle` / `ToggleGroup` | `Tabs` (those swap content panels) |
49
+ | Entering a one-time code | `OtpField` | `Input type="number"` |
50
+ | Entering a number, nudging with keys or steppers | `NumberField` | `Input type="number"` |
51
+ | Scrubbing a number by dragging, in tool chrome | `ScrubField` | `Slider` (page register) |
52
+ | Choosing a value along a range | `Slider` | `NumberField` when the exact value matters more than position |
53
+ | Adding files | `FileDropzone`, with accepted files listed as `Attachment` rows | a bare `<input type="file">` |
54
+
55
+ ## Showing more or less
56
+
57
+ | The user is… | Use | Not |
58
+ |---|---|---|
59
+ | Switching between peer views of one object | `Tabs` | `Accordion` (stacked, not peer) |
60
+ | Scanning several sections and opening one or more | `Accordion` | a column of `Collapsible`s you coordinate by hand |
61
+ | Opening one optional block | `Collapsible` | `Accordion` with one item |
62
+ | Reading a long block that starts clipped | `ShowMore` | a fixed-height box with `overflow-auto` |
63
+
64
+ ## Status, progress and feedback
65
+
66
+ | What you are communicating | Use | Not |
67
+ |---|---|---|
68
+ | Work happening, with a known percentage | `Progress` | `Meter` (not task progress) |
69
+ | How full something is within a known range (storage, quota, strength) | `Meter`, or `Meter shape="ring"` in a stat card | `Progress` |
70
+ | Where the user is in a multi-step flow | `Stepper` | `Progress` (loses which step) |
71
+ | Content loading in place, with a known shape | `Skeleton` | `Spinner` (makes the layout jump when content lands) |
72
+ | A short wait with no known shape | `Spinner` | `Skeleton` |
73
+ | Someone typing, or a reply composing | `Spinner appearance="dots"` / `StreamingText` | the ring `Spinner` |
74
+ | The whole app booting | `PageLoader` | a centred `Spinner` |
75
+ | The result of an action, transient | `Toast` | `Banner` (persistent) |
76
+ | A one-line status beside the thing it describes | `InlineStatus` | `Toast` (detached from the cause) |
77
+ | Guidance or a caveat inside content | `Note` | `Banner` |
78
+ | A page-wide announcement | `Banner` | `Note` |
79
+ | Which scope or environment the user is in (preview, staging) | `Banner` with a `label` chip | a hand-built strip |
80
+ | Nothing here yet, with a next step | `EmptyState` | an empty `Table` |
81
+ | A failure with a recovery action, in a region | `EmptyState tone="destructive"` (`surface="card"` among cards, `size="lg"` for a page) | `Note tone="destructive"` |
82
+ | A state label (live, failed, draft) | `Badge appearance="plain" tone=… dot` (dot plus label) | `StatusDot` alone: it is decorative and silent |
83
+ | Metadata or a category | `Badge`; `Pill` when it can be selected | `Badge appearance="plain" dot` (that is a status) |
84
+
85
+ ## Tables and trees
86
+
87
+ | The data is… | Use | Not |
88
+ |---|---|---|
89
+ | Static rows you lay out by hand | `Table` | `DataTable` |
90
+ | Rows from an array that need sorting or selection | `DataTable` | `Table` plus hand-written sort state |
91
+ | Files, folders, layers or an outline | `Tree` (`TreeRow icon`) | a nested list built by hand |
92
+
93
+ ## Navigation
94
+
95
+ | The user is… | Use | Not |
96
+ |---|---|---|
97
+ | Moving between the app's top-level areas on desktop | `Sidebar` inside `AppShell` | `SideNav` |
98
+ | Moving between sub-pages of a section (settings, docs) | `SideNav` | `Sidebar` (that is app chrome) |
99
+ | Moving between top-level areas on mobile | `BottomNav` | a hamburger-only `Sheet` |
100
+ | Paging through results with known counts | `Pagination` | `Button variant="secondary" loading` |
101
+ | Continuing a feed | `<Button variant="secondary" loading loadingLabel="Loading…">` | `Pagination` |
102
+
103
+ ## When nothing fits
104
+
105
+ Compose from layout primitives (`Stack`, `Row`, `Grid`, `Region`) and the
106
+ closest component. Do not restyle a component into a different one. If you
107
+ find yourself fighting a component's role, you are holding the wrong one.
108
+ Retrieve its `usage.bestPractices`, which name the neighbouring component to
109
+ use instead.
@@ -0,0 +1,139 @@
1
+ # Task: converting an existing app to Sable
2
+
3
+ Migrating a React app that has its own styles. The order below front-loads the
4
+ decisions that invalidate later work if you get them wrong.
5
+
6
+ ## 0. Classify every surface first
7
+
8
+ Before touching a component, **list the routes and assign a register to each** —
9
+ see fork 1 in `llms.txt`. Do not decide once for the app. Every conversion so far
10
+ has applied a single register throughout and been wrong somewhere: the tell is a
11
+ route with arrow-key or bracket-key bindings (triage), or one that never scrolls,
12
+ sitting in a page-register app.
13
+
14
+ Converting a tool with page-register instincts produces something that passes
15
+ every lint and still looks wrong. The measurable tells that a surface was built
16
+ with page instincts but is actually a tool:
17
+
18
+ | tell | page register | you are probably a tool |
19
+ |---|---|---|
20
+ | dominant control height | 40–44px | should be 32 (24 in dense chrome) |
21
+ | dominant radius | 8–16px, some pills | ~5px, **no pills in chrome** |
22
+ | dominant UI type | 14px / 600 | 10–12px at 450–550 |
23
+ | panel grouping | `Card` | `PanelSection` + hairlines |
24
+
25
+ Measure these in the running app before deciding — `getComputedStyle` over every
26
+ button and text node takes a minute and settles the argument.
27
+
28
+ ## 1. Wire the package
29
+
30
+ Follow **Consuming** in `llms.txt`: `.npmrc`, `pnpm add`, React dedupe, the CSS
31
+ import order, `@source`, fonts, providers. React dedupe is the one that fails
32
+ loudest and least clearly ("invalid hook call").
33
+
34
+ ## 2. Delete your own tokens
35
+
36
+ The most reliable signal that a conversion is real: the app's own CSS variable
37
+ block shrinks to almost nothing. Anything that remains should be *domain* colour,
38
+ not UI colour.
39
+
40
+ Grep for orphaned references afterwards. A CSS variable that no longer exists
41
+ resolves to nothing and the declaration is dropped — **silently**. A checkerboard
42
+ built on two renamed variables rendered as blank for four releases before anyone
43
+ noticed.
44
+
45
+ ## 3. Replace the shell, then the chrome, then the leaves
46
+
47
+ Shell first (`AppShell` or `Workspace`), because it establishes the density scope
48
+ and the landmarks. Then panels/headers. Leaf components last — they are the
49
+ easiest and the least consequential.
50
+
51
+ Expect the shell swap to delete more code than it adds. If it does not, you are
52
+ probably keeping a layout that Sable would have given you.
53
+
54
+ ## 4. Icons
55
+
56
+ **Reicon works by default.** No icon provider is needed. To use another icon
57
+ package, map Sable's semantic names in an app-owned IconSet and mount it through
58
+ IconProvider. To add domain-specific names while retaining Reicon:
59
+
60
+ ```tsx
61
+ import { IconProvider, composeIconSets, reiconIconSet } from "@sableui/react"
62
+ <IconProvider set={composeIconSets(appExtras, reiconIconSet)} />
63
+ ```
64
+
65
+ Keep only the names Sable's contract does not carry and let everything else fall
66
+ through. First set to return non-null wins.
67
+
68
+ **A set must return `null` to fall through — not a placeholder.** This is the trap
69
+ both hand-copied adapters fell into, and it is silent in the worst way: ending
70
+ your renderer with `{node ?? <circle cx="12" cy="12" r="9" />}` means an unknown
71
+ name draws a grey dot, so a typo, an off-contract name and a real icon all look
72
+ like something rendered. Return `null` and the next set gets a turn; miss every
73
+ set and you see nothing, which is at least visibly wrong.
74
+
75
+ Verify the adapter rather than trusting it — nothing in Sable can see it, and
76
+ `sable-lint` does not check it:
77
+
78
+ ```tsx
79
+ // Every contract name resolves, and an unknown one returns null.
80
+ const names = registry.iconContract.names // registry.json
81
+ names.filter((n) => !mySet.render({ name: n })) // → must be []
82
+ mySet.render({ name: "definitely-not-an-icon" }) // → must be null
83
+ ```
84
+
85
+ Do not hand-copy a name→component mapping. Every app that did ended up with a
86
+ drifted copy.
87
+
88
+ ## 5. Verify by measuring, not by looking
89
+
90
+ ```bash
91
+ npx sable-lint # lints ./src; pass dirs to change (npx sable-lint app components)
92
+ ```
93
+
94
+ `sable-lint` ships with the package and reads the token vocabulary from the
95
+ `registry.json` of the version you installed, so it checks your app against your
96
+ exact tokens. It exits 1 on any violation. `--rules` explains each one; `--ignore
97
+ '<glob>'` skips a data/fixture layer, where a hex value is content rather than a
98
+ style. A rare, genuine exception takes an auditable
99
+ `// design-lint-disable-next-line <ruleId> -- <reason>`.
100
+
101
+ Then your own `tsc` and tests. (`pnpm design-lint && pnpm build:registry &&
102
+ pnpm test:run` are the *in-repo* gates — they only run inside the Sable repo.)
103
+
104
+ Then in the browser, check the things a screenshot cannot show:
105
+
106
+ - every left edge in a hierarchy panel resolves to the rail or an exact multiple
107
+ - control heights match the ladder for the register you chose
108
+ - `--panel`, `--canvas` etc. resolve to real values, not empty
109
+ - **the fonts actually loaded.** `--font-*` resolving is not evidence: the token
110
+ names a family, and if the host never fetched it the browser silently falls back
111
+ to `system-ui` while every token check passes. One app rendered in the wrong
112
+ typeface for its entire life this way, and the documented checks all passed on
113
+ it. The theme names the family; **the host loads it.** Ask the font system, not
114
+ the cascade:
115
+
116
+ ```js
117
+ // In the browser console. Both must be true.
118
+ document.fonts.check("1em Figtree") // the theme's sans
119
+ document.fonts.check("1em DM Mono") // the theme's mono
120
+ ```
121
+
122
+ If either is false, add the `@font-face`/link the theme expects — see the theme's
123
+ entry in `registry.json → themes` for the families it assumes.
124
+ - a converted colour renders the same bytes as before — compare notations through
125
+ one canvas readback, never against a constant, because the readback itself has
126
+ an offset
127
+
128
+ ## Known-good example
129
+
130
+ Glitch (an image-compositing tool) converted in one pass: `globals.css` went
131
+ 229 → 49 lines, its local `components/workspace/` was deleted entirely, and the
132
+ layers panel lost 174 lines by adopting `Tree`.
133
+
134
+ ## Read next
135
+
136
+ - [building-a-tool.md](./building-a-tool.md) or
137
+ [building-a-page.md](./building-a-page.md)
138
+ - `DESIGN.md → Registers`
139
+ - `llms/reference/token-intent.md`
@@ -0,0 +1,43 @@
1
+ # Task: create a preset from inspiration
2
+
3
+ A preset is a versioned design input: Sable theme values, font requirements,
4
+ design guidance, and optional attributed observations. It is reusable across
5
+ products. It does not encode a reference site's content or page structure.
6
+
7
+ Use the agent's `inspire` mode with images, URLs, a description, or a combination.
8
+ Inspect the actual supplied evidence. Record typography relationships, surface
9
+ contrast, spacing, shape, imagery treatment, and observed motion. Label each
10
+ reference decision as measured, estimated, derived, or user-edited. A screenshot
11
+ cannot establish animation timing, hidden behavior, or an unseen responsive state.
12
+
13
+ Choose an original direction suitable for the user's future product. Map that
14
+ direction onto the installed Sable token vocabulary. Preserve readable semantic
15
+ roles and supported control behavior rather than recoloring every primitive
16
+ independently. Load fonts through the consuming project's font setup.
17
+
18
+ ```bash
19
+ pnpm exec sable preset --template
20
+ pnpm exec sable preset --check .sable/presets/clay.json --json
21
+ pnpm exec sable preset --css .sable/presets/clay.json
22
+ ```
23
+
24
+ The template starts from a complete Base theme snapshot. Give it a unique ID and
25
+ label; author its tokens, direction, and reference observations. Avoid IDs of
26
+ bundled themes. Save the record at `.sable/presets/<id>.json`. Advance `revision`
27
+ when changing a shared record; keep earlier revisions when reproducibility matters.
28
+
29
+ Preview using real Sable components or import the JSON in Canvas. Inspect both
30
+ light and dark modes and test long text, focus, disabled controls, floating
31
+ surfaces, and errors. Token validation checks the contract and safe CSS structure;
32
+ rendered review establishes contrast, coherence, and suitability.
33
+
34
+ Canvas's Export view downloads this same format with resolved theme values and
35
+ the current customizations. Enter a name and design guidance, save the JSON, and
36
+ place it in the target project. The browser cannot write into that project for
37
+ the agent. An imported preset can be edited and downloaded as a new revision.
38
+
39
+ Deliver the preset, its component preview, fonts to load, and any unverified
40
+ observations. Stop before building a full product. The next step is a concrete
41
+ `build <idea> using <preset file>` request, described in
42
+ [building-with-sable.md](./building-with-sable.md) and
43
+ [using-presets.md](./using-presets.md).
@@ -0,0 +1,96 @@
1
+ # Task: create assets that carry the design
2
+
3
+ Use this workflow when imagery, illustration, or type materially establishes the
4
+ surface's identity or explains its content. A useful asset changes what someone
5
+ understands or feels. Adding a decorative image to every section is unnecessary.
6
+
7
+ ## Define the asset's job before producing it
8
+
9
+ Keep the asset plan with the surface brief, for example `design/<surface>/assets.json`.
10
+ The optional [asset-plan template](../taste/asset-plan.template.json) is a plain
11
+ planning record, not a `sable grade` or `sable explore` schema. Fill only relevant
12
+ fields; `null` means unresolved, never tested or zero cost.
13
+
14
+ For each important asset, specify:
15
+
16
+ | Decision | Concrete example |
17
+ |---|---|
18
+ | Role and message | A workshop photograph shows the scale of the table and what a visitor will make. |
19
+ | Subject and composition | Hands working clay at lower right; clear space at upper left for the heading. |
20
+ | Visual treatment | Side lighting, warm paper background, shallow shadows; retain clay texture and avoid a glossy product-render finish. |
21
+ | Relationship to the set | Product illustrations share line weight and viewpoint while their silhouettes distinguish the actual varieties. |
22
+ | Placement and crop | Wide establishing view on desktop; a tighter alternate composition on mobile that retains both hands and the object. |
23
+ | Source and readiness | User-provided photo with confirmed usage scope; original SVG; generated illustration; or explicitly provisional placeholder. |
24
+ | Delivery target | Expected display dimensions, format candidates, byte budget, and loading priority. |
25
+
26
+ Words such as "premium" or "editorial" need these visible decisions to become
27
+ actionable. For an exact reference reconstruction, preserve its imagery roles and
28
+ framing; report unavailable source assets or substitutions. Atlas captures are
29
+ study evidence, not a product asset library.
30
+
31
+ ## Produce the right material
32
+
33
+ - Use provided assets when they fit the brief and permitted usage. Record their
34
+ source, creator when known, license or permission reference, attribution needs,
35
+ and any unresolved usage restriction. Finding an image online does not establish
36
+ permission to ship it. Do not label unresolved rights as cleared.
37
+ - Use original SVG or other code-native art for diagrams, marks, or illustration
38
+ systems that benefit from controlled geometry. Build a representative set before
39
+ repeating it: six recolored versions of one symbol may fail to distinguish six
40
+ subjects. Informative marks must not imply inaccurate counts or measurements.
41
+ - Use an available image-generation workflow for original raster material when
42
+ it serves the brief. Include subject, framing, light, material, palette, crop
43
+ constraints, and exclusions in the prompt. Inspect the actual result; a prompt
44
+ is not an asset. Record generated origin honestly. Generated scenes are not
45
+ evidence of a real customer's product, venue, testimonial, or outcome.
46
+ - For video, define the subject, shot sequence or loop, pacing, crop, poster, and
47
+ whether sound is meaningful. Use supplied, appropriately sourced, or generated
48
+ footage within the task's scope. Keep a usable poster/fallback. Motion review is
49
+ covered in [reviewing-motion.md](./reviewing-motion.md).
50
+
51
+ For fonts, record family and role, required weights, language/glyph coverage,
52
+ source, and relevant license. Load the actual files through the app's supported
53
+ font pipeline, such as local `@font-face`, a framework loader, or a provider link.
54
+ Declaring a font in a theme does not load it. Verify the font system and rendered
55
+ text, including fallback, long titles, numerals, and relevant accented characters.
56
+ An unavailable face needs an honest substitute rather than a broken provider URL.
57
+
58
+ ## Compose, crop, and deliver
59
+
60
+ Test assets in the layout early with real copy. Choose the image focal point and
61
+ text position together. Use `srcset`/`sizes` for resolution choices and `<picture>`
62
+ or another explicit art-direction mechanism when the mobile composition should
63
+ change. Inspect crops at the actual breakpoints: scaling a wide frame can remove
64
+ its subject or leave unusably tiny detail.
65
+
66
+ Give meaningful images useful alternative text; keep decorative imagery out of
67
+ the reading order. Avoid baking essential copy into an image when live text can
68
+ carry it. Provide equivalent information for a diagram or meaningful video.
69
+
70
+ Choose a project-specific byte budget, including the combined initial image and
71
+ font payload. Measure actual output bytes. Compare formats and encodings at the
72
+ required size; retain a lossless or vector original when useful, and inspect the
73
+ optimized export for damaged edges, banding, small-text loss, or changed color.
74
+ Do not preload every asset or lazy-load the primary visual by habit. Verify the
75
+ chosen loading behavior in the actual page, including slow or failed media.
76
+
77
+ A temporary placeholder may unblock composition. Label it in the review/asset
78
+ plan, preserve its intended dimensions, and make missing material clear to the
79
+ reviewer. Replace it before calling that visual complete, or state the remaining
80
+ limitation. Generation prompts and implementation notes belong in authoring
81
+ artifacts, not the finished product flow.
82
+
83
+ ## Verify the assembled result
84
+
85
+ Inspect desktop and mobile renders after fonts and media load, then test their
86
+ fallback states. Check that the asset advances the intended message, remains
87
+ recognizable at its smallest use, has a consistent relationship to the set, and
88
+ does not compromise text or controls. Retain before/after evidence when revising
89
+ an asset or crop; changing a file without viewing the new bytes is not review.
90
+
91
+ Record delivered paths, dimensions, bytes, and the captures actually inspected.
92
+ Use [reviewing-a-design.md](./reviewing-a-design.md) to connect those artifacts to
93
+ the build's source hash. Include local asset directories in the fingerprint;
94
+ remote resources need separate provenance because the source hash cannot freeze
95
+ their contents. Reuse a successful treatment as a conditional principle, not a
96
+ requirement that every future brand use the same illustration or font pairing.