@knpkv/rly 0.6.1 → 0.9.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 (64) hide show
  1. package/README.md +85 -1
  2. package/dist/{Dialog-CplGpe1F.js → Dialog-_t9OodCK.js} +16 -12
  3. package/dist/Dialog-_t9OodCK.js.map +1 -0
  4. package/dist/ThemeProvider-BFeldMeE.js +87 -0
  5. package/dist/ThemeProvider-BFeldMeE.js.map +1 -0
  6. package/dist/base.css +17 -0
  7. package/dist/components.css +1 -1
  8. package/dist/dts/foundations/ThemeProvider.d.ts +31 -1
  9. package/dist/dts/foundations/ThemeProvider.d.ts.map +1 -1
  10. package/dist/dts/foundations/index.d.ts +2 -2
  11. package/dist/dts/foundations/index.d.ts.map +1 -1
  12. package/dist/dts/patterns/DecisionBar.d.ts +68 -0
  13. package/dist/dts/patterns/DecisionBar.d.ts.map +1 -0
  14. package/dist/dts/patterns/Hero.d.ts +69 -0
  15. package/dist/dts/patterns/Hero.d.ts.map +1 -0
  16. package/dist/dts/patterns/Region.d.ts +56 -0
  17. package/dist/dts/patterns/Region.d.ts.map +1 -0
  18. package/dist/dts/patterns/RelayDock.d.ts.map +1 -1
  19. package/dist/dts/patterns/StageRail.d.ts +11 -1
  20. package/dist/dts/patterns/StageRail.d.ts.map +1 -1
  21. package/dist/dts/patterns/TimelineRow.d.ts +25 -0
  22. package/dist/dts/patterns/TimelineRow.d.ts.map +1 -1
  23. package/dist/dts/patterns/index.d.ts +8 -2
  24. package/dist/dts/patterns/index.d.ts.map +1 -1
  25. package/dist/dts/primitives/ChartLegend.d.ts +23 -0
  26. package/dist/dts/primitives/ChartLegend.d.ts.map +1 -0
  27. package/dist/dts/primitives/LimitTrack.d.ts +62 -0
  28. package/dist/dts/primitives/LimitTrack.d.ts.map +1 -0
  29. package/dist/dts/primitives/Notice.d.ts +64 -0
  30. package/dist/dts/primitives/Notice.d.ts.map +1 -0
  31. package/dist/dts/primitives/Select.d.ts.map +1 -1
  32. package/dist/dts/primitives/StatePanel.d.ts +8 -1
  33. package/dist/dts/primitives/StatePanel.d.ts.map +1 -1
  34. package/dist/dts/primitives/ThemeSelect.d.ts +27 -0
  35. package/dist/dts/primitives/ThemeSelect.d.ts.map +1 -0
  36. package/dist/dts/primitives/TrackKey.d.ts +20 -0
  37. package/dist/dts/primitives/TrackKey.d.ts.map +1 -0
  38. package/dist/dts/primitives/index.d.ts +10 -0
  39. package/dist/dts/primitives/index.d.ts.map +1 -1
  40. package/dist/dts/tokens/semantic-tokens.d.ts +1 -1
  41. package/dist/dts/tokens/semantic-tokens.d.ts.map +1 -1
  42. package/dist/foundations/index.js +4 -4
  43. package/dist/generated-tokens.css +36 -0
  44. package/dist/index.js +9 -8
  45. package/dist/patterns/index.js +2 -2
  46. package/dist/{patterns-QMuqnQKw.js → patterns-lOWLKH70.js} +1247 -948
  47. package/dist/patterns-lOWLKH70.js.map +1 -0
  48. package/dist/primitives/index.js +3 -3
  49. package/dist/primitives-Dj-qYrUZ.js +583 -0
  50. package/dist/primitives-Dj-qYrUZ.js.map +1 -0
  51. package/dist/styles.css +1 -1
  52. package/dist/tokens/index.js +1 -1
  53. package/dist/{tokens-CjK9bxo7.js → tokens-DaF1m6rr.js} +2 -2
  54. package/dist/tokens-DaF1m6rr.js.map +1 -0
  55. package/package.json +3 -2
  56. package/registry/components.json +856 -12
  57. package/registry/search.json +215 -2
  58. package/dist/Dialog-CplGpe1F.js.map +0 -1
  59. package/dist/foundations-CEGDSEb-.js +0 -29
  60. package/dist/foundations-CEGDSEb-.js.map +0 -1
  61. package/dist/patterns-QMuqnQKw.js.map +0 -1
  62. package/dist/primitives-BXWPLy2C.js +0 -346
  63. package/dist/primitives-BXWPLy2C.js.map +0 -1
  64. package/dist/tokens-CjK9bxo7.js.map +0 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `rly` is the release-oriented design system for `@knpkv/control-center`. It provides browser-safe presentation contracts for delivery decisions, service provenance, collaborators, governed agents, and complete pull-request diffs.
4
4
 
5
- The package is intentionally application-independent: it contains no vendor clients, persistence, authorization, release hashing, or server runtime.
5
+ The package is intentionally application-independent: it contains no vendor clients, persistence, authorization, release hashing, or server runtime. The one exception is viewer-local presentation preferences, such as the theme, remembered through opt-in hooks in storage the application hands in. It never stores application data.
6
6
 
7
7
  ## Status
8
8
 
@@ -73,6 +73,43 @@ export const App = () => (
73
73
  ```
74
74
 
75
75
  `ThemeProvider` is controlled and performs no storage or preference reads.
76
+ To remember the viewer's choice, pair it with `useStoredTheme` and `ThemeSelect`:
77
+
78
+ ```tsx
79
+ import { PortalProvider, ThemeProvider, useDocumentTheme, useStoredTheme } from "@knpkv/rly/foundations"
80
+ import { ThemeSelect } from "@knpkv/rly/primitives"
81
+ import type { ReactNode } from "react"
82
+
83
+ const browserStorage = () => window.localStorage
84
+
85
+ // PortalProvider gives the select's menu somewhere to open; without one it has no options.
86
+ export const Shell = ({ children }: { readonly children: ReactNode }) => {
87
+ const [theme, setTheme] = useStoredTheme("my_app_theme", browserStorage)
88
+ useDocumentTheme(theme)
89
+ return (
90
+ <ThemeProvider theme={theme}>
91
+ <PortalProvider>
92
+ <ThemeSelect labelVisibility="hidden" onValueChange={setTheme} value={theme} />
93
+ {children}
94
+ </PortalProvider>
95
+ </ThemeProvider>
96
+ )
97
+ }
98
+ ```
99
+
100
+ The storage key is required, because composed apps share an origin. Storage is
101
+ passed lazily: server rendering uses `system` and never touches it, hydration
102
+ then switches to the stored theme, and a browser that refuses storage keeps
103
+ the choice until unload or a matching storage update. Consumers sharing the
104
+ same storage object and key stay in sync; native storage events sync other tabs
105
+ using that storage area. If storage access itself is refused, share a stable
106
+ storage supplier to share fallback choices; inline suppliers keep their own
107
+ fallback across renders. `ThemeProvider` themes only its own boundary, so the page that
108
+ owns the whole document also calls `useDocumentTheme`; it sets `data-theme` on
109
+ `<html>` so the viewport canvas, overscroll area, and page scrollbars match.
110
+ Embedded surfaces skip it. `decodeRlyTheme` validates a stored value for apps that keep
111
+ their own storage. `ThemeSelect` builds on `Select`; settings pages show its
112
+ "Appearance" label, compact headers hide it with `labelVisibility="hidden"`.
76
113
  `LinkProvider` accepts an application-owned anchor bridge without importing a
77
114
  router. `PortalProvider` owns an in-tree target unless a custom target is
78
115
  supplied; explicit `null` never falls back to the global document body. `Icon`
@@ -114,6 +151,15 @@ consumer-owned input, textarea, or `Select` through its render callback, so it
114
151
  can connect visible labels, descriptions, required state, and announced errors
115
152
  without cloning a framework-specific control.
116
153
 
154
+ `Notice` says one sentence of context or outcome inline, between controls and
155
+ content, with the same `tone` vocabulary as `StatePanel` and an optional
156
+ `action`. Neutral notices carry no glyph; other tones add a decorative icon, so
157
+ the sentence itself must name the tone ("Sync failed: …"). Use `StatePanel`
158
+ when the state needs a title and replaces a region. With `announce="polite"`,
159
+ keep the notice mounted and swap its children, because a status region reliably
160
+ announces changes, not its own appearance. An `announce="assertive"` alert may
161
+ announce as soon as it is inserted.
162
+
117
163
  `ToggleGroup` chooses one of a few peer options, such as a range or a measure,
118
164
  and is always controlled: exactly one option is on, pressing it again changes
119
165
  nothing, and arrow keys move to the next option and choose it. It renders a
@@ -131,6 +177,44 @@ state, contain and restore focus, lock document scrolling, and become
131
177
  full-screen at compact widths. Titles are always visible and required, while
132
178
  motion is governed by the central theme tokens.
133
179
 
180
+ ## Limits and chart keys
181
+
182
+ `LimitTrack` shows how full a limit is on a 0–100% track: the fill is the latest
183
+ reading, a two-tone hairline marks the near threshold (80% unless `near` says otherwise),
184
+ a dotted extension shows a `projected` level the application computed, and a
185
+ `stale` reading is hatched. Rly derives only the tone (`limitTrackTone`); it never
186
+ estimates a projection. The track is decorative by default because the number sits
187
+ beside it, so that text must also carry the age and the projection ("61%, old
188
+ reading", "84%, about 103% at reset"). A non-finite `value` is drawn as no reading,
189
+ and a non-finite `near` throws. No reading draws a faint full-width hatch in a dashed edge, so it
190
+ never reads as 0%. Under forced colours the track keeps a `CanvasText` edge. Pass `decorative={false}` with a `label` and the caller's `valueText`
191
+ to expose it as a meter instead. With `value={null}`, it is a named image whose
192
+ accessible label combines `label` and `valueText`, without a numeric range.
193
+
194
+ `TrackKey` explains the marks the tracks draw, listing only the marks in use.
195
+ `ChartLegend` says which colour is which series, with the swatch before each label.
196
+ Series colours are the tokens `--rly-color-series-1` to `-8` plus
197
+ `--rly-color-series-other` for folded series. Use `rlySeriesColor(series)` in SVG
198
+ fills, and keep an id on the same slot while it is visible. Colour is never the only
199
+ carrier: every chart ships a labelled key and a table equivalent, and forced-colours
200
+ mode collapses the series to `CanvasText`.
201
+
202
+ ```tsx
203
+ import { ChartLegend, LimitTrack, TrackKey } from "@knpkv/rly/primitives"
204
+
205
+ <p>
206
+ Weekly <LimitTrack projected={103} value={84} /> 84%, about 103% at reset
207
+ </p>
208
+ <TrackKey
209
+ label="What the track marks mean"
210
+ items={[
211
+ { mark: "near", label: "80%, near the limit" },
212
+ { mark: "projected", label: "Where it is heading at reset" }
213
+ ]}
214
+ />
215
+ <ChartLegend label="Bookings by colour" items={[{ id: "b1", label: "RLY-142", series: 1 }]} />
216
+ ```
217
+
134
218
  ## Provenance and collaborators
135
219
 
136
220
  The first product patterns keep source, freshness, and human responsibility as
@@ -93,7 +93,7 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
93
93
  overflow: n.style.overflow,
94
94
  overscrollBehavior: n.style.overscrollBehavior
95
95
  }), n.style.overflow = "hidden", n.style.overscrollBehavior = "none";
96
- }, ne = (e) => {
96
+ }, L = (e) => {
97
97
  let t = T.get(e);
98
98
  if (t === void 0) return;
99
99
  if (t.count > 1) {
@@ -102,13 +102,13 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
102
102
  }
103
103
  let n = e.documentElement;
104
104
  n.style.overflow = t.overflow, n.style.overscrollBehavior = t.overscrollBehavior, T.delete(e);
105
- }, re = (e, t) => {
105
+ }, ne = (e, t) => {
106
106
  y(() => {
107
107
  if (!t) return;
108
108
  let n = e.current?.ownerDocument;
109
- if (n !== void 0) return I(n), () => ne(n);
109
+ if (n !== void 0) return I(n), () => L(n);
110
110
  }, [t, e]);
111
- }, L = () => {
111
+ }, re = () => {
112
112
  D += 1;
113
113
  }, ie = (e) => {
114
114
  let t = ++D;
@@ -242,7 +242,7 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
242
242
  i === void 0 && o(e), r?.(e);
243
243
  };
244
244
  return y(() => {
245
- u.current !== l && (u.current = l, l ? L() : ie(c.current));
245
+ u.current !== l && (u.current = l, l ? re() : ie(c.current));
246
246
  }, [l]), /* @__PURE__ */ S(j, { children: /* @__PURE__ */ S(ce.Provider, {
247
247
  value: {
248
248
  open: l,
@@ -585,8 +585,9 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
585
585
  }, Ne = /* @__PURE__ */ De((n) => {
586
586
  let r = "value" in n, { "aria-describedby": a, "aria-errormessage": o, "aria-invalid": s, "aria-label": c, "aria-labelledby": l, "aria-required": d, autoComplete: f, className: m, defaultOpen: g, defaultValue: _, disabled: v = !1, form: y, id: b, name: x, onBlur: w, onFocus: T, onKeyDown: E, onOpenChange: D, onValueChange: O, open: k, options: A, placeholder: j = "Select an option", ref: M, required: N = !1, size: P = je.size, tabIndex: ee, value: te } = n;
587
587
  Me(A, v), i(c ?? l, c === void 0 ? "Select aria-labelledby" : "Select aria-label");
588
- let F = i(j, "Select placeholder"), I = te ?? _;
589
- if (I !== void 0 && !A.some((e) => e.value === I)) throw Error(`Select value does not match an option: ${I}`);
588
+ let F = i(j, "Select placeholder"), I = te ?? _, L = I === void 0 ? void 0 : A.find((e) => e.value === I);
589
+ if (I !== void 0 && L === void 0) throw Error(`Select value does not match an option: ${I}`);
590
+ let ne = r ? L?.label : void 0;
590
591
  return /* @__PURE__ */ C(p.Root, {
591
592
  ...f === void 0 ? {} : { autoComplete: f },
592
593
  ...g === void 0 ? {} : { defaultOpen: g },
@@ -613,7 +614,10 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
613
614
  onKeyDown: E,
614
615
  ref: M,
615
616
  tabIndex: ee,
616
- children: [/* @__PURE__ */ S(p.Value, { placeholder: F }), /* @__PURE__ */ S(p.Icon, {
617
+ children: [/* @__PURE__ */ S(p.Value, {
618
+ placeholder: F,
619
+ children: ne
620
+ }), /* @__PURE__ */ S(p.Icon, {
617
621
  asChild: !0,
618
622
  children: /* @__PURE__ */ S("span", {
619
623
  "aria-hidden": "true",
@@ -757,7 +761,7 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
757
761
  caution: "clock",
758
762
  progress: "loader"
759
763
  }, He = ({ action: t, announce: n = "off", className: r, description: a, icon: o, title: s, tone: c = "neutral", ...l }) => {
760
- let d = n === "assertive" ? "alert" : n === "polite" ? "status" : void 0;
764
+ let d = n === "assertive" ? "alert" : n === "polite" ? "status" : l.role;
761
765
  return /* @__PURE__ */ C("section", {
762
766
  ...l,
763
767
  "aria-live": n === "off" ? void 0 : n,
@@ -979,7 +983,7 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
979
983
  e && (u.current = l.current ?? $()), r === void 0 && a(e), n?.(e);
980
984
  };
981
985
  return y(() => {
982
- c.current !== s && (c.current = s, s ? L() : ie(u.current));
986
+ c.current !== s && (c.current = s, s ? re() : ie(u.current));
983
987
  }, [s]), /* @__PURE__ */ S(j, { children: /* @__PURE__ */ S(Xe.Provider, {
984
988
  value: {
985
989
  open: s,
@@ -1009,6 +1013,6 @@ var w = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap(), E = m(
1009
1013
  }
1010
1014
  });
1011
1015
  //#endregion
1012
- export { fe as A, U as C, B as D, me as E, P as F, F as I, re as L, L as M, A as N, se as O, ie as P, N as R, ge as S, V as T, we as _, qe as a, be as b, ze as c, Fe as d, Le as f, Te as g, Ne as h, Je as i, j, oe as k, He as l, Ae as m, $e as n, Ke as o, je as p, Qe as r, Be as s, tt as t, Ie as u, Ce as v, ve as w, G as x, xe as y, M as z };
1016
+ export { fe as A, U as C, B as D, me as E, P as F, F as I, ne as L, re as M, A as N, se as O, ie as P, N as R, ge as S, V as T, we as _, qe as a, be as b, ze as c, Fe as d, Le as f, Te as g, Ne as h, Je as i, j, oe as k, He as l, Ae as m, $e as n, Ke as o, je as p, Qe as r, Be as s, tt as t, Ie as u, Ce as v, ve as w, G as x, xe as y, M as z };
1013
1017
 
1014
- //# sourceMappingURL=Dialog-CplGpe1F.js.map
1018
+ //# sourceMappingURL=Dialog-_t9OodCK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog-_t9OodCK.js","names":["Predicate.isBoolean","Predicate.isObjectOrArray","layer","trigger","overlay","content","header","title","description","body","style","styles","Predicate.isString","RadixDialog","Predicate.isFunction","root","primary","secondary","quiet","compact","defaultSize","principal","content","label","style","styles","Predicate.isNumber","Predicate.isString","root","neutral","positive","critical","caution","progress","compact","defaultSize","style","styles","toneIcons","root","defaultSize","circle","style","styles","Avatar","RadixAvatar","root","compact","defaultSize","style","styles","compact","defaultSize","content","style","styles","RadixSelect","Select","root","style","styles","root","content","title","description","style","styles","description","defaultSize","style","styles","Predicate.isString","Predicate.isFunction","Predicate.isObjectOrArray","styles","Predicate.isFunction","RadixDialog","Dialog","Icon","isBoolean","isObjectOrArray","isString","cssClass","isFunction","defineVariants","isNumber","classNames","inertRecords","scrollLockRecords","ModalNestingContext","createContext","focusTransitionGeneration","useModalEntryMotion","committedCycleRef","useRef","requested","resolved","open","useLayoutEffect","hasHTMLElementInertContract","element","isHTMLElement","ModalNestingBoundary","isContentMounted","setContentMounted","useState","createElement","children","useParentModalReady","useContext","useParentModalPresent","useModalContentRegistration","retainInert","record","releaseInert","useModalIsolation","isOpen","layer","layerRef","retained","observedParents","MutationObserverConstructor","observer","synchronizeIsolation","current","parent","hasReachedCurrent","sibling","parentElement","host","retainScrollLock","ownerDocument","root","releaseScrollLock","useModalScrollLock","invalidateModalFocusRestore","restoreModalFocusAfterCleanup","generation","target","Sheet_module_default","style$9","name","RLY_SHEET_VARIANTS","RLY_SHEET_DEFAULT_VARIANTS","SheetStateContext","useSheetState","state","SheetLayer","entryMotion","requireVisibleAction","value","requireText","component","Sheet","props","className","Dialog","type","resolvedEntryMotion","visibleTitle","title","visibleDescription","description","accessibleCloseLabel","closeLabel","contentRef","setContentRef","useCallback","node","ref","PortalBoundary","side","event","activeElement","initialFocusRef","tabIndex","componentProps","defaultState","setDefaultState","defaultOpen","isParentModalReady","restoreFocusRef","resolvedOpen","previousOpenRef","requestOpenChange","nextOpen","onOpenChange","onClick","Button_module_default","style$8","RLY_BUTTON_VARIANTS","RLY_BUTTON_DEFAULT_VARIANTS","Button","ariaLabel","ariaLabelledBy","loading","variant","size","stretch","disabled","leadingIcon","trailingIcon","StateLabel_module_default","style$7","RLY_STATE_LABEL_VARIANTS","RLY_STATE_LABEL_DEFAULT_VARIANTS","toneIcons$1","StateLabel","tone","icon","label","Avatar_module_default","style$6","RLY_AVATAR_VARIANTS","RLY_AVATAR_DEFAULT_VARIANTS","Avatar$1","accessibleLabel","decorative","visibleFallback","fallback","Avatar","form","src","IconButton_module_default","style$5","RLY_ICON_BUTTON_VARIANTS","RLY_ICON_BUTTON_DEFAULT_VARIANTS","IconButton","registeredFieldControls","registerFieldControl","isRegisteredFieldControl","Select_module_default","style$4","RLY_SELECT_VARIANTS","RLY_SELECT_DEFAULT_VARIANTS","validateOptions","options","option","values","Select$1","valueControlled","ariaDescribedBy","ariaErrorMessage","ariaInvalid","ariaRequired","visiblePlaceholder","placeholder","selectedValue","defaultValue","selectedOption","selectedLabel","Select","autoComplete","onValueChange","required","Skeleton_module_default","style$3","RLY_SKELETON_VARIANTS","RLY_SKELETON_DEFAULT_VARIANTS","Skeleton","inlineStyle","dimensions","height","width","StatePanel_module_default","style$2","RLY_STATE_PANEL_VARIANTS","RLY_STATE_PANEL_DEFAULT_VARIANTS","toneIcons","StatePanel","role","announce","action","Field_module_default","style$1","semanticControlKeys","concreteControlElements","RLY_FIELD_VARIANTS","RLY_FIELD_DEFAULT_VARIANTS","Field","generatedId","useId","resolvedControlId","controlId","labelId","descriptionId","errorId","error","commonControlProps","id","controlProps","control","controlType","key","Dialog_module_default","style","DialogStateContext","useDialogState","assignRef","getActiveElement","RLY_DIALOG_VARIANTS","RLY_DIALOG_DEFAULT_VARIANTS","DialogLayer","Dialog$1","triggerRef","restoreTargetRef"],"sources":["../src/internal/modal.ts","../src/primitives/Sheet.module.css","../src/primitives/Sheet.tsx","../src/primitives/Button.module.css","../src/primitives/Button.tsx","../src/primitives/StateLabel.module.css","../src/primitives/StateLabel.tsx","../src/primitives/Avatar.module.css","../src/primitives/Avatar.tsx","../src/primitives/IconButton.module.css","../src/primitives/IconButton.tsx","../src/internal/field-control.ts","../src/primitives/Select.module.css","../src/primitives/Select.tsx","../src/primitives/Skeleton.module.css","../src/primitives/Skeleton.tsx","../src/primitives/StatePanel.module.css","../src/primitives/StatePanel.tsx","../src/primitives/Field.module.css","../src/primitives/Field.tsx","../src/primitives/Dialog.module.css","../src/primitives/Dialog.tsx"],"sourcesContent":["import {\n createContext,\n createElement,\n type ReactElement,\n type ReactNode,\n type RefObject,\n useContext,\n useLayoutEffect,\n useRef,\n useState\n} from \"react\"\nimport * as Predicate from \"./predicates.js\"\n\ninterface InertRecord {\n count: number\n readonly previous: boolean\n}\n\ninterface ScrollLockRecord {\n count: number\n readonly overflow: string\n readonly overscrollBehavior: string\n}\n\ninterface ModalNestingState {\n readonly isContentMounted: boolean\n readonly setContentMounted: (isMounted: boolean) => void\n}\n\ninterface ModalNestingBoundaryProps {\n readonly children: ReactNode\n}\n\nconst inertRecords = new WeakMap<HTMLElement, InertRecord>()\nconst scrollLockRecords = new WeakMap<Document, ScrollLockRecord>()\nconst ModalNestingContext = createContext<ModalNestingState | null>(null)\nlet focusTransitionGeneration = 0\n\n/** Entry-motion ownership sampled once for each closed-to-open modal cycle. */\nexport type ModalEntryMotion = \"external\" | \"intrinsic\"\n\n/** Keep entry ownership stable while a modal is open, then resample it for the next open cycle. */\nexport const useModalEntryMotion = (open: boolean, requested: ModalEntryMotion): ModalEntryMotion => {\n const committedCycleRef = useRef({ open, value: requested })\n const resolved = open && !committedCycleRef.current.open ? requested : committedCycleRef.current.value\n useLayoutEffect(() => {\n if (!open) committedCycleRef.current = { open: false, value: requested }\n else if (!committedCycleRef.current.open) committedCycleRef.current = { open: true, value: requested }\n }, [open, requested])\n return resolved\n}\n\nconst hasHTMLElementInertContract = <Value extends object>(element: Value): element is Value & HTMLElement =>\n \"inert\" in element && Predicate.isBoolean(element.inert)\n\n/** Narrows DOM elements through the HTML-only inert contract without relying on a realm-specific constructor. */\nexport const isHTMLElement = (element: Element | null): element is HTMLElement =>\n element !== null && hasHTMLElementInertContract(element)\n\n/** Stages nested default-open or controlled overlays until their parent content is mounted. */\nexport const ModalNestingBoundary = ({ children }: ModalNestingBoundaryProps): ReactElement => {\n const [isContentMounted, setContentMounted] = useState(false)\n return createElement(ModalNestingContext.Provider, { value: { isContentMounted, setContentMounted } }, children)\n}\n\n/** Reports whether a logical parent modal is ready to host a nested overlay. */\nexport const useParentModalReady = (): boolean => useContext(ModalNestingContext)?.isContentMounted ?? true\n\n/** Reports whether this component belongs to a logical parent modal. */\nexport const useParentModalPresent = (): boolean => useContext(ModalNestingContext) !== null\n\n/** Registers the current portal content so deeper overlays mount in logical stack order. */\nexport const useModalContentRegistration = (): void => {\n const setContentMounted = useContext(ModalNestingContext)?.setContentMounted\n useLayoutEffect(() => {\n setContentMounted?.(true)\n return () => setContentMounted?.(false)\n }, [setContentMounted])\n}\n\nconst retainInert = (element: HTMLElement): void => {\n const record = inertRecords.get(element)\n if (record === undefined) {\n inertRecords.set(element, { count: 1, previous: element.inert })\n element.inert = true\n return\n }\n record.count += 1\n}\n\nconst releaseInert = (element: HTMLElement): void => {\n const record = inertRecords.get(element)\n if (record === undefined) return\n if (record.count > 1) {\n record.count -= 1\n return\n }\n element.inert = record.previous\n inertRecords.delete(element)\n}\n\n/** Shares reference-counted background isolation across every rly modal primitive. */\nexport const useModalIsolation = (layerRef: RefObject<HTMLDivElement | null>, isOpen: boolean): void => {\n useLayoutEffect(() => {\n if (!isOpen) return\n const layer = layerRef.current\n if (layer === null) return\n const retained = new Set<HTMLElement>()\n const observedParents = new Set<Node>()\n const MutationObserverConstructor = layer.ownerDocument.defaultView?.MutationObserver\n let observer: MutationObserver | null = null\n\n const synchronizeIsolation = (): void => {\n let current: Element = layer\n while (true) {\n if (current === current.ownerDocument.body) break\n const parent = current.parentNode\n if (parent === null) break\n if (!observedParents.has(parent)) {\n observer?.observe(parent, { childList: true })\n observedParents.add(parent)\n }\n let hasReachedCurrent = false\n for (const sibling of parent.children) {\n if (sibling === current) {\n hasReachedCurrent = true\n continue\n }\n const isLaterModalLayer = hasReachedCurrent && sibling.hasAttribute(\"data-rly-modal-layer\")\n if (!isLaterModalLayer && isHTMLElement(sibling) && !retained.has(sibling)) {\n retainInert(sibling)\n retained.add(sibling)\n }\n }\n const parentElement = current.parentElement\n if (parentElement !== null) {\n current = parentElement\n continue\n }\n if (!(\"host\" in parent)) break\n const host = parent.host\n if (!Predicate.isObjectOrArray(host) || !hasHTMLElementInertContract(host)) break\n current = host\n }\n }\n\n if (MutationObserverConstructor !== undefined) observer = new MutationObserverConstructor(synchronizeIsolation)\n synchronizeIsolation()\n\n return () => {\n observer?.disconnect()\n for (const element of retained) releaseInert(element)\n }\n }, [isOpen, layerRef])\n}\n\nconst retainScrollLock = (ownerDocument: Document): void => {\n const record = scrollLockRecords.get(ownerDocument)\n if (record !== undefined) {\n record.count += 1\n return\n }\n const root = ownerDocument.documentElement\n scrollLockRecords.set(ownerDocument, {\n count: 1,\n overflow: root.style.overflow,\n overscrollBehavior: root.style.overscrollBehavior\n })\n root.style.overflow = \"hidden\"\n root.style.overscrollBehavior = \"none\"\n}\n\nconst releaseScrollLock = (ownerDocument: Document): void => {\n const record = scrollLockRecords.get(ownerDocument)\n if (record === undefined) return\n if (record.count > 1) {\n record.count -= 1\n return\n }\n const root = ownerDocument.documentElement\n root.style.overflow = record.overflow\n root.style.overscrollBehavior = record.overscrollBehavior\n scrollLockRecords.delete(ownerDocument)\n}\n\n/** Shares a reference-counted document scroll lock across custom rly modal surfaces. */\nexport const useModalScrollLock = (layerRef: RefObject<HTMLDivElement | null>, isOpen: boolean): void => {\n useLayoutEffect(() => {\n if (!isOpen) return\n const ownerDocument = layerRef.current?.ownerDocument\n if (ownerDocument === undefined) return\n retainScrollLock(ownerDocument)\n return () => releaseScrollLock(ownerDocument)\n }, [isOpen, layerRef])\n}\n\n/** Invalidates an older deferred restoration whenever a newer modal transition starts. */\nexport const invalidateModalFocusRestore = (): void => {\n focusTransitionGeneration += 1\n}\n\n/** Restores focus only after modal cleanup and only while this remains the newest transition. */\nexport const restoreModalFocusAfterCleanup = (target: HTMLElement | null): void => {\n const generation = ++focusTransitionGeneration\n setTimeout(() => {\n if (focusTransitionGeneration === generation && target !== null && target.isConnected === true) target.focus()\n }, 0)\n}\n","@layer rly.components {\n .layer {\n inset: var(--rly-space-0);\n isolation: isolate;\n pointer-events: none;\n position: fixed;\n z-index: 100;\n }\n\n .trigger,\n .close,\n .iconClose {\n align-items: center;\n appearance: none;\n background: var(--rly-color-surface-1);\n border: 1px solid var(--rly-color-border-2);\n color: var(--rly-color-text-1);\n cursor: pointer;\n display: inline-flex;\n font: var(--rly-type-label-weight) var(--rly-type-label-size) / var(--rly-type-label-line-height)\n var(--rly-type-label-font);\n justify-content: center;\n }\n\n .trigger,\n .close {\n border-radius: var(--rly-radius-field);\n min-block-size: var(--rly-space-40);\n padding: var(--rly-space-8) var(--rly-space-16);\n }\n\n .trigger:hover,\n .close:hover,\n .iconClose:hover {\n background: var(--rly-color-surface-2);\n }\n\n .trigger:active,\n .close:active,\n .iconClose:active {\n background: var(--rly-color-surface-3);\n }\n\n .trigger:disabled,\n .close:disabled,\n .iconClose:disabled {\n cursor: not-allowed;\n opacity: 0.58;\n }\n\n .overlay {\n animation: rly-sheet-overlay-out var(--rly-motion-standard-duration) var(--rly-motion-standard-easing);\n background: var(--rly-color-text-1);\n inset: var(--rly-space-0);\n opacity: 0.3;\n position: fixed;\n pointer-events: auto;\n z-index: 0;\n }\n\n .overlay[data-state=\"open\"] {\n animation-name: rly-sheet-overlay-in;\n }\n\n .content {\n background: var(--rly-color-surface-1);\n block-size: 100vh;\n block-size: 100dvh;\n color: var(--rly-color-text-1);\n display: flex;\n flex-direction: column;\n inset-block: var(--rly-space-0);\n max-inline-size: min(92vw, 30rem);\n overflow: hidden;\n pointer-events: auto;\n position: fixed;\n z-index: 1;\n }\n\n .end {\n animation: rly-sheet-end-out var(--rly-motion-standard-duration) var(--rly-motion-standard-easing);\n border-inline-start: 1px solid var(--rly-color-border-1);\n border-start-start-radius: var(--rly-radius-overlay);\n border-end-start-radius: var(--rly-radius-overlay);\n inset-inline-end: var(--rly-space-0);\n }\n\n .end[data-state=\"open\"] {\n animation-name: rly-sheet-end-in;\n }\n\n .start {\n animation: rly-sheet-start-out var(--rly-motion-standard-duration) var(--rly-motion-standard-easing);\n border-inline-end: 1px solid var(--rly-color-border-1);\n border-start-end-radius: var(--rly-radius-overlay);\n border-end-end-radius: var(--rly-radius-overlay);\n inset-inline-start: var(--rly-space-0);\n }\n\n .start[data-state=\"open\"] {\n animation-name: rly-sheet-start-in;\n }\n\n .layer[data-rly-sheet-entry-motion=\"external\"] .overlay[data-state=\"open\"],\n .layer[data-rly-sheet-entry-motion=\"external\"] .content[data-state=\"open\"] {\n animation: none;\n }\n\n .header {\n align-items: flex-start;\n border-block-end: 1px solid var(--rly-color-border-1);\n display: flex;\n flex: none;\n gap: var(--rly-space-16);\n justify-content: space-between;\n padding: var(--rly-space-20) var(--rly-space-24);\n }\n\n .heading {\n display: grid;\n gap: var(--rly-space-6);\n min-inline-size: var(--rly-space-0);\n }\n\n .title {\n font: var(--rly-type-card-title-weight) var(--rly-type-card-title-size) / var(--rly-type-card-title-line-height)\n var(--rly-type-card-title-font);\n letter-spacing: var(--rly-type-card-title-tracking);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n\n .description {\n color: var(--rly-color-text-2);\n font: var(--rly-type-body-weight) var(--rly-type-body-size) / var(--rly-type-body-line-height)\n var(--rly-type-body-font);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n\n .iconClose {\n border-radius: var(--rly-radius-field);\n flex: none;\n min-block-size: var(--rly-space-40);\n min-inline-size: var(--rly-space-40);\n padding: var(--rly-space-8);\n }\n\n .body {\n flex: 1 1 auto;\n min-block-size: var(--rly-space-0);\n min-inline-size: var(--rly-space-0);\n overflow: auto;\n overscroll-behavior: contain;\n padding: var(--rly-space-24);\n }\n\n .footer {\n align-items: center;\n border-block-start: 1px solid var(--rly-color-border-1);\n display: flex;\n flex: none;\n flex-wrap: wrap;\n gap: var(--rly-space-12);\n justify-content: flex-end;\n padding: var(--rly-space-16) var(--rly-space-24);\n }\n\n @media (max-width: 40rem) {\n .content {\n border: 0;\n border-radius: var(--rly-space-0);\n inline-size: 100vw;\n max-inline-size: none;\n }\n\n .header,\n .body {\n padding-inline: var(--rly-space-16);\n }\n\n .footer {\n padding: var(--rly-space-12) var(--rly-space-16);\n }\n }\n}\n\n@keyframes rly-sheet-overlay-in {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 0.3;\n }\n}\n\n@keyframes rly-sheet-overlay-out {\n from {\n opacity: 0.3;\n }\n\n to {\n opacity: 0;\n }\n}\n\n@keyframes rly-sheet-end-in {\n from {\n transform: translateX(100%);\n }\n\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes rly-sheet-end-out {\n from {\n transform: translateX(0);\n }\n\n to {\n transform: translateX(100%);\n }\n}\n\n@keyframes rly-sheet-start-in {\n from {\n transform: translateX(-100%);\n }\n\n to {\n transform: translateX(0);\n }\n}\n\n@keyframes rly-sheet-start-out {\n from {\n transform: translateX(0);\n }\n\n to {\n transform: translateX(-100%);\n }\n}\n","import { Dialog as RadixDialog } from \"radix-ui\"\nimport {\n type ComponentPropsWithRef,\n createContext,\n type ReactElement,\n type ReactNode,\n type RefObject,\n useCallback,\n useContext,\n useLayoutEffect,\n useRef,\n useState\n} from \"react\"\nimport { Icon } from \"../foundations/Icon.js\"\nimport { PortalBoundary } from \"../foundations/PortalProvider.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport {\n invalidateModalFocusRestore,\n isHTMLElement,\n ModalNestingBoundary,\n restoreModalFocusAfterCleanup,\n useModalContentRegistration,\n useModalEntryMotion,\n useModalIsolation,\n useParentModalReady\n} from \"../internal/modal.js\"\nimport styles from \"./Sheet.module.css\"\nimport * as Predicate from \"../internal/predicates.js\"\n\nconst style = (name: string): string => cssClass(styles, name)\n\n/** Semantic logical-side metadata for the sheet surface. */\nexport const RLY_SHEET_VARIANTS = defineVariants({\n side: {\n end: {\n className: style(\"end\"),\n purpose: \"Supporting detail that follows the current reading flow\",\n tokens: [\"color-surface-1\", \"radius-overlay\", \"motion-standard\"]\n },\n start: {\n className: style(\"start\"),\n purpose: \"Navigation or context that precedes the current reading flow\",\n tokens: [\"color-surface-1\", \"radius-overlay\", \"motion-standard\"]\n }\n }\n})\n\n/** Default logical edge used when no sheet side is selected. */\nexport const RLY_SHEET_DEFAULT_VARIANTS = defineVariants({ side: \"end\" })\n/** Logical inline edges supported by a sheet. */\nexport type RlySheetSide = keyof typeof RLY_SHEET_VARIANTS.side\n\ninterface SheetRootBaseProps {\n readonly children: ReactNode\n}\n\ntype ControlledSheetRootProps = SheetRootBaseProps & {\n readonly defaultOpen?: never\n readonly onOpenChange: (open: boolean) => void\n readonly open: boolean\n}\n\ntype DefaultSheetRootProps = SheetRootBaseProps & {\n readonly defaultOpen?: boolean\n readonly onOpenChange?: (open: boolean) => void\n readonly open?: never\n}\n\n/** Controlled-first or locally owned state for a sheet tree. */\nexport type SheetRootProps = ControlledSheetRootProps | DefaultSheetRootProps\n\n/** Props for the visible native button that opens a sheet. */\nexport type SheetTriggerProps = Omit<ComponentPropsWithRef<\"button\">, \"children\"> & {\n /** Visible text that identifies the panel being opened. */\n readonly children: number | string\n}\n\n/** Props for a visible native button that closes a sheet. */\nexport type SheetCloseProps = Omit<ComponentPropsWithRef<\"button\">, \"children\"> & {\n /** Visible text that identifies the close action. */\n readonly children: number | string\n}\n\n/** Accessible content, naming, focus, and logical placement for a sheet surface. */\nexport type SheetContentProps = Omit<\n ComponentPropsWithRef<\"div\">,\n \"aria-label\" | \"aria-labelledby\" | \"children\" | \"title\"\n> & {\n readonly children: ReactNode\n /** Accessible text for the built-in icon close action. */\n readonly closeLabel?: string\n /** Optional visible supporting description announced with the title. */\n readonly description?: string\n /**\n * Defaults to intrinsic and is sampled when the sheet opens. While open, changes apply to the next cycle.\n * External suppresses surface and overlay entry only; exit stays intrinsic and shared reduced-motion rules apply.\n */\n readonly entryMotion?: \"external\" | \"intrinsic\"\n /** Optional target for deterministic initial focus. Content receives focus by default. */\n readonly initialFocusRef?: RefObject<HTMLElement | null>\n readonly side?: RlySheetSide\n /** Required visible heading and accessible name for the sheet. */\n readonly title: string\n}\n\n/** Props for the independently scrolling sheet body. */\nexport type SheetBodyProps = ComponentPropsWithRef<\"div\">\n/** Props for the persistent sheet action footer. */\nexport type SheetFooterProps = ComponentPropsWithRef<\"footer\">\n\ninterface SheetState {\n readonly open: boolean\n readonly requestOpenChange: (open: boolean) => void\n readonly restoreFocusRef: RefObject<HTMLElement | null>\n}\n\nconst SheetStateContext = createContext<SheetState | null>(null)\n\nconst useSheetState = (): SheetState => {\n const state = useContext(SheetStateContext)\n if (state === null) throw new Error(\"Sheet compound components must be nested inside Sheet.Root\")\n return state\n}\n\nconst SheetLayer = ({\n children,\n entryMotion\n}: {\n readonly children: ReactNode\n readonly entryMotion: \"external\" | \"intrinsic\"\n}): ReactElement => {\n const state = useSheetState()\n const layerRef = useRef<HTMLDivElement>(null)\n useModalContentRegistration()\n useModalIsolation(layerRef, state.open)\n return (\n <div\n className={style(\"layer\")}\n data-rly-modal-layer=\"\"\n data-rly-sheet-entry-motion={entryMotion}\n data-rly-sheet-layer=\"\"\n ref={layerRef}\n >\n {children}\n </div>\n )\n}\n\nconst requireVisibleAction = (value: number | string, component: string): number | string => {\n if (Predicate.isString(value)) requireText(value, `${component} children`)\n return value\n}\n\nconst SheetRoot = (componentProps: SheetRootProps): ReactElement => {\n const { children, defaultOpen = false, onOpenChange, open } = componentProps\n const [defaultState, setDefaultState] = useState(defaultOpen)\n const isParentModalReady = useParentModalReady()\n const restoreFocusRef = useRef<HTMLElement | null>(null)\n const resolvedOpen = (open ?? defaultState) && isParentModalReady\n const previousOpenRef = useRef(resolvedOpen)\n const requestOpenChange = (nextOpen: boolean): void => {\n if (open === undefined) setDefaultState(nextOpen)\n onOpenChange?.(nextOpen)\n }\n\n useLayoutEffect(() => {\n if (previousOpenRef.current === resolvedOpen) return\n previousOpenRef.current = resolvedOpen\n if (resolvedOpen) invalidateModalFocusRestore()\n else restoreModalFocusAfterCleanup(restoreFocusRef.current)\n }, [resolvedOpen])\n\n return (\n <ModalNestingBoundary>\n <SheetStateContext.Provider value={{ open: resolvedOpen, requestOpenChange, restoreFocusRef }}>\n <RadixDialog.Root modal onOpenChange={requestOpenChange} open={resolvedOpen}>\n {children}\n </RadixDialog.Root>\n </SheetStateContext.Provider>\n </ModalNestingBoundary>\n )\n}\n\nconst SheetTrigger = ({ children, className, onClick, type, ...props }: SheetTriggerProps): ReactElement => {\n const state = useSheetState()\n return (\n <RadixDialog.Trigger\n {...props}\n className={classNames(style(\"trigger\"), className)}\n onClick={(event) => {\n state.restoreFocusRef.current = event.currentTarget\n onClick?.(event)\n }}\n type={type ?? \"button\"}\n >\n {requireVisibleAction(children, \"Sheet.Trigger\")}\n </RadixDialog.Trigger>\n )\n}\n\nconst SheetClose = ({ children, className, type, ...props }: SheetCloseProps): ReactElement => (\n <RadixDialog.Close {...props} className={classNames(style(\"close\"), className)} type={type ?? \"button\"}>\n {requireVisibleAction(children, \"Sheet.Close\")}\n </RadixDialog.Close>\n)\n\nconst SheetBody = ({ className, ...props }: SheetBodyProps): ReactElement => (\n <div {...props} className={classNames(style(\"body\"), className)} />\n)\n\nconst SheetFooter = ({ className, ...props }: SheetFooterProps): ReactElement => (\n <footer {...props} className={classNames(style(\"footer\"), className)} />\n)\n\nconst SheetContent = ({\n children,\n className,\n closeLabel,\n description,\n entryMotion = \"intrinsic\",\n initialFocusRef,\n ref,\n side = RLY_SHEET_DEFAULT_VARIANTS.side,\n tabIndex,\n title,\n ...props\n}: SheetContentProps): ReactElement => {\n const state = useSheetState()\n const resolvedEntryMotion = useModalEntryMotion(state.open, entryMotion)\n const visibleTitle = requireText(title, \"Sheet title\")\n const visibleDescription = description === undefined ? undefined : requireText(description, \"Sheet description\")\n const accessibleCloseLabel = requireText(closeLabel ?? `Close ${visibleTitle}`, \"Sheet closeLabel\")\n const contentRef = useRef<HTMLDivElement | null>(null)\n const setContentRef = useCallback(\n (node: HTMLDivElement | null): void => {\n contentRef.current = node\n if (Predicate.isFunction(ref)) ref(node)\n else if (ref !== null && ref !== undefined) ref.current = node\n },\n [ref]\n )\n\n return (\n <PortalBoundary>\n {(container) => (\n <RadixDialog.Portal container={container}>\n <SheetLayer entryMotion={resolvedEntryMotion}>\n <RadixDialog.Overlay\n className={style(\"overlay\")}\n data-rly-sheet-overlay=\"\"\n onPointerDown={() => state.requestOpenChange(false)}\n />\n <RadixDialog.Content\n {...props}\n {...(visibleDescription === undefined ? { \"aria-describedby\": undefined } : {})}\n className={classNames(style(\"content\"), RLY_SHEET_VARIANTS.side[side].className, className)}\n data-rly-sheet-side={side}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onOpenAutoFocus={(event) => {\n event.preventDefault()\n const activeElement = contentRef.current?.ownerDocument.activeElement ?? null\n if (state.restoreFocusRef.current === null && isHTMLElement(activeElement)) {\n state.restoreFocusRef.current = activeElement\n }\n const target = initialFocusRef?.current ?? contentRef.current\n target?.focus()\n if (target !== contentRef.current && target?.ownerDocument.activeElement !== target)\n contentRef.current?.focus()\n }}\n ref={setContentRef}\n tabIndex={tabIndex ?? -1}\n >\n <div className={style(\"header\")}>\n <div className={style(\"heading\")}>\n <RadixDialog.Title className={style(\"title\")}>{visibleTitle}</RadixDialog.Title>\n {visibleDescription === undefined ? null : (\n <RadixDialog.Description className={style(\"description\")}>\n {visibleDescription}\n </RadixDialog.Description>\n )}\n </div>\n <RadixDialog.Close aria-label={accessibleCloseLabel} className={style(\"iconClose\")} type=\"button\">\n <Icon decorative name=\"close\" size=\"default\" />\n </RadixDialog.Close>\n </div>\n {children}\n </RadixDialog.Content>\n </SheetLayer>\n </RadixDialog.Portal>\n )}\n </PortalBoundary>\n )\n}\n\n/** Owned compound side panel with modal focus, dismissal, and portal behavior. */\nexport const Sheet = Object.freeze({\n Body: SheetBody,\n Close: SheetClose,\n Content: SheetContent,\n Footer: SheetFooter,\n Root: SheetRoot,\n Trigger: SheetTrigger\n})\n","@layer rly.components {\n .root {\n align-items: center;\n appearance: none;\n border: 1px solid var(--rly-color-border-2);\n border-radius: var(--rly-radius-control);\n box-sizing: border-box;\n cursor: pointer;\n display: inline-grid;\n font: var(--rly-type-label-weight) var(--rly-type-label-size) / var(--rly-type-label-line-height)\n var(--rly-type-label-font);\n justify-items: center;\n letter-spacing: var(--rly-type-label-tracking);\n max-inline-size: 100%;\n padding-inline: var(--rly-space-16);\n position: relative;\n text-align: center;\n }\n\n .root:disabled {\n cursor: not-allowed;\n opacity: 0.58;\n }\n .primary {\n background: var(--rly-color-action-background);\n border-color: var(--rly-color-action-background);\n color: var(--rly-color-action-foreground);\n }\n .secondary {\n background: var(--rly-color-surface-1);\n color: var(--rly-color-text-1);\n }\n .quiet {\n background: var(--rly-color-surface-2);\n border-color: var(--rly-color-surface-2);\n color: var(--rly-color-text-1);\n }\n .compact {\n min-block-size: var(--rly-space-40);\n }\n .defaultSize {\n min-block-size: var(--rly-space-48);\n }\n .principal {\n min-block-size: calc(var(--rly-space-48) + var(--rly-space-8));\n padding-inline: var(--rly-space-24);\n }\n .stretch {\n inline-size: 100%;\n }\n\n .content {\n align-items: center;\n display: inline-flex;\n gap: var(--rly-space-8);\n justify-content: center;\n min-inline-size: var(--rly-space-0);\n padding-block: var(--rly-space-8);\n }\n\n .label {\n min-inline-size: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n .loadingContent {\n opacity: 0;\n }\n .loader {\n display: grid;\n inset: var(--rly-space-0);\n place-items: center;\n position: absolute;\n }\n\n @media (hover: hover) {\n .root:not(:disabled):hover {\n border-color: var(--rly-color-text-2);\n }\n .primary:not(:disabled):hover {\n background: var(--rly-color-text-2);\n }\n }\n}\n","import type { ComponentPropsWithRef, ReactElement } from \"react\"\nimport { Icon, type RlyIconName } from \"../foundations/Icon.js\"\nimport { classNames, cssClass, defineVariants } from \"../internal/component.js\"\nimport styles from \"./Button.module.css\"\nimport * as Predicate from \"../internal/predicates.js\"\n\nconst style = (name: string): string => cssClass(styles, name)\nexport const RLY_BUTTON_VARIANTS = defineVariants({\n variant: {\n primary: {\n className: style(\"primary\"),\n purpose: \"Single principal action\",\n tokens: [\"color-action-background\", \"color-action-foreground\"]\n },\n secondary: {\n className: style(\"secondary\"),\n purpose: \"Default supporting action\",\n tokens: [\"color-surface-1\", \"color-border-2\"]\n },\n quiet: {\n className: style(\"quiet\"),\n purpose: \"Low-emphasis contextual action\",\n tokens: [\"color-text-1\", \"color-surface-2\"]\n }\n },\n size: {\n compact: { className: style(\"compact\"), purpose: \"Dense text action\", tokens: [\"space-40\"] },\n default: { className: style(\"defaultSize\"), purpose: \"Standard action\", tokens: [\"space-48\"] },\n principal: {\n className: style(\"principal\"),\n purpose: \"Prominent consequential action\",\n tokens: [\"space-48\", \"space-8\"]\n }\n }\n})\nexport const RLY_BUTTON_DEFAULT_VARIANTS = defineVariants({ variant: \"secondary\", size: \"default\" })\nexport type RlyButtonVariant = keyof typeof RLY_BUTTON_VARIANTS.variant\nexport type RlyButtonSize = keyof typeof RLY_BUTTON_VARIANTS.size\nexport type ButtonProps = Omit<ComponentPropsWithRef<\"button\">, \"children\"> & {\n /** Plain visible text. Use IconButton for an icon-only action. */\n readonly children: number | string\n readonly leadingIcon?: RlyIconName\n readonly loading?: boolean\n readonly size?: RlyButtonSize\n readonly stretch?: boolean\n readonly trailingIcon?: RlyIconName\n readonly variant?: RlyButtonVariant\n}\n\n/** Render a visible-text action with stable disabled and loading geometry. */\nexport const Button = ({\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n children,\n className,\n disabled,\n leadingIcon,\n loading = false,\n size = \"default\",\n stretch = false,\n trailingIcon,\n type,\n variant = \"secondary\",\n ...props\n}: ButtonProps): ReactElement => {\n if (\n (!Predicate.isNumber(children) && !Predicate.isString(children)) ||\n (Predicate.isString(children) && children.trim().length === 0)\n ) {\n throw new Error(\"Button children must contain visible content\")\n }\n return (\n <button\n {...props}\n aria-busy={loading ? \"true\" : undefined}\n aria-label={loading && !ariaLabelledBy?.trim() ? (ariaLabel?.trim() ? ariaLabel : String(children)) : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={classNames(\n style(\"root\"),\n RLY_BUTTON_VARIANTS.variant[variant].className,\n RLY_BUTTON_VARIANTS.size[size].className,\n stretch && style(\"stretch\"),\n className\n )}\n data-loading={loading ? \"true\" : undefined}\n disabled={disabled || loading}\n type={type ?? \"button\"}\n >\n <span className={classNames(style(\"content\"), loading && style(\"loadingContent\"))}>\n {leadingIcon === undefined ? null : (\n <Icon decorative name={leadingIcon} size={size === \"compact\" ? \"small\" : \"default\"} />\n )}\n <span className={style(\"label\")}>{children}</span>\n {trailingIcon === undefined ? null : (\n <Icon decorative name={trailingIcon} size={size === \"compact\" ? \"small\" : \"default\"} />\n )}\n </span>\n {loading ? (\n <span aria-hidden=\"true\" className={style(\"loader\")}>\n <Icon decorative name=\"loader\" size={size === \"compact\" ? \"small\" : \"default\"} />\n </span>\n ) : null}\n </button>\n )\n}\n","@layer rly.components {\n .root {\n align-items: center;\n border: 1px solid currentcolor;\n border-radius: var(--rly-radius-tag);\n display: inline-flex;\n gap: var(--rly-space-6);\n max-inline-size: 100%;\n padding: var(--rly-space-4) var(--rly-space-8);\n overflow-wrap: anywhere;\n }\n\n .neutral {\n background: var(--rly-color-surface-2);\n color: var(--rly-color-text-2);\n }\n .positive {\n background: var(--rly-color-success-tint);\n color: var(--rly-color-success-ink);\n }\n .critical {\n background: var(--rly-color-blocked-tint);\n color: var(--rly-color-blocked-ink);\n }\n .caution {\n background: var(--rly-color-held-tint);\n color: var(--rly-color-held-ink);\n }\n .progress {\n background: var(--rly-color-deploying-tint);\n color: var(--rly-color-deploying-ink);\n }\n .compact {\n font: var(--rly-type-meta-weight) var(--rly-type-meta-size) / var(--rly-type-meta-line-height)\n var(--rly-type-meta-font);\n }\n .defaultSize {\n font: var(--rly-type-label-weight) var(--rly-type-label-size) / var(--rly-type-label-line-height)\n var(--rly-type-label-font);\n }\n}\n","import type { ComponentPropsWithRef, ReactElement } from \"react\"\nimport { Icon, type RlyIconName } from \"../foundations/Icon.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport styles from \"./StateLabel.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\nexport type RlyStateTone = \"neutral\" | \"positive\" | \"critical\" | \"caution\" | \"progress\"\nexport const RLY_STATE_LABEL_VARIANTS = defineVariants({\n tone: {\n neutral: {\n className: style(\"neutral\"),\n purpose: \"Neutral or unavailable state\",\n tokens: [\"color-text-2\", \"color-surface-2\"]\n },\n positive: {\n className: style(\"positive\"),\n purpose: \"Positive state\",\n tokens: [\"color-success-ink\", \"color-success-tint\"]\n },\n critical: {\n className: style(\"critical\"),\n purpose: \"Critical state\",\n tokens: [\"color-blocked-ink\", \"color-blocked-tint\"]\n },\n caution: { className: style(\"caution\"), purpose: \"Caution state\", tokens: [\"color-held-ink\", \"color-held-tint\"] },\n progress: {\n className: style(\"progress\"),\n purpose: \"Work in progress\",\n tokens: [\"color-deploying-ink\", \"color-deploying-tint\"]\n }\n },\n size: {\n compact: { className: style(\"compact\"), purpose: \"Dense metadata state\", tokens: [\"type-meta\"] },\n default: { className: style(\"defaultSize\"), purpose: \"Standard explicit state\", tokens: [\"type-label\"] }\n }\n})\nexport const RLY_STATE_LABEL_DEFAULT_VARIANTS = defineVariants({ tone: \"neutral\", size: \"default\" })\nexport type RlyStateLabelSize = keyof typeof RLY_STATE_LABEL_VARIANTS.size\nconst toneIcons = {\n neutral: \"minus\",\n positive: \"check\",\n critical: \"alert\",\n caution: \"clock\",\n progress: \"loader\"\n} satisfies Readonly<Record<RlyStateTone, RlyIconName>>\nexport type StateLabelProps = Omit<ComponentPropsWithRef<\"span\">, \"children\"> & {\n readonly icon?: RlyIconName\n readonly label: string\n readonly size?: RlyStateLabelSize\n readonly tone?: RlyStateTone\n}\n\n/** Render state through a visible word, icon, ink, and restrained tint. */\nexport const StateLabel = ({\n className,\n icon,\n label,\n size = \"default\",\n tone = \"neutral\",\n ...props\n}: StateLabelProps): ReactElement => (\n <span\n {...props}\n className={classNames(\n style(\"root\"),\n RLY_STATE_LABEL_VARIANTS.tone[tone].className,\n RLY_STATE_LABEL_VARIANTS.size[size].className,\n className\n )}\n >\n <Icon decorative name={icon ?? toneIcons[tone]} size=\"small\" />\n <span>{requireText(label, \"StateLabel label\")}</span>\n </span>\n)\n","@layer rly.components {\n .root {\n align-items: center;\n background: var(--rly-color-surface-2);\n box-sizing: border-box;\n color: var(--rly-color-text-2);\n display: inline-flex;\n flex: none;\n justify-content: center;\n overflow: hidden;\n }\n\n .small {\n block-size: var(--rly-space-24);\n inline-size: var(--rly-space-24);\n }\n .defaultSize {\n block-size: var(--rly-space-32);\n inline-size: var(--rly-space-32);\n }\n .large {\n block-size: var(--rly-space-40);\n inline-size: var(--rly-space-40);\n }\n .hero {\n block-size: calc(var(--rly-space-48) + var(--rly-space-8));\n inline-size: calc(var(--rly-space-48) + var(--rly-space-8));\n }\n .circle {\n border-radius: var(--rly-radius-round);\n }\n .roundedSquare {\n border-radius: var(--rly-radius-field);\n }\n .image {\n block-size: 100%;\n inline-size: 100%;\n object-fit: cover;\n }\n\n .fallback {\n align-items: center;\n block-size: 100%;\n display: flex;\n font: var(--rly-type-meta-weight) var(--rly-type-meta-size) / var(--rly-type-meta-line-height)\n var(--rly-type-meta-font);\n inline-size: 100%;\n justify-content: center;\n overflow: hidden;\n text-transform: uppercase;\n }\n}\n","import type { ComponentPropsWithRef, ReactElement } from \"react\"\nimport { Avatar as RadixAvatar } from \"radix-ui\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport styles from \"./Avatar.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\nexport const RLY_AVATAR_VARIANTS = defineVariants({\n size: {\n small: { className: style(\"small\"), purpose: \"Compact supporting identity\", tokens: [\"space-24\"] },\n default: { className: style(\"defaultSize\"), purpose: \"Standard identity\", tokens: [\"space-32\"] },\n large: { className: style(\"large\"), purpose: \"Prominent identity\", tokens: [\"space-40\"] },\n hero: { className: style(\"hero\"), purpose: \"Page-level identity\", tokens: [\"space-48\", \"space-8\"] }\n },\n form: {\n circle: { className: style(\"circle\"), purpose: \"Human or general identity\", tokens: [\"radius-round\"] },\n \"rounded-square\": {\n className: style(\"roundedSquare\"),\n purpose: \"Non-human or system identity\",\n tokens: [\"radius-field\"]\n }\n }\n})\nexport const RLY_AVATAR_DEFAULT_VARIANTS = defineVariants({ size: \"default\", form: \"circle\" })\nexport type RlyAvatarSize = keyof typeof RLY_AVATAR_VARIANTS.size\nexport type RlyAvatarForm = keyof typeof RLY_AVATAR_VARIANTS.form\ntype AvatarBaseProps = Omit<ComponentPropsWithRef<\"span\">, \"aria-label\" | \"children\"> & {\n readonly fallback: string\n readonly form?: RlyAvatarForm\n readonly size?: RlyAvatarSize\n readonly src?: string\n}\nexport type AvatarProps = AvatarBaseProps &\n ({ readonly decorative: true; readonly label?: never } | { readonly decorative?: false; readonly label: string })\n\n/** Render a fixed-geometry image with deterministic fallback and owned accessible name. */\nexport const Avatar = ({\n className,\n decorative,\n fallback,\n form = \"circle\",\n label,\n size = \"default\",\n src,\n ...props\n}: AvatarProps): ReactElement => {\n const accessibleLabel = decorative ? undefined : requireText(label, \"Avatar label\")\n const visibleFallback = requireText(fallback, \"Avatar fallback\")\n return (\n <RadixAvatar.Root\n {...props}\n aria-hidden={decorative ? \"true\" : undefined}\n aria-label={accessibleLabel}\n className={classNames(\n style(\"root\"),\n RLY_AVATAR_VARIANTS.size[size].className,\n RLY_AVATAR_VARIANTS.form[form].className,\n className\n )}\n role={decorative ? undefined : \"img\"}\n >\n {src === undefined ? null : (\n <RadixAvatar.Image alt=\"\" className={style(\"image\")} decoding=\"async\" loading=\"lazy\" src={src} />\n )}\n <RadixAvatar.Fallback aria-hidden=\"true\" className={style(\"fallback\")}>\n {visibleFallback}\n </RadixAvatar.Fallback>\n </RadixAvatar.Root>\n )\n}\n","@layer rly.components {\n .root {\n appearance: none;\n border: 1px solid var(--rly-color-border-2);\n border-radius: var(--rly-radius-control);\n box-sizing: border-box;\n cursor: pointer;\n display: inline-grid;\n padding: var(--rly-space-0);\n place-items: center;\n }\n\n .root:disabled {\n cursor: not-allowed;\n opacity: 0.58;\n }\n .primary {\n background: var(--rly-color-action-background);\n border-color: var(--rly-color-action-background);\n color: var(--rly-color-action-foreground);\n }\n .secondary {\n background: var(--rly-color-surface-1);\n color: var(--rly-color-text-1);\n }\n .quiet {\n background: var(--rly-color-surface-2);\n border-color: var(--rly-color-surface-2);\n color: var(--rly-color-text-1);\n }\n .compact {\n block-size: calc(var(--rly-space-40) + var(--rly-space-4));\n inline-size: calc(var(--rly-space-40) + var(--rly-space-4));\n }\n .defaultSize {\n block-size: var(--rly-space-48);\n inline-size: var(--rly-space-48);\n }\n .principal {\n block-size: calc(var(--rly-space-48) + var(--rly-space-8));\n inline-size: calc(var(--rly-space-48) + var(--rly-space-8));\n }\n\n @media (hover: hover) {\n .root:not(:disabled):hover {\n border-color: var(--rly-color-text-2);\n }\n .primary:not(:disabled):hover {\n background: var(--rly-color-text-2);\n }\n }\n}\n","import type { ComponentPropsWithRef, ReactElement } from \"react\"\nimport { Icon, type RlyIconName } from \"../foundations/Icon.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport styles from \"./IconButton.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\nexport const RLY_ICON_BUTTON_VARIANTS = defineVariants({\n variant: {\n primary: {\n className: style(\"primary\"),\n purpose: \"Principal icon action\",\n tokens: [\"color-action-background\", \"color-action-foreground\"]\n },\n secondary: {\n className: style(\"secondary\"),\n purpose: \"Default icon action\",\n tokens: [\"color-surface-1\", \"color-border-2\"]\n },\n quiet: {\n className: style(\"quiet\"),\n purpose: \"Low-emphasis icon action\",\n tokens: [\"color-text-1\", \"color-surface-2\"]\n }\n },\n size: {\n compact: {\n className: style(\"compact\"),\n purpose: \"Minimum accessible icon target\",\n tokens: [\"space-40\", \"space-4\"]\n },\n default: { className: style(\"defaultSize\"), purpose: \"Standard icon target\", tokens: [\"space-48\"] },\n principal: { className: style(\"principal\"), purpose: \"Prominent icon target\", tokens: [\"space-48\", \"space-8\"] }\n }\n})\nexport const RLY_ICON_BUTTON_DEFAULT_VARIANTS = defineVariants({ variant: \"secondary\", size: \"default\" })\nexport type RlyIconButtonVariant = keyof typeof RLY_ICON_BUTTON_VARIANTS.variant\nexport type RlyIconButtonSize = keyof typeof RLY_ICON_BUTTON_VARIANTS.size\nexport type IconButtonProps = Omit<ComponentPropsWithRef<\"button\">, \"aria-label\" | \"children\"> & {\n readonly icon: RlyIconName\n readonly label: string\n readonly loading?: boolean\n readonly size?: RlyIconButtonSize\n readonly variant?: RlyIconButtonVariant\n}\n\n/** Render an icon-only action whose accessible name is mandatory and owned. */\nexport const IconButton = ({\n className,\n disabled,\n icon,\n label,\n loading = false,\n size = \"default\",\n type,\n variant = \"secondary\",\n ...props\n}: IconButtonProps): ReactElement => (\n <button\n {...props}\n aria-busy={loading ? \"true\" : undefined}\n aria-label={requireText(label, \"IconButton label\")}\n className={classNames(\n style(\"root\"),\n RLY_ICON_BUTTON_VARIANTS.variant[variant].className,\n RLY_ICON_BUTTON_VARIANTS.size[size].className,\n className\n )}\n data-loading={loading ? \"true\" : undefined}\n disabled={disabled || loading}\n type={type ?? \"button\"}\n >\n <Icon decorative name={loading ? \"loader\" : icon} size={size === \"compact\" ? \"small\" : \"default\"} />\n </button>\n)\n","const registeredFieldControls = new WeakSet<object>()\n\n/** Register an owned control that applies Field semantics to its final DOM host. */\nexport const registerFieldControl = <Component extends object>(component: Component): Component => {\n registeredFieldControls.add(component)\n return component\n}\n\n/** Distinguish rly-owned controls from consumer components that may discard DOM semantics. */\nexport const isRegisteredFieldControl = <Component extends object>(component: Component): boolean =>\n registeredFieldControls.has(component)\n","@layer rly.components {\n .trigger {\n align-items: center;\n appearance: none;\n background: var(--rly-color-surface-1);\n border: 1px solid var(--rly-color-border-2);\n border-radius: var(--rly-radius-field);\n box-sizing: border-box;\n color: var(--rly-color-text-1);\n cursor: pointer;\n display: inline-flex;\n font: var(--rly-type-body-weight) var(--rly-type-body-size) / var(--rly-type-body-line-height)\n var(--rly-type-body-font);\n gap: var(--rly-space-12);\n inline-size: 100%;\n justify-content: space-between;\n max-inline-size: 100%;\n min-inline-size: var(--rly-space-0);\n padding: var(--rly-space-8) var(--rly-space-12);\n text-align: start;\n }\n\n .trigger[data-placeholder] {\n color: var(--rly-color-text-2);\n }\n\n .trigger > span:first-child {\n min-inline-size: var(--rly-space-0);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .trigger[data-state=\"open\"] {\n border-color: var(--rly-color-focus);\n }\n\n .trigger:disabled {\n cursor: not-allowed;\n opacity: 0.58;\n }\n\n .trigger[aria-invalid=\"true\"] {\n border-color: var(--rly-color-blocked-ink);\n }\n\n .compact {\n min-block-size: var(--rly-space-40);\n padding-block: var(--rly-space-6);\n }\n\n .defaultSize {\n min-block-size: var(--rly-space-48);\n }\n\n .triggerIcon {\n display: inline-flex;\n flex: none;\n transition: transform var(--rly-motion-fast-duration) var(--rly-motion-fast-easing);\n }\n\n .trigger[data-state=\"open\"] .triggerIcon {\n transform: rotate(180deg);\n }\n\n .content {\n background: var(--rly-color-surface-1);\n border: 1px solid var(--rly-color-border-1);\n border-radius: var(--rly-radius-field);\n box-sizing: border-box;\n color: var(--rly-color-text-1);\n max-block-size: min(20rem, var(--radix-select-content-available-height));\n max-inline-size: min(calc(100vw - var(--rly-space-32)), 32rem);\n min-inline-size: var(--radix-select-trigger-width);\n overflow: hidden;\n transform-origin: var(--radix-select-content-transform-origin);\n z-index: 100;\n }\n\n .viewport {\n box-sizing: border-box;\n padding: var(--rly-space-4);\n }\n\n .item {\n align-items: center;\n border-radius: var(--rly-radius-tag);\n box-sizing: border-box;\n cursor: default;\n display: flex;\n gap: var(--rly-space-8);\n justify-content: space-between;\n min-block-size: var(--rly-space-40);\n min-inline-size: var(--rly-space-0);\n outline: none;\n overflow-wrap: anywhere;\n padding: var(--rly-space-8) var(--rly-space-12);\n user-select: none;\n }\n\n .item[data-highlighted],\n .item[data-state=\"checked\"] {\n background: var(--rly-color-surface-2);\n }\n\n .item[data-disabled] {\n opacity: 0.58;\n }\n\n .itemIndicator {\n display: inline-flex;\n flex: none;\n }\n\n .scrollButton {\n align-items: center;\n color: var(--rly-color-text-2);\n display: flex;\n justify-content: center;\n min-block-size: var(--rly-space-32);\n }\n\n @media (max-width: 30rem) {\n .content {\n inline-size: var(--radix-select-trigger-width);\n max-inline-size: calc(100vw - var(--rly-space-16));\n }\n }\n}\n","import { Select as RadixSelect } from \"radix-ui\"\nimport type { AriaAttributes, ComponentPropsWithRef, ReactElement } from \"react\"\nimport { Icon } from \"../foundations/Icon.js\"\nimport { PortalBoundary } from \"../foundations/PortalProvider.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport { registerFieldControl } from \"../internal/field-control.js\"\nimport styles from \"./Select.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\n\nexport const RLY_SELECT_VARIANTS = defineVariants({\n size: {\n compact: { className: style(\"compact\"), purpose: \"Dense form rows\", tokens: [\"space-40\", \"type-meta\"] },\n default: {\n className: style(\"defaultSize\"),\n purpose: \"Standard selection control\",\n tokens: [\"space-48\", \"type-body\"]\n }\n }\n})\n\nexport const RLY_SELECT_DEFAULT_VARIANTS = defineVariants({ size: \"default\" })\nexport type RlySelectSize = keyof typeof RLY_SELECT_VARIANTS.size\n\nexport interface RlySelectOption {\n readonly disabled?: boolean\n readonly label: string\n readonly value: string\n}\n\ntype TriggerProps = Pick<\n ComponentPropsWithRef<\"button\">,\n \"className\" | \"id\" | \"onBlur\" | \"onFocus\" | \"onKeyDown\" | \"ref\" | \"tabIndex\"\n>\n\ntype SelectAccessibleName =\n | { readonly \"aria-label\": string; readonly \"aria-labelledby\"?: never }\n | { readonly \"aria-label\"?: never; readonly \"aria-labelledby\": string }\n\ntype SelectValueState =\n | {\n readonly defaultValue?: never\n readonly onValueChange: (value: string) => void\n readonly value: string | undefined\n }\n | {\n readonly defaultValue?: string\n readonly onValueChange?: (value: string) => void\n readonly value?: never\n }\n\ntype SelectOpenState =\n | {\n readonly defaultOpen?: never\n readonly onOpenChange: (open: boolean) => void\n readonly open: boolean\n }\n | {\n readonly defaultOpen?: boolean\n readonly onOpenChange?: (open: boolean) => void\n readonly open?: never\n }\n\ninterface SelectBaseProps extends TriggerProps {\n readonly \"aria-describedby\"?: AriaAttributes[\"aria-describedby\"]\n readonly \"aria-errormessage\"?: AriaAttributes[\"aria-errormessage\"]\n readonly \"aria-invalid\"?: AriaAttributes[\"aria-invalid\"]\n readonly \"aria-required\"?: AriaAttributes[\"aria-required\"]\n readonly autoComplete?: string\n readonly disabled?: boolean\n readonly form?: string\n readonly name?: string\n readonly options: ReadonlyArray<RlySelectOption>\n readonly placeholder?: string\n readonly required?: boolean\n readonly size?: RlySelectSize\n}\n\nexport type SelectProps = SelectBaseProps & SelectAccessibleName & SelectValueState & SelectOpenState\n\nconst validateOptions = (options: ReadonlyArray<RlySelectOption>, disabled: boolean): void => {\n if (!disabled && !options.some((option) => !option.disabled)) {\n throw new Error(\"Select options must contain at least one enabled option\")\n }\n const values = new Set<string>()\n for (const option of options) {\n requireText(option.label, \"Select option label\")\n const value = requireText(option.value, \"Select option value\")\n if (values.has(value)) throw new Error(`Duplicate Select option value: ${value}`)\n values.add(value)\n }\n}\n\n/** Render an owned, portal-safe single-value selector without exposing Radix component shapes. */\nconst SelectImplementation = (componentProps: SelectProps): ReactElement => {\n const valueControlled = \"value\" in componentProps\n const {\n \"aria-describedby\": ariaDescribedBy,\n \"aria-errormessage\": ariaErrorMessage,\n \"aria-invalid\": ariaInvalid,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-required\": ariaRequired,\n autoComplete,\n className,\n defaultOpen,\n defaultValue,\n disabled = false,\n form,\n id,\n name,\n onBlur,\n onFocus,\n onKeyDown,\n onOpenChange,\n onValueChange,\n open,\n options,\n placeholder = \"Select an option\",\n ref,\n required = false,\n size = RLY_SELECT_DEFAULT_VARIANTS.size,\n tabIndex,\n value\n } = componentProps\n validateOptions(options, disabled)\n const accessibleName = ariaLabel ?? ariaLabelledBy\n requireText(accessibleName, ariaLabel === undefined ? \"Select aria-labelledby\" : \"Select aria-label\")\n const visiblePlaceholder = requireText(placeholder, \"Select placeholder\")\n const selectedValue = value ?? defaultValue\n const selectedOption =\n selectedValue === undefined ? undefined : options.find((option) => option.value === selectedValue)\n if (selectedValue !== undefined && selectedOption === undefined) {\n throw new Error(`Select value does not match an option: ${selectedValue}`)\n }\n // Only a controlled value is known on every render; an uncontrolled one is left to Radix to mirror.\n const selectedLabel = valueControlled ? selectedOption?.label : undefined\n\n return (\n <RadixSelect.Root\n {...(autoComplete === undefined ? {} : { autoComplete })}\n {...(defaultOpen === undefined ? {} : { defaultOpen })}\n {...(defaultValue === undefined ? {} : { defaultValue })}\n disabled={disabled}\n {...(form === undefined ? {} : { form })}\n {...(name === undefined ? {} : { name })}\n {...(onOpenChange === undefined ? {} : { onOpenChange })}\n {...(onValueChange === undefined ? {} : { onValueChange })}\n {...(open === undefined ? {} : { open })}\n required={required}\n {...(valueControlled ? { value: value ?? \"\" } : {})}\n >\n <RadixSelect.Trigger\n aria-describedby={ariaDescribedBy}\n aria-errormessage={ariaErrorMessage}\n aria-invalid={ariaInvalid}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-required={ariaRequired ?? (required ? true : undefined)}\n className={classNames(style(\"trigger\"), RLY_SELECT_VARIANTS.size[size].className, className)}\n id={id}\n onBlur={onBlur}\n onFocus={onFocus}\n onKeyDown={onKeyDown}\n ref={ref}\n tabIndex={tabIndex}\n >\n {/* Render the label directly so a controlled value shows before the list mounts, including on the server. */}\n <RadixSelect.Value placeholder={visiblePlaceholder}>{selectedLabel}</RadixSelect.Value>\n <RadixSelect.Icon asChild>\n <span aria-hidden=\"true\" className={style(\"triggerIcon\")}>\n <Icon decorative name=\"chevron-down\" size=\"small\" />\n </span>\n </RadixSelect.Icon>\n </RadixSelect.Trigger>\n <PortalBoundary>\n {(container) => (\n <RadixSelect.Portal container={container}>\n <RadixSelect.Content\n align=\"start\"\n className={classNames(style(\"content\"), RLY_SELECT_VARIANTS.size[size].className)}\n position=\"popper\"\n sideOffset={6}\n >\n <RadixSelect.ScrollUpButton aria-hidden=\"true\" className={style(\"scrollButton\")}>\n <Icon decorative name=\"chevron-up\" size=\"small\" />\n </RadixSelect.ScrollUpButton>\n <RadixSelect.Viewport className={style(\"viewport\")}>\n {options.map((option) => (\n <RadixSelect.Item\n className={style(\"item\")}\n {...(option.disabled === undefined ? {} : { disabled: option.disabled })}\n key={option.value}\n textValue={option.label}\n value={option.value}\n >\n <RadixSelect.ItemText>{option.label}</RadixSelect.ItemText>\n <RadixSelect.ItemIndicator className={style(\"itemIndicator\")}>\n <Icon decorative name=\"check\" size=\"small\" />\n </RadixSelect.ItemIndicator>\n </RadixSelect.Item>\n ))}\n </RadixSelect.Viewport>\n <RadixSelect.ScrollDownButton aria-hidden=\"true\" className={style(\"scrollButton\")}>\n <Icon decorative name=\"chevron-down\" size=\"small\" />\n </RadixSelect.ScrollDownButton>\n </RadixSelect.Content>\n </RadixSelect.Portal>\n )}\n </PortalBoundary>\n </RadixSelect.Root>\n )\n}\n\nexport const Select = /* @__PURE__ */ registerFieldControl(SelectImplementation)\n","@layer rly.components {\n .root {\n background: var(--rly-color-surface-3);\n border: 1px solid var(--rly-color-border-1);\n box-sizing: border-box;\n max-inline-size: 100%;\n }\n\n .text {\n block-size: var(--rly-type-body-line-height);\n border-radius: var(--rly-radius-tag);\n inline-size: 100%;\n }\n .block {\n border-radius: var(--rly-radius-control);\n inline-size: 100%;\n min-block-size: var(--rly-space-64);\n }\n .circle {\n aspect-ratio: 1;\n border-radius: var(--rly-radius-round);\n inline-size: var(--rly-space-48);\n }\n}\n","import type { ComponentPropsWithRef, CSSProperties, ReactElement } from \"react\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport styles from \"./Skeleton.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\n\nexport const RLY_SKELETON_VARIANTS = defineVariants({\n variant: {\n text: { className: style(\"text\"), purpose: \"A line of unavailable copy\", tokens: [\"type-body\", \"radius-tag\"] },\n block: {\n className: style(\"block\"),\n purpose: \"A rectangular unavailable region\",\n tokens: [\"space-64\", \"radius-control\"]\n },\n circle: { className: style(\"circle\"), purpose: \"A circular unavailable image\", tokens: [\"radius-round\"] }\n }\n})\n\nexport const RLY_SKELETON_DEFAULT_VARIANTS = defineVariants({ variant: \"text\" })\nexport type RlySkeletonVariant = keyof typeof RLY_SKELETON_VARIANTS.variant\n\ntype SkeletonBaseProps = Omit<ComponentPropsWithRef<\"div\">, \"aria-label\" | \"children\" | \"style\"> & {\n readonly height?: CSSProperties[\"blockSize\"]\n readonly style?: CSSProperties\n readonly variant?: RlySkeletonVariant\n readonly width?: CSSProperties[\"inlineSize\"]\n}\nexport type SkeletonProps = SkeletonBaseProps &\n ({ readonly decorative?: true; readonly label?: never } | { readonly decorative: false; readonly label: string })\n\n/** Reserve content geometry without suggesting progress or motion. */\nexport const Skeleton = ({\n className,\n decorative = true,\n height,\n label,\n style: inlineStyle,\n variant = \"text\",\n width,\n ...props\n}: SkeletonProps): ReactElement => {\n if (!decorative && label === undefined) throw new Error(\"Skeleton label must contain visible text\")\n const accessibleLabel = decorative || label === undefined ? undefined : requireText(label, \"Skeleton label\")\n const dimensions: CSSProperties = {\n ...(!(height === undefined) && { blockSize: height }),\n ...(!(width === undefined) && { inlineSize: width })\n }\n\n return (\n <div\n {...props}\n aria-busy={decorative ? undefined : \"true\"}\n aria-hidden={decorative ? \"true\" : undefined}\n aria-label={accessibleLabel}\n className={classNames(style(\"root\"), RLY_SKELETON_VARIANTS.variant[variant].className, className)}\n role={decorative ? \"presentation\" : \"status\"}\n style={{ ...inlineStyle, ...dimensions }}\n />\n )\n}\n","@layer rly.components {\n .root {\n background: var(--rly-color-surface-2);\n border: 1px solid currentcolor;\n border-radius: var(--rly-radius-control);\n box-sizing: border-box;\n color: var(--rly-color-text-2);\n display: grid;\n gap: var(--rly-space-16);\n grid-template-columns: var(--rly-space-4) auto minmax(var(--rly-space-0), 1fr);\n max-inline-size: 100%;\n padding: var(--rly-space-24);\n }\n\n .neutral {\n background: var(--rly-color-surface-2);\n color: var(--rly-color-text-2);\n }\n .positive {\n background: var(--rly-color-success-tint);\n color: var(--rly-color-success-ink);\n }\n .critical {\n background: var(--rly-color-blocked-tint);\n color: var(--rly-color-blocked-ink);\n }\n .caution {\n background: var(--rly-color-held-tint);\n color: var(--rly-color-held-ink);\n }\n .progress {\n background: var(--rly-color-deploying-tint);\n color: var(--rly-color-deploying-ink);\n }\n .rail {\n align-self: stretch;\n background: currentcolor;\n border-radius: var(--rly-radius-round);\n }\n .icon {\n display: inline-flex;\n padding-block-start: var(--rly-space-2);\n }\n .content {\n color: var(--rly-color-text-1);\n display: grid;\n gap: var(--rly-space-8);\n min-inline-size: var(--rly-space-0);\n }\n\n .title {\n color: var(--rly-color-text-2);\n font: var(--rly-type-card-title-weight) var(--rly-type-card-title-size) / var(--rly-type-card-title-line-height)\n var(--rly-type-card-title-font);\n letter-spacing: var(--rly-type-card-title-tracking);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n\n .positive .title {\n color: var(--rly-color-success-ink);\n }\n .critical .title {\n color: var(--rly-color-blocked-ink);\n }\n .caution .title {\n color: var(--rly-color-held-ink);\n }\n .progress .title {\n color: var(--rly-color-deploying-ink);\n }\n\n .description {\n color: var(--rly-color-text-2);\n font: var(--rly-type-body-weight) var(--rly-type-body-size) / var(--rly-type-body-line-height)\n var(--rly-type-body-font);\n overflow-wrap: anywhere;\n }\n .description > :first-child {\n margin-block-start: var(--rly-space-0);\n }\n .description > :last-child {\n margin-block-end: var(--rly-space-0);\n }\n .action {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: var(--rly-space-8);\n padding-block-start: var(--rly-space-8);\n }\n}\n","import type { ComponentPropsWithRef, ReactElement, ReactNode } from \"react\"\nimport { Icon, type RlyIconName } from \"../foundations/Icon.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport styles from \"./StatePanel.module.css\"\n\nconst style = (name: string): string => cssClass(styles, name)\n\nexport const RLY_STATE_PANEL_VARIANTS = defineVariants({\n tone: {\n neutral: {\n className: style(\"neutral\"),\n purpose: \"Neutral explanatory state\",\n tokens: [\"color-text-2\", \"color-surface-2\"]\n },\n positive: {\n className: style(\"positive\"),\n purpose: \"Positive outcome\",\n tokens: [\"color-success-ink\", \"color-success-tint\"]\n },\n critical: {\n className: style(\"critical\"),\n purpose: \"Critical outcome requiring attention\",\n tokens: [\"color-blocked-ink\", \"color-blocked-tint\"]\n },\n caution: {\n className: style(\"caution\"),\n purpose: \"Held outcome requiring review\",\n tokens: [\"color-held-ink\", \"color-held-tint\"]\n },\n progress: {\n className: style(\"progress\"),\n purpose: \"Work currently in progress\",\n tokens: [\"color-deploying-ink\", \"color-deploying-tint\"]\n }\n }\n})\n\nexport const RLY_STATE_PANEL_DEFAULT_VARIANTS = defineVariants({ tone: \"neutral\" })\nexport type RlyStatePanelTone = keyof typeof RLY_STATE_PANEL_VARIANTS.tone\nexport type RlyStatePanelAnnouncement = \"off\" | \"polite\" | \"assertive\"\n\nconst toneIcons = {\n neutral: \"minus\",\n positive: \"check\",\n critical: \"alert\",\n caution: \"clock\",\n progress: \"loader\"\n} satisfies Readonly<Record<RlyStatePanelTone, RlyIconName>>\n\nexport type StatePanelProps = Omit<ComponentPropsWithRef<\"section\">, \"aria-live\" | \"children\" | \"title\"> & {\n readonly action?: ReactNode\n readonly announce?: RlyStatePanelAnnouncement\n readonly description?: ReactNode\n readonly icon?: RlyIconName\n readonly title: string\n readonly tone?: RlyStatePanelTone\n}\n\n/**\n * Explain an outcome with redundant word, icon, rail, ink, and tint cues.\n *\n * `announce` makes the panel a live region. A polite status region reliably announces\n * changes only once it is already mounted: mount it persistently and swap its content.\n * An assertive alert may announce as soon as it is inserted. Keep `announce=\"off\"`\n * for static states.\n */\nexport const StatePanel = ({\n action,\n announce = \"off\",\n className,\n description,\n icon,\n title,\n tone = \"neutral\",\n ...props\n}: StatePanelProps): ReactElement => {\n // An announcing region owns its role; otherwise the caller's role (for example \"note\") stands.\n const role = announce === \"assertive\" ? \"alert\" : announce === \"polite\" ? \"status\" : props.role\n\n return (\n <section\n {...props}\n aria-live={announce === \"off\" ? undefined : announce}\n className={classNames(style(\"root\"), RLY_STATE_PANEL_VARIANTS.tone[tone].className, className)}\n role={role}\n >\n <span aria-hidden=\"true\" className={style(\"rail\")} />\n <span aria-hidden=\"true\" className={style(\"icon\")}>\n <Icon decorative name={icon ?? toneIcons[tone]} />\n </span>\n <div className={style(\"content\")}>\n <strong className={style(\"title\")}>{requireText(title, \"StatePanel title\")}</strong>\n {description === undefined ? null : <div className={style(\"description\")}>{description}</div>}\n {action === undefined ? null : <div className={style(\"action\")}>{action}</div>}\n </div>\n </section>\n )\n}\n","@layer rly.components {\n .root {\n align-content: start;\n color: var(--rly-color-text-1);\n display: grid;\n gap: var(--rly-space-8);\n inline-size: 100%;\n max-inline-size: 100%;\n min-inline-size: var(--rly-space-0);\n }\n\n .label {\n align-items: baseline;\n display: flex;\n flex-wrap: wrap;\n font: var(--rly-type-label-weight) var(--rly-type-label-size) / var(--rly-type-label-line-height)\n var(--rly-type-label-font);\n gap: var(--rly-space-8);\n letter-spacing: var(--rly-type-label-tracking);\n }\n\n .required,\n .description,\n .error {\n font: var(--rly-type-meta-weight) var(--rly-type-meta-size) / var(--rly-type-meta-line-height)\n var(--rly-type-meta-font);\n letter-spacing: var(--rly-type-meta-tracking);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n\n .required,\n .description {\n color: var(--rly-color-text-2);\n }\n\n .error {\n color: var(--rly-color-blocked-ink);\n }\n\n .controlSlot {\n min-inline-size: var(--rly-space-0);\n }\n\n .control {\n appearance: none;\n background: var(--rly-color-surface-1);\n border: 1px solid var(--rly-color-border-2);\n border-radius: var(--rly-radius-field);\n box-sizing: border-box;\n color: var(--rly-color-text-1);\n font: var(--rly-type-body-weight) var(--rly-type-body-size) / var(--rly-type-body-line-height)\n var(--rly-type-body-font);\n inline-size: 100%;\n max-inline-size: 100%;\n min-inline-size: var(--rly-space-0);\n padding: var(--rly-space-8) var(--rly-space-12);\n }\n\n .control:is(textarea) {\n min-block-size: var(--rly-space-96);\n resize: vertical;\n }\n\n .compact .control {\n min-block-size: var(--rly-space-40);\n padding-block: var(--rly-space-6);\n }\n\n .defaultSize .control {\n min-block-size: var(--rly-space-48);\n }\n\n .control:disabled,\n .control[aria-disabled=\"true\"] {\n cursor: not-allowed;\n opacity: 0.58;\n }\n\n .control[aria-invalid=\"true\"] {\n border-color: var(--rly-color-blocked-ink);\n }\n}\n","import { type AriaAttributes, type ComponentPropsWithRef, type ReactElement, useId } from \"react\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport { isRegisteredFieldControl } from \"../internal/field-control.js\"\nimport styles from \"./Field.module.css\"\nimport * as Predicate from \"../internal/predicates.js\"\n\nconst style = (name: string): string => cssClass(styles, name)\nconst semanticControlKeys: ReadonlyArray<keyof FieldControlProps> = [\n \"aria-describedby\",\n \"aria-errormessage\",\n \"aria-invalid\",\n \"aria-labelledby\",\n \"aria-required\",\n \"id\",\n \"required\"\n]\nconst concreteControlElements = new Set([\"button\", \"input\", \"select\", \"textarea\"])\n\nexport const RLY_FIELD_VARIANTS = defineVariants({\n size: {\n compact: { className: style(\"compact\"), purpose: \"Dense form rows\", tokens: [\"space-40\", \"type-meta\"] },\n default: { className: style(\"defaultSize\"), purpose: \"Standard form controls\", tokens: [\"space-48\", \"type-label\"] }\n }\n})\n\nexport const RLY_FIELD_DEFAULT_VARIANTS = defineVariants({ size: \"default\" })\nexport type RlyFieldSize = keyof typeof RLY_FIELD_VARIANTS.size\n\n/** DOM-neutral semantics to spread onto an input, textarea, native select, or rly Select. */\nexport interface FieldControlProps {\n readonly \"aria-describedby\"?: AriaAttributes[\"aria-describedby\"]\n readonly \"aria-errormessage\"?: AriaAttributes[\"aria-errormessage\"]\n readonly \"aria-invalid\"?: AriaAttributes[\"aria-invalid\"]\n readonly \"aria-labelledby\": string\n readonly \"aria-required\"?: AriaAttributes[\"aria-required\"]\n readonly className: string\n readonly id: string\n readonly required?: true\n}\n\nexport type FieldProps = Omit<ComponentPropsWithRef<\"div\">, \"children\"> & {\n readonly children: (controlProps: FieldControlProps) => ReactElement<FieldControlProps>\n readonly controlId?: string\n readonly description?: string\n readonly error?: string\n readonly label: string\n readonly required?: boolean\n readonly size?: RlyFieldSize\n}\n\n/** Compose visible field context with control semantics without cloning an implementation-specific control. */\nexport const Field = ({\n children,\n className,\n controlId,\n description,\n error,\n label,\n required = false,\n size = RLY_FIELD_DEFAULT_VARIANTS.size,\n ...props\n}: FieldProps): ReactElement => {\n const generatedId = useId()\n const resolvedControlId = controlId ?? `rly-field-${generatedId}`\n const labelId = `${resolvedControlId}-label`\n const descriptionId = description === undefined ? undefined : `${resolvedControlId}-description`\n const errorId = error === undefined ? undefined : `${resolvedControlId}-error`\n const describedBy = [descriptionId, errorId].filter((id): id is string => id !== undefined).join(\" \") || undefined\n const commonControlProps: FieldControlProps = {\n \"aria-describedby\": describedBy,\n \"aria-errormessage\": errorId,\n \"aria-invalid\": error === undefined ? undefined : true,\n \"aria-labelledby\": labelId,\n \"aria-required\": required ? true : undefined,\n className: style(\"control\"),\n id: resolvedControlId\n }\n const controlProps: FieldControlProps = required ? { ...commonControlProps, required: true } : commonControlProps\n const control = children(controlProps)\n const controlType = control.type\n const supportedControl = Predicate.isString(controlType)\n ? concreteControlElements.has(controlType)\n : (Predicate.isFunction(controlType) || Predicate.isObjectOrArray(controlType)) &&\n controlType !== null &&\n isRegisteredFieldControl(controlType)\n if (!supportedControl) throw new Error(\"Field children must render one concrete or rly-owned control\")\n for (const key of semanticControlKeys) {\n if (control.props[key] !== controlProps[key]) throw new Error(`Field control must apply ${key}`)\n }\n if (!control.props.className.split(\" \").includes(controlProps.className)) {\n throw new Error(\"Field control must apply className\")\n }\n\n return (\n <div\n {...props}\n className={classNames(style(\"root\"), RLY_FIELD_VARIANTS.size[size].className, className)}\n data-error={error === undefined ? undefined : \"true\"}\n >\n <label className={style(\"label\")} htmlFor={resolvedControlId} id={labelId}>\n {requireText(label, \"Field label\")}\n {required ? <span className={style(\"required\")}>Required</span> : null}\n </label>\n {description === undefined ? null : (\n <p className={style(\"description\")} id={descriptionId}>\n {requireText(description, \"Field description\")}\n </p>\n )}\n <div className={style(\"controlSlot\")}>{control}</div>\n {error === undefined ? null : (\n <p className={style(\"error\")} id={errorId} role=\"alert\">\n {requireText(error, \"Field error\")}\n </p>\n )}\n </div>\n )\n}\n","@layer rly.components {\n .layer {\n inset: var(--rly-space-0);\n isolation: isolate;\n pointer-events: none;\n position: fixed;\n z-index: 100;\n }\n\n .overlay {\n background: var(--rly-color-canvas);\n inset: var(--rly-space-0);\n opacity: 0.78;\n pointer-events: auto;\n position: fixed;\n z-index: 0;\n }\n\n .content {\n background: var(--rly-color-surface-1);\n border: 1px solid var(--rly-color-border-1);\n border-radius: var(--rly-radius-overlay);\n box-sizing: border-box;\n color: var(--rly-color-text-1);\n display: grid;\n gap: var(--rly-space-24);\n inset-block-start: 50%;\n inset-inline-start: 50%;\n max-block-size: calc(100dvh - var(--rly-space-32));\n max-inline-size: calc(100vw - var(--rly-space-32));\n overflow: auto;\n overscroll-behavior: contain;\n padding: var(--rly-space-32);\n pointer-events: auto;\n position: fixed;\n transform: translate(-50%, -50%);\n z-index: 1;\n }\n\n .defaultSize {\n inline-size: 32rem;\n }\n .wide {\n inline-size: 48rem;\n }\n\n .header {\n display: grid;\n gap: var(--rly-space-8);\n min-inline-size: var(--rly-space-0);\n }\n\n .title {\n font: var(--rly-type-section-title-weight) var(--rly-type-section-title-size) /\n var(--rly-type-section-title-line-height) var(--rly-type-section-title-font);\n letter-spacing: var(--rly-type-section-title-tracking);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n text-wrap: balance;\n }\n\n .description {\n color: var(--rly-color-text-2);\n font: var(--rly-type-body-large-weight) var(--rly-type-body-large-size) / var(--rly-type-body-large-line-height)\n var(--rly-type-body-large-font);\n margin: var(--rly-space-0);\n overflow-wrap: anywhere;\n text-wrap: pretty;\n }\n\n .body {\n min-inline-size: var(--rly-space-0);\n overflow-wrap: anywhere;\n }\n\n .overlay[data-state=\"open\"] {\n animation: dialog-fade-in var(--rly-motion-standard-duration) var(--rly-motion-standard-easing);\n }\n .overlay[data-state=\"closed\"] {\n animation: dialog-fade-out var(--rly-motion-fast-duration) var(--rly-motion-fast-easing);\n }\n .content[data-state=\"open\"] {\n animation: dialog-enter var(--rly-motion-standard-duration) var(--rly-motion-standard-easing);\n }\n .content[data-state=\"closed\"] {\n animation: dialog-exit var(--rly-motion-fast-duration) var(--rly-motion-fast-easing);\n }\n\n .layer[data-rly-dialog-entry-motion=\"external\"] .overlay[data-state=\"open\"],\n .layer[data-rly-dialog-entry-motion=\"external\"] .content[data-state=\"open\"] {\n animation: none;\n }\n\n @keyframes dialog-fade-in {\n from {\n opacity: 0;\n }\n }\n @keyframes dialog-fade-out {\n to {\n opacity: 0;\n }\n }\n @keyframes dialog-enter {\n from {\n opacity: 0;\n transform: translate(-50%, calc(-50% + var(--rly-space-8)));\n }\n }\n @keyframes dialog-exit {\n to {\n opacity: 0;\n transform: translate(-50%, calc(-50% + var(--rly-space-8)));\n }\n }\n\n @media (max-width: 25rem) {\n .content {\n block-size: 100dvh;\n border: var(--rly-space-0);\n border-radius: var(--rly-space-0);\n inset: var(--rly-space-0);\n inline-size: 100%;\n max-block-size: none;\n max-inline-size: none;\n padding: var(--rly-space-20);\n transform: none;\n }\n\n .content[data-state=\"open\"] {\n animation-name: dialog-compact-in;\n }\n .content[data-state=\"closed\"] {\n animation-name: dialog-compact-out;\n }\n }\n\n @keyframes dialog-compact-in {\n from {\n opacity: 0;\n }\n }\n @keyframes dialog-compact-out {\n to {\n opacity: 0;\n }\n }\n}\n","import {\n type ComponentPropsWithRef,\n createContext,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefObject,\n useContext,\n useLayoutEffect,\n useRef,\n useState\n} from \"react\"\nimport { Dialog as RadixDialog } from \"radix-ui\"\nimport { PortalBoundary } from \"../foundations/PortalProvider.js\"\nimport { classNames, cssClass, defineVariants, requireText } from \"../internal/component.js\"\nimport {\n invalidateModalFocusRestore,\n isHTMLElement,\n ModalNestingBoundary,\n restoreModalFocusAfterCleanup,\n useModalContentRegistration,\n useModalEntryMotion,\n useModalIsolation,\n useParentModalReady\n} from \"../internal/modal.js\"\nimport { Button, type ButtonProps } from \"./Button.js\"\nimport styles from \"./Dialog.module.css\"\nimport * as Predicate from \"../internal/predicates.js\"\n\nconst style = (name: string): string => cssClass(styles, name)\n\ninterface DialogState {\n readonly open: boolean\n readonly requestOpenChange: (open: boolean) => void\n readonly restoreTargetRef: RefObject<HTMLElement | null>\n readonly triggerRef: RefObject<HTMLButtonElement | null>\n}\n\nconst DialogStateContext = createContext<DialogState | null>(null)\n\nconst useDialogState = (): DialogState => {\n const state = useContext(DialogStateContext)\n if (state === null) throw new Error(\"Dialog compound components must be nested inside Dialog\")\n return state\n}\n\nconst assignRef = <Element,>(ref: Ref<Element> | undefined, value: Element | null): void => {\n if (Predicate.isFunction(ref)) ref(value)\n else if (ref !== null && ref !== undefined) ref.current = value\n}\n\nconst getActiveElement = (): HTMLElement | null => {\n try {\n return isHTMLElement(document.activeElement) ? document.activeElement : null\n } catch {\n return null\n }\n}\n\n/** Semantic size metadata for the dialog surface. */\nexport const RLY_DIALOG_VARIANTS = defineVariants({\n size: {\n default: {\n className: style(\"defaultSize\"),\n purpose: \"Focused decision or short form\",\n tokens: [\"radius-overlay\", \"space-32\"]\n },\n wide: {\n className: style(\"wide\"),\n purpose: \"Detailed comparison or multi-section form\",\n tokens: [\"radius-overlay\", \"space-64\"]\n }\n }\n})\n\n/** Default dialog size used when no size is selected. */\nexport const RLY_DIALOG_DEFAULT_VARIANTS = defineVariants({ size: \"default\" })\n/** Available dialog surface sizes. */\nexport type RlyDialogSize = keyof typeof RLY_DIALOG_VARIANTS.size\n\ntype DialogBaseProps = { readonly children: ReactNode }\ntype ControlledDialogProps = DialogBaseProps & {\n readonly defaultOpen?: never\n readonly onOpenChange: (open: boolean) => void\n readonly open: boolean\n}\ntype DefaultDialogProps = DialogBaseProps & {\n readonly defaultOpen?: boolean\n readonly onOpenChange?: (open: boolean) => void\n readonly open?: never\n}\n/** Controlled-first or locally owned state for a dialog tree. */\nexport type DialogRootProps = ControlledDialogProps | DefaultDialogProps\n\n/** Props for the visible rly button that opens a dialog. */\nexport type DialogTriggerProps = ButtonProps\n/** Props for a visible rly button that closes a dialog. */\nexport type DialogCloseProps = ButtonProps\n\n/** Accessible content, naming, focus, and sizing for a modal dialog surface. */\nexport type DialogContentProps = Omit<\n ComponentPropsWithRef<\"div\">,\n \"aria-describedby\" | \"aria-label\" | \"aria-labelledby\" | \"children\" | \"title\"\n> & {\n readonly children: ReactNode\n readonly description?: string\n /**\n * Defaults to intrinsic and is sampled when the dialog opens. While open, changes apply to the next cycle.\n * External suppresses surface and overlay entry only; exit stays intrinsic and shared reduced-motion rules apply.\n */\n readonly entryMotion?: \"external\" | \"intrinsic\"\n readonly initialFocusRef?: RefObject<HTMLElement | null>\n readonly size?: RlyDialogSize\n readonly title: string\n}\n\nconst DialogLayer = ({\n children,\n entryMotion\n}: {\n readonly children: ReactNode\n readonly entryMotion: \"external\" | \"intrinsic\"\n}): ReactElement => {\n const state = useDialogState()\n const ref = useRef<HTMLDivElement>(null)\n useModalContentRegistration()\n useModalIsolation(ref, state.open)\n return (\n <div\n className={style(\"layer\")}\n data-rly-dialog-entry-motion={entryMotion}\n data-rly-dialog-layer=\"\"\n data-rly-modal-layer=\"\"\n ref={ref}\n >\n {children}\n </div>\n )\n}\n\n/** Own controlled or reusable default dialog state without exposing Radix contracts. */\nconst DialogRoot = ({ children, defaultOpen = false, onOpenChange, open }: DialogRootProps): ReactElement => {\n const [defaultState, setDefaultState] = useState(defaultOpen)\n const isParentModalReady = useParentModalReady()\n const resolvedOpen = (open ?? defaultState) && isParentModalReady\n const previousOpenRef = useRef(resolvedOpen)\n const triggerRef = useRef<HTMLButtonElement>(null)\n const restoreTargetRef = useRef<HTMLElement | null>(resolvedOpen ? getActiveElement() : null)\n if (resolvedOpen && !previousOpenRef.current) {\n restoreTargetRef.current = triggerRef.current ?? getActiveElement()\n }\n const requestOpenChange = (nextOpen: boolean): void => {\n if (nextOpen) restoreTargetRef.current = triggerRef.current ?? getActiveElement()\n if (open === undefined) setDefaultState(nextOpen)\n onOpenChange?.(nextOpen)\n }\n\n useLayoutEffect(() => {\n if (previousOpenRef.current === resolvedOpen) return\n previousOpenRef.current = resolvedOpen\n if (resolvedOpen) invalidateModalFocusRestore()\n else restoreModalFocusAfterCleanup(restoreTargetRef.current)\n }, [resolvedOpen])\n\n return (\n <ModalNestingBoundary>\n <DialogStateContext.Provider value={{ open: resolvedOpen, requestOpenChange, restoreTargetRef, triggerRef }}>\n <RadixDialog.Root modal onOpenChange={requestOpenChange} open={resolvedOpen}>\n {children}\n </RadixDialog.Root>\n </DialogStateContext.Provider>\n </ModalNestingBoundary>\n )\n}\n\n/** Render the explicit action that opens a dialog using rly Button semantics. */\nconst DialogTrigger = ({ ref, ...props }: DialogTriggerProps): ReactElement => {\n const state = useDialogState()\n return (\n <RadixDialog.Trigger asChild>\n <Button\n {...props}\n ref={(element) => {\n state.triggerRef.current = element\n assignRef(ref, element)\n }}\n />\n </RadixDialog.Trigger>\n )\n}\n\n/** Render the explicit action that closes the current dialog. */\nconst DialogClose = (props: DialogCloseProps): ReactElement => (\n <RadixDialog.Close asChild>\n <Button {...props} />\n </RadixDialog.Close>\n)\n\n/** Render a named modal surface in the application-controlled portal boundary. */\nconst DialogContent = ({\n children,\n className,\n description,\n entryMotion = \"intrinsic\",\n initialFocusRef,\n size = \"default\",\n title,\n ...props\n}: DialogContentProps): ReactElement => {\n const state = useDialogState()\n const resolvedEntryMotion = useModalEntryMotion(state.open, entryMotion)\n const visibleTitle = requireText(title, \"Dialog title\")\n const visibleDescription = description === undefined ? undefined : requireText(description, \"Dialog description\")\n\n return (\n <PortalBoundary>\n {(container) => (\n <RadixDialog.Portal container={container}>\n <DialogLayer entryMotion={resolvedEntryMotion}>\n <RadixDialog.Overlay\n className={style(\"overlay\")}\n data-rly-dialog-overlay=\"\"\n onPointerDown={() => {\n state.requestOpenChange(false)\n }}\n />\n <RadixDialog.Content\n {...props}\n {...(visibleDescription === undefined ? { \"aria-describedby\": undefined } : {})}\n className={classNames(style(\"content\"), RLY_DIALOG_VARIANTS.size[size].className, className)}\n onCloseAutoFocus={(event) => {\n event.preventDefault()\n }}\n onOpenAutoFocus={(event) => {\n const activeElement = getActiveElement()\n if (state.triggerRef.current === null && activeElement !== null) {\n state.restoreTargetRef.current = activeElement\n }\n const target = initialFocusRef?.current\n if (target !== null && target !== undefined) {\n event.preventDefault()\n target.focus()\n }\n }}\n >\n <header className={style(\"header\")}>\n <RadixDialog.Title className={style(\"title\")}>{visibleTitle}</RadixDialog.Title>\n {visibleDescription === undefined ? null : (\n <RadixDialog.Description className={style(\"description\")}>\n {visibleDescription}\n </RadixDialog.Description>\n )}\n </header>\n <div className={style(\"body\")}>{children}</div>\n </RadixDialog.Content>\n </DialogLayer>\n </RadixDialog.Portal>\n )}\n </PortalBoundary>\n )\n}\n\n/** Owned compound dialog API with no Radix types or slots in its public contract. */\nexport const Dialog = Object.freeze({\n Close: DialogClose,\n Content: DialogContent,\n Root: DialogRoot,\n Trigger: DialogTrigger\n})\n"],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { i as e } from "./component-DKy8kjZl.js";
2
+ import { Slot as t } from "radix-ui";
3
+ import { cloneElement as n, createContext as r, useCallback as i, useEffect as a, useInsertionEffect as o, useRef as s, useSyncExternalStore as c } from "react";
4
+ import { jsx as l } from "react/jsx-runtime";
5
+ //#region src/foundations/GlobalStyles.tsx
6
+ var u = (e) => {
7
+ let { asChild: r, children: i, ...a } = e;
8
+ return r ? /* @__PURE__ */ l(t.Root, {
9
+ ...a,
10
+ children: n(i, { "data-rly-root": "" })
11
+ }) : /* @__PURE__ */ l("div", {
12
+ ...a,
13
+ "data-rly-root": "",
14
+ children: i
15
+ });
16
+ }, d = ((e) => e)([
17
+ "system",
18
+ "light",
19
+ "dark"
20
+ ]), f = r("system"), p = ({ theme: e, ...t }) => /* @__PURE__ */ l(f.Provider, {
21
+ value: e,
22
+ children: /* @__PURE__ */ l(u, {
23
+ ...t,
24
+ "data-theme": e
25
+ })
26
+ }), m = (e) => e === "system" || e === "light" || e === "dark" ? e : void 0, h = /* @__PURE__ */ new Set(), g = /* @__PURE__ */ new WeakMap(), _ = (e, t) => {
27
+ try {
28
+ let t = e();
29
+ return {
30
+ identity: t,
31
+ readable: t
32
+ };
33
+ } catch {
34
+ return {
35
+ identity: t,
36
+ readable: null
37
+ };
38
+ }
39
+ }, v = (e, t) => {
40
+ let n = g.get(t.identity)?.get(e);
41
+ if (n !== void 0) return n;
42
+ if (t.readable === null) return "system";
43
+ try {
44
+ return m(t.readable.getItem(e)) ?? "system";
45
+ } catch {
46
+ return "system";
47
+ }
48
+ }, y = () => "system", b = (t, n) => {
49
+ let r = e(t, "useStoredTheme storage key"), a = s(n), l = s(n);
50
+ o(() => {
51
+ l.current = n;
52
+ });
53
+ let u = i((e) => {
54
+ let t = (t) => {
55
+ if (t.key !== r && t.key !== null) return;
56
+ let n = _(l.current, a.current).identity;
57
+ t.storageArea === n && (g.get(n)?.delete(r), e());
58
+ }, n = (t, n) => {
59
+ n === r && t === _(l.current, a.current).identity && e();
60
+ };
61
+ return window.addEventListener("storage", t), h.add(n), () => {
62
+ window.removeEventListener("storage", t), h.delete(n);
63
+ };
64
+ }, [r]);
65
+ return [c(u, () => v(r, _(n, a.current)), y), i((e) => {
66
+ let t = a.current;
67
+ try {
68
+ let n = l.current();
69
+ t = n, n.setItem(r, e), g.get(t)?.delete(r);
70
+ } catch {
71
+ let n = g.get(t) ?? /* @__PURE__ */ new Map();
72
+ n.set(r, e), g.set(t, n);
73
+ }
74
+ for (let e of h) e(t, r);
75
+ }, [r])];
76
+ }, x = (e) => {
77
+ a(() => {
78
+ let t = document.documentElement;
79
+ return t.dataset.theme = e, () => {
80
+ t.removeAttribute("data-theme");
81
+ };
82
+ }, [e]);
83
+ };
84
+ //#endregion
85
+ export { b as a, x as i, p as n, u as o, m as r, d as t };
86
+
87
+ //# sourceMappingURL=ThemeProvider-BFeldMeE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeProvider-BFeldMeE.js","names":["requireText","GlobalStyles","props","componentProps","asChild","Slot","cloneElement","children","RLY_THEME_NAMES","names","ThemeContext","createContext","ThemeProvider","theme","decodeRlyTheme","value","sameTabListeners","unsavedThemes","storageAccess","readable","storage","unavailable","readStoredTheme","unsaved","access","key","serverTheme","useStoredTheme","storageKey","unavailableStorage","useRef","storageRef","subscribe","useCallback","onStorage","event","current","notify","onSameTab","changed","changedStorage","useSyncExternalStore","identity","next","themes","listener","useDocumentTheme","root"],"sources":["../src/foundations/GlobalStyles.tsx","../src/foundations/ThemeProvider.tsx"],"sourcesContent":["import { Slot } from \"radix-ui\"\nimport { cloneElement, type ComponentPropsWithRef, type ReactElement, type ReactNode } from \"react\"\n\ntype RootProps = Omit<ComponentPropsWithRef<\"div\">, \"children\"> & {\n readonly \"data-rly-root\"?: string\n}\n\nexport interface GlobalStylesRootProps extends RootProps {\n readonly asChild?: false\n readonly children?: ReactNode\n}\n\nexport interface GlobalStylesChildProps extends Omit<RootProps, \"ref\"> {\n readonly asChild: true\n readonly children: ReactElement<RootProps>\n readonly ref?: never\n}\n\n/** Props for the structural boundary targeted by rly's reset and base layers. */\nexport type GlobalStylesProps = GlobalStylesRootProps | GlobalStylesChildProps\n\n/** Establishes rly's style scope without injecting CSS or reading browser globals. */\nexport const GlobalStyles = (componentProps: GlobalStylesProps): ReactElement => {\n const { asChild, children, ...props } = componentProps\n if (asChild) {\n return <Slot.Root {...props}>{cloneElement(children, { \"data-rly-root\": \"\" })}</Slot.Root>\n }\n return (\n <div {...props} data-rly-root=\"\">\n {children}\n </div>\n )\n}\n","import {\n createContext,\n type ReactElement,\n useCallback,\n useContext,\n useEffect,\n useInsertionEffect,\n useRef,\n useSyncExternalStore\n} from \"react\"\nimport { requireText } from \"../internal/component.js\"\nimport { GlobalStyles, type GlobalStylesProps } from \"./GlobalStyles.js\"\n\nconst themeNames = <const Names extends ReadonlyArray<string>>(names: Names): Names => names\n\nexport const RLY_THEME_NAMES = themeNames([\"system\", \"light\", \"dark\"])\nexport type RlyTheme = (typeof RLY_THEME_NAMES)[number]\n\nexport type ThemeProviderProps = GlobalStylesProps & {\n readonly theme: RlyTheme\n}\n\nconst ThemeContext = createContext<RlyTheme>(\"system\")\n\n/** Internal bridge used by portal-aware foundations without exposing context internals. */\nexport const useRlyTheme = (): RlyTheme => useContext(ThemeContext)\n\n/** Controlled theme boundary. Pair it with `useStoredTheme` to remember the viewer's choice. */\nexport const ThemeProvider = ({ theme, ...props }: ThemeProviderProps): ReactElement => (\n <ThemeContext.Provider value={theme}>\n <GlobalStyles {...props} data-theme={theme} />\n </ThemeContext.Provider>\n)\n\n/** Decode an untrusted stored or submitted value into a theme name, or `undefined` when it is not one. */\nexport const decodeRlyTheme = (value: string | null | undefined): RlyTheme | undefined =>\n value === \"system\" || value === \"light\" || value === \"dark\" ? value : undefined\n\n/** The storage operations `useStoredTheme` needs; the browser's local storage satisfies it. */\nexport type RlyPreferenceStorage = Pick<Storage, \"getItem\" | \"setItem\">\n\ntype PreferenceStorageIdentity = RlyPreferenceStorage | (() => RlyPreferenceStorage)\n\n// Same-tab writers notify here; the `storage` event only reaches other tabs.\nconst sameTabListeners = new Set<(storage: PreferenceStorageIdentity, key: string) => void>()\n// Holds a choice for this page lifetime when storage writes are refused.\nconst unsavedThemes = new WeakMap<PreferenceStorageIdentity, Map<string, RlyTheme>>()\n\n/** Which store a hook talks to: its identity for fallbacks and sync, and the store when readable. */\ninterface StorageAccess {\n readonly identity: PreferenceStorageIdentity\n readonly readable: RlyPreferenceStorage | null\n}\n\n/** Use the storage object as identity, or the stable supplier when access is refused. */\nconst storageAccess = (storage: () => RlyPreferenceStorage, unavailable: () => RlyPreferenceStorage): StorageAccess => {\n try {\n const readable = storage()\n return { identity: readable, readable }\n } catch {\n return { identity: unavailable, readable: null }\n }\n}\n\nconst readStoredTheme = (key: string, access: StorageAccess): RlyTheme => {\n const unsaved = unsavedThemes.get(access.identity)?.get(key)\n if (unsaved !== undefined) return unsaved\n if (access.readable === null) return \"system\"\n try {\n return decodeRlyTheme(access.readable.getItem(key)) ?? \"system\"\n } catch {\n return \"system\"\n }\n}\n\nconst serverTheme = (): RlyTheme => \"system\"\n\n/**\n * Remember the viewer's theme under an application-chosen key in storage the\n * application hands in. It is a viewer-local presentation preference, never\n * application data. Pass storage lazily, so server rendering never touches it and\n * a browser that refuses access falls back to `system`.\n *\n * The server snapshot is always `system`, so server-rendered pages hydrate and then\n * switch to the stored theme. Consumers sharing a storage object and key stay in sync;\n * native storage events sync other tabs using the same storage area. When storage\n * refuses the write, the choice lasts until unload or a matching storage update.\n * If access itself is refused, consumers share fallback choices only through the\n * same stable storage supplier. Inline suppliers keep their fallback across renders.\n *\n * @example\n * // browserStorage is an application function returning the browser's local storage.\n * const [theme, setTheme] = useStoredTheme(\"jcf_theme\", browserStorage)\n * <ThemeProvider theme={theme}><ThemeSelect onValueChange={setTheme} value={theme} /></ThemeProvider>\n */\nexport const useStoredTheme = (\n storageKey: string,\n storage: () => RlyPreferenceStorage\n): readonly [RlyTheme, (theme: RlyTheme) => void] => {\n const key = requireText(storageKey, \"useStoredTheme storage key\")\n\n const unavailableStorage = useRef(storage)\n // Callers usually pass an inline thunk; a ref keeps setTheme stable across renders. Insertion\n // effects run before any layout or passive effect, so a descendant's effect calling setTheme\n // already writes to the storage this render supplied.\n const storageRef = useRef(storage)\n useInsertionEffect(() => {\n storageRef.current = storage\n })\n\n const subscribe = useCallback(\n (notify: () => void): (() => void) => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key !== key && event.key !== null) return\n const current = storageAccess(storageRef.current, unavailableStorage.current).identity\n if (event.storageArea !== current) return\n unsavedThemes.get(current)?.delete(key)\n notify()\n }\n const onSameTab = (changedStorage: PreferenceStorageIdentity, changed: string): void => {\n if (\n changed === key &&\n changedStorage === storageAccess(storageRef.current, unavailableStorage.current).identity\n )\n notify()\n }\n window.addEventListener(\"storage\", onStorage)\n sameTabListeners.add(onSameTab)\n return () => {\n window.removeEventListener(\"storage\", onStorage)\n sameTabListeners.delete(onSameTab)\n }\n },\n [key]\n )\n\n const theme = useSyncExternalStore(\n subscribe,\n () => readStoredTheme(key, storageAccess(storage, unavailableStorage.current)),\n serverTheme\n )\n\n const setTheme = useCallback(\n (next: RlyTheme): void => {\n let identity: PreferenceStorageIdentity = unavailableStorage.current\n try {\n const current = storageRef.current()\n identity = current\n current.setItem(key, next)\n unsavedThemes.get(identity)?.delete(key)\n } catch {\n const themes = unsavedThemes.get(identity) ?? new Map<string, RlyTheme>()\n themes.set(key, next)\n unsavedThemes.set(identity, themes)\n }\n for (const listener of sameTabListeners) listener(identity, key)\n },\n [key]\n )\n\n return [theme, setTheme]\n}\n\n/**\n * Apply the theme to the document root as well as the rly boundary, so the viewport\n * canvas, overscroll area, and page scrollbars follow the viewer's choice. Call it once,\n * next to `useStoredTheme`, in the application that owns the whole page; embedded rly\n * surfaces should not. The attribute is removed on unmount.\n */\nexport const useDocumentTheme = (theme: RlyTheme): void => {\n useEffect(() => {\n const root = document.documentElement\n root.dataset.theme = theme\n return () => {\n root.removeAttribute(\"data-theme\")\n }\n }, [theme])\n}\n"],"mappings":"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"}
package/dist/base.css CHANGED
@@ -34,3 +34,20 @@
34
34
  outline-offset: 2px;
35
35
  }
36
36
  }
37
+
38
+ /* After rly.components, so a component's own display cannot defeat the native hidden
39
+ attribute. Not !important and layered: unlayered application CSS (for example a print
40
+ rule revealing inactive tab panels) still wins. until-found keeps its browser behaviour. */
41
+ @layer rly.state {
42
+ :where([data-rly-root], [data-rly-catalog])[hidden]:not([hidden="until-found"]),
43
+ :where([data-rly-root], [data-rly-catalog]) [hidden]:not([hidden="until-found"]) {
44
+ display: none;
45
+ }
46
+ }
47
+
48
+ @layer rly.base {
49
+ /* useDocumentTheme themes <html>; paint its canvas so overscroll matches the rly surface. */
50
+ :root[data-theme] {
51
+ background: var(--rly-color-canvas);
52
+ }
53
+ }