@kerfjs/ui 5.0.0-beta.100 → 5.0.0-beta.102

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 (148) hide show
  1. package/README.md +3 -1
  2. package/ai/component-catalog.json +214 -18
  3. package/ai/component-composition.json +216 -8
  4. package/ai/components/README.md +2 -0
  5. package/ai/components/backdrop-image.md +84 -0
  6. package/ai/components/nav-stack.md +6 -1
  7. package/ai/components/pane.md +11 -1
  8. package/ai/components/recipe-media-detail.md +57 -0
  9. package/ai/components/surface-scaffold.md +13 -6
  10. package/ai/components/tab-navigator.md +6 -1
  11. package/ai/components/toolbar-control-group.md +1 -0
  12. package/ai/public-api-signatures-v1.md +356 -32
  13. package/ai/skill.md +53 -5
  14. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  15. package/dist/backdrop-image.d.ts +132 -0
  16. package/dist/backdrop-image.js +5 -0
  17. package/dist/backdrop-image.js.map +1 -0
  18. package/dist/browser/backdrop-image.js +3 -0
  19. package/dist/catalog-resources.d.ts +2 -2
  20. package/dist/catalog.d.ts +2 -2
  21. package/dist/catalog.js +10 -9
  22. package/dist/catalog.js.map +1 -1
  23. package/dist/{chunk-QIG4IGUP.js → chunk-2PXZBFEC.js} +3 -1
  24. package/dist/chunk-2PXZBFEC.js.map +1 -0
  25. package/dist/{chunk-T4WJ5VGP.js → chunk-3SKAEPPZ.js} +2 -2
  26. package/dist/{chunk-T4WJ5VGP.js.map → chunk-3SKAEPPZ.js.map} +1 -1
  27. package/dist/chunk-5NQF35VO.js +67 -0
  28. package/dist/chunk-5NQF35VO.js.map +1 -0
  29. package/dist/{chunk-CMETHPKR.js → chunk-6E2H2E5A.js} +29 -8
  30. package/dist/chunk-6E2H2E5A.js.map +1 -0
  31. package/dist/{chunk-PIVY32AE.js → chunk-BYFTHA4I.js} +16 -3
  32. package/dist/chunk-BYFTHA4I.js.map +1 -0
  33. package/dist/{chunk-HGV7J4T6.js → chunk-EKHDLBRP.js} +1 -1
  34. package/dist/chunk-EKHDLBRP.js.map +1 -0
  35. package/dist/{chunk-QROYYGWU.js → chunk-FNZQOUZ5.js} +2 -2
  36. package/dist/{chunk-QROYYGWU.js.map → chunk-FNZQOUZ5.js.map} +1 -1
  37. package/dist/{chunk-EOO6PYGF.js → chunk-IXLUZHGT.js} +4 -4
  38. package/dist/{chunk-EOO6PYGF.js.map → chunk-IXLUZHGT.js.map} +1 -1
  39. package/dist/{chunk-UMKV7YVQ.js → chunk-LPHU44S3.js} +5 -1
  40. package/dist/chunk-LPHU44S3.js.map +1 -0
  41. package/dist/{chunk-INI4WDWC.js → chunk-M5KZ26VV.js} +27 -4
  42. package/dist/chunk-M5KZ26VV.js.map +1 -0
  43. package/dist/{chunk-TLMMFQWM.js → chunk-NS762OVM.js} +3 -3
  44. package/dist/{chunk-TLMMFQWM.js.map → chunk-NS762OVM.js.map} +1 -1
  45. package/dist/{chunk-CXZXHZTR.js → chunk-OZ6X76G5.js} +34 -1
  46. package/dist/chunk-OZ6X76G5.js.map +1 -0
  47. package/dist/{chunk-YOA5W5ZH.js → chunk-PKL6QJKK.js} +31 -3
  48. package/dist/chunk-PKL6QJKK.js.map +1 -0
  49. package/dist/chunk-QTSMHH5J.js +85 -0
  50. package/dist/chunk-QTSMHH5J.js.map +1 -0
  51. package/dist/chunk-SCDWURBO.js +136 -0
  52. package/dist/chunk-SCDWURBO.js.map +1 -0
  53. package/dist/{chunk-RHD7RKGI.js → chunk-SIW64V65.js} +3 -3
  54. package/dist/{chunk-RHD7RKGI.js.map → chunk-SIW64V65.js.map} +1 -1
  55. package/dist/chunk-SQ5DH66U.js +37 -0
  56. package/dist/chunk-SQ5DH66U.js.map +1 -0
  57. package/dist/{chunk-PYBBYSZ3.js → chunk-YBWQR2OK.js} +5 -5
  58. package/dist/{chunk-PYBBYSZ3.js.map → chunk-YBWQR2OK.js.map} +1 -1
  59. package/dist/{chunk-3GLYIIFR.js → chunk-ZYU5WRC3.js} +2 -2
  60. package/dist/{chunk-3GLYIIFR.js.map → chunk-ZYU5WRC3.js.map} +1 -1
  61. package/dist/collapsible-panel.js +3 -2
  62. package/dist/collapsible-panel.js.map +1 -1
  63. package/dist/index.d.ts +5 -3
  64. package/dist/index.js +24 -20
  65. package/dist/list-action-row.js +1 -1
  66. package/dist/list-header.js +1 -1
  67. package/dist/list-item.js +1 -1
  68. package/dist/{nav-stack-VXTumzKa.d.ts → nav-stack-CU_0dKtI.d.ts} +41 -5
  69. package/dist/nav-stack.d.ts +4 -4
  70. package/dist/nav-stack.js +2 -1
  71. package/dist/pane.d.ts +76 -4
  72. package/dist/pane.js +2 -1
  73. package/dist/select.js +1 -1
  74. package/dist/split-view.d.ts +4 -4
  75. package/dist/split-view.js +3 -2
  76. package/dist/split-view.js.map +1 -1
  77. package/dist/state-banner.js +1 -1
  78. package/dist/styles/backdrop-image.css +241 -0
  79. package/dist/styles/components/actions/toolbar/toolbar.css +9 -0
  80. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +128 -0
  81. package/dist/styles/components/layout/pane/pane.css +121 -1
  82. package/dist/styles/components/navigation/nav-stack/nav-stack.css +207 -6
  83. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +121 -3
  84. package/dist/styles/components/surfaces/backdrop-image/backdrop-image.css +241 -0
  85. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +166 -2
  86. package/dist/styles/foundation.css +165 -8
  87. package/dist/styles/nav-stack.css +207 -6
  88. package/dist/styles/overlay-sheet.css +211 -0
  89. package/dist/styles/pane.css +121 -1
  90. package/dist/styles/styles.css +1 -0
  91. package/dist/styles/surface-scaffold.css +166 -2
  92. package/dist/styles/tab-navigator.css +121 -3
  93. package/dist/styles/toolbar-control-group.css +128 -0
  94. package/dist/styles/toolbar.css +9 -0
  95. package/dist/styles/webawesome.css +10 -0
  96. package/dist/surface-scaffold-register.d.ts +3 -2
  97. package/dist/surface-scaffold-register.js +113 -10
  98. package/dist/surface-scaffold-register.js.map +1 -1
  99. package/dist/surface-scaffold.d.ts +38 -5
  100. package/dist/surface-scaffold.js +1 -1
  101. package/dist/tab-navigator.d.ts +43 -3
  102. package/dist/tab-navigator.js +35 -6
  103. package/dist/tab-navigator.js.map +1 -1
  104. package/dist/toolbar-control-group.d.ts +13 -2
  105. package/dist/toolbar-control-group.js +1 -1
  106. package/dist/toolbar-text.d.ts +6 -1
  107. package/dist/toolbar-text.js +1 -1
  108. package/dist/toolbar.d.ts +1 -0
  109. package/dist/wire-catalog.d.ts +2 -2
  110. package/dist/wire-catalog.js +1 -1
  111. package/dist/wire-nav-stack.js +29 -3
  112. package/dist/wire-nav-stack.js.map +1 -1
  113. package/dist/wire-overlay-sheets.d.ts +20 -0
  114. package/dist/wire-overlay-sheets.js +4 -0
  115. package/dist/wire-overlay-sheets.js.map +1 -0
  116. package/dist/wire-scroll-dividers.js +1 -1
  117. package/dist/wire-token-search-fields.js +1 -1
  118. package/dist/workbench.d.ts +2 -2
  119. package/dist/workbench.js +2 -1
  120. package/docs/accessibility.md +35 -0
  121. package/docs/app-layouts.md +13 -0
  122. package/docs/backdrop-image.md +128 -0
  123. package/docs/component-contract.md +2 -0
  124. package/docs/component-selection.md +11 -2
  125. package/docs/design/templates/toolbar-control-group/glass-dark.svg +1 -0
  126. package/docs/design/templates/toolbar-control-group/glass.svg +1 -0
  127. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  128. package/docs/design/templates/toolbar-control-group.svg +1 -1
  129. package/docs/design/templates.md +8 -0
  130. package/docs/layout.md +135 -5
  131. package/docs/manual-test-plan.md +29 -0
  132. package/docs/nav-stack.md +37 -1
  133. package/docs/recipes.md +24 -0
  134. package/docs/surface-scaffold.md +149 -3
  135. package/docs/tab-navigator.md +36 -0
  136. package/docs/ux-demo.md +11 -1
  137. package/llms.txt +42 -4
  138. package/package.json +13 -1
  139. package/ux-demo/recipes/loaders.ts +2 -0
  140. package/ux-demo/recipes/media-detail.tsx +272 -0
  141. package/dist/chunk-CMETHPKR.js.map +0 -1
  142. package/dist/chunk-CXZXHZTR.js.map +0 -1
  143. package/dist/chunk-HGV7J4T6.js.map +0 -1
  144. package/dist/chunk-INI4WDWC.js.map +0 -1
  145. package/dist/chunk-PIVY32AE.js.map +0 -1
  146. package/dist/chunk-QIG4IGUP.js.map +0 -1
  147. package/dist/chunk-UMKV7YVQ.js.map +0 -1
  148. package/dist/chunk-YOA5W5ZH.js.map +0 -1
package/README.md CHANGED
@@ -50,7 +50,7 @@ geometry inspection, and metadata ownership. AI tools can discover that
50
50
  contract and its public imports through the shipped
51
51
  [`catalog-authoring.json`](./ai/catalog-authoring.json) artifact.
52
52
 
53
- For connective application patterns, use the ten [production composition
53
+ For connective application patterns, use the eleven [production composition
54
54
  recipes](./docs/recipes.md). Each has a stable catalog route, runnable state,
55
55
  public-subpath source, semantic layout ownership, and explicit application
56
56
  customization boundaries. The composer recipe demonstrates one coherent form
@@ -252,9 +252,11 @@ interchangeable. `ListItem` and `ListActionRow` deliberately expose no raw
252
252
  | `wireTabBars`, `reorderTabs` | `@kerfjs/ui/wire-tab-bars` | — |
253
253
  | `wireScrollDividers` | `@kerfjs/ui/wire-scroll-dividers` | — |
254
254
  | `wireToolbarVisibility` | `@kerfjs/ui/wire-toolbar-visibility` | — |
255
+ | `wireOverlaySheets` | `@kerfjs/ui/wire-overlay-sheets` | `@kerfjs/ui/overlay-sheet.css` |
255
256
  | `LoadingSpinner` | `@kerfjs/ui/loading-spinner` | `@kerfjs/ui/loading-spinner.css` |
256
257
  | `Skeleton` | `@kerfjs/ui/skeleton` | `@kerfjs/ui/skeleton.css` |
257
258
  | `SunkenPanel` | `@kerfjs/ui/sunken-panel` | `@kerfjs/ui/sunken-panel.css` |
259
+ | `BackdropImage` | `@kerfjs/ui/backdrop-image` | `@kerfjs/ui/backdrop-image.css` |
258
260
  | `Select` | `@kerfjs/ui/select` | `@kerfjs/ui/select.css` |
259
261
  | `SegmentedControl` | `@kerfjs/ui/segmented-control` | `@kerfjs/ui/segmented-control.css` |
260
262
  | `ChoiceChips` | `@kerfjs/ui/choice-chips` | `@kerfjs/ui/choice-chips.css` |
@@ -648,7 +648,10 @@
648
648
  "A child table or other component needs edge-to-edge decoration with text aligned to other pane content.",
649
649
  "An opaque edge-to-edge child needs a reserved vertical scrollbar gutter.",
650
650
  "Scrolling content must pass beneath chrome laid over the pane edge (a DialogSurface floating footer) while its first and last items still rest clear of it (scrollClearance).",
651
- "The header should float over the scrolling content on a translucent material so content scrolls visibly beneath it, with the first item still resting below the header (headerPresentation: overlay, measured by wireScrollDividers)."
651
+ "The header should float over the scrolling content on a translucent material so content scrolls visibly beneath it, with the first item still resting below the header (headerPresentation: overlay, measured by wireScrollDividers).",
652
+ "A pane needs an app-chosen surface color, such as a media detail page painted in its artwork's color (background, a typed CssColor; also in PaneConfig).",
653
+ "A region should take on an app color (a media page in its artwork's color) with its text, quiet text, links, fills, and chrome material re-derived to keep AA (tint: { color, scheme }; also in PaneConfig).",
654
+ "The header lies over imagery and must be fully transparent, with glass control groups for legibility and the material fading back in once content scrolls beneath (headerPresentation: clear, scrollEdge: material)."
652
655
  ],
653
656
  "avoidWhen": [
654
657
  "Do not use Pane as a padded card or add a competing scroll owner around its content slot.",
@@ -674,7 +677,10 @@
674
677
  "whether to reserve a vertical scrollbar gutter for opaque edge-to-edge content (--kui-pane-scrollbar-gutter)",
675
678
  "optional keyboard or programmatic focus (tabIndex) and persistent focus outline (outlined)",
676
679
  "whether the scrolling content clears overlaying chrome on its block edges (scrollClearance, also in PaneConfig, so a Workbench mainPane or panel pane, a CollapsiblePanel pane, or a NavStack view pane forwards it)",
677
- "whether the header is docked in its own row or laid over the scrolling content on the translucent chrome material (headerPresentation, also in PaneConfig)"
680
+ "whether the header is docked in its own row or laid over the scrolling content on the translucent chrome material (headerPresentation, also in PaneConfig)",
681
+ "an optional app-chosen surface color that replaces the appearance's fill (background, a typed CssColor from @kerfjs/ui/css-values, also in PaneConfig, so a Workbench panel or mainPane, a CollapsiblePanel pane, or a NavStack view pane forwards it)",
682
+ "an optional scoped tint (tint: { color, scheme }, also in PaneConfig): the color and the declared light or dark scheme it belongs to; Kerf does no color detection",
683
+ "an optional tint accent (tint.accent) that re-derives the brand ramp, links, and focus ring inside the tinted scope at AA"
678
684
  ],
679
685
  "variants": [
680
686
  "content only",
@@ -687,12 +693,18 @@
687
693
  "chrome dividers only while scrolled (default, wired by wireScrollDividers)",
688
694
  "always or no chrome dividers",
689
695
  "scrollClearance block-start, block-end, or both on the scrolling content",
690
- "overlay header on the translucent chrome material (headerPresentation: overlay)"
696
+ "overlay header on the translucent chrome material (headerPresentation: overlay)",
697
+ "app-chosen background color (background), replacing the default or sunken fill",
698
+ "scoped tint re-deriving surface and text tokens from an app color (tint)",
699
+ "clear (transparent) overlay header, optionally with the scroll-edge material (headerPresentation: clear, scrollEdge: material)"
691
700
  ],
692
701
  "accessibility": [
693
702
  "Choose semantic root and content elements that match the region and provide labels for landmark elements.",
694
703
  "Keep fixed header/footer chrome outside the single scrolling content slot so reading and focus order remain coherent.",
695
- "An overlay header's text uses the normal text color, which keeps WCAG AA over the 80% chrome material on any content; the material turns opaque under prefers-reduced-transparency, prefers-contrast: more, forced colors, or without backdrop-filter."
704
+ "An overlay header's text uses the normal text color, which keeps WCAG AA over the 80% chrome material on any content; the material turns opaque under prefers-reduced-transparency, prefers-contrast: more, forced colors, or without backdrop-filter.",
705
+ "A background color is app-chosen: Keep text at WCAG AA over the color: a dark background needs dark-tone content or a scoped tint; forced colors paint Canvas instead.",
706
+ "A tint clamps the re-derived surface and text lightness so normal, quiet, link, and neutral-on-quiet text keep 4.5:1 over the tinted surface, raised surface, and quiet fills for any color; under prefers-contrast: more or without relative color syntax the scope keeps the plain palette in its declared scheme.",
707
+ "A clear header has no material of its own: keep its controls in glass ToolbarControlGroups and a title over imagery on a scrim; it turns opaque under reduced transparency or increased contrast and paints Canvas in forced colors."
696
708
  ],
697
709
  "geometry": {
698
710
  "margin": "child",
@@ -705,7 +717,8 @@
705
717
  "deepInset adds --kui-space-xs to every side of the scrolling content and its block scroll padding; header/footer chrome still owns safe-area padding at its edge.",
706
718
  "For opaque edge-to-edge children, --kui-pane-scrollbar-gutter: stable reserves a vertical scrollbar gutter outside the published content insets; the default is auto.",
707
719
  "scrollClearance adds the inherited --kui-scroll-clearance-block-start/end context, published by overlaying chrome, to the scrolling content block padding and scroll padding alongside safe areas and deepInset.",
708
- "headerPresentation overlay: once wireScrollDividers measures the header, the header shares the content cell above the scroller, its border-box height is published as --kui-scroll-clearance-block-start on the Pane root, and the content clears it automatically; unwired or with chromePlacement auto, the header stays docked."
720
+ "headerPresentation overlay: once wireScrollDividers measures the header, the header shares the content cell above the scroller, its border-box height is published as --kui-scroll-clearance-block-start on the Pane root, and the content clears it automatically; unwired or with chromePlacement auto, the header stays docked.",
721
+ "The scrolling content also publishes its resolved block-start padding (safe area, deepInset, and overlay or clear header clearance) as --kui-pane-content-inset-block-start, so a first child such as an underChrome BackdropImage can run up beneath it."
709
722
  ]
710
723
  },
711
724
  "delivery": {
@@ -825,7 +838,9 @@
825
838
  "top-toolbar defaults, title size, and heading level",
826
839
  "whether the chrome and bottom-toolbar dividers follow the scroll, always show, or never show (chromeDividers)",
827
840
  "whether the top chrome docks above the active view or lies over it on the translucent chrome material (chromePresentation)",
828
- "whether the bottom toolbar docks below the active view or lies over it on the translucent chrome material (bottomToolbarPresentation)"
841
+ "whether the bottom toolbar docks below the active view or lies over it on the translucent chrome material (bottomToolbarPresentation)",
842
+ "an optional app-chosen color for the stack, its docked chrome and bottom toolbar, and every view (background, a typed CssColor); one view's own color goes in its pane: { background }",
843
+ "an optional scoped tint for the stack, its chrome, and views: tint: { color, scheme } re-derives surface and text tokens from an app color with text kept at AA"
829
844
  ],
830
845
  "variants": [
831
846
  "single root view",
@@ -839,7 +854,10 @@
839
854
  "chrome and bottom-toolbar dividers only while the active view scrolls beneath them (wired by wireScrollDividers)",
840
855
  "permanent or no chrome dividers (chromeDividers always or none)",
841
856
  "translucent top chrome laid over the active view (chromePresentation overlay, measured by wireNavStack)",
842
- "translucent bottom toolbar laid over the active view (bottomToolbarPresentation overlay, measured by wireNavStack)"
857
+ "translucent bottom toolbar laid over the active view (bottomToolbarPresentation overlay, measured by wireNavStack)",
858
+ "app-chosen stack background color (background); overlay chrome keeps the chrome material",
859
+ "scoped tint (tint)",
860
+ "clear (transparent) bars with an optional scroll-edge material (chromePresentation / bottomToolbarPresentation: clear, scrollEdge: material)"
843
861
  ],
844
862
  "accessibility": [
845
863
  "Give the stack region an accessible label and every back control a stable action label; visible backText names the control itself.",
@@ -847,7 +865,9 @@
847
865
  "Use wireNavStack so each controlled push moves focus into the new top view and each pop restores the revealed view's remembered descendant.",
848
866
  "Mark a preferred initial heading or control with data-nav-focus when the first enabled focusable descendant is not the right target.",
849
867
  "Overlay chrome (chromePresentation overlay) keeps WCAG AA over content passing beneath the translucent material: an untoned ToolbarText there takes the normal text color for its title and lifts its description halfway toward it; keep other quiet copy in the view. The material turns opaque under reduced transparency, increased contrast, forced colors, or without backdrop-filter.",
850
- "An overlay bottom toolbar (bottomToolbarPresentation overlay) keeps the same AA rule: an untoned ToolbarText there lifts its title and description, and a default-tone ToolbarControlGroup in either overlaid bar takes the normal text color for its controls. Its 1px top edge stays visible in forced colors."
868
+ "An overlay bottom toolbar (bottomToolbarPresentation overlay) keeps the same AA rule: an untoned ToolbarText there lifts its title and description, and a default-tone ToolbarControlGroup in either overlaid bar takes the normal text color for its controls. Its 1px top edge stays visible in forced colors.",
869
+ "A background color is app-chosen: Keep text at WCAG AA over the color: a dark background needs dark-tone content or a scoped tint; forced colors paint Canvas instead.",
870
+ "Clear bars have no material of their own: keep their controls in glass ToolbarControlGroups; they turn opaque under reduced transparency or increased contrast and paint Canvas in forced colors."
851
871
  ],
852
872
  "geometry": {
853
873
  "margin": "none",
@@ -1142,7 +1162,9 @@
1142
1162
  "responsive replacement on larger device classes",
1143
1163
  "whether the bar divider follows the scroll, always shows, or never shows (chromeDividers)",
1144
1164
  "whether the bar's top separator follows a sunken active surface or stays hidden (separator)",
1145
- "optional active-scene focus (tabIndex) and persistent outline (outlined)"
1165
+ "optional active-scene focus (tabIndex) and persistent outline (outlined)",
1166
+ "an optional app-chosen color for the navigator and its docked bar (background) and per scene (tab.background), each a typed CssColor that replaces the default or sunken fill",
1167
+ "an optional scoped tint for the navigator (tint) or one scene (tab.tint): tint: { color, scheme } re-derives surface and text tokens from an app color with text kept at AA"
1146
1168
  ],
1147
1169
  "variants": [
1148
1170
  "text-only tabs",
@@ -1152,14 +1174,19 @@
1152
1174
  "bar divider only while the active scene continues below it (wired by wireScrollDividers)",
1153
1175
  "permanent or no bar divider (chromeDividers always or none)",
1154
1176
  "sunken active-scene separator (separator auto or hidden)",
1155
- "translucent tab bar laid over the active scene (barPresentation overlay, measured by wireTabNavigator)"
1177
+ "translucent tab bar laid over the active scene (barPresentation overlay, measured by wireTabNavigator)",
1178
+ "app-chosen navigator or per-scene background color (background, tab.background); the overlay bar keeps the chrome material",
1179
+ "scoped tint (tint)",
1180
+ "clear (transparent) bars with an optional scroll-edge material (barPresentation: clear, scrollEdge: material)"
1156
1181
  ],
1157
1182
  "accessibility": [
1158
1183
  "Give the tablist an accessible label and keep exactly the active tab in the sequential focus order.",
1159
1184
  "Wire selection through the controlled active id while leaving inactive scenes mounted and hidden.",
1160
1185
  "A focusable scene is a tabpanel named by its tab label; optional scene tabIndex applies only while active.",
1161
1186
  "A tab badge is aria-hidden; supply badgeLabel so the tab's accessible name becomes \"<label>, <badgeLabel>\" (it defaults to the badge text). The dot form, badge: true, has no text, so its badgeLabel is required.",
1162
- "Over the overlay bar's translucent material, unselected labels lift halfway toward --kui-color-text and the selected label mixes 40% of it into the brand color, so both keep 4.5:1 over worst-case content; the material turns opaque under reduced transparency or increased contrast and paints Canvas with a CanvasText edge in forced colors."
1187
+ "Over the overlay bar's translucent material, unselected labels lift halfway toward --kui-color-text and the selected label mixes 40% of it into the brand color, so both keep 4.5:1 over worst-case content; the material turns opaque under reduced transparency or increased contrast and paints Canvas with a CanvasText edge in forced colors.",
1188
+ "A background color is app-chosen: Keep text at WCAG AA over the color: a dark background needs dark-tone content or a scoped tint; forced colors paint Canvas instead.",
1189
+ "Clear bars have no material of their own: keep their controls in glass ToolbarControlGroups; they turn opaque under reduced transparency or increased contrast and paint Canvas in forced colors."
1163
1190
  ],
1164
1191
  "geometry": {
1165
1192
  "margin": "none",
@@ -1632,6 +1659,86 @@
1632
1659
  "recipe": "docs/sunken-panel.md#ownership"
1633
1660
  }
1634
1661
  },
1662
+ {
1663
+ "id": "backdrop-image",
1664
+ "name": "BackdropImage",
1665
+ "category": "Structure",
1666
+ "kind": "component",
1667
+ "source": "kerf",
1668
+ "description": "An image block at the top of a scrolling view that can lag the scroll, fade into an app color, and keep painting that color under the content below.",
1669
+ "uses": [],
1670
+ "purpose": "Open a scrolling Pane or NavStack view with a hero or tiled backdrop image (an artist, album, or place page) that lags the scroll and progressively blurs on a compositor-only scroll timeline, dissolves through a mask into an app-chosen color, and continues that color under the following content, with Kerf-owned static fallbacks.",
1671
+ "publicExports": [
1672
+ "BackdropImage"
1673
+ ],
1674
+ "useWhen": [
1675
+ "A scrolling view opens with a large mood or identity image (an artist, album, place, or collection header) whose page should read as one surface with it.",
1676
+ "The image should lag the scroll (parallax), blur progressively as it scrolls away, or dissolve into a color without app scroll listeners, animated filters, or background-attachment: fixed."
1677
+ ],
1678
+ "avoidWhen": [
1679
+ "Do not use for inline content images, thumbnails, avatars, or media a person inspects; render an img or the matching media component.",
1680
+ "Do not hand-roll scroll listeners, background-attachment: fixed, animated blur filters, or mask gradients around an image in app CSS.",
1681
+ "Do not put it in a region that does not scroll vertically; parallax needs a scroll container ancestor."
1682
+ ],
1683
+ "alternatives": [
1684
+ {
1685
+ "id": "pane",
1686
+ "relationship": "composes-with",
1687
+ "when": "Place BackdropImage first in a Pane's scrolling content (or a NavStack view) so its scroller drives the parallax."
1688
+ },
1689
+ {
1690
+ "id": "sunken-panel",
1691
+ "relationship": "nearest",
1692
+ "when": "Use SunkenPanel for a lowered work surface without imagery."
1693
+ }
1694
+ ],
1695
+ "appOwns": [
1696
+ "the image, its alt text (empty for a decorative image), and responsive candidates",
1697
+ "the fade color, whether the image lags the scroll, and whether it blurs (and any pre-blurred asset)",
1698
+ "overlay content and its contrast against the image and scrim",
1699
+ "the content below the image",
1700
+ "whether the image runs up beneath overlay or clear chrome and the top safe area (underChrome); it must then be the first child of the Pane's content"
1701
+ ],
1702
+ "variants": [
1703
+ "fitted image (cover, contain, fill) with a focal position",
1704
+ "tiled background (repeat, repeat-x, repeat-y)",
1705
+ "parallax none, subtle, or a 0 to 1 lag factor",
1706
+ "progressive blur from a once-filtered copy or an app-supplied blurred asset, with an optional dim",
1707
+ "fade into a color that also paints below",
1708
+ "bottom or full scrim under overlay content",
1709
+ "image running beneath clear or overlay chrome (underChrome)"
1710
+ ],
1711
+ "accessibility": [
1712
+ "alt describes a meaningful image; pass an empty alt for a decorative one. A tiled image with alt text is exposed as role=\"img\". Parallax and the progressive blur run only with prefers-reduced-motion: no-preference and scroll-driven animation support, and are off under data-kui-effects=\"static\" and on slow-update displays, leaving a static, sharp image; the blurred copy is decorative and aria-hidden. Reduced transparency or increased contrast replaces the fade with a hard edge on solid fadeTo and puts overlay content on a solid fadeTo band. Overlay text needs a scrim or fade color that keeps it at AA."
1713
+ ],
1714
+ "geometry": {
1715
+ "margin": "none",
1716
+ "border": "none",
1717
+ "padding": "none",
1718
+ "notes": [
1719
+ "The root bleeds to the Pane's resolved inline insets (the safe area) and hands them back to its overlay and content, so their items keep the ordinary text axis.",
1720
+ "The image block is height tall (default 320px) and grows with taller overlay content."
1721
+ ]
1722
+ },
1723
+ "delivery": {
1724
+ "browserImport": "@kerfjs/ui/backdrop-image",
1725
+ "manualCssImport": "@kerfjs/ui/backdrop-image.css",
1726
+ "sideEffects": [
1727
+ "browser-condition-css"
1728
+ ]
1729
+ },
1730
+ "wiring": [],
1731
+ "publicClasses": [
1732
+ "kui-backdrop-image"
1733
+ ],
1734
+ "publicTokens": [],
1735
+ "rootElement": "div",
1736
+ "links": {
1737
+ "catalogRoute": "?component=backdrop-image",
1738
+ "documentation": "docs/backdrop-image.md",
1739
+ "recipe": "docs/backdrop-image.md#fallbacks"
1740
+ }
1741
+ },
1635
1742
  {
1636
1743
  "id": "toolbar",
1637
1744
  "name": "Toolbar",
@@ -1801,6 +1908,7 @@
1801
1908
  "For an avatar control, set content=avatar and avatarImage instead of inserting an img; one button paints the group and multiple buttons paint only the pressed highlight.",
1802
1909
  "In a Workbench or CollapsiblePanel toolbar, mark a group relocateOnCollapse to keep it available in the work-area toolbar when the panel closes; place it directly or in an array in any toolbar zone.",
1803
1910
  "Use tileTone for a contained single group containing only a direct decorative SVG icon; interactive or mixed groups retain their existing control chrome.",
1911
+ "Set material=glass on a group floating over imagery or a clear (transparent) toolbar: it paints the translucent, blurred chrome material with a specular edge, controls keep AA in the normal text color, and the fill turns opaque under reduced transparency, increased contrast, or static effects. Keep the solid default on an opaque toolbar.",
1804
1912
  "Set expandedOverflow=visible on an expanded search to anchor application suggestions, date pickers, or help below the group without overriding its geometry.",
1805
1913
  "Use typed hideBelow/showBelow lengths with wireToolbarVisibility(root) for per-item toolbar priority; keep primary actions available and provide a showBelow overflow alternative."
1806
1914
  ],
@@ -2013,7 +2121,8 @@
2013
2121
  "publicExports": [
2014
2122
  "DialogSurface",
2015
2123
  "PopoverDialogSurface",
2016
- "PopupSurface"
2124
+ "PopupSurface",
2125
+ "wireOverlaySheets"
2017
2126
  ],
2018
2127
  "useWhen": [
2019
2128
  "A Web Awesome dialog or dropdown needs a standard size, presentation, body/footer inset, or list-compatible menu inset.",
@@ -2021,6 +2130,7 @@
2021
2130
  "A modal needs a typed preferred width, viewport gutter, or dynamic viewport-bounded height cap while retaining native body scrolling and footer actions.",
2022
2131
  "A dialog that must leave the page interactive and close on Escape or an outside press uses PopoverDialogSurface; one that fills its body with a SplitView or Pane uses height and bodyLayout=\"fill\".",
2023
2132
  "A chat-style dialog floats its composer over the transcript with footerPresentation=\"floating\" and keeps a heading or notice above one scrolling transcript with bodyLayout=\"fill-last\".",
2133
+ "A phone-first task (compose, quick edit, a picker) should rise from the bottom edge as a sheet the person can resize: presentation=\"bottom-sheet\" with height, and resizable with minHeight / maxHeight bounds.",
2024
2134
  "A dialog's title bar should float over the body on the translucent chrome material so a transcript scrolls visibly beneath both the title bar and a floating composer (headerPresentation=\"floating\" with bodyLayout=\"fill\" and a List or Pane that opts into scrollClearance=\"both\")."
2025
2135
  ],
2026
2136
  "avoidWhen": [
@@ -2034,12 +2144,14 @@
2034
2144
  "focus lifecycle",
2035
2145
  "dismissal policy",
2036
2146
  "content",
2037
- "outer placement"
2147
+ "outer placement",
2148
+ "whether core kerfjs/overlay dialogs presented as bottom sheets take this package's geometry and slide (import @kerfjs/ui/overlay-sheet.css; exitClass 'kui-overlay-sheet-exit' for the slide out; the kui-overlay-sheet-resizable class plus wireOverlaySheets for a resize grabber)"
2038
2149
  ],
2039
2150
  "variants": [
2040
2151
  "small, medium, or large dialog",
2041
2152
  "data-* rootAttributes (a data-key) so sibling dialogs morph by identity",
2042
- "modal, side-sheet, or fullscreen dialog",
2153
+ "modal, side-sheet, fullscreen, or bottom-sheet dialog",
2154
+ "bottom sheet attached to the bottom edge: square bottom corners, safe-area and virtual-keyboard clearance behind the panel, slide (or standard) enter and exit motion, and an optional resizable top grabber bounded by minHeight and maxHeight",
2043
2155
  "typed modal preferred width, viewport gutter, and maximum height, including viewport cap",
2044
2156
  "definite viewport-bounded modal height and a fill body layout for one child",
2045
2157
  "fill-last body layout: fixed rows above one filling, scrolling last child",
@@ -2050,12 +2162,15 @@
2050
2162
  "floating header divider as scroll state: a hairline under the title bar only while the body or a filling child beneath it is scrolled away from its top",
2051
2163
  "non-modal light-dismiss PopoverDialogSurface, overlay or embedded in flow, named by label or labelledBy and described by describedBy",
2052
2164
  "none, compact, or comfortable body and footer inset",
2053
- "standard, compact, or zero-inset popup menu"
2165
+ "standard, compact, or zero-inset popup menu",
2166
+ "core kerfjs/overlay confirm/prompt/form/choice as a bottom sheet styled by @kerfjs/ui/overlay-sheet.css",
2167
+ "resizable core kerfjs/overlay bottom sheet: the kui-overlay-sheet-resizable class plus wireOverlaySheets adds the resizable DialogSurface sheet grabber"
2054
2168
  ],
2055
2169
  "accessibility": [
2056
2170
  "Supply the wrapped wa-dialog label and retain Web Awesome's native modal and focus behavior.",
2057
2171
  "Name a PopoverDialogSurface with label or labelledBy, and describe it with describedBy (rendered as aria-describedby), usually a title ToolbarText descriptionId.",
2058
2172
  "A floating header's title uses the normal text color, which keeps WCAG AA over the 80% raised chrome material on any content; the material turns opaque under prefers-reduced-transparency, prefers-contrast: more, forced colors, or without backdrop-filter.",
2173
+ "A resizable bottom sheet's grabber is a focusable horizontal separator (\"Resize sheet\") with aria-valuemin, aria-valuenow, and aria-valuemax in visible px; the arrow keys, Page Up/Down, Home, and End resize it, and its slide motion falls back to Web Awesome's under prefers-reduced-motion.",
2059
2174
  "Use popup surfaces only for real dropdown menus with named triggers."
2060
2175
  ],
2061
2176
  "geometry": {
@@ -2072,20 +2187,29 @@
2072
2187
  "custom-element-registration"
2073
2188
  ]
2074
2189
  },
2075
- "wiring": [],
2190
+ "wiring": [
2191
+ {
2192
+ "export": "wireOverlaySheets",
2193
+ "import": "@kerfjs/ui/wire-overlay-sheets",
2194
+ "required": false,
2195
+ "stateAttributes": []
2196
+ }
2197
+ ],
2076
2198
  "publicClasses": [
2077
2199
  "kui-dialog-surface",
2078
2200
  "kui-popover-dialog-surface",
2079
2201
  "kui-popup-surface"
2080
2202
  ],
2081
2203
  "publicTokens": [
2082
- "--kui-radius-l",
2204
+ "--kui-layout-pill-radius",
2083
2205
  "--kui-wa-container-inset",
2084
2206
  "--kui-wa-surface-inset",
2085
2207
  "--kui-chrome-material-raised",
2086
2208
  "--kui-chrome-material-filter",
2087
2209
  "--kui-scroll-clearance-block-start",
2088
- "--kui-scroll-clearance-block-end"
2210
+ "--kui-scroll-clearance-block-end",
2211
+ "--kui-safe-area-block-start",
2212
+ "--kui-safe-area-block-end"
2089
2213
  ],
2090
2214
  "links": {
2091
2215
  "catalogRoute": "?component=surface-scaffold",
@@ -2165,6 +2289,30 @@
2165
2289
  "footerOffset={remify(8)}"
2166
2290
  ]
2167
2291
  },
2292
+ {
2293
+ "path": "minHeight",
2294
+ "grammar": "length",
2295
+ "helpers": [
2296
+ "px",
2297
+ "rem",
2298
+ "remify",
2299
+ "em",
2300
+ "pct",
2301
+ "space",
2302
+ "lengthVar",
2303
+ "calc"
2304
+ ],
2305
+ "nonStandaloneHelpers": [
2306
+ "plus"
2307
+ ],
2308
+ "shorthands": [],
2309
+ "canonicalShorthands": [],
2310
+ "exceptionalShorthands": [],
2311
+ "rawPolicy": "forbid",
2312
+ "examples": [
2313
+ "minHeight={remify(200)}"
2314
+ ]
2315
+ },
2168
2316
  {
2169
2317
  "path": "maxHeight",
2170
2318
  "grammar": "length",
@@ -10231,6 +10379,54 @@
10231
10379
  "documentation": "docs/recipes.md#collapsible-sidebar",
10232
10380
  "recipe": "docs/recipes.md#collapsible-sidebar"
10233
10381
  }
10382
+ },
10383
+ {
10384
+ "id": "recipe-media-detail",
10385
+ "name": "Media Detail",
10386
+ "category": "Recipes",
10387
+ "kind": "recipe",
10388
+ "source": "kerf",
10389
+ "purpose": "An Apple-Music-style album page: clear NavStack chrome with the scroll-edge material, a scoped tint from the artwork's color, an underChrome BackdropImage with parallax and progressive blur, and glass control groups.",
10390
+ "uses": [
10391
+ "nav-stack",
10392
+ "pane",
10393
+ "backdrop-image",
10394
+ "toolbar",
10395
+ "toolbar-text",
10396
+ "toolbar-control-group",
10397
+ "list",
10398
+ "list-header",
10399
+ "list-item",
10400
+ "text",
10401
+ "lucide-icon",
10402
+ "layout"
10403
+ ],
10404
+ "useWhen": [
10405
+ "A detail page for media (an album, artist, film, or place) should take on its artwork's color with the artwork running beneath transparent chrome."
10406
+ ],
10407
+ "avoidWhen": [
10408
+ "Use the Navigation Stack recipe for a plain drill-down; imagery, tint, and clear chrome are for pages whose artwork is the identity."
10409
+ ],
10410
+ "appOwns": [
10411
+ "the artwork and its accessible description",
10412
+ "the artwork's bottom color and the scheme it belongs to (the tint)",
10413
+ "the stack (a signal of views)",
10414
+ "playback",
10415
+ "data"
10416
+ ],
10417
+ "accessibility": [
10418
+ "Glass control groups keep every chrome control at AA over the artwork; the tint clamps text to AA over the tinted surface; parallax, blur, and the scroll-edge fade go static under reduced motion or the static-effects switch, and the chrome and fade go opaque under reduced transparency and increased contrast."
10419
+ ],
10420
+ "delivery": {
10421
+ "sideEffects": [
10422
+ "lazy-recipe-css"
10423
+ ]
10424
+ },
10425
+ "links": {
10426
+ "catalogRoute": "?component=recipe-media-detail",
10427
+ "documentation": "docs/recipes.md#media-detail",
10428
+ "recipe": "docs/recipes.md#media-detail"
10429
+ }
10234
10430
  }
10235
10431
  ]
10236
10432
  }