@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.
- package/AGENTS.md +157 -0
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +221 -0
- package/DESIGN.md +548 -0
- package/LICENSE +21 -0
- package/README.md +194 -2
- package/THIRD_PARTY_NOTICES.md +44 -0
- package/bin/sable-lint.mjs +0 -0
- package/bin/sable.mjs +1223 -0
- package/dist/_contract-CYScOdf5.d.ts +150 -0
- package/dist/chunk-3IWZYBGH.js +398 -0
- package/dist/chunk-5Z5KLOAU.js +167 -0
- package/dist/chunk-7MG4SHYX.js +15 -0
- package/dist/chunk-ADLLDWLW.js +15 -0
- package/dist/chunk-EP55KSYA.js +14 -0
- package/dist/chunk-J23ELQQZ.js +9396 -0
- package/dist/chunk-JI7WJP2L.js +286 -0
- package/dist/chunk-K7RUHUGC.js +14 -0
- package/dist/chunk-KO3NAEBF.js +15 -0
- package/dist/chunk-L4IEIQQA.js +14 -0
- package/dist/chunk-L6Y46C62.js +270 -0
- package/dist/chunk-NEQNXI2G.js +25 -0
- package/dist/chunk-PAOZBZME.js +40 -0
- package/dist/chunk-RWP4XBSK.js +139 -0
- package/dist/chunk-SRQWQUTW.js +63 -0
- package/dist/chunk-YXBOYWBN.js +15 -0
- package/dist/foundations/index.d.ts +88 -0
- package/dist/foundations/index.js +792 -0
- package/dist/icon/index.d.ts +60 -0
- package/dist/icon/index.js +2 -0
- package/dist/icon-sets/brands.d.ts +44 -0
- package/dist/icon-sets/brands.js +147 -0
- package/dist/icon-sets/reicon.d.ts +8 -0
- package/dist/icon-sets/reicon.js +1 -0
- package/dist/index.d.ts +7013 -0
- package/dist/index.js +15462 -0
- package/dist/ios/chrome.css +83 -0
- package/dist/ios/frame.css +5 -0
- package/dist/ios/index.d.ts +70 -0
- package/dist/ios/index.js +313 -0
- package/dist/ios/preview.d.ts +64 -0
- package/dist/ios/preview.js +133 -0
- package/dist/ios/styles.css +77 -0
- package/dist/layout/index.d.ts +280 -0
- package/dist/layout/index.js +2 -0
- package/dist/meta-6dUzgC6M.d.ts +205 -0
- package/dist/patterns/index.d.ts +36 -0
- package/dist/patterns/index.js +934 -0
- package/dist/presets/index.d.ts +37 -0
- package/dist/presets/index.js +117 -0
- package/dist/registry-CyL28OzJ.d.ts +28 -0
- package/dist/styles.css +1893 -0
- package/dist/theme-provider-BWcizhAt.d.ts +105 -0
- package/dist/themes/atelier/atelier.css +213 -0
- package/dist/themes/atelier/index.d.ts +9 -0
- package/dist/themes/atelier/index.js +1 -0
- package/dist/themes/base/base.css +443 -0
- package/dist/themes/base/index.d.ts +9 -0
- package/dist/themes/base/index.js +1 -0
- package/dist/themes/ember/ember.css +270 -0
- package/dist/themes/ember/index.d.ts +9 -0
- package/dist/themes/ember/index.js +1 -0
- package/dist/themes/index.d.ts +10 -0
- package/dist/themes/index.js +8 -0
- package/dist/themes/meridian/index.d.ts +9 -0
- package/dist/themes/meridian/index.js +1 -0
- package/dist/themes/meridian/meridian.css +255 -0
- package/dist/themes/poppy/index.d.ts +6 -0
- package/dist/themes/poppy/index.js +1 -0
- package/dist/themes/poppy/poppy.css +174 -0
- package/dist/themes/prism/index.d.ts +6 -0
- package/dist/themes/prism/index.js +1 -0
- package/dist/themes/prism/prism.css +183 -0
- package/dist/themes/verdant/index.d.ts +9 -0
- package/dist/themes/verdant/index.js +1 -0
- package/dist/themes/verdant/verdant.css +210 -0
- package/dist/themes.css +30 -0
- package/llms/components/accordion.md +93 -0
- package/llms/components/app-header.md +70 -0
- package/llms/components/app-shell.md +117 -0
- package/llms/components/attachment.md +105 -0
- package/llms/components/avatar.md +141 -0
- package/llms/components/badge.md +117 -0
- package/llms/components/banner.md +119 -0
- package/llms/components/bottom-nav.md +80 -0
- package/llms/components/breadcrumbs.md +75 -0
- package/llms/components/browser.md +67 -0
- package/llms/components/bubble.md +119 -0
- package/llms/components/button.md +199 -0
- package/llms/components/calendar.md +75 -0
- package/llms/components/card.md +135 -0
- package/llms/components/carousel.md +87 -0
- package/llms/components/chat-composer.md +126 -0
- package/llms/components/checkbox-card.md +98 -0
- package/llms/components/checkbox.md +90 -0
- package/llms/components/code-block.md +81 -0
- package/llms/components/code.md +62 -0
- package/llms/components/collapsible.md +83 -0
- package/llms/components/color-field.md +100 -0
- package/llms/components/combobox.md +119 -0
- package/llms/components/command-menu.md +158 -0
- package/llms/components/context-card.md +73 -0
- package/llms/components/context-menu.md +79 -0
- package/llms/components/data-table.md +99 -0
- package/llms/components/date-picker.md +97 -0
- package/llms/components/description.md +96 -0
- package/llms/components/destructive-action-modal.md +76 -0
- package/llms/components/dialog.md +127 -0
- package/llms/components/dots-menu.md +64 -0
- package/llms/components/empty-state.md +113 -0
- package/llms/components/entity.md +67 -0
- package/llms/components/feedback.md +63 -0
- package/llms/components/fieldset.md +106 -0
- package/llms/components/file-dropzone.md +103 -0
- package/llms/components/form.md +111 -0
- package/llms/components/gliding-highlight.md +84 -0
- package/llms/components/inline-status.md +62 -0
- package/llms/components/input.md +127 -0
- package/llms/components/kbd.md +57 -0
- package/llms/components/label.md +73 -0
- package/llms/components/marker.md +99 -0
- package/llms/components/marquee.md +66 -0
- package/llms/components/menu.md +122 -0
- package/llms/components/message-scroller.md +110 -0
- package/llms/components/message.md +114 -0
- package/llms/components/meter.md +124 -0
- package/llms/components/middle-truncate.md +62 -0
- package/llms/components/note.md +81 -0
- package/llms/components/notification-badge.md +67 -0
- package/llms/components/number-field.md +70 -0
- package/llms/components/otp-field.md +101 -0
- package/llms/components/page-header.md +76 -0
- package/llms/components/page-loader.md +67 -0
- package/llms/components/pagination.md +92 -0
- package/llms/components/panel-group.md +120 -0
- package/llms/components/panel-section.md +122 -0
- package/llms/components/phone.md +70 -0
- package/llms/components/pill.md +73 -0
- package/llms/components/popover.md +79 -0
- package/llms/components/progress.md +98 -0
- package/llms/components/radio-card.md +90 -0
- package/llms/components/radio.md +86 -0
- package/llms/components/relative-time.md +60 -0
- package/llms/components/scroller.md +88 -0
- package/llms/components/scrub-field.md +97 -0
- package/llms/components/search-input.md +84 -0
- package/llms/components/select.md +106 -0
- package/llms/components/separator.md +70 -0
- package/llms/components/settings-menu.md +139 -0
- package/llms/components/sheet.md +97 -0
- package/llms/components/show-more.md +59 -0
- package/llms/components/side-nav.md +106 -0
- package/llms/components/sidebar.md +87 -0
- package/llms/components/skeleton.md +62 -0
- package/llms/components/slider.md +114 -0
- package/llms/components/spinner.md +87 -0
- package/llms/components/status-bar.md +91 -0
- package/llms/components/status-dot.md +70 -0
- package/llms/components/stepper.md +125 -0
- package/llms/components/streaming-text.md +86 -0
- package/llms/components/switch.md +87 -0
- package/llms/components/table.md +112 -0
- package/llms/components/tabs.md +84 -0
- package/llms/components/textarea.md +88 -0
- package/llms/components/theme-selector.md +95 -0
- package/llms/components/theme-switcher.md +71 -0
- package/llms/components/thinking-orb.md +91 -0
- package/llms/components/toast.md +130 -0
- package/llms/components/toggle.md +95 -0
- package/llms/components/toolbar.md +168 -0
- package/llms/components/tooltip.md +93 -0
- package/llms/components/tree.md +227 -0
- package/llms/components/video.md +66 -0
- package/llms/components/workspace.md +138 -0
- package/llms/foundations/accessibility.md +60 -0
- package/llms/foundations/motion.md +96 -0
- package/llms/foundations/scrolling.md +41 -0
- package/llms/foundations/shape.md +73 -0
- package/llms/foundations/spacing.md +82 -0
- package/llms/foundations/surfaces.md +69 -0
- package/llms/foundations/typography.md +120 -0
- package/llms/layout/aspect-box.md +37 -0
- package/llms/layout/bleed.md +31 -0
- package/llms/layout/center.md +37 -0
- package/llms/layout/columns.md +51 -0
- package/llms/layout/grid-overlay.md +40 -0
- package/llms/layout/grid.md +47 -0
- package/llms/layout/page.md +39 -0
- package/llms/layout/region.md +49 -0
- package/llms/layout/row.md +40 -0
- package/llms/layout/safe-area.md +37 -0
- package/llms/layout/spacer.md +39 -0
- package/llms/layout/split-pane.md +41 -0
- package/llms/layout/stack.md +39 -0
- package/llms/patterns/auth-page.md +68 -0
- package/llms/patterns/brand-landing-page.md +423 -0
- package/llms/patterns/chat-panel.md +210 -0
- package/llms/patterns/dashboard-page.md +170 -0
- package/llms/patterns/feed-page.md +103 -0
- package/llms/patterns/landing-page.md +122 -0
- package/llms/patterns/list-detail-page.md +100 -0
- package/llms/patterns/settings-page.md +121 -0
- package/llms/reference/token-intent.md +324 -0
- package/llms/reference/utilities.md +129 -0
- package/llms/tasks/adding-a-component.md +113 -0
- package/llms/tasks/auditing-an-app.md +216 -0
- package/llms/tasks/building-a-page.md +109 -0
- package/llms/tasks/building-a-tool.md +141 -0
- package/llms/tasks/building-an-ios-prototype.md +105 -0
- package/llms/tasks/building-with-sable.md +73 -0
- package/llms/tasks/choosing-a-component.md +109 -0
- package/llms/tasks/converting-an-app.md +139 -0
- package/llms/tasks/creating-a-preset.md +43 -0
- package/llms/tasks/creating-design-assets.md +96 -0
- package/llms/tasks/designing-a-surface.md +115 -0
- package/llms/tasks/developing-taste.md +157 -0
- package/llms/tasks/getting-started.md +63 -0
- package/llms/tasks/learning-from-builds.md +74 -0
- package/llms/tasks/reviewing-a-design.md +114 -0
- package/llms/tasks/reviewing-a-journey.md +84 -0
- package/llms/tasks/reviewing-motion.md +92 -0
- package/llms/tasks/using-presets.md +87 -0
- package/llms/taste/asset-plan.template.json +53 -0
- package/llms/taste/composition-follows-content.md +20 -0
- package/llms/taste/hierarchy-is-a-relationship.md +22 -0
- package/llms/taste/lessons.json +57 -0
- package/llms/taste/motion-needs-a-reason.md +21 -0
- package/llms/taste/surface-states-travel-together.md +20 -0
- package/llms-full.txt +14906 -0
- package/llms.txt +405 -0
- package/package.json +185 -3
- package/registry.json +18812 -0
- package/scripts/agent-setup.mjs +54 -0
- package/scripts/design-atlas.mjs +221 -0
- package/scripts/design-directions.mjs +123 -0
- package/scripts/design-memory.mjs +166 -0
- package/scripts/design-preferences.mjs +227 -0
- package/scripts/design-presets.mjs +48 -0
- package/scripts/grade.mjs +509 -0
- package/scripts/lint-rules.mjs +1065 -0
- package/scripts/review-evidence.mjs +120 -0
- package/scripts/skeleton.mjs +248 -0
- package/skills/entry/SKILL.md +34 -0
- package/skills/sable/SKILL.md +187 -0
- package/skills/sable/agents/openai.yaml +6 -0
- package/targets/ios.json +982 -0
- 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,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 };
|