@sableui/react 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/AGENTS.md +3 -1
  2. package/CHANGELOG.md +120 -0
  3. package/CONTRIBUTING.md +8 -0
  4. package/README.md +1 -0
  5. package/bin/sable-lint.mjs +0 -0
  6. package/bin/sable.mjs +68 -26
  7. package/dist/{_contract-CYScOdf5.d.ts → _contract-o3tUc8Ig.d.ts} +1 -1
  8. package/dist/blocks/index.d.ts +396 -0
  9. package/dist/blocks/index.js +1997 -0
  10. package/dist/chunk-6LCIBMMG.js +4908 -0
  11. package/dist/{chunk-J23ELQQZ.js → chunk-CKNM7KSB.js} +1664 -4270
  12. package/dist/chunk-K6LII3OE.js +5093 -0
  13. package/dist/{chunk-RWP4XBSK.js → chunk-KFZ43RKU.js} +1 -1
  14. package/dist/{chunk-PAOZBZME.js → chunk-NGBDP3BM.js} +1 -1
  15. package/dist/{chunk-5Z5KLOAU.js → chunk-TH563WLW.js} +14 -1
  16. package/dist/{chunk-JI7WJP2L.js → chunk-X276MF2W.js} +5 -0
  17. package/dist/foundations/index.js +1 -1
  18. package/dist/icon/index.js +2 -2
  19. package/dist/icon-sets/reicon.js +1 -1
  20. package/dist/index.d.ts +624 -133
  21. package/dist/index.js +767 -4964
  22. package/dist/ios/index.d.ts +14 -2
  23. package/dist/ios/index.js +10 -9
  24. package/dist/ios/preview.d.ts +11 -1
  25. package/dist/ios/preview.js +14 -9
  26. package/dist/ios/styles.css +26 -6
  27. package/dist/{meta-6dUzgC6M.d.ts → meta-CcE_SeFu.d.ts} +37 -1
  28. package/dist/patterns/index.d.ts +1 -1
  29. package/dist/patterns/index.js +1684 -471
  30. package/dist/presets/index.d.ts +1 -1
  31. package/dist/presets/index.js +1 -1
  32. package/dist/styles.css +238 -40
  33. package/dist/{theme-provider-BWcizhAt.d.ts → theme-provider-BE-KqUG-.d.ts} +1 -1
  34. package/dist/themes/atelier/atelier.css +3 -0
  35. package/dist/themes/atelier/index.d.ts +1 -1
  36. package/dist/themes/base/base.css +34 -4
  37. package/dist/themes/base/index.d.ts +1 -1
  38. package/dist/themes/ember/ember.css +3 -0
  39. package/dist/themes/ember/index.d.ts +1 -1
  40. package/dist/themes/index.d.ts +2 -2
  41. package/dist/themes/index.js +2 -2
  42. package/dist/themes/meridian/index.d.ts +1 -1
  43. package/dist/themes/meridian/meridian.css +10 -5
  44. package/dist/themes/poppy/index.d.ts +1 -1
  45. package/dist/themes/poppy/poppy.css +16 -0
  46. package/dist/themes/prism/index.d.ts +1 -1
  47. package/dist/themes/prism/prism.css +4 -1
  48. package/dist/themes/verdant/index.d.ts +1 -1
  49. package/dist/themes/verdant/verdant.css +7 -2
  50. package/llms/blocks/api-keys-block.md +79 -0
  51. package/llms/blocks/chat-thread-block.md +51 -0
  52. package/llms/blocks/command-palette-block.md +48 -0
  53. package/llms/blocks/cta-band-block.md +62 -0
  54. package/llms/blocks/danger-zone-block.md +69 -0
  55. package/llms/blocks/device-comparison-block.md +91 -0
  56. package/llms/blocks/event-card-block.md +84 -0
  57. package/llms/blocks/faq-block.md +72 -0
  58. package/llms/blocks/feature-grid-block.md +75 -0
  59. package/llms/blocks/feedback-form-block.md +66 -0
  60. package/llms/blocks/goal-progress-block.md +54 -0
  61. package/llms/blocks/hero-block.md +65 -0
  62. package/llms/blocks/invite-team-block.md +61 -0
  63. package/llms/blocks/invoice-history-block.md +84 -0
  64. package/llms/blocks/kpi-sparkline-block.md +57 -0
  65. package/llms/blocks/media-player-block.md +100 -0
  66. package/llms/blocks/newsletter-block.md +64 -0
  67. package/llms/blocks/not-found-block.md +56 -0
  68. package/llms/blocks/notification-list-block.md +63 -0
  69. package/llms/blocks/notification-settings-block.md +54 -0
  70. package/llms/blocks/order-tracking-block.md +62 -0
  71. package/llms/blocks/pricing-card-block.md +82 -0
  72. package/llms/blocks/pricing-table-block.md +129 -0
  73. package/llms/blocks/product-card-block.md +69 -0
  74. package/llms/blocks/profile-card-block.md +103 -0
  75. package/llms/blocks/profile-form-block.md +114 -0
  76. package/llms/blocks/revenue-breakdown-block.md +70 -0
  77. package/llms/blocks/sign-in-block.md +64 -0
  78. package/llms/blocks/sign-up-block.md +78 -0
  79. package/llms/blocks/site-footer-block.md +96 -0
  80. package/llms/blocks/stat-row-block.md +59 -0
  81. package/llms/blocks/steps-block.md +73 -0
  82. package/llms/blocks/task-list-block.md +81 -0
  83. package/llms/blocks/team-members-block.md +91 -0
  84. package/llms/blocks/testimonial-block.md +78 -0
  85. package/llms/blocks/traffic-sources-block.md +63 -0
  86. package/llms/blocks/traffic-trend-block.md +95 -0
  87. package/llms/blocks/upload-list-block.md +66 -0
  88. package/llms/blocks/verify-code-block.md +59 -0
  89. package/llms/blocks/wallet-block.md +84 -0
  90. package/llms/components/area-chart.md +112 -0
  91. package/llms/components/bar-chart.md +131 -0
  92. package/llms/components/bubble.md +5 -5
  93. package/llms/components/button.md +1 -1
  94. package/llms/components/card.md +2 -2
  95. package/llms/components/checkbox-card.md +1 -1
  96. package/llms/components/context-card.md +2 -2
  97. package/llms/components/context-menu.md +1 -1
  98. package/llms/components/date-tile.md +79 -0
  99. package/llms/components/dialog.md +1 -1
  100. package/llms/components/empty-state.md +4 -4
  101. package/llms/components/entity.md +5 -3
  102. package/llms/components/fieldset.md +1 -1
  103. package/llms/components/kbd.md +18 -6
  104. package/llms/components/line-chart.md +101 -0
  105. package/llms/components/menu.md +1 -1
  106. package/llms/components/{dots-menu.md → overflow-menu.md} +9 -9
  107. package/llms/components/pie-chart.md +103 -0
  108. package/llms/components/pill.md +1 -1
  109. package/llms/components/radar-chart.md +106 -0
  110. package/llms/components/radial-chart.md +102 -0
  111. package/llms/components/segmented-bar.md +102 -0
  112. package/llms/components/settings-menu.md +2 -2
  113. package/llms/components/side-nav.md +1 -1
  114. package/llms/components/skeleton.md +1 -1
  115. package/llms/components/sparkline.md +80 -0
  116. package/llms/components/table.md +1 -1
  117. package/llms/components/theme-selector.md +1 -1
  118. package/llms/foundations/shape.md +1 -1
  119. package/llms/patterns/auth-page.md +132 -42
  120. package/llms/patterns/brand-landing-page.md +271 -175
  121. package/llms/patterns/chat-panel.md +120 -39
  122. package/llms/patterns/dashboard-page.md +276 -65
  123. package/llms/patterns/feed-page.md +245 -64
  124. package/llms/patterns/landing-page.md +473 -75
  125. package/llms/patterns/list-detail-page.md +377 -68
  126. package/llms/patterns/settings-page.md +268 -67
  127. package/llms/reference/token-intent.md +4 -2
  128. package/llms/reference/utilities.md +20 -5
  129. package/llms/tasks/building-an-ios-prototype.md +4 -1
  130. package/llms/tasks/choosing-a-component.md +2 -2
  131. package/llms-full.txt +6428 -892
  132. package/llms.txt +67 -16
  133. package/package.json +5 -1
  134. package/registry.json +3217 -552
  135. package/targets/ios.json +88 -18
  136. package/llms/components/description.md +0 -96
  137. package/dist/{chunk-L6Y46C62.js → chunk-MXJX3NZI.js} +1 -1
@@ -21,8 +21,13 @@ declare function iosThemeStyle(tokens: IosBrandTokens): CSSProperties;
21
21
  * the chrome and any Sable content then follow the preset together. `tokens`
22
22
  * overrides individual semantic values. `mode` also sets `dark` on <html>, so
23
23
  * Sable popups portaled to <body> follow the prototype's appearance.
24
+ *
25
+ * `systemChrome={false}` is for a prototype shown on a real phone, without
26
+ * PhoneFrame: the device already draws its own status bar and home indicator,
27
+ * so the fake ones go, and the safe areas come from the device
28
+ * (`env(safe-area-inset-*)`) instead of the standard phone's fixed geometry.
24
29
  */
25
- declare function NativeApp({ children, mode, glassTint, reducedMotion, className, tokens, style, }: {
30
+ declare function NativeApp({ children, mode, glassTint, reducedMotion, className, tokens, style, systemChrome, }: {
26
31
  children: ReactNode;
27
32
  mode?: "light" | "dark";
28
33
  glassTint?: number;
@@ -30,6 +35,8 @@ declare function NativeApp({ children, mode, glassTint, reducedMotion, className
30
35
  className?: string;
31
36
  tokens?: IosBrandTokens;
32
37
  style?: CSSProperties;
38
+ /** Draw the status bar, island and home indicator. Turn off on a real phone. @default true */
39
+ systemChrome?: boolean;
33
40
  }): react.JSX.Element;
34
41
 
35
42
  type Navigation = {
@@ -45,7 +52,12 @@ declare function NavigationStack({ children, active, label }: {
45
52
  label: string;
46
53
  }): react.JSX.Element;
47
54
 
48
- /** Browser dialog owns focus trapping, Escape, inert background, and restoration. */
55
+ /**
56
+ * An iOS 26 sheet. At the medium detent it floats inset from the screen edges in
57
+ * Liquid Glass; at the large detent it is opaque and meets the bottom edge. The
58
+ * header is a glass close button and a centered inline title.
59
+ * Browser dialog owns focus trapping, Escape, inert background, and restoration.
60
+ */
49
61
  declare function IosSheet({ open, onOpenChange, title, children, detent }: {
50
62
  open: boolean;
51
63
  onOpenChange: (open: boolean) => void;
package/dist/ios/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StatusIcons } from '../chunk-NEQNXI2G.js';
2
- import { App, Sheet, Button, Page, NavbarBackLink, TabbarLink, Navbar, Tabbar, Glass } from 'konsta/react';
2
+ import { App, Sheet, Button, Glass, Page, NavbarBackLink, TabbarLink, Navbar, Tabbar } from 'konsta/react';
3
3
  import { createContext, useEffect, useContext, useState, useRef, useId } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -124,7 +124,8 @@ function NativeApp({
124
124
  reducedMotion = false,
125
125
  className = "",
126
126
  tokens = {},
127
- style
127
+ style,
128
+ systemChrome = true
128
129
  }) {
129
130
  const dark = mode === "dark";
130
131
  useEffect(() => {
@@ -136,18 +137,18 @@ function NativeApp({
136
137
  {
137
138
  theme: "ios",
138
139
  dark,
139
- className: `ios-prototype ${dark ? "dark" : ""} ${className}`,
140
+ className: `ios-prototype ${dark ? "dark" : ""} ${systemChrome ? "" : "ios-bare"} ${className}`,
140
141
  "data-reduced-motion": reducedMotion || void 0,
141
142
  style: { ...iosThemeStyle(tokens), "--ios-glass-tint": tint, ...style },
142
143
  children: [
143
144
  /* @__PURE__ */ jsx(LiquidGlassMaterial, {}),
144
- /* @__PURE__ */ jsxs("div", { className: "ios-status-bar", "aria-hidden": "true", children: [
145
+ systemChrome ? /* @__PURE__ */ jsxs("div", { className: "ios-status-bar", "aria-hidden": "true", children: [
145
146
  /* @__PURE__ */ jsx("span", { children: "9:41" }),
146
147
  /* @__PURE__ */ jsx("span", { className: "ios-island" }),
147
148
  /* @__PURE__ */ jsx(StatusIcons, { className: "ios-status-icons" })
148
- ] }),
149
+ ] }) : null,
149
150
  children,
150
- /* @__PURE__ */ jsx("div", { className: "ios-home-indicator", "aria-hidden": "true" })
151
+ systemChrome ? /* @__PURE__ */ jsx("div", { className: "ios-home-indicator", "aria-hidden": "true" }) : null
151
152
  ]
152
153
  }
153
154
  );
@@ -245,7 +246,7 @@ function IosSheet({ open, onOpenChange, title, children, detent = "medium" }) {
245
246
  onClick: (event) => {
246
247
  if (event.target === event.currentTarget) onOpenChange(false);
247
248
  },
248
- children: /* @__PURE__ */ jsxs(Sheet, { opened: shown, backdrop: false, className: "ios-sheet", style: { height: expanded ? "90%" : "56%" }, children: [
249
+ children: /* @__PURE__ */ jsxs(Sheet, { opened: shown, backdrop: false, className: "ios-sheet", "data-detent": expanded ? "large" : "medium", style: { height: expanded ? "90%" : "56%" }, children: [
249
250
  /* @__PURE__ */ jsx(
250
251
  Button,
251
252
  {
@@ -276,8 +277,8 @@ function IosSheet({ open, onOpenChange, title, children, detent = "medium" }) {
276
277
  }
277
278
  ),
278
279
  /* @__PURE__ */ jsxs("div", { className: "ios-sheet-heading", children: [
279
- /* @__PURE__ */ jsx("h2", { id: titleId, tabIndex: -1, children: title }),
280
- /* @__PURE__ */ jsx(Button, { clear: true, onClick: () => onOpenChange(false), children: "Cancel" })
280
+ /* @__PURE__ */ jsx(Glass, { className: "ios-sheet-close", children: /* @__PURE__ */ jsx(Button, { inline: true, rounded: true, clear: true, className: "ios-icon-action", "aria-label": "Close", onClick: () => onOpenChange(false), children: /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", viewBox: "0 0 16 16", width: "16", height: "16", children: /* @__PURE__ */ jsx("path", { d: "M3.5 3.5l9 9m0-9l-9 9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", fill: "none" }) }) }) }),
281
+ /* @__PURE__ */ jsx("h2", { id: titleId, tabIndex: -1, children: title })
281
282
  ] }),
282
283
  /* @__PURE__ */ jsx("div", { className: "ios-sheet-content", children })
283
284
  ] })
@@ -26,11 +26,21 @@ declare const phoneSizes: {
26
26
  * scaled, never the viewport, so the prototype lays out as it would on a phone.
27
27
  * The document draws its own status bar, island and home indicator, so the
28
28
  * frame draws neither.
29
+ *
30
+ * `fit="width"` (the default) scales the device to its host's width and sizes
31
+ * the host to match. `fit="contain"` scales it to fit BOTH of the host's
32
+ * dimensions and centres it, so the whole device stays in view: give the host
33
+ * a height through `className` (a viewport-relative one keeps the phone on
34
+ * screen beside other controls).
29
35
  */
30
- declare function PhoneFrame({ src, title, size, frameRef, onLoad, }: {
36
+ declare function PhoneFrame({ src, title, size, fit, className, frameRef, onLoad, }: {
31
37
  src: string;
32
38
  title?: string;
33
39
  size?: keyof typeof phoneSizes;
40
+ /** "width" fills the host's width; "contain" fits the host's width and height. @default "width" */
41
+ fit?: "width" | "contain";
42
+ /** Classes for the host box. With fit="contain", this is where its height comes from. */
43
+ className?: string;
34
44
  frameRef?: Ref<HTMLIFrameElement>;
35
45
  onLoad?: ReactEventHandler<HTMLIFrameElement>;
36
46
  }): react.JSX.Element;
@@ -1,6 +1,6 @@
1
- import { Phone } from '../chunk-RWP4XBSK.js';
1
+ import { Phone } from '../chunk-KFZ43RKU.js';
2
2
  import '../chunk-NEQNXI2G.js';
3
- import '../chunk-SRQWQUTW.js';
3
+ import { cn } from '../chunk-SRQWQUTW.js';
4
4
  import { useRef, useState, useEffect, useCallback } from 'react';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
 
@@ -15,12 +15,14 @@ function PhoneFrame({
15
15
  src,
16
16
  title = "iPhone prototype",
17
17
  size = "standard",
18
+ fit = "width",
19
+ className,
18
20
  frameRef,
19
21
  onLoad
20
22
  }) {
21
23
  const host = useRef(null);
22
24
  const device = useRef(null);
23
- const [fit, setFit] = useState({ scale: 1, width: 0, height: 0 });
25
+ const [box, setBox] = useState({ scale: 1, width: 0, height: 0, offset: 0 });
24
26
  const { width, height } = phoneSizes[size];
25
27
  useEffect(() => {
26
28
  const hostEl = host.current;
@@ -28,26 +30,29 @@ function PhoneFrame({
28
30
  if (!hostEl || !deviceEl) return;
29
31
  const measure = () => {
30
32
  const natural = { width: deviceEl.offsetWidth, height: deviceEl.offsetHeight };
31
- const scale = natural.width ? Math.min(1, hostEl.clientWidth / natural.width) : 1;
32
- setFit({ scale, ...natural });
33
+ if (!natural.width) return setBox({ scale: 1, ...natural, offset: 0 });
34
+ const byHeight = fit === "contain" && hostEl.clientHeight ? hostEl.clientHeight / natural.height : 1;
35
+ const scale = Math.min(1, hostEl.clientWidth / natural.width, byHeight);
36
+ const offset = fit === "contain" ? Math.max(0, (hostEl.clientWidth - natural.width * scale) / 2) : 0;
37
+ setBox({ scale, ...natural, offset });
33
38
  };
34
39
  const observer = new ResizeObserver(measure);
35
40
  observer.observe(hostEl);
36
41
  observer.observe(deviceEl);
37
42
  return () => observer.disconnect();
38
- }, []);
43
+ }, [fit]);
39
44
  return /* @__PURE__ */ jsx(
40
45
  "div",
41
46
  {
42
47
  ref: host,
43
- className: "ios-preview-host",
44
- style: { maxWidth: fit.width || void 0, height: fit.height ? fit.height * fit.scale : height },
48
+ className: cn("ios-preview-host", className),
49
+ style: fit === "contain" ? void 0 : { maxWidth: box.width || void 0, height: box.height ? box.height * box.scale : height },
45
50
  children: /* @__PURE__ */ jsx(
46
51
  "div",
47
52
  {
48
53
  ref: device,
49
54
  className: "ios-phone-frame",
50
- style: { width: width / 0.926, transform: `scale(${fit.scale})` },
55
+ style: { width: width / 0.926, insetInlineStart: box.offset, transform: `scale(${box.scale})` },
51
56
  children: /* @__PURE__ */ jsx(Phone, { statusBar: false, systemChrome: false, className: "w-full", children: /* @__PURE__ */ jsx("iframe", { ref: frameRef, onLoad, src, title, className: "ios-phone-screen" }) })
52
57
  }
53
58
  )
@@ -24,6 +24,12 @@
24
24
  --ios-motion: 300ms; --ios-safe-top: 62px; --ios-header-height: 154px; --ios-tabbar-bottom: 28px; --ios-content-bottom: 110px;
25
25
  font-family: var(--font-sans, system-ui, sans-serif); color: var(--ios-ink); background: var(--ios-canvas); height: 100dvh; overflow: hidden;
26
26
  }
27
+ /* On a real phone (NativeApp systemChrome={false}): the device's own insets, the
28
+ same chrome heights above and below them. */
29
+ .ios-prototype.ios-bare {
30
+ --ios-safe-top: env(safe-area-inset-top, 0px); --ios-header-height: calc(92px + var(--ios-safe-top));
31
+ --ios-tabbar-bottom: max(12px, env(safe-area-inset-bottom, 0px)); --ios-content-bottom: calc(82px + var(--ios-tabbar-bottom));
32
+ }
27
33
  .ios-prototype.dark {
28
34
  --ios-canvas: var(--background, #000); --ios-ink: var(--foreground, #f2f2f7); --ios-secondary: var(--muted-foreground, #aeaeb2);
29
35
  --ios-panel: var(--card, #1c1c1e); --ios-primary-ink: var(--primary-foreground, #fff);
@@ -61,17 +67,31 @@
61
67
  .ios-prototype .native-tabbar > .k-glass { width: 100%; }
62
68
  .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
69
  .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; }
70
+ /* iOS 26 sheets. Large: opaque, attached to the bottom edge. Medium: floats 8px in from
71
+ the edges, concentric with the screen corners, in the same glass as the bars. */
72
+ .ios-sheet { border-radius: 38px 38px 0 0; background: var(--ios-canvas); transition-duration: var(--ios-motion); padding-bottom: 34px; }
73
+ .ios-sheet[data-detent='medium'] {
74
+ /* Concentric: the top with the close button (its 22px radius + 16px inset), the
75
+ bottom with the 62px screen corner, less the 8px float. */
76
+ left: 8px; right: 8px; top: calc(100% - 8px); border-radius: 38px 38px 54px 54px; padding-bottom: 26px;
77
+ background: color-mix(in srgb, var(--ios-canvas) calc(max(var(--ios-glass-tint, .7), .7) * 100%), transparent);
78
+ -webkit-backdrop-filter: blur(24px) saturate(1.6); backdrop-filter: blur(24px) saturate(1.6);
79
+ box-shadow: inset 0 .75px .5px rgb(255 255 255 / .6), 0 8px 40px color-mix(in srgb, var(--ios-ink) 14%, transparent);
80
+ }
81
+ .ios-prototype.dark .ios-sheet[data-detent='medium'], .dark .ios-sheet[data-detent='medium'] { box-shadow: inset 0 .75px .5px rgb(255 255 255 / .25), 0 8px 40px rgb(0 0 0 / .4); }
65
82
  .ios-sheet-grabber { display: flex; margin-inline: auto; width: 80px; touch-action: none; }
66
83
  .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); }
84
+ /* A glass close button, a centered inline title, and an empty column that keeps it centered. */
85
+ .ios-sheet-heading { display: grid; grid-template-columns: 44px 1fr 44px; align-items: center; gap: 8px; padding-inline: 16px; }
86
+ .ios-sheet-heading h2 { grid-column: 2; text-align: center; font-size: 17px; line-height: 22px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
87
+ .ios-sheet-close { grid-column: 1; grid-row: 1; border-radius: 9999px; color: var(--ios-ink); }
88
+ /* Glass on glass: the sheet already blurs what's behind, and refracting the sheet's own
89
+ flat surface only smears pale patches into the rim. Keep the fill and highlights. */
90
+ .ios-prototype .ios-sheet .k-glass { -webkit-backdrop-filter: none; backdrop-filter: none; }
71
91
  .ios-sheet-content { height: calc(100% - 88px); overflow-y: auto; scrollbar-width: none; padding: 16px 20px; }
72
92
  .ios-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
73
93
  @keyframes ios-route-enter { from { opacity: .7; transform: translateX(28px); } to { opacity: 1; transform: translateX(0); } }
74
94
  @keyframes ios-route-back { from { opacity: .7; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }
75
95
  .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
96
  @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; } }
97
+ @media (prefers-reduced-transparency: reduce) { .ios-prototype { --ios-glass-tint: 1 !important; } .ios-sheet[data-detent='medium'] { background: var(--ios-canvas); } }
@@ -140,6 +140,42 @@ interface PatternMeta {
140
140
  /** Accessibility notes. */
141
141
  a11y?: string[];
142
142
  }
143
+ /**
144
+ * A block: a section-sized composition of components with sample content —
145
+ * smaller than a page shell, with no behaviour of its own to upgrade. Ships
146
+ * the way patterns do (copy-in source + importable preview). Its props are
147
+ * the block contract only — `className`, `framed` on Card-built blocks, and
148
+ * the primary action's callback — never content; content is the constant at
149
+ * the top of the source, edited after copying.
150
+ */
151
+ interface BlockMeta {
152
+ /** Block name, e.g. "SignInBlock". Always ends in "Block". */
153
+ name: string;
154
+ /** One-line summary. */
155
+ summary: string;
156
+ /** When to reach for this block — and when a component or page shell fits better. */
157
+ intent: string;
158
+ /**
159
+ * "card" — one self-contained object that fills its container up to a
160
+ * designed width. "section" — a full-width band of a page.
161
+ */
162
+ kind: "card" | "section";
163
+ /**
164
+ * The width range it is designed for, as CSS lengths. The block renders
165
+ * `w-full` capped at `max`; `min` is the narrowest it still holds together
166
+ * (a grid's `minItemWidth`). Section blocks omit it.
167
+ */
168
+ width?: {
169
+ min: string;
170
+ max: string;
171
+ };
172
+ /** Groups related blocks in the catalog ("Auth", "Commerce", …). */
173
+ family: string;
174
+ /** Canonical decisions the block encodes. */
175
+ notes?: string[];
176
+ /** Accessibility notes. */
177
+ a11y?: string[];
178
+ }
143
179
  interface ComponentMeta {
144
180
  /** Public component name, e.g. "Button". Must be an actual export. */
145
181
  name: string;
@@ -202,4 +238,4 @@ interface ComponentMeta {
202
238
  examples: ExampleMeta[];
203
239
  }
204
240
 
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 };
241
+ export type { AnatomyMeta as A, BlockMeta as B, ComponentMeta as C, ExampleMeta as E, MotionMeta as M, PatternMeta as P, RelatedMeta as R, SubcomponentMeta as S, UsageMeta as U, BestPracticeMeta as a, MotionSlot as b, PropMeta as c };
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { P as PatternMeta } from '../meta-6dUzgC6M.js';
2
+ import { P as PatternMeta } from '../meta-CcE_SeFu.js';
3
3
 
4
4
  declare function AuthPage(): react.JSX.Element;
5
5