@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
@@ -646,6 +646,21 @@
646
646
  "id": "whether the header is docked in its own row or laid over the scrolling content on the translucent chrome material (headerPresentation, also in PaneConfig)",
647
647
  "owner": "application",
648
648
  "required": false
649
+ },
650
+ {
651
+ "id": "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)",
652
+ "owner": "application",
653
+ "required": false
654
+ },
655
+ {
656
+ "id": "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",
657
+ "owner": "application",
658
+ "required": false
659
+ },
660
+ {
661
+ "id": "an optional tint accent (tint.accent) that re-derives the brand ramp, links, and focus ring inside the tinted scope at AA",
662
+ "owner": "application",
663
+ "required": false
649
664
  }
650
665
  ],
651
666
  "wiring": {
@@ -687,7 +702,8 @@
687
702
  "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.",
688
703
  "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.",
689
704
  "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.",
690
- "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."
705
+ "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.",
706
+ "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."
691
707
  ]
692
708
  }
693
709
  },
@@ -695,7 +711,10 @@
695
711
  "obligations": [
696
712
  "Choose semantic root and content elements that match the region and provide labels for landmark elements.",
697
713
  "Keep fixed header/footer chrome outside the single scrolling content slot so reading and focus order remain coherent.",
698
- "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."
714
+ "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.",
715
+ "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.",
716
+ "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.",
717
+ "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."
699
718
  ]
700
719
  },
701
720
  "cssValueProps": [],
@@ -797,6 +816,16 @@
797
816
  "id": "whether the bottom toolbar docks below the active view or lies over it on the translucent chrome material (bottomToolbarPresentation)",
798
817
  "owner": "application",
799
818
  "required": false
819
+ },
820
+ {
821
+ "id": "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 }",
822
+ "owner": "application",
823
+ "required": false
824
+ },
825
+ {
826
+ "id": "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",
827
+ "owner": "application",
828
+ "required": false
800
829
  }
801
830
  ],
802
831
  "wiring": {
@@ -882,7 +911,9 @@
882
911
  "Use wireNavStack so each controlled push moves focus into the new top view and each pop restores the revealed view's remembered descendant.",
883
912
  "Mark a preferred initial heading or control with data-nav-focus when the first enabled focusable descendant is not the right target.",
884
913
  "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.",
885
- "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."
914
+ "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.",
915
+ "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.",
916
+ "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."
886
917
  ]
887
918
  },
888
919
  "cssValueProps": [],
@@ -1109,6 +1140,16 @@
1109
1140
  "id": "optional active-scene focus (tabIndex) and persistent outline (outlined)",
1110
1141
  "owner": "application",
1111
1142
  "required": false
1143
+ },
1144
+ {
1145
+ "id": "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",
1146
+ "owner": "application",
1147
+ "required": false
1148
+ },
1149
+ {
1150
+ "id": "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",
1151
+ "owner": "application",
1152
+ "required": false
1112
1153
  }
1113
1154
  ],
1114
1155
  "wiring": {
@@ -1156,7 +1197,9 @@
1156
1197
  "Wire selection through the controlled active id while leaving inactive scenes mounted and hidden.",
1157
1198
  "A focusable scene is a tabpanel named by its tab label; optional scene tabIndex applies only while active.",
1158
1199
  "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.",
1159
- "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."
1200
+ "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.",
1201
+ "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.",
1202
+ "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."
1160
1203
  ]
1161
1204
  },
1162
1205
  "cssValueProps": [],
@@ -1552,6 +1595,81 @@
1552
1595
  "composition": "generated-permissive-default"
1553
1596
  }
1554
1597
  },
1598
+ {
1599
+ "key": "@kerfjs/ui:backdrop-image",
1600
+ "package": "@kerfjs/ui",
1601
+ "id": "backdrop-image",
1602
+ "name": "BackdropImage",
1603
+ "kind": "component",
1604
+ "source": "kerf",
1605
+ "parents": { "mode": "any", "entries": [] },
1606
+ "contexts": ["application-ui"],
1607
+ "zones": [],
1608
+ "children": { "mode": "any", "concepts": [], "requiredConcepts": [] },
1609
+ "state": [
1610
+ {
1611
+ "id": "the image, its alt text (empty for a decorative image), and responsive candidates",
1612
+ "owner": "application",
1613
+ "required": false
1614
+ },
1615
+ {
1616
+ "id": "the fade color, whether the image lags the scroll, and whether it blurs (and any pre-blurred asset)",
1617
+ "owner": "application",
1618
+ "required": false
1619
+ },
1620
+ {
1621
+ "id": "overlay content and its contrast against the image and scrim",
1622
+ "owner": "application",
1623
+ "required": false
1624
+ },
1625
+ {
1626
+ "id": "the content below the image",
1627
+ "owner": "application",
1628
+ "required": false
1629
+ },
1630
+ {
1631
+ "id": "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",
1632
+ "owner": "application",
1633
+ "required": false
1634
+ }
1635
+ ],
1636
+ "wiring": {
1637
+ "required": false,
1638
+ "helpers": [],
1639
+ "obligations": [],
1640
+ "stateAttributes": []
1641
+ },
1642
+ "responsive": { "owner": "not-applicable", "behaviors": [] },
1643
+ "layout": {
1644
+ "roles": ["structure"],
1645
+ "geometry": {
1646
+ "margin": "none",
1647
+ "border": "none",
1648
+ "padding": "none",
1649
+ "notes": [
1650
+ "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.",
1651
+ "The image block is height tall (default 320px) and grows with taller overlay content."
1652
+ ]
1653
+ }
1654
+ },
1655
+ "accessibility": {
1656
+ "obligations": [
1657
+ "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."
1658
+ ]
1659
+ },
1660
+ "cssValueProps": [],
1661
+ "boundaries": {
1662
+ "rootClass": "kui-backdrop-image",
1663
+ "publicClasses": ["kui-backdrop-image"],
1664
+ "rootElement": "div",
1665
+ "publicTokens": []
1666
+ },
1667
+ "diagnostics": [],
1668
+ "provenance": {
1669
+ "selection": "ai/component-catalog.json",
1670
+ "composition": "generated-permissive-default"
1671
+ }
1672
+ },
1555
1673
  {
1556
1674
  "key": "@kerfjs/ui:toolbar",
1557
1675
  "package": "@kerfjs/ui",
@@ -1891,11 +2009,16 @@
1891
2009
  { "id": "focus lifecycle", "owner": "application", "required": false },
1892
2010
  { "id": "dismissal policy", "owner": "application", "required": false },
1893
2011
  { "id": "content", "owner": "application", "required": false },
1894
- { "id": "outer placement", "owner": "application", "required": false }
2012
+ { "id": "outer placement", "owner": "application", "required": false },
2013
+ {
2014
+ "id": "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)",
2015
+ "owner": "application",
2016
+ "required": false
2017
+ }
1895
2018
  ],
1896
2019
  "wiring": {
1897
2020
  "required": false,
1898
- "helpers": [],
2021
+ "helpers": ["wireOverlaySheets"],
1899
2022
  "obligations": [],
1900
2023
  "stateAttributes": []
1901
2024
  },
@@ -1909,6 +2032,7 @@
1909
2032
  "Supply the wrapped wa-dialog label and retain Web Awesome's native modal and focus behavior.",
1910
2033
  "Name a PopoverDialogSurface with label or labelledBy, and describe it with describedBy (rendered as aria-describedby), usually a title ToolbarText descriptionId.",
1911
2034
  "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.",
2035
+ "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.",
1912
2036
  "Use popup surfaces only for real dropdown menus with named triggers."
1913
2037
  ]
1914
2038
  },
@@ -1973,6 +2097,26 @@
1973
2097
  "rawPolicy": "forbid",
1974
2098
  "examples": ["footerOffset={remify(8)}"]
1975
2099
  },
2100
+ {
2101
+ "path": "minHeight",
2102
+ "grammar": "length",
2103
+ "helpers": [
2104
+ "px",
2105
+ "rem",
2106
+ "remify",
2107
+ "em",
2108
+ "pct",
2109
+ "space",
2110
+ "lengthVar",
2111
+ "calc"
2112
+ ],
2113
+ "nonStandaloneHelpers": ["plus"],
2114
+ "shorthands": [],
2115
+ "canonicalShorthands": [],
2116
+ "exceptionalShorthands": [],
2117
+ "rawPolicy": "forbid",
2118
+ "examples": ["minHeight={remify(200)}"]
2119
+ },
1976
2120
  {
1977
2121
  "path": "maxHeight",
1978
2122
  "grammar": "length",
@@ -2002,13 +2146,15 @@
2002
2146
  "kui-popup-surface"
2003
2147
  ],
2004
2148
  "publicTokens": [
2005
- "--kui-radius-l",
2149
+ "--kui-layout-pill-radius",
2006
2150
  "--kui-wa-container-inset",
2007
2151
  "--kui-wa-surface-inset",
2008
2152
  "--kui-chrome-material-raised",
2009
2153
  "--kui-chrome-material-filter",
2010
2154
  "--kui-scroll-clearance-block-start",
2011
- "--kui-scroll-clearance-block-end"
2155
+ "--kui-scroll-clearance-block-end",
2156
+ "--kui-safe-area-block-start",
2157
+ "--kui-safe-area-block-end"
2012
2158
  ]
2013
2159
  },
2014
2160
  "diagnostics": [],
@@ -8724,6 +8870,68 @@
8724
8870
  "selection": "ai/component-catalog.json",
8725
8871
  "composition": "generated-permissive-default"
8726
8872
  }
8873
+ },
8874
+ {
8875
+ "key": "@kerfjs/ui:recipe-media-detail",
8876
+ "package": "@kerfjs/ui",
8877
+ "id": "recipe-media-detail",
8878
+ "name": "Media Detail",
8879
+ "kind": "recipe",
8880
+ "source": "kerf",
8881
+ "parents": { "mode": "root", "entries": [] },
8882
+ "contexts": ["application-reference"],
8883
+ "zones": [],
8884
+ "children": { "mode": "any", "concepts": [], "requiredConcepts": [] },
8885
+ "state": [
8886
+ {
8887
+ "id": "the artwork and its accessible description",
8888
+ "owner": "application",
8889
+ "required": false
8890
+ },
8891
+ {
8892
+ "id": "the artwork's bottom color and the scheme it belongs to (the tint)",
8893
+ "owner": "application",
8894
+ "required": false
8895
+ },
8896
+ {
8897
+ "id": "the stack (a signal of views)",
8898
+ "owner": "application",
8899
+ "required": false
8900
+ },
8901
+ { "id": "playback", "owner": "application", "required": false },
8902
+ { "id": "data", "owner": "application", "required": false }
8903
+ ],
8904
+ "wiring": {
8905
+ "required": false,
8906
+ "helpers": [],
8907
+ "obligations": [],
8908
+ "stateAttributes": []
8909
+ },
8910
+ "responsive": { "owner": "application", "behaviors": [] },
8911
+ "layout": {
8912
+ "roles": ["reference-composition"],
8913
+ "geometry": {
8914
+ "margin": "composed",
8915
+ "border": "composed",
8916
+ "padding": "composed"
8917
+ }
8918
+ },
8919
+ "accessibility": {
8920
+ "obligations": [
8921
+ "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."
8922
+ ]
8923
+ },
8924
+ "cssValueProps": [],
8925
+ "boundaries": {
8926
+ "rootClass": null,
8927
+ "publicClasses": [],
8928
+ "publicTokens": []
8929
+ },
8930
+ "diagnostics": [],
8931
+ "provenance": {
8932
+ "selection": "ai/component-catalog.json",
8933
+ "composition": "generated-permissive-default"
8934
+ }
8727
8935
  }
8728
8936
  ]
8729
8937
  }
@@ -25,6 +25,7 @@ Exact prop and callback types live in [`public-api-signatures-v1.md`](../public-
25
25
  - [Workbench](./workbench.md) — Arrange a complex tool or editor as a stable central work area with optional peripheral rails and a bottom drawer.
26
26
  - [CollapsiblePanel](./collapsible-panel.md) — Add one independently collapsible peripheral panel without adopting the complete multi-panel Workbench shell.
27
27
  - [SunkenPanel](./sunken-panel.md) — Group a vertical list of application content on a visually lowered surface with one owned 8px inset and an explicit rounded or square-corner shape.
28
+ - [BackdropImage](./backdrop-image.md) — 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.
28
29
  - [Toolbar](./toolbar.md) — Leading, centered, and trailing toolbar composition.
29
30
  - [ToolbarControlGroup](./toolbar-control-group.md) — Provide complete toolbar action geometry through configuration, including semantic anchors, overflow containment, and dropdown menu insets without application CSS.
30
31
  - [Surface Scaffolds](./surface-scaffold.md) — Configure recurring dialog and popup geometry without consumer ::part() overrides.
@@ -183,3 +184,4 @@ Exact prop and callback types live in [`public-api-signatures-v1.md`](../public-
183
184
  - [Navigation Stack](./recipe-navigation-stack.md) — A push/pop navigation stack drill-down with animated slides and a live device class.
184
185
  - [Loading Inspector](./recipe-loading-inspector.md) — A record inspector whose per-record values load asynchronously — every value-bearing component takes a shared placeholder flag, so the same chrome renders a faithful loading state and then the populated record.
185
186
  - [Collapsible Sidebar](./recipe-collapsible-sidebar.md) — A mini app frame whose left navigation rail and bottom activity drawer are standalone CollapsiblePanels driven by wireSidebar: per-side collapse/expand toggles, focus move-in and restore, a dismissable compact overlay, and per-panel persistence.
187
+ - [Media Detail](./recipe-media-detail.md) — 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.
@@ -0,0 +1,84 @@
1
+ # BackdropImage
2
+
3
+ <!-- Generated by scripts/sync-component-docs.mjs from ai/component-catalog.json and ai/component-composition.json. Do not edit; run `npm run catalog:sync`. -->
4
+
5
+ `@kerfjs/ui:backdrop-image` · Kerf UI · component · Structure
6
+
7
+ 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.
8
+
9
+ ## When to use
10
+
11
+ 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.
12
+
13
+ - 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.
14
+ - 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.
15
+
16
+ **Not when:**
17
+
18
+ - Do not use for inline content images, thumbnails, avatars, or media a person inspects; render an img or the matching media component.
19
+ - Do not hand-roll scroll listeners, background-attachment: fixed, animated blur filters, or mask gradients around an image in app CSS.
20
+ - Do not put it in a region that does not scroll vertically; parallax needs a scroll container ancestor.
21
+
22
+ **Alternatives:**
23
+
24
+ - [Pane](./pane.md) (composes-with) — Place BackdropImage first in a Pane's scrolling content (or a NavStack view) so its scroller drives the parallax.
25
+ - [SunkenPanel](./sunken-panel.md) (nearest) — Use SunkenPanel for a lowered work surface without imagery.
26
+
27
+ ## Imports
28
+
29
+ - JavaScript: `@kerfjs/ui/backdrop-image` — exports `BackdropImage`.
30
+ - CSS: the browser build of `@kerfjs/ui/backdrop-image` pulls its CSS automatically; import `@kerfjs/ui/backdrop-image.css` manually only without the `browser` export condition.
31
+ - Declared side effects: browser-condition-css.
32
+
33
+ ## Props
34
+
35
+ Exact prop names and types: [`@kerfjs/ui/backdrop-image`](../public-api-signatures-v1.md#kerfjsuibackdrop-image) in `public-api-signatures-v1.md`. Do not infer props from examples.
36
+
37
+ **Variants (configure, do not restyle):**
38
+
39
+ - fitted image (cover, contain, fill) with a focal position
40
+ - tiled background (repeat, repeat-x, repeat-y)
41
+ - parallax none, subtle, or a 0 to 1 lag factor
42
+ - progressive blur from a once-filtered copy or an app-supplied blurred asset, with an optional dim
43
+ - fade into a color that also paints below
44
+ - bottom or full scrim under overlay content
45
+ - image running beneath clear or overlay chrome (underChrome)
46
+
47
+ ## Composition
48
+
49
+ - Parents: any.
50
+ - Contexts: `application-ui`.
51
+ - Children: unrestricted.
52
+
53
+ ## State and wiring
54
+
55
+ **The app owns:** the image, its alt text (empty for a decorative image), and responsive candidates; the fade color, whether the image lags the scroll, and whether it blurs (and any pre-blurred asset); overlay content and its contrast against the image and scrim; the content below the image; 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.
56
+
57
+ No wiring helper.
58
+
59
+ ## Geometry
60
+
61
+ Margin: none · border: none · padding: none (layout role: structure). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
62
+
63
+ - 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.
64
+ - The image block is height tall (default 320px) and grows with taller overlay content.
65
+
66
+ ## Accessibility
67
+
68
+ - 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.
69
+
70
+ ## Styling boundary
71
+
72
+ The component owns its own styles. Configure it through its props and variants; do not override its internals.
73
+
74
+ Public class hooks (select for layout placement only, never to change the component's look): `kui-backdrop-image`.
75
+
76
+ Never put `kui-backdrop-image` on an element you write; render `BackdropImage` instead (`KUI-L103`).
77
+
78
+ Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
79
+
80
+ ## Related
81
+
82
+ - Components: [Pane](./pane.md), [SunkenPanel](./sunken-panel.md).
83
+ - Docs: [`docs/backdrop-image.md`](../../docs/backdrop-image.md), [`docs/backdrop-image.md#fallbacks`](../../docs/backdrop-image.md#fallbacks).
84
+ - UX catalog route: `?component=backdrop-image`.
@@ -50,6 +50,9 @@ Exact prop names and types: [`@kerfjs/ui/nav-stack`](../public-api-signatures-v1
50
50
  - permanent or no chrome dividers (chromeDividers always or none)
51
51
  - translucent top chrome laid over the active view (chromePresentation overlay, measured by wireNavStack)
52
52
  - translucent bottom toolbar laid over the active view (bottomToolbarPresentation overlay, measured by wireNavStack)
53
+ - app-chosen stack background color (background); overlay chrome keeps the chrome material
54
+ - scoped tint (tint)
55
+ - clear (transparent) bars with an optional scroll-edge material (chromePresentation / bottomToolbarPresentation: clear, scrollEdge: material)
53
56
 
54
57
  ## Composition
55
58
 
@@ -60,7 +63,7 @@ Exact prop names and types: [`@kerfjs/ui/nav-stack`](../public-api-signatures-v1
60
63
 
61
64
  ## State and wiring
62
65
 
63
- **The app owns:** ordered view stack and stable keys; view content and structured top and bottom toolbar configurations; push and pop actions; preferred initial focus targets when DOM order is insufficient; per-view bottom toolbar or a persistent structured fallback; per-view leading, center, and trailing top-toolbar groups; per-view header and footer with List and Pane configuration; top-toolbar defaults, title size, and heading level; whether the chrome and bottom-toolbar dividers follow the scroll, always show, or never show (chromeDividers); whether the top chrome docks above the active view or lies over it on the translucent chrome material (chromePresentation); whether the bottom toolbar docks below the active view or lies over it on the translucent chrome material (bottomToolbarPresentation).
66
+ **The app owns:** ordered view stack and stable keys; view content and structured top and bottom toolbar configurations; push and pop actions; preferred initial focus targets when DOM order is insufficient; per-view bottom toolbar or a persistent structured fallback; per-view leading, center, and trailing top-toolbar groups; per-view header and footer with List and Pane configuration; top-toolbar defaults, title size, and heading level; whether the chrome and bottom-toolbar dividers follow the scroll, always show, or never show (chromeDividers); whether the top chrome docks above the active view or lies over it on the translucent chrome material (chromePresentation); whether the bottom toolbar docks below the active view or lies over it on the translucent chrome material (bottomToolbarPresentation); 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 }; 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.
64
67
 
65
68
  **Wiring:** `wireNavStack`, `wireScrollDividers` is required.
66
69
 
@@ -95,6 +98,8 @@ Margin: none · border: conditional · padding: child (layout role: structure).
95
98
  - Mark a preferred initial heading or control with data-nav-focus when the first enabled focusable descendant is not the right target.
96
99
  - 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.
97
100
  - 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.
101
+ - 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.
102
+ - 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.
98
103
 
99
104
  ## Styling boundary
100
105
 
@@ -18,6 +18,9 @@ Organize a sidebar, main area, inspector, or dialog column into fixed vertical h
18
18
  - An opaque edge-to-edge child needs a reserved vertical scrollbar gutter.
19
19
  - 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).
20
20
  - 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).
21
+ - 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).
22
+ - 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).
23
+ - 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).
21
24
 
22
25
  **Not when:**
23
26
 
@@ -53,6 +56,9 @@ Exact prop names and types: [`@kerfjs/ui/pane`](../public-api-signatures-v1.md#k
53
56
  - always or no chrome dividers
54
57
  - scrollClearance block-start, block-end, or both on the scrolling content
55
58
  - overlay header on the translucent chrome material (headerPresentation: overlay)
59
+ - app-chosen background color (background), replacing the default or sunken fill
60
+ - scoped tint re-deriving surface and text tokens from an app color (tint)
61
+ - clear (transparent) overlay header, optionally with the scroll-edge material (headerPresentation: clear, scrollEdge: material)
56
62
 
57
63
  ## Composition
58
64
 
@@ -68,7 +74,7 @@ Zones (a zone is bound to JSX only through its listed prop; never assume the zon
68
74
 
69
75
  ## State and wiring
70
76
 
71
- **The app owns:** root and content semantics; accessible labels; header, content, and footer children; which logical boundaries show separators; which sides may compensate for device safe areas in an app-owned arrangement (safeAreaEdges); responsive placement and visibility; whether the chrome dividers follow the scroll, always show, or never show (chromeDividers); default or sunken scrolling work surface (appearance); whether to reserve a vertical scrollbar gutter for opaque edge-to-edge content (--kui-pane-scrollbar-gutter); optional keyboard or programmatic focus (tabIndex) and persistent focus outline (outlined); 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); whether the header is docked in its own row or laid over the scrolling content on the translucent chrome material (headerPresentation, also in PaneConfig).
77
+ **The app owns:** root and content semantics; accessible labels; header, content, and footer children; which logical boundaries show separators; which sides may compensate for device safe areas in an app-owned arrangement (safeAreaEdges); responsive placement and visibility; whether the chrome dividers follow the scroll, always show, or never show (chromeDividers); default or sunken scrolling work surface (appearance); whether to reserve a vertical scrollbar gutter for opaque edge-to-edge content (--kui-pane-scrollbar-gutter); optional keyboard or programmatic focus (tabIndex) and persistent focus outline (outlined); 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); whether the header is docked in its own row or laid over the scrolling content on the translucent chrome material (headerPresentation, also in PaneConfig); 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); 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; an optional tint accent (tint.accent) that re-derives the brand ramp, links, and focus ring inside the tinted scope at AA.
72
78
 
73
79
  **Wiring:** `wireScrollDividers` is optional.
74
80
 
@@ -89,12 +95,16 @@ Margin: child · border: conditional · padding: child (layout role: structure).
89
95
  - 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.
90
96
  - 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.
91
97
  - 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.
98
+ - 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.
92
99
 
93
100
  ## Accessibility
94
101
 
95
102
  - Choose semantic root and content elements that match the region and provide labels for landmark elements.
96
103
  - Keep fixed header/footer chrome outside the single scrolling content slot so reading and focus order remain coherent.
97
104
  - 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.
105
+ - 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.
106
+ - 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.
107
+ - 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.
98
108
 
99
109
  ## Styling boundary
100
110
 
@@ -0,0 +1,57 @@
1
+ # Media Detail
2
+
3
+ <!-- Generated by scripts/sync-component-docs.mjs from ai/component-catalog.json and ai/component-composition.json. Do not edit; run `npm run catalog:sync`. -->
4
+
5
+ `@kerfjs/ui:recipe-media-detail` · Kerf UI · production recipe · Recipes
6
+
7
+ ## When to use
8
+
9
+ 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.
10
+
11
+ - 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.
12
+
13
+ **Not when:**
14
+
15
+ - Use the Navigation Stack recipe for a plain drill-down; imagery, tint, and clear chrome are for pages whose artwork is the identity.
16
+
17
+ ## Imports
18
+
19
+ - Nothing to import: this is a reference composition.
20
+ - Declared side effects: lazy-recipe-css.
21
+
22
+ ## Props
23
+
24
+ No props: this entry has no JavaScript API.
25
+
26
+ ## Composition
27
+
28
+ - Parents: used at the root of a screen or reference, not nested.
29
+ - Contexts: `application-reference`.
30
+ - Children: unrestricted.
31
+ - Built from: [NavStack](./nav-stack.md), [Pane](./pane.md), [BackdropImage](./backdrop-image.md), [Toolbar](./toolbar.md), [ToolbarText](./toolbar-text.md), [ToolbarControlGroup](./toolbar-control-group.md), [List](./list.md), [ListHeader](./list-header.md), [ListItem](./list-item.md), [Text](./text.md), [LucideIcon](./lucide-icon.md), [Application Layout](./layout.md).
32
+
33
+ ## State and wiring
34
+
35
+ **The app owns:** the artwork and its accessible description; the artwork's bottom color and the scheme it belongs to (the tint); the stack (a signal of views); playback; data.
36
+
37
+ No wiring helper.
38
+
39
+ ## Geometry
40
+
41
+ Margin: composed · border: composed · padding: composed (layout role: reference-composition). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
42
+
43
+ ## Accessibility
44
+
45
+ - 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.
46
+
47
+ ## Styling boundary
48
+
49
+ The component owns its own styles. Configure it through its props and variants; do not override its internals.
50
+
51
+ Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-*` selectors (`KUI-L001`, `KUI-L010`), unknown or private tokens (`KUI-L002`, `KUI-L012`), and any application rule that restyles the component, forced dimensions included (`KUI-L019`).
52
+
53
+ ## Related
54
+
55
+ - Components: [NavStack](./nav-stack.md), [Pane](./pane.md), [BackdropImage](./backdrop-image.md), [Toolbar](./toolbar.md), [ToolbarText](./toolbar-text.md), [ToolbarControlGroup](./toolbar-control-group.md), [List](./list.md), [ListHeader](./list-header.md), [ListItem](./list-item.md), [Text](./text.md), [LucideIcon](./lucide-icon.md), [Application Layout](./layout.md).
56
+ - Docs: [`docs/recipes.md#media-detail`](../../docs/recipes.md#media-detail).
57
+ - UX catalog route: `?component=recipe-media-detail`.