@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,77 @@
1
+ @import 'konsta/react/theme.css';
2
+ @import './chrome.css';
3
+ @source './**/*.{ts,tsx,js,mjs}';
4
+ /* Import after Tailwind in the native document only. */
5
+ /*
6
+ * Chrome follows the Sable preset. When Sable's styles are loaded in this
7
+ * document (the recommended setup), every value below resolves from the active
8
+ * preset and mode, so `data-sable-theme` on <html> themes the native chrome and
9
+ * Sable content together. Without them, the iOS system values apply.
10
+ * NativeApp's `tokens` prop sets the same semantic names inline to override.
11
+ */
12
+ .ios-prototype {
13
+ --ios-canvas: var(--background, #f2f2f7); --ios-ink: var(--foreground, #1c1c1e); --ios-secondary: var(--muted-foreground, #636366);
14
+ --ios-panel: var(--card, #fff); --ios-primary-ink: var(--primary-foreground, #fff);
15
+ --ios-display: var(--font-display, var(--font-sans, inherit)); --ios-title-weight: var(--type-title-weight, 600);
16
+ --color-primary: var(--primary, var(--k-color-primary));
17
+ --color-ios-primary-shade: color-mix(in srgb, var(--color-primary) 85%, var(--ios-ink));
18
+ --color-ios-light-surface: var(--ios-canvas); --color-ios-dark-surface: var(--ios-canvas);
19
+ --color-ios-light-surface-1: var(--ios-panel); --color-ios-dark-surface-1: var(--ios-panel);
20
+ --color-ios-light-surface-1-shade: var(--muted, #f1f1f5); --color-ios-dark-surface-1-tint: var(--muted, #444447);
21
+ /* Geometry of the standard 402×874 phone. Retune together for other sizes. */
22
+ /* The page margin native titles, search and inset lists sit on: align content to it (px-(--ios-inset)). */
23
+ --ios-inset: 16px;
24
+ --ios-motion: 300ms; --ios-safe-top: 62px; --ios-header-height: 154px; --ios-tabbar-bottom: 28px; --ios-content-bottom: 110px;
25
+ font-family: var(--font-sans, system-ui, sans-serif); color: var(--ios-ink); background: var(--ios-canvas); height: 100dvh; overflow: hidden;
26
+ }
27
+ .ios-prototype.dark {
28
+ --ios-canvas: var(--background, #000); --ios-ink: var(--foreground, #f2f2f7); --ios-secondary: var(--muted-foreground, #aeaeb2);
29
+ --ios-panel: var(--card, #1c1c1e); --ios-primary-ink: var(--primary-foreground, #fff);
30
+ }
31
+ /*
32
+ * Sable and Konsta share Tailwind's spacing and radius names. Sable's preset
33
+ * and density rescale them; native chrome keeps iOS geometry, so the chrome
34
+ * pins Tailwind's defaults. Sable content placed beside it keeps the preset's.
35
+ */
36
+ .ios-prototype :is(.k-navbar, .k-tabbar, .k-toolbar, .ios-sheet-grabber, .ios-sheet-heading) {
37
+ --spacing: 0.25rem; --theme-radius-xs: 0.125rem; --theme-radius-sm: 0.25rem; --theme-radius-md: 0.375rem; --theme-radius-lg: 0.5rem; --theme-radius-xl: 0.75rem;
38
+ }
39
+ /* The system status bar: time and icons centred on the island's midline, each in
40
+ the space beside it, in the system font (it never takes the app's). */
41
+ .ios-status-bar { position: absolute; inset: 0 0 auto; height: var(--ios-safe-top); display: grid; grid-template-columns: 1fr 84px 1fr; grid-template-rows: 35px; align-content: start; align-items: center; padding: 11px 16px 0; z-index: 40; pointer-events: none; font: 600 17px/1 -apple-system, "SF Pro Text", system-ui, sans-serif; letter-spacing: -0.02em; }
42
+ .ios-status-bar > span:first-child { justify-self: center; font-variant-numeric: tabular-nums; }
43
+ /* The smaller island of current Pro phones. */
44
+ .ios-island { width: 84px; height: 35px; border-radius: 18px; background: #000; }
45
+ .ios-status-icons { justify-self: center; display: flex; align-items: center; gap: 5px; }
46
+ .ios-home-indicator { position: absolute; z-index: 100; bottom: 8px; left: 50%; transform: translateX(-50%); width: 134px; height: 5px; border-radius: 4px; background: var(--ios-ink); pointer-events: none; }
47
+ .ios-navigation-stack { position: absolute; inset: 0; }
48
+ .ios-route { position: relative; height: 100%; animation: ios-route-enter var(--ios-motion) cubic-bezier(.2,.8,.2,1); }
49
+ .ios-navigation-stack[data-direction='pop'] .ios-route { animation-name: ios-route-back; }
50
+ .ios-route[hidden], .ios-navigation-stack[hidden] { display: none; }
51
+ .ios-route > .k-page { height: 100%; padding-bottom: var(--ios-content-bottom); scrollbar-width: none; }
52
+ .ios-route > .k-page::-webkit-scrollbar { display: none; }
53
+ /* A screen's content starts a step below its title, as iOS spaces a large title from the first section. */
54
+ .ios-prototype .k-page > .k-navbar + * { margin-block-start: 12px; }
55
+ /* Touch targets on native chrome; Sable content keeps its preset's control sizes. */
56
+ .ios-prototype :is(.k-navbar, .k-tabbar, .ios-sheet-heading) :is(button, a) { min-height: 44px; }
57
+ /* Headings take focus on navigation for screen readers; that is not a ring to show. */
58
+ .ios-prototype [tabindex='-1']:focus { outline: none; }
59
+ .ios-prototype :focus-visible { outline: 3px solid var(--color-primary); outline-offset: 2px; }
60
+ .ios-prototype .native-tabbar { position: absolute; }
61
+ .ios-prototype .native-tabbar > .k-glass { width: 100%; }
62
+ .ios-sheet-dialog { position: fixed; inset: 0; padding: 0; border: 0; width: 100%; max-width: none; height: 100%; max-height: none; background: transparent; color: var(--ios-ink); overflow: hidden; }
63
+ .ios-sheet-dialog::backdrop { background: #0005; }
64
+ .ios-sheet { border-radius: 32px 32px 0 0; background: var(--ios-canvas); transition-duration: var(--ios-motion); padding-bottom: 34px; }
65
+ .ios-sheet-grabber { display: flex; margin-inline: auto; width: 80px; touch-action: none; }
66
+ .ios-grabber-mark { width: 36px; height: 5px; border-radius: 4px; background: var(--ios-secondary); }
67
+ .ios-sheet-heading { display: flex; align-items: center; justify-content: space-between; padding-inline: 20px; }
68
+ .ios-sheet-heading h2 { font-size: 22px; font-weight: 700; }
69
+ .ios-sheet-heading > button { width: auto; flex-shrink: 0; }
70
+ .ios-sheet::after { content: ''; position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); width: 134px; height: 5px; border-radius: 4px; background: var(--ios-ink); }
71
+ .ios-sheet-content { height: calc(100% - 88px); overflow-y: auto; scrollbar-width: none; padding: 16px 20px; }
72
+ .ios-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
73
+ @keyframes ios-route-enter { from { opacity: .7; transform: translateX(28px); } to { opacity: 1; transform: translateX(0); } }
74
+ @keyframes ios-route-back { from { opacity: .7; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }
75
+ .ios-prototype[data-reduced-motion] *, .ios-prototype[data-reduced-motion] *::before, .ios-prototype[data-reduced-motion] *::after { animation-duration: 0s !important; transition-duration: 0s !important; }
76
+ @media (prefers-reduced-motion: reduce) { .ios-prototype *, .ios-prototype *::before, .ios-prototype *::after { animation-duration: 0s !important; transition-duration: 0s !important; } }
77
+ @media (prefers-reduced-transparency: reduce) { .ios-prototype { --ios-glass-tint: 1 !important; } }
@@ -0,0 +1,280 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithRef, ElementType, HTMLAttributes, ReactNode } from 'react';
3
+
4
+ /** The device safe-area edges (notch, home indicator, rounded corners). */
5
+ type Edge = "top" | "bottom" | "left" | "right";
6
+
7
+ /**
8
+ * Purpose-named content widths (resolve to theme tokens). Pick by ROLE, not size.
9
+ * Cap text measure (`prose`) separately from layout width.
10
+ * - `prose` 65ch — long-form reading text (post body, descriptions, articles)
11
+ * - `form` 480px — narrow single-column forms, auth, dialogs
12
+ * - `feed` 640px — single-column timeline / reading feed
13
+ * - `content` 896px — general content + settings/admin panes (some 2-up fields)
14
+ * - `wide` 1280px — dashboards, tables, multi-column grids
15
+ * - `full` 1536px — full-bleed safety ceiling
16
+ * - `ultrawide` 1800px — legacy; prefer `full`
17
+ */
18
+ type RegionMax = "prose" | "form" | "feed" | "content" | "wide" | "full" | "ultrawide";
19
+ interface RegionProps extends ComponentPropsWithRef<"div"> {
20
+ /** Semantic element to render (e.g. "main", "section", "nav"). Default "div". */
21
+ as?: ElementType;
22
+ /** Apply the fluid horizontal page inset (`--page-inset`). Default true. */
23
+ inset?: boolean;
24
+ /**
25
+ * Opt-in centered max-width cap: a purpose-named region ("prose" | "form" |
26
+ * "feed" | "content" | "wide" | "full") or any raw CSS length (e.g. "56rem").
27
+ * Omit for fully fluid, edge-to-edge (the default).
28
+ */
29
+ max?: RegionMax | (string & {});
30
+ /** Full-bleed: edge to edge with no inset. Overrides `inset`. */
31
+ bleed?: boolean;
32
+ /**
33
+ * Apply `env(safe-area-inset-*)` padding — for fixed or native-shell surfaces.
34
+ * `true` pads top + bottom (the legacy behavior); pass an edge array
35
+ * (`["top"]`, `["bottom"]`, …) to target specific edges, mirroring `SafeArea`.
36
+ */
37
+ safeArea?: boolean | Edge[];
38
+ }
39
+ /**
40
+ * The page region. Fluid-first: full width with a fluid page inset, NOT a
41
+ * centered fixed-width container. Reach for `max` only when a specific region
42
+ * (a reading column, a settings pane) genuinely needs a cap.
43
+ */
44
+ declare function Region({ as, inset, max, bleed, safeArea, className, style, children, ref, ...rest }: RegionProps): react.JSX.Element;
45
+
46
+ type Align$1 = "start" | "center" | "end" | "stretch";
47
+ type Justify$1 = "start" | "center" | "end" | "between" | "around";
48
+ interface StackProps extends ComponentPropsWithRef<"div"> {
49
+ /** Vertical gap. Number = rhythm units (×8px; fractions ok); string = raw CSS. Default 2 (16px). */
50
+ gap?: number | string;
51
+ align?: Align$1;
52
+ justify?: Justify$1;
53
+ }
54
+ /** Vertical flow on the 8px rhythm. The default container for stacked content. */
55
+ declare function Stack({ gap, align, justify, className, style, children, ref, ...rest }: StackProps): react.JSX.Element;
56
+
57
+ interface PageProps extends RegionProps {
58
+ /**
59
+ * Vertical gap between the stacked page sections. Number = rhythm units
60
+ * (×8px); string = raw CSS. @default 4 (32px)
61
+ */
62
+ gap?: StackProps["gap"];
63
+ /** Cross-axis alignment of the stacked sections. @default "stretch" */
64
+ align?: StackProps["align"];
65
+ }
66
+ /**
67
+ * Page — a route shell. A `Region` (fluid page inset, opt-in `max` cap, optional
68
+ * `safeArea`) whose children flow as vertically-rhythmic sections (a `Stack`).
69
+ * Replaces the repeated `<Region><Stack>…</Stack></Region>` wrapper around
70
+ * create / settings / admin routes.
71
+ *
72
+ * Distinct from `AppShell` (the app frame the page lives inside) and `PageHeader`
73
+ * (the heading block at the top of a page) — `Page` is the content region itself.
74
+ *
75
+ * <Page max="feed" gap={4}>…</Page>
76
+ * <Page as="main" max="wide">…</Page>
77
+ * <Page bleed>…</Page>
78
+ */
79
+ declare function Page({ gap, align, children, ...regionProps }: PageProps): react.JSX.Element;
80
+ declare namespace Page {
81
+ var displayName: string;
82
+ }
83
+
84
+ /** A breakpoint name, or `base` for the default (mobile-first) value. */
85
+ type Breakpoint = "base" | "sm" | "md" | "lg" | "xl" | "2xl";
86
+ /** A static value, or a per-breakpoint map (`{ base, sm, md, lg, xl, "2xl" }`). */
87
+ type Responsive<T> = T | Partial<Record<Breakpoint, T>>;
88
+
89
+ interface GridProps extends ComponentPropsWithRef<"div"> {
90
+ /**
91
+ * Fixed column count — a number, or a breakpoint map (`{ base: 2, md: 4, lg: 6 }`)
92
+ * for fewer columns on small screens. Ignored when `minItemWidth` is set.
93
+ * Default 12.
94
+ */
95
+ cols?: Responsive<number>;
96
+ /**
97
+ * Make cells "breathe": `repeat(auto-fit, minmax(minItemWidth, 1fr))`.
98
+ * The grid grows/wraps responsively without breakpoints. e.g. "16rem".
99
+ */
100
+ minItemWidth?: string;
101
+ /** Gap between cells. Number = rhythm units (×8px); string = raw CSS. Default `--grid-gutter`. */
102
+ gap?: number | string;
103
+ }
104
+ /**
105
+ * CSS Grid layout primitive. Three modes: a fixed `cols` count, a responsive
106
+ * `cols` breakpoint map (fewer columns on small screens), or `minItemWidth`
107
+ * auto-fit cells that breathe with no breakpoints at all.
108
+ */
109
+ declare function Grid({ cols, minItemWidth, gap, className, style, children, ref, ...rest }: GridProps): react.JSX.Element;
110
+
111
+ interface ColumnsProps extends ComponentPropsWithRef<"div"> {
112
+ /**
113
+ * Number of columns in the track — a number, or a breakpoint map for fewer
114
+ * columns on small screens (`{ base: 2, md: 6, lg: 12 }`). Default
115
+ * `--grid-columns` (12).
116
+ */
117
+ count?: Responsive<number>;
118
+ /**
119
+ * Gap between columns and rows. Number = rhythm units (×8px); string = raw
120
+ * CSS. Default `--grid-gutter`. The column gap is capped so the `count - 1`
121
+ * gaps together never exceed 75% of the track's width (see `columnGap`).
122
+ */
123
+ gap?: number | string;
124
+ }
125
+ interface ColProps extends ComponentPropsWithRef<"div"> {
126
+ /** Columns to span — a number, or a breakpoint map (`{ base: 12, md: 8, lg: 7 }`). Default 1. */
127
+ span?: Responsive<number>;
128
+ /** 1-based column to start at — a number or a breakpoint map. */
129
+ start?: Responsive<number>;
130
+ }
131
+ /**
132
+ * An explicit N-column track (default 12). `count` accepts a breakpoint map so
133
+ * the track itself can thin out on small screens (e.g. 2 → 6 → 12). Pair with
134
+ * `Col` for spans. The track template resolves through the `[data-sable-grid-cols]`
135
+ * rules in `styles.css`, mobile-first.
136
+ */
137
+ declare function Columns({ count, gap, className, style, children, ref, ...rest }: ColumnsProps): react.JSX.Element;
138
+ /**
139
+ * A cell spanning `span` columns within a `Columns` track. `span` and `start`
140
+ * accept either a number or a breakpoint map for responsive layouts:
141
+ *
142
+ * <Col span={{ base: 12, md: 8, lg: 7 }} />
143
+ *
144
+ * The per-breakpoint values are emitted as inline CSS custom properties and
145
+ * resolved mobile-first by the `[data-sable-col]` rules in `styles.css`, so any
146
+ * span value works without the consuming app's Tailwind scanning a class.
147
+ */
148
+ declare function Col({ span, start, className, style, children, ref, ...rest }: ColProps): react.JSX.Element;
149
+
150
+ type Align = "start" | "center" | "end" | "stretch" | "baseline";
151
+ type Justify = "start" | "center" | "end" | "between" | "around";
152
+ interface RowProps extends ComponentPropsWithRef<"div"> {
153
+ /** Horizontal gap. Number = rhythm units (×8px; fractions ok); string = raw CSS. Default 2 (16px). */
154
+ gap?: number | string;
155
+ align?: Align;
156
+ justify?: Justify;
157
+ wrap?: boolean;
158
+ }
159
+ /** Horizontal flow. Inline rows of controls, toolbars, action clusters. */
160
+ declare function Row({ gap, align, justify, wrap, className, style, children, ref, ...rest }: RowProps): react.JSX.Element;
161
+
162
+ interface SpacerProps {
163
+ /** Fixed size in rhythm units (×8px; fractions ok). Ignored when `grow`. */
164
+ size?: number;
165
+ /** Axis the fixed size applies to. Default "vertical". */
166
+ axis?: "vertical" | "horizontal";
167
+ /** Flex-grow to push siblings apart (fills available space). */
168
+ grow?: boolean;
169
+ className?: string;
170
+ }
171
+ /** Explicit space on the rhythm, or a flexible `grow` pusher between siblings. */
172
+ declare function Spacer({ size, axis, grow, className }: SpacerProps): react.JSX.Element;
173
+
174
+ interface CenterProps extends ComponentPropsWithRef<"div"> {
175
+ /** Cap width: a named region ("prose" | "feed" | "wide" | "ultrawide") or any raw CSS length. */
176
+ max?: string;
177
+ }
178
+ /** Horizontally center a block, optionally capping its width (commonly "prose"). */
179
+ declare function Center({ max, className, style, children, ref, ...rest }: CenterProps): react.JSX.Element;
180
+
181
+ interface AspectBoxProps extends HTMLAttributes<HTMLDivElement> {
182
+ /** Width / height ratio. Default 16/9. Reserves space so late media never shifts layout. */
183
+ ratio?: number;
184
+ }
185
+ /** Reserves grid space via aspect-ratio — async/media content loads without layout shift. */
186
+ declare function AspectBox({ ratio, className, style, children, ...rest }: AspectBoxProps): react.JSX.Element;
187
+
188
+ interface BleedProps extends ComponentPropsWithRef<"div"> {
189
+ }
190
+ /**
191
+ * Bleed — lets content escape a width-capped or inset ancestor and span the full
192
+ * width of its layout container: a profile cover, a hero band, or full-bleed
193
+ * media inside a `max`-capped `Region` / `Page`.
194
+ *
195
+ * It uses container-query width units (`width: 100cqw` + `margin-inline: calc(50%
196
+ * - 50cqw)`), so it spans the nearest query container rather than the raw
197
+ * viewport. `AppShell`'s content region is a container (`container-type:
198
+ * inline-size`), so a Bleed inside it fills the content area and stops at a fixed
199
+ * left `Sidebar` instead of bleeding under it. With no container ancestor (a
200
+ * full-bleed app, or `Bleed` used standalone) `cqw` resolves against the viewport,
201
+ * so behavior is unchanged there. Nest a `Region` / `Center` inside to re-inset.
202
+ *
203
+ * <Region max="feed">
204
+ * <Bleed><ProfileCover /></Bleed>
205
+ * …
206
+ * </Region>
207
+ */
208
+ declare function Bleed({ className, style, children, ref, ...rest }: BleedProps): react.JSX.Element;
209
+ declare namespace Bleed {
210
+ var displayName: string;
211
+ }
212
+
213
+ type Collapse = "sm" | "md" | "lg" | "xl";
214
+ interface SplitPaneProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
215
+ /** The leading / larger pane (e.g. the media column). */
216
+ primary: ReactNode;
217
+ /** The trailing / supporting pane (e.g. the detail column). */
218
+ secondary: ReactNode;
219
+ /**
220
+ * Breakpoint at/above which the two panes sit side by side; below it they
221
+ * stack (primary on top). @default "lg"
222
+ */
223
+ collapseBelow?: Collapse;
224
+ /**
225
+ * Primary:secondary width ratio when side by side, as `"a/b"` fr units.
226
+ * @default "1/1" (e.g. "2/1", "3/2")
227
+ */
228
+ ratio?: string;
229
+ /** Gap between panes. Number = rhythm units (×8px); string = raw CSS. Default `--grid-gutter`. */
230
+ gap?: number | string;
231
+ }
232
+ /**
233
+ * SplitPane — a responsive two-pane layout. At/above `collapseBelow` the panes
234
+ * sit side by side at `ratio`; below it they stack (primary first). Owns the
235
+ * structural pattern behind list/detail and media/detail pages, so a route stops
236
+ * hand-building a desktop two-column layout plus a separate mobile stack.
237
+ *
238
+ * The breakpoint switch + ratio resolve through the `[data-sable-split]` rules in
239
+ * `styles.css` (inline CSS custom properties), so any `ratio` works.
240
+ *
241
+ * `collapseBelow` is **viewport-relative**, not container-relative: it asks how
242
+ * wide the window is, not how wide this SplitPane is. Dropped inside a narrow
243
+ * column — a sidebar, a modal, one cell of a Grid — it will happily stay
244
+ * side-by-side in 300px because the window is 1440px. The same caveat applies to
245
+ * the breakpoint maps on `Grid cols`, `Columns`, and `Col`. When a block has to
246
+ * adapt to the space it actually occupies, reach for `Grid minItemWidth`, which
247
+ * is intrinsically container-driven and needs no query at all.
248
+ *
249
+ * <SplitPane collapseBelow="lg" ratio="3/2" primary={<Media />} secondary={<Details />} />
250
+ */
251
+ declare function SplitPane({ primary, secondary, collapseBelow, ratio, gap, className, style, ref, ...rest }: SplitPaneProps): react.JSX.Element;
252
+ declare namespace SplitPane {
253
+ var displayName: string;
254
+ }
255
+
256
+ interface SafeAreaProps extends ComponentPropsWithRef<"div"> {
257
+ /** Which edges get `env(safe-area-inset-*)` padding. Default all four. */
258
+ edges?: Edge[];
259
+ }
260
+ /** Pads for device safe-areas (notch, home indicator) on fixed/native surfaces. */
261
+ declare function SafeArea({ edges, className, style, children, ref, ...rest }: SafeAreaProps): react.JSX.Element;
262
+
263
+ interface GridOverlayProps {
264
+ /** Number of column bands. Default `--grid-columns` (12). */
265
+ columns?: number;
266
+ /** Gap between bands. Default `--grid-gutter`. */
267
+ gap?: string;
268
+ /** Align bands to the fluid page inset. Default true. */
269
+ inset?: boolean;
270
+ /** Toggle visibility. Default true. */
271
+ show?: boolean;
272
+ className?: string;
273
+ }
274
+ /**
275
+ * Dev/docs structure visualizer — overlays the column grid so the layout's
276
+ * skeleton is legible. Place inside a `position: relative` container.
277
+ */
278
+ declare function GridOverlay({ columns, gap, inset, show, className, }: GridOverlayProps): react.JSX.Element | null;
279
+
280
+ export { AspectBox, type AspectBoxProps, Bleed, type BleedProps, type Breakpoint, Center, type CenterProps, Col, type ColProps, Columns, type ColumnsProps, Grid, GridOverlay, type GridOverlayProps, type GridProps, Page, type PageProps, Region, type RegionMax, type RegionProps, type Responsive, Row, type RowProps, SafeArea, type SafeAreaProps, Spacer, type SpacerProps, SplitPane, type SplitPaneProps, Stack, type StackProps };
@@ -0,0 +1,2 @@
1
+ export { AspectBox, Bleed, Center, Col, Columns, Grid, GridOverlay, Page, Region, Row, SafeArea, Spacer, SplitPane, Stack } from '../chunk-3IWZYBGH.js';
2
+ import '../chunk-SRQWQUTW.js';
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Machine-readable component metadata — the AI-readable surface of the system.
3
+ * Each component ships a `<name>.meta.ts` exporting one `ComponentMeta`. These
4
+ * aggregate into `registry.json` and are what other agents read to understand
5
+ * the component's contract. Keep meta in sync with the component.
6
+ */
7
+ interface PropMeta {
8
+ name: string;
9
+ type: string;
10
+ default?: string;
11
+ description: string;
12
+ /**
13
+ * Set when the prop is deprecated: what to use instead. The registry
14
+ * contract refuses "deprecated" in prose without it, so an agent reads a
15
+ * field rather than interpreting a sentence.
16
+ */
17
+ deprecated?: string;
18
+ }
19
+ interface ExampleMeta {
20
+ label: string;
21
+ /** A self-contained JSX snippet demonstrating the example. */
22
+ code: string;
23
+ }
24
+ interface BestPracticeMeta {
25
+ /** Whether this is positive guidance or an anti-pattern to avoid. */
26
+ guidance: "do" | "dont";
27
+ /** Short design guidance focused on when and how to use the component. */
28
+ description: string;
29
+ }
30
+ interface AnatomyMeta {
31
+ /** Visible or structural part name, e.g. "Label", "Trigger", "Content". */
32
+ name: string;
33
+ /** Whether the part is required for the component to make sense. */
34
+ required: boolean;
35
+ /** The role this part plays in the composed UI. */
36
+ description: string;
37
+ }
38
+ interface UsageMeta {
39
+ /** What the component is for and when to reach for it. */
40
+ description: string;
41
+ /** Do/don't guidance for designers, developers, and AI agents. */
42
+ bestPractices?: BestPracticeMeta[];
43
+ /** Structural parts that make up the component. */
44
+ anatomy?: AnatomyMeta[];
45
+ }
46
+ /**
47
+ * The four roles motion plays in a component. Every component fills the slots
48
+ * that apply to it and explicitly declines the ones that don't.
49
+ *
50
+ * The vocabulary exists because the motion rules were entirely PROHIBITIVE:
51
+ * `raw-duration`, `builtin-animate`, the bare-colour-transition rule and
52
+ * `unknown-motion` all fire on motion that is present and wrong, and not one of
53
+ * them can fire on motion that is absent. A component with no motion passed every gate
54
+ * in the system. That is precisely the shape an agent optimises into — it writes
55
+ * what is checked — so the catalog filled up with correct, static components and
56
+ * motion became a thing a human had to ask for by name, every time.
57
+ *
58
+ * arrival how it enters the page or becomes visible
59
+ * response what it does under the pointer — hover, press, focus
60
+ * transition how it moves between its own states (open/closed, on/off)
61
+ * activity what it does while working, indeterminately
62
+ *
63
+ * The slots are deliberately about the USER'S experience of the component, not
64
+ * about CSS mechanism, so "which slots apply here" is answerable from the
65
+ * component's purpose before a line of it is written.
66
+ */
67
+ type MotionSlot = "arrival" | "response" | "transition" | "activity";
68
+ interface MotionMeta {
69
+ /** Which of the four roles this entry covers. */
70
+ slot: MotionSlot;
71
+ /**
72
+ * The motion recipe that fills it — `motion-interactive`, `motion-pop`,
73
+ * `motion-disclosure`, … — validated against the recipes the package
74
+ * actually ships, so a renamed recipe cannot rot here silently.
75
+ *
76
+ * Two literals are sanctioned besides a recipe name:
77
+ *
78
+ * "none" the slot does NOT apply here, and this is the only way to say so
79
+ * "custom" the slot is filled outside the recipe layer — a rAF loop, a
80
+ * canvas, a Base UI transition — because no CSS utility can
81
+ * express it. ThinkingOrb draws a point cloud per frame; there is
82
+ * no recipe that could ever cover it, and forcing it to claim
83
+ * `"none"` would file a component that never stops moving under
84
+ * "does not animate".
85
+ *
86
+ * Both still require a `why`, as does a filled slot. An unfilled slot is
87
+ * either a considered decision or an oversight, and the difference has to be
88
+ * legible to the next reader; a `"custom"` with no explanation is just an
89
+ * opt-out with extra steps. This mirrors `design-lint-disable … -- <reason>`:
90
+ * the exception is allowed, the silent exception is not.
91
+ */
92
+ recipe: string;
93
+ /**
94
+ * The anatomy part it applies to ("Trigger", "Panel", "root"). Motion that
95
+ * cannot name what moves is usually motion nobody checked.
96
+ */
97
+ part: string;
98
+ /**
99
+ * Why this slot resolves this way here. Not a restatement of the recipe —
100
+ * `"motion-pop on the panel"` says nothing a reader could not already see.
101
+ * State what the motion is FOR: what the user would misread without it.
102
+ */
103
+ why: string;
104
+ }
105
+ interface RelatedMeta {
106
+ /** Components commonly composed with, compared to, or preferred over this one. */
107
+ components?: string[];
108
+ /** Canonical page patterns that show this component in context. */
109
+ patterns?: string[];
110
+ }
111
+ /**
112
+ * A composed child component exported alongside the root (e.g. ProgressTrack,
113
+ * SelectItem) — a separate element in JSX, NOT a prop of the root. Keeping
114
+ * these out of `props` is what tells a consumer to write
115
+ * `<Progress><ProgressTrack/></Progress>` rather than
116
+ * `<Progress ProgressTrack={{…}}/>`.
117
+ */
118
+ interface SubcomponentMeta {
119
+ /** Exported component name, e.g. "ProgressTrack". */
120
+ name: string;
121
+ /** The props it accepts (same shorthand style as PropMeta.type). */
122
+ type: string;
123
+ description: string;
124
+ }
125
+ /**
126
+ * Machine-readable metadata for a page pattern — a canonical full-page
127
+ * composition in `src/patterns/`. The pattern's *source* is the contract: the
128
+ * registry generator extracts the code after the `// ---pattern-code---` marker
129
+ * verbatim, so what ships in `registry.json → patterns` always compiles.
130
+ */
131
+ interface PatternMeta {
132
+ /** Pattern name, e.g. "SettingsPage". */
133
+ name: string;
134
+ /** One-line summary. */
135
+ summary: string;
136
+ /** When to reach for this shell — and when not to. */
137
+ intent: string;
138
+ /** Canonical decisions the pattern encodes (region cap, rhythm, hierarchy). */
139
+ notes?: string[];
140
+ /** Accessibility notes. */
141
+ a11y?: string[];
142
+ }
143
+ interface ComponentMeta {
144
+ /** Public component name, e.g. "Button". Must be an actual export. */
145
+ name: string;
146
+ /**
147
+ * Set when `name` labels a SYSTEM rather than a single importable value —
148
+ * `Toast` is `ToastProvider` + `toast()` + parts, and nothing is exported as
149
+ * plain `Toast`. The build otherwise requires `name` to exist in the package's
150
+ * export surface, because the catalog is the API as far as an agent is
151
+ * concerned and `import { Error }` (for `ErrorState`) was written by every
152
+ * agent that read it. Declaring this is a visible claim that the name is a
153
+ * family label; do not reach for it to silence an ordinary mismatch.
154
+ */
155
+ system?: boolean;
156
+ /** One-line summary. */
157
+ summary: string;
158
+ /** Search terms and synonyms agents/users might use to discover it. */
159
+ keywords?: string[];
160
+ /** Usage guidance: intent, do/don'ts, and anatomy. */
161
+ usage?: UsageMeta;
162
+ /** Related components and patterns for composition/choice guidance. */
163
+ related?: RelatedMeta;
164
+ /** Maturity. */
165
+ /**
166
+ * Maturity.
167
+ *
168
+ * `deprecated` exists because it had to: `CategoryPill` announced
169
+ * "DEPRECATED" in its summary prose while carrying `status: "stable"`, since
170
+ * the union had no way to say otherwise. An agent reading the contract would
171
+ * have used it without hesitation. A deprecated component MUST also name its
172
+ * replacement in `supersededBy` — build-registry enforces both directions, so
173
+ * prose and status cannot disagree again.
174
+ */
175
+ status: "stable" | "beta" | "planned" | "deprecated";
176
+ /**
177
+ * What to use instead. Required when `status` is `deprecated`, and meaningless
178
+ * otherwise — a deprecation with no forward path just strands the reader.
179
+ */
180
+ supersededBy?: string;
181
+ /** Public props of the root component only — never subcomponents. */
182
+ props: PropMeta[];
183
+ /**
184
+ * Composed child components, if any. A component that lists subcomponents
185
+ * must also ship at least one example showing the composed JSX form.
186
+ */
187
+ subcomponents?: SubcomponentMeta[];
188
+ /** Variant axis → allowed values. */
189
+ variants?: Record<string, string[]>;
190
+ /** Semantic tokens the component reads (for theming audits). */
191
+ tokensUsed: string[];
192
+ /**
193
+ * How the component moves, one entry per applicable motion slot.
194
+ *
195
+ * Ordering follows the slot vocabulary (arrival → response → transition →
196
+ * activity), which is also the order a user encounters them.
197
+ */
198
+ motion?: MotionMeta[];
199
+ /** Accessibility notes. */
200
+ a11y?: string[];
201
+ /** Usage examples. */
202
+ examples: ExampleMeta[];
203
+ }
204
+
205
+ export type { AnatomyMeta as A, BestPracticeMeta as B, ComponentMeta as C, ExampleMeta as E, MotionMeta as M, PatternMeta as P, RelatedMeta as R, SubcomponentMeta as S, UsageMeta as U, MotionSlot as a, PropMeta as b };
@@ -0,0 +1,36 @@
1
+ import * as react from 'react';
2
+ import { P as PatternMeta } from '../meta-6dUzgC6M.js';
3
+
4
+ declare function AuthPage(): react.JSX.Element;
5
+
6
+ declare const authPageMeta: PatternMeta;
7
+
8
+ declare function BrandLandingPage(): react.JSX.Element;
9
+
10
+ declare const brandLandingPageMeta: PatternMeta;
11
+
12
+ declare function ChatPanel(): react.JSX.Element;
13
+
14
+ declare const chatPanelMeta: PatternMeta;
15
+
16
+ declare function DashboardPage(): react.JSX.Element;
17
+
18
+ declare const dashboardPageMeta: PatternMeta;
19
+
20
+ declare function FeedPage(): react.JSX.Element;
21
+
22
+ declare const feedPageMeta: PatternMeta;
23
+
24
+ declare function LandingPage(): react.JSX.Element;
25
+
26
+ declare const landingPageMeta: PatternMeta;
27
+
28
+ declare function ListDetailPage(): react.JSX.Element;
29
+
30
+ declare const listDetailPageMeta: PatternMeta;
31
+
32
+ declare function SettingsPage(): react.JSX.Element;
33
+
34
+ declare const settingsPageMeta: PatternMeta;
35
+
36
+ export { AuthPage, BrandLandingPage, ChatPanel, DashboardPage, FeedPage, LandingPage, ListDetailPage, SettingsPage, authPageMeta, brandLandingPageMeta, chatPanelMeta, dashboardPageMeta, feedPageMeta, landingPageMeta, listDetailPageMeta, settingsPageMeta };