@kerfjs/ui 5.0.0-beta.110 → 5.0.0-beta.111

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 (158) hide show
  1. package/README.md +1 -0
  2. package/ai/component-catalog.json +9 -2
  3. package/ai/component-composition.json +8 -1
  4. package/ai/components/button.md +5 -2
  5. package/ai/components/surface-scaffold.md +3 -0
  6. package/ai/public-api-signatures-v1.md +86 -39
  7. package/ai/skill.md +4 -0
  8. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  9. package/dist/backdrop-image.d.ts +8 -0
  10. package/dist/backdrop-image.js +1 -1
  11. package/dist/browser/content-item.js +1 -0
  12. package/dist/button.d.ts +4 -2
  13. package/dist/button.js +1 -1
  14. package/dist/catalog-resources.d.ts +2 -1
  15. package/dist/catalog.d.ts +2 -1
  16. package/dist/catalog.js +2 -1
  17. package/dist/catalog.js.map +1 -1
  18. package/dist/{chunk-P7EJ4CAD.js → chunk-ETFP7HAR.js} +2 -2
  19. package/dist/{chunk-P7EJ4CAD.js.map → chunk-ETFP7HAR.js.map} +1 -1
  20. package/dist/{chunk-SOFPUW3O.js → chunk-IIQ2XIMU.js} +4 -3
  21. package/dist/chunk-IIQ2XIMU.js.map +1 -0
  22. package/dist/chunk-IRC5N7FA.js +16 -0
  23. package/dist/chunk-IRC5N7FA.js.map +1 -0
  24. package/dist/{chunk-SQ5DH66U.js → chunk-ITENVZ6Q.js} +2 -15
  25. package/dist/chunk-ITENVZ6Q.js.map +1 -0
  26. package/dist/{chunk-WSEJ2FDE.js → chunk-OUCHU7JB.js} +2 -2
  27. package/dist/{chunk-WSEJ2FDE.js.map → chunk-OUCHU7JB.js.map} +1 -1
  28. package/dist/{chunk-KPINJHIC.js → chunk-PS24PVR6.js} +3 -1
  29. package/dist/chunk-PS24PVR6.js.map +1 -0
  30. package/dist/{chunk-SCDWURBO.js → chunk-SOBRGH7T.js} +18 -3
  31. package/dist/chunk-SOBRGH7T.js.map +1 -0
  32. package/dist/{chunk-6CDILXF4.js → chunk-VGECEXM6.js} +11 -2
  33. package/dist/chunk-VGECEXM6.js.map +1 -0
  34. package/dist/chunk-WD6KABEU.js +200 -0
  35. package/dist/chunk-WD6KABEU.js.map +1 -0
  36. package/dist/{chunk-HQZG3OFL.js → chunk-XRZSK2WC.js} +3 -2
  37. package/dist/chunk-XRZSK2WC.js.map +1 -0
  38. package/dist/{chunk-CPSGUGAY.js → chunk-YPAQFBMN.js} +4 -4
  39. package/dist/{chunk-CPSGUGAY.js.map → chunk-YPAQFBMN.js.map} +1 -1
  40. package/dist/collapsible-panel.d.ts +1 -0
  41. package/dist/collapsible-panel.js +3 -2
  42. package/dist/collapsible-panel.js.map +1 -1
  43. package/dist/content-item.d.ts +44 -2
  44. package/dist/content-item.js +4 -1
  45. package/dist/index.d.ts +3 -2
  46. package/dist/index.js +6 -5
  47. package/dist/list-detail-dialog.d.ts +2 -1
  48. package/dist/list-detail-dialog.js +4 -3
  49. package/dist/list-detail-dialog.js.map +1 -1
  50. package/dist/{nav-stack-CU_0dKtI.d.ts → nav-stack-CrZF95VO.d.ts} +2 -1
  51. package/dist/nav-stack.d.ts +2 -1
  52. package/dist/nav-stack.js +2 -1
  53. package/dist/pane.d.ts +3 -23
  54. package/dist/pane.js +2 -1
  55. package/dist/split-view.d.ts +2 -1
  56. package/dist/split-view.js +2 -1
  57. package/dist/styles/backdrop-image.css +18 -1
  58. package/dist/styles/components/surfaces/backdrop-image/backdrop-image.css +18 -1
  59. package/dist/styles/components/surfaces/content-item/content-item.css +52 -0
  60. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +9 -1
  61. package/dist/styles/content-item.css +3 -168
  62. package/dist/styles/foundation.css +4 -4
  63. package/dist/styles/surface-scaffold.css +9 -1
  64. package/dist/styles/webawesome.css +2 -2
  65. package/dist/surface-scaffold.d.ts +6 -2
  66. package/dist/surface-scaffold.js +1 -1
  67. package/dist/surface-tint-DQgYCaP9.d.ts +25 -0
  68. package/dist/tab-navigator.d.ts +2 -1
  69. package/dist/tab-navigator.js +2 -1
  70. package/dist/tab-navigator.js.map +1 -1
  71. package/dist/toolbar.d.ts +1 -0
  72. package/dist/wire-catalog.d.ts +2 -1
  73. package/dist/wire-content-items.js +62 -0
  74. package/dist/wire-content-items.js.map +1 -1
  75. package/dist/workbench-dialog.d.ts +2 -1
  76. package/dist/workbench-dialog.js +3 -2
  77. package/dist/workbench-dialog.js.map +1 -1
  78. package/dist/workbench.d.ts +2 -1
  79. package/dist/workbench.js +2 -1
  80. package/docs/accessibility.md +6 -0
  81. package/docs/backdrop-image.md +4 -0
  82. package/docs/button.md +15 -0
  83. package/docs/component-contract.md +7 -0
  84. package/docs/component-selection.md +1 -1
  85. package/docs/design/templates/button/emphasis-dark.svg +1 -1
  86. package/docs/design/templates/button-dark.svg +1 -1
  87. package/docs/design/templates/checkbox/error-dark.svg +1 -1
  88. package/docs/design/templates/checkbox/states.svg +1 -1
  89. package/docs/design/templates/checkbox-dark.svg +1 -1
  90. package/docs/design/templates/checkbox-group/horizontal-dark.svg +1 -1
  91. package/docs/design/templates/checkbox-group/vertical.svg +1 -1
  92. package/docs/design/templates/checkbox-group-dark.svg +1 -1
  93. package/docs/design/templates/checkbox-group.svg +1 -1
  94. package/docs/design/templates/checkbox.svg +1 -1
  95. package/docs/design/templates/choice-chips/color-palette-dark.svg +1 -1
  96. package/docs/design/templates/choice-chips-dark.svg +1 -1
  97. package/docs/design/templates/color-field/error-dark.svg +1 -1
  98. package/docs/design/templates/color-field/hint.svg +1 -1
  99. package/docs/design/templates/color-field-dark.svg +1 -1
  100. package/docs/design/templates/color-field.svg +1 -1
  101. package/docs/design/templates/color-picker/gray.svg +1 -1
  102. package/docs/design/templates/color-picker/hsb.svg +1 -1
  103. package/docs/design/templates/color-picker/rgb.svg +1 -1
  104. package/docs/design/templates/color-picker.svg +1 -1
  105. package/docs/design/templates/color-swatches/palette-dark.svg +1 -1
  106. package/docs/design/templates/color-swatches-dark.svg +1 -1
  107. package/docs/design/templates/disclosure/embedded.svg +1 -1
  108. package/docs/design/templates/disclosure/framed-sunken.svg +1 -1
  109. package/docs/design/templates/disclosure/plain.svg +1 -1
  110. package/docs/design/templates/disclosure.svg +1 -1
  111. package/docs/design/templates/message-composer/chat.svg +1 -1
  112. package/docs/design/templates/message-composer.svg +1 -1
  113. package/docs/design/templates/progress-bar/determinate.svg +1 -1
  114. package/docs/design/templates/progress-bar/tones.svg +1 -1
  115. package/docs/design/templates/progress-bar.svg +1 -1
  116. package/docs/design/templates/radio-group/horizontal-dark.svg +1 -1
  117. package/docs/design/templates/radio-group/vertical.svg +1 -1
  118. package/docs/design/templates/radio-group-dark.svg +1 -1
  119. package/docs/design/templates/radio-group.svg +1 -1
  120. package/docs/design/templates/search-field/empty.svg +1 -1
  121. package/docs/design/templates/search-field/tokens.svg +1 -1
  122. package/docs/design/templates/search-field.svg +1 -1
  123. package/docs/design/templates/select/form.svg +1 -1
  124. package/docs/design/templates/select.svg +1 -1
  125. package/docs/design/templates/slider/compact.svg +1 -1
  126. package/docs/design/templates/slider/readout.svg +1 -1
  127. package/docs/design/templates/slider/ticks.svg +1 -1
  128. package/docs/design/templates/slider.svg +1 -1
  129. package/docs/design/templates/switch/states.svg +1 -1
  130. package/docs/design/templates/switch.svg +1 -1
  131. package/docs/design/templates/tag-field/error-dark.svg +1 -1
  132. package/docs/design/templates/tag-field/error.svg +1 -1
  133. package/docs/design/templates/tag-field/tags.svg +1 -1
  134. package/docs/design/templates/tag-field-dark.svg +1 -1
  135. package/docs/design/templates/tag-field.svg +1 -1
  136. package/docs/design/templates/text-field/error-dark.svg +1 -1
  137. package/docs/design/templates/text-field/labeled.svg +1 -1
  138. package/docs/design/templates/text-field/multiline.svg +1 -1
  139. package/docs/design/templates/text-field/search.svg +1 -1
  140. package/docs/design/templates/text-field-dark.svg +1 -1
  141. package/docs/design/templates/text-field.svg +1 -1
  142. package/docs/design/templates/toolbar-control-group/icon-tile-danger-dark.svg +1 -1
  143. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  144. package/docs/design/templates.md +3 -1
  145. package/docs/layout.md +25 -0
  146. package/docs/manual-test-plan.md +27 -1
  147. package/docs/recipes.md +9 -0
  148. package/docs/surface-scaffold.md +4 -1
  149. package/llms.txt +3 -0
  150. package/package.json +1 -1
  151. package/dist/chunk-5USKCWGL.js +0 -113
  152. package/dist/chunk-5USKCWGL.js.map +0 -1
  153. package/dist/chunk-6CDILXF4.js.map +0 -1
  154. package/dist/chunk-HQZG3OFL.js.map +0 -1
  155. package/dist/chunk-KPINJHIC.js.map +0 -1
  156. package/dist/chunk-SCDWURBO.js.map +0 -1
  157. package/dist/chunk-SOFPUW3O.js.map +0 -1
  158. package/dist/chunk-SQ5DH66U.js.map +0 -1
package/README.md CHANGED
@@ -908,6 +908,7 @@ The detailed rationale lives in the theme contract.
908
908
  - [Apple HIG translation](./docs/apple-hig.md)
909
909
  - [Writing interface copy](./docs/writing.md)
910
910
  - [Accessibility and keyboard contracts](./docs/accessibility.md)
911
+ - [Button current-step semantics](./docs/button.md)
911
912
  - [Component and integration contract](./docs/component-contract.md)
912
913
  - [Spacing and application layout](./docs/layout.md)
913
914
  - [Web Awesome theme contract](./docs/webawesome-theme.md)
@@ -2151,6 +2151,7 @@
2151
2151
  "A modal task needs a dialog: render a DialogSurface with a label (or labelledBy plus a heading), its body as children, and its buttons as footer, then open it with openDialog() and close it with closeDialog(), the close button, or any element with data-dialog-close. A raw Web Awesome dropdown that needs a list-compatible menu inset uses PopupSurface.",
2152
2152
  "A dialog body should compose rows, sections, or aligned bare prose through List and ListInsetText.",
2153
2153
  "A modal needs a typed preferred width, viewport gutter, or dynamic viewport-bounded height cap while retaining native body scrolling and footer actions.",
2154
+ "A dialog needs a less prominent built-in close control: closeEmphasis=\"quiet\" softens the glyph while keeping the native button target and close behavior.",
2154
2155
  "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\".",
2155
2156
  "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\".",
2156
2157
  "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.",
@@ -2190,12 +2191,14 @@
2190
2191
  "core kerfjs/overlay confirm/prompt/form/choice as a bottom sheet styled by @kerfjs/ui/overlay-sheet.css",
2191
2192
  "resizable core kerfjs/overlay bottom sheet: the kui-overlay-sheet-resizable class plus wireOverlaySheets adds the resizable DialogSurface sheet grabber",
2192
2193
  "header with label text or heading content, headerActions, a close button, and an optional footer; withoutHeader for content that owns its heading and close control",
2194
+ "normal (default) or quiet closeEmphasis on the built-in close button",
2193
2195
  "accessible description by describedBy: the heading ToolbarText descriptionId or app-owned body summary copy",
2194
2196
  "light dismiss (a backdrop press closes; otherwise the panel pulses)"
2195
2197
  ],
2196
2198
  "accessibility": [
2197
2199
  "DialogSurface is a native <dialog> opened modally (showModal): it is in the top layer, the rest of the page is inert, focus stays inside, and it returns to the opener on close. Focus starts on an autofocus element, otherwise on the panel.",
2198
2200
  "Name it with label (also its title text) or labelledBy (the id of a heading, such as a ToolbarText). The close button is named Close (closeLabel) and Escape asks to close; the cancelable kui-request-close event reports source close-button, escape, or backdrop.",
2201
+ "closeEmphasis=\"quiet\" uses a smaller, quiet-colored close glyph inside the same 40px target; hover, focus ring, forced colors, accessible naming, and close requests remain available.",
2199
2202
  "Name a PopoverDialogSurface with label or labelledBy. Describe either surface with describedBy (rendered as aria-describedby on the dialog root), usually the heading ToolbarText descriptionId or app-owned summary copy in the body; rootAttributes accepts only data-* attributes, so it never sets aria-describedby.",
2200
2203
  "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.",
2201
2204
  "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 is skipped under prefers-reduced-motion.",
@@ -7580,6 +7583,7 @@
7580
7583
  "A dialog footer, a form, or content needs an action button: tone=\"brand\" for the main action, tone=\"success\" for a positive action such as a pricing check, tone=\"danger\" for a destructive one, and emphasis=\"secondary\" or \"plain\" for the quieter actions beside it.",
7581
7584
  "A form submits or resets: type=\"submit\" or \"reset\" (the default type never submits), with name, value, and form when needed.",
7582
7585
  "An action navigates: href renders a link with the same chrome.",
7586
+ "A setup or progress sequence uses a Button as its current step: currentStep marks that button or link with aria-current=\"step\" without changing its styling.",
7583
7587
  "A single primary action spans its form, sheet, or narrow column: fill makes it span the parent's available inline width, label centered, as SplitButton's fill does."
7584
7588
  ],
7585
7589
  "avoidWhen": [
@@ -7600,6 +7604,7 @@
7600
7604
  ],
7601
7605
  "appOwns": [
7602
7606
  "the label and its delegated action",
7607
+ "which step is current when currentStep marks a sequence",
7603
7608
  "what a press does while loading",
7604
7609
  "form ownership"
7605
7610
  ],
@@ -7612,6 +7617,7 @@
7612
7617
  "leading icon",
7613
7618
  "icon-only square (icon and ariaLabel, no label)",
7614
7619
  "pressed toggle (aria-pressed) on any tone and emphasis",
7620
+ "currentStep marker (aria-current=\"step\") on a button or link",
7615
7621
  "loading",
7616
7622
  "disabled",
7617
7623
  "link (href)",
@@ -7621,7 +7627,8 @@
7621
7627
  "accessibility": [
7622
7628
  "A native <button> (type=\"button\" unless set) or, with href, an <a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring 1px outside the box; with focusRingInset it is drawn inside the box, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on a loud fill of its own hue.",
7623
7629
  "Loading shows a spinner in place of the label, which still names the button, and sets aria-busy=\"true\". A disabled link drops its href and reports aria-disabled=\"true\". Disabled success uses a quiet success fill and readable on-fill text instead of fading the label.",
7624
- "With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag."
7630
+ "With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag.",
7631
+ "With currentStep, a native button or link reports aria-current=\"step\" even when disabled; the app owns the step label and which step is current."
7625
7632
  ],
7626
7633
  "geometry": {
7627
7634
  "margin": "none",
@@ -7671,7 +7678,7 @@
7671
7678
  "rootElement": "button",
7672
7679
  "links": {
7673
7680
  "catalogRoute": "?component=button",
7674
- "documentation": "docs/component-selection.md",
7681
+ "documentation": "docs/button.md",
7675
7682
  "recipe": "docs/component-selection.md#problem-to-component-matrix"
7676
7683
  }
7677
7684
  },
@@ -2059,6 +2059,7 @@
2059
2059
  "obligations": [
2060
2060
  "DialogSurface is a native <dialog> opened modally (showModal): it is in the top layer, the rest of the page is inert, focus stays inside, and it returns to the opener on close. Focus starts on an autofocus element, otherwise on the panel.",
2061
2061
  "Name it with label (also its title text) or labelledBy (the id of a heading, such as a ToolbarText). The close button is named Close (closeLabel) and Escape asks to close; the cancelable kui-request-close event reports source close-button, escape, or backdrop.",
2062
+ "closeEmphasis=\"quiet\" uses a smaller, quiet-colored close glyph inside the same 40px target; hover, focus ring, forced colors, accessible naming, and close requests remain available.",
2062
2063
  "Name a PopoverDialogSurface with label or labelledBy. Describe either surface with describedBy (rendered as aria-describedby on the dialog root), usually the heading ToolbarText descriptionId or app-owned summary copy in the body; rootAttributes accepts only data-* attributes, so it never sets aria-describedby.",
2063
2064
  "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.",
2064
2065
  "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 is skipped under prefers-reduced-motion.",
@@ -6073,6 +6074,11 @@
6073
6074
  "owner": "application",
6074
6075
  "required": false
6075
6076
  },
6077
+ {
6078
+ "id": "which step is current when currentStep marks a sequence",
6079
+ "owner": "application",
6080
+ "required": false
6081
+ },
6076
6082
  {
6077
6083
  "id": "what a press does while loading",
6078
6084
  "owner": "application",
@@ -6095,7 +6101,8 @@
6095
6101
  "obligations": [
6096
6102
  "A native <button> (type=\"button\" unless set) or, with href, an <a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring 1px outside the box; with focusRingInset it is drawn inside the box, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on a loud fill of its own hue.",
6097
6103
  "Loading shows a spinner in place of the label, which still names the button, and sets aria-busy=\"true\". A disabled link drops its href and reports aria-disabled=\"true\". Disabled success uses a quiet success fill and readable on-fill text instead of fading the label.",
6098
- "With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag."
6104
+ "With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag.",
6105
+ "With currentStep, a native button or link reports aria-current=\"step\" even when disabled; the app owns the step label and which step is current."
6099
6106
  ]
6100
6107
  },
6101
6108
  "cssValueProps": [],
@@ -13,6 +13,7 @@ Trigger an action from a dialog footer, a form, or content outside any Toolbar,
13
13
  - A dialog footer, a form, or content needs an action button: tone="brand" for the main action, tone="success" for a positive action such as a pricing check, tone="danger" for a destructive one, and emphasis="secondary" or "plain" for the quieter actions beside it.
14
14
  - A form submits or resets: type="submit" or "reset" (the default type never submits), with name, value, and form when needed.
15
15
  - An action navigates: href renders a link with the same chrome.
16
+ - A setup or progress sequence uses a Button as its current step: currentStep marks that button or link with aria-current="step" without changing its styling.
16
17
  - A single primary action spans its form, sheet, or narrow column: fill makes it span the parent's available inline width, label centered, as SplitButton's fill does.
17
18
 
18
19
  **Not when:**
@@ -46,6 +47,7 @@ Exact prop names and types: [`@kerfjs/ui/button`](../public-api-signatures-v1.md
46
47
  - leading icon
47
48
  - icon-only square (icon and ariaLabel, no label)
48
49
  - pressed toggle (aria-pressed) on any tone and emphasis
50
+ - currentStep marker (aria-current="step") on a button or link
49
51
  - loading
50
52
  - disabled
51
53
  - link (href)
@@ -61,7 +63,7 @@ Exact prop names and types: [`@kerfjs/ui/button`](../public-api-signatures-v1.md
61
63
 
62
64
  ## State and wiring
63
65
 
64
- **The app owns:** the label and its delegated action; what a press does while loading; form ownership.
66
+ **The app owns:** the label and its delegated action; which step is current when currentStep marks a sequence; what a press does while loading; form ownership.
65
67
 
66
68
  No wiring helper.
67
69
 
@@ -74,6 +76,7 @@ Margin: none · border: self · padding: self (layout role: controls). `self` me
74
76
  - A native \<button> (type="button" unless set) or, with href, an \<a>; its label names it, or ariaLabel when the label is not enough. Focus shows the standard focus ring 1px outside the box; with focusRingInset it is drawn inside the box, two-tone with a surface-colored line inside the ring color, so it keeps 3:1 contrast on a loud fill of its own hue.
75
77
  - Loading shows a spinner in place of the label, which still names the button, and sets aria-busy="true". A disabled link drops its href and reports aria-disabled="true". Disabled success uses a quiet success fill and readable on-fill text instead of fading the label.
76
78
  - With pressed, it is a toggle: aria-pressed reports the state, and the pressed look adds a doubled outline (secondary and plain) or a darkened fill with an inset ring (primary), so the state never relies on hue alone; forced-colors mode paints it Highlight. An icon-only Button is named by ariaLabel, and help tags show that name on hover and keyboard focus. A title gives native hover text that says more than the name and replaces the help tag.
79
+ - With currentStep, a native button or link reports aria-current="step" even when disabled; the app owns the step label and which step is current.
77
80
 
78
81
  ## Styling boundary
79
82
 
@@ -90,5 +93,5 @@ Everything else is internal: `kerf-ui-analyze` reports private or unknown `.kui-
90
93
  ## Related
91
94
 
92
95
  - Components: [LoadingSpinner](./loading-spinner.md), [Button](./wa-button.md), [ToolbarControlGroup](./toolbar-control-group.md).
93
- - Docs: [`docs/component-selection.md`](../../docs/component-selection.md), [`docs/component-selection.md#problem-to-component-matrix`](../../docs/component-selection.md#problem-to-component-matrix).
96
+ - Docs: [`docs/button.md`](../../docs/button.md), [`docs/component-selection.md#problem-to-component-matrix`](../../docs/component-selection.md#problem-to-component-matrix).
94
97
  - UX catalog route: `?component=button`.
@@ -13,6 +13,7 @@ Configure recurring dialog and popup geometry without consumer ::part() override
13
13
  - A modal task needs a dialog: render a DialogSurface with a label (or labelledBy plus a heading), its body as children, and its buttons as footer, then open it with openDialog() and close it with closeDialog(), the close button, or any element with data-dialog-close. A raw Web Awesome dropdown that needs a list-compatible menu inset uses PopupSurface.
14
14
  - A dialog body should compose rows, sections, or aligned bare prose through List and ListInsetText.
15
15
  - A modal needs a typed preferred width, viewport gutter, or dynamic viewport-bounded height cap while retaining native body scrolling and footer actions.
16
+ - A dialog needs a less prominent built-in close control: closeEmphasis="quiet" softens the glyph while keeping the native button target and close behavior.
16
17
  - 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".
17
18
  - 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".
18
19
  - 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.
@@ -57,6 +58,7 @@ Exact prop names and types: [`@kerfjs/ui/surface-scaffold`](../public-api-signat
57
58
  - core kerfjs/overlay confirm/prompt/form/choice as a bottom sheet styled by @kerfjs/ui/overlay-sheet.css
58
59
  - resizable core kerfjs/overlay bottom sheet: the kui-overlay-sheet-resizable class plus wireOverlaySheets adds the resizable DialogSurface sheet grabber
59
60
  - header with label text or heading content, headerActions, a close button, and an optional footer; withoutHeader for content that owns its heading and close control
61
+ - normal (default) or quiet closeEmphasis on the built-in close button
60
62
  - accessible description by describedBy: the heading ToolbarText descriptionId or app-owned body summary copy
61
63
  - light dismiss (a backdrop press closes; otherwise the panel pulses)
62
64
 
@@ -89,6 +91,7 @@ Margin: parent · border: child · padding: self (layout role: structure). `self
89
91
 
90
92
  - DialogSurface is a native \<dialog> opened modally (showModal): it is in the top layer, the rest of the page is inert, focus stays inside, and it returns to the opener on close. Focus starts on an autofocus element, otherwise on the panel.
91
93
  - Name it with label (also its title text) or labelledBy (the id of a heading, such as a ToolbarText). The close button is named Close (closeLabel) and Escape asks to close; the cancelable kui-request-close event reports source close-button, escape, or backdrop.
94
+ - closeEmphasis="quiet" uses a smaller, quiet-colored close glyph inside the same 40px target; hover, focus ring, forced colors, accessible naming, and close requests remain available.
92
95
  - Name a PopoverDialogSurface with label or labelledBy. Describe either surface with describedBy (rendered as aria-describedby on the dialog root), usually the heading ToolbarText descriptionId or app-owned summary copy in the body; rootAttributes accepts only data-\* attributes, so it never sets aria-describedby.
93
96
  - 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.
94
97
  - 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 is skipped under prefers-reduced-motion.
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.110` and `kerfjs@5.0.0-beta.110`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.111` and `kerfjs@5.0.0-beta.111`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/css-values`
6
6
 
@@ -167,6 +167,7 @@ import { S as Sides } from './sides-BPSWde0A.js';
167
167
  import { PaneSeparatorSide } from './pane.js';
168
168
  import './scroll-clearance-BikHljTc.js';
169
169
  import './css-values.js';
170
+ import './surface-tint-DQgYCaP9.js';
170
171
 
171
172
  type ToolbarPosition = 'header' | 'footer';
172
173
  /** Control-band density: `default` (60px with 44px groups) or `compact`. */
@@ -1195,7 +1196,11 @@ export { ListInsetText, type ListInsetTextProps, Sides };
1195
1196
  ```ts
1196
1197
  import * as kerfjs from 'kerfjs';
1197
1198
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1199
+ import { CssColor } from './css-values.js';
1198
1200
  import { S as SemanticTone } from './semantic-tone-CdW013KF.js';
1201
+ import { S as SurfaceTint } from './surface-tint-DQgYCaP9.js';
1202
+ import { BackdropImagePosition, BackdropImageParallax, BackdropImageBlur, BackdropImageScrim } from './backdrop-image.js';
1203
+ import './extension-attributes-mu8MFUl0.js';
1199
1204
 
1200
1205
  /**
1201
1206
  * Base surface treatment, semantic status colors, or `brand-loud`: the solid
@@ -1214,12 +1219,41 @@ type ContentItemFrame = 'none' | 'framed';
1214
1219
  type ContentItemShape = 'rounded' | 'pill';
1215
1220
  /** `single` uses an option; `multiple` uses a grid row; `toggle` uses a pressed button. */
1216
1221
  type ContentItemSelectionMode = 'none' | 'single' | 'multiple' | 'toggle';
1222
+ /** One card-owned lead image in the containing feed's shared scrollport. */
1223
+ interface ContentItemMedia {
1224
+ src: string;
1225
+ alt: string;
1226
+ srcset?: string;
1227
+ sizes?: string;
1228
+ /** Width divided by height; reserves the image block before it loads. Default 16/9. */
1229
+ aspectRatio?: number;
1230
+ position?: BackdropImagePosition;
1231
+ /** Default subtle (35% lag); set none for a static lead image. */
1232
+ parallax?: BackdropImageParallax;
1233
+ /** App-supplied soft copy; its opacity changes, never its blur filter. */
1234
+ blurredSrc?: string;
1235
+ /** Shared BackdropImage blur behavior; use instead of `blurredSrc`. */
1236
+ blur?: BackdropImageBlur;
1237
+ /** Color the image dissolves into and the card body continues on. */
1238
+ fadeTo?: CssColor;
1239
+ /** Content layered over the image, including native controls. */
1240
+ overlay?: KerfUiContent;
1241
+ /** Dim the image behind overlay content. */
1242
+ scrim?: BackdropImageScrim;
1243
+ /** Extend the image to the card's top, left, and right inner edges. */
1244
+ edgeToEdge?: boolean;
1245
+ }
1217
1246
  type ContentItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1218
1247
  'data-component'?: never;
1219
1248
  'data-appearance'?: never;
1220
1249
  'data-outlined'?: never;
1221
1250
  'data-bubble'?: never;
1222
1251
  'data-grid-row'?: never;
1252
+ 'data-media-state'?: never;
1253
+ 'data-media-edge'?: never;
1254
+ 'data-media-fade'?: never;
1255
+ 'data-kui-tint'?: never;
1256
+ 'data-kui-tint-accent'?: never;
1223
1257
  }>;
1224
1258
  type ContentItemInteractiveRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1225
1259
  'data-component'?: never;
@@ -1227,6 +1261,11 @@ type ContentItemInteractiveRootAttributes = Readonly<Record<`data-${string}`, st
1227
1261
  'data-outlined'?: never;
1228
1262
  'data-bubble'?: never;
1229
1263
  'data-grid-row'?: never;
1264
+ 'data-media-state'?: never;
1265
+ 'data-media-edge'?: never;
1266
+ 'data-media-fade'?: never;
1267
+ 'data-kui-tint'?: never;
1268
+ 'data-kui-tint-accent'?: never;
1230
1269
  'data-action'?: never;
1231
1270
  'data-item-id'?: never;
1232
1271
  'data-interactive'?: never;
@@ -1242,6 +1281,10 @@ interface ContentItemBaseProps {
1242
1281
  * semantic values match StateBanner. Defaults to `transparent`.
1243
1282
  */
1244
1283
  appearance?: ContentItemAppearance;
1284
+ /** Reserved lead image with a per-image scroll timeline. Wire the containing root with `wireContentItems`. */
1285
+ media?: ContentItemMedia;
1286
+ /** App-sampled image color, scoped to this card once its image loads. */
1287
+ tint?: SurfaceTint;
1245
1288
  /** Item content; a plain string is allowed for bare copy. */
1246
1289
  children?: KerfUiContent | string;
1247
1290
  /**
@@ -1318,9 +1361,9 @@ type ContentItemProps = ContentItemBaseProps & ({
1318
1361
  * border (transparent unless `framed`), 8px padding, and a rounded or pill
1319
1362
  * radius. It owns that whole geometry, so wrappers must not add more.
1320
1363
  */
1321
- declare function ContentItem({ appearance, children, frame, flush, shape, bubble, title, ariaLabel, focusTarget, outlined, tabIndex, gridRow, interactive, action, itemId, selectionMode, selected, disabled, className, rootAttributes, slot, }: ContentItemProps): kerfjs.SafeHtml;
1364
+ declare function ContentItem({ appearance, media, tint, children, frame, flush, shape, bubble, title, ariaLabel, focusTarget, outlined, tabIndex, gridRow, interactive, action, itemId, selectionMode, selected, disabled, className, rootAttributes, slot, }: ContentItemProps): kerfjs.SafeHtml;
1322
1365
 
1323
- export { ContentItem, type ContentItemAppearance, type ContentItemBubble, type ContentItemFrame, type ContentItemProps, type ContentItemSelectionMode, type ContentItemShape, SemanticTone };
1366
+ export { ContentItem, type ContentItemAppearance, type ContentItemBubble, type ContentItemFrame, type ContentItemMedia, type ContentItemProps, type ContentItemSelectionMode, type ContentItemShape, SemanticTone };
1324
1367
  ```
1325
1368
 
1326
1369
  ## `@kerfjs/ui/value-table`
@@ -1722,12 +1765,13 @@ export { wireListFocusScroll };
1722
1765
  ## `@kerfjs/ui/nav-stack`
1723
1766
 
1724
1767
  ```ts
1725
- export { c as NavStack, d as NavStackBottomToolbarPresentation, e as NavStackChromePresentation, N as NavStackProps, f as NavStackToolbarConfig, a as NavStackView, g as NavStackViewBottomToolbar, h as NavStackViewToolbar } from './nav-stack-CU_0dKtI.js';
1768
+ export { c as NavStack, d as NavStackBottomToolbarPresentation, e as NavStackChromePresentation, N as NavStackProps, f as NavStackToolbarConfig, a as NavStackView, g as NavStackViewBottomToolbar, h as NavStackViewToolbar } from './nav-stack-CrZF95VO.js';
1726
1769
  import 'kerfjs';
1727
1770
  import './semantic-content-BbzjvSu9.js';
1728
1771
  import './pane.js';
1729
1772
  import './scroll-clearance-BikHljTc.js';
1730
1773
  import './css-values.js';
1774
+ import './surface-tint-DQgYCaP9.js';
1731
1775
  import './list.js';
1732
1776
  import './extension-attributes-mu8MFUl0.js';
1733
1777
  import './flex-alignment-4ms8ZbV8.js';
@@ -1767,11 +1811,12 @@ export { type WireNavStackOptions, wireNavStack };
1767
1811
  ```ts
1768
1812
  import * as kerfjs from 'kerfjs';
1769
1813
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1770
- import { N as NavStackProps, a as NavStackView } from './nav-stack-CU_0dKtI.js';
1814
+ import { N as NavStackProps, a as NavStackView } from './nav-stack-CrZF95VO.js';
1771
1815
  import { ResizableRegionProps } from './resizable-region.js';
1772
1816
  import './pane.js';
1773
1817
  import './scroll-clearance-BikHljTc.js';
1774
1818
  import './css-values.js';
1819
+ import './surface-tint-DQgYCaP9.js';
1775
1820
  import './list.js';
1776
1821
  import './extension-attributes-mu8MFUl0.js';
1777
1822
  import './flex-alignment-4ms8ZbV8.js';
@@ -1876,6 +1921,8 @@ import * as kerfjs from 'kerfjs';
1876
1921
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1877
1922
  import { S as ScrollClearance } from './scroll-clearance-BikHljTc.js';
1878
1923
  import { CssColor } from './css-values.js';
1924
+ import { S as SurfaceTint } from './surface-tint-DQgYCaP9.js';
1925
+ export { a as SurfaceTintScheme } from './surface-tint-DQgYCaP9.js';
1879
1926
 
1880
1927
  /**
1881
1928
  * What clear (fully transparent) chrome fades in as content scrolls beneath
@@ -1886,28 +1933,6 @@ import { CssColor } from './css-values.js';
1886
1933
  */
1887
1934
  type ScrollEdge = 'none' | 'material' | 'surface';
1888
1935
 
1889
- /** The color scheme an app declares for a {@link SurfaceTint}'s color. */
1890
- type SurfaceTintScheme = 'light' | 'dark';
1891
- /**
1892
- * A scoped tint for a layout surface: the app's color (usually sampled from
1893
- * artwork it already knows) and the scheme that color belongs to. Kerf does no
1894
- * color detection; `scheme` says whether the color reads as a light or a dark
1895
- * surface. Inside the scope the surface, raised surface, text, quiet text, and
1896
- * everything built on them (quiet fills, separators' backdrop, the chrome
1897
- * material) re-derive from the color, with lightness clamped so text keeps
1898
- * WCAG AA over any color.
1899
- */
1900
- interface SurfaceTint {
1901
- color: CssColor;
1902
- scheme: SurfaceTintScheme;
1903
- /**
1904
- * An optional accent (usually the artwork's most vivid color). The scope's
1905
- * brand ramp re-derives from it: loud fills and their text, brand text and
1906
- * links, text on brand fills, and the focus ring, each clamped to keep AA.
1907
- */
1908
- accent?: CssColor;
1909
- }
1910
-
1911
1936
  /**
1912
1937
  * Logical pane sides: where a {@link Pane} can show a separator and where it can
1913
1938
  * compensate for a device safe-area inset.
@@ -2129,7 +2154,7 @@ type PaneConfig = Pick<PaneProps, 'contentElement' | 'contentLabel' | 'separator
2129
2154
  */
2130
2155
  declare function Pane({ header, children, footer, chromePlacement, chromeDividers, compactChrome, compactChromeOpen, headerPresentation, scrollEdge, scrollEdgeDistance, appearance, background, tint, deepInset, scrollClearance, tabIndex, outlined, element, contentElement, separators, safeAreaEdges, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, slot, }: PaneProps): kerfjs.SafeHtml;
2131
2156
 
2132
- export { Pane, type PaneAppearance, type PaneChromeDividers, type PaneChromePlacement, type PaneCompactChrome, type PaneCompactChromePolicy, type PaneConfig, type PaneContentElement, type PaneElement, type PaneHeaderPresentation, type PaneProps, type PaneSeparatorSide, ScrollClearance, type ScrollEdge, type SurfaceTint, type SurfaceTintScheme };
2157
+ export { Pane, type PaneAppearance, type PaneChromeDividers, type PaneChromePlacement, type PaneCompactChrome, type PaneCompactChromePolicy, type PaneConfig, type PaneContentElement, type PaneElement, type PaneHeaderPresentation, type PaneProps, type PaneSeparatorSide, ScrollClearance, type ScrollEdge, SurfaceTint };
2133
2158
  ```
2134
2159
 
2135
2160
  ## `@kerfjs/ui/workbench`
@@ -2137,13 +2162,14 @@ export { Pane, type PaneAppearance, type PaneChromeDividers, type PaneChromePlac
2137
2162
  ```ts
2138
2163
  import { SafeHtml } from 'kerfjs';
2139
2164
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2140
- import { b as NavPane, N as NavStackProps } from './nav-stack-CU_0dKtI.js';
2165
+ import { b as NavPane, N as NavStackProps } from './nav-stack-CrZF95VO.js';
2141
2166
  import { ListConfig } from './list.js';
2142
2167
  import { PaneConfig } from './pane.js';
2143
2168
  import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
2144
2169
  import { a as PanelToolbar, c as PanelBottomToolbar, b as PanelChromePlacement, d as PanelToggle } from './panel-toolbar-vg6Ir5Dy.js';
2145
2170
  import { ToolbarConfig } from './toolbar.js';
2146
2171
  import './css-values.js';
2172
+ import './surface-tint-DQgYCaP9.js';
2147
2173
  import './toolbar-text.js';
2148
2174
  import './extension-attributes-mu8MFUl0.js';
2149
2175
  import './flex-alignment-4ms8ZbV8.js';
@@ -2557,6 +2583,7 @@ import './sides-BPSWde0A.js';
2557
2583
  import './extension-attributes-mu8MFUl0.js';
2558
2584
  import './flex-alignment-4ms8ZbV8.js';
2559
2585
  import './scroll-clearance-BikHljTc.js';
2586
+ import './surface-tint-DQgYCaP9.js';
2560
2587
 
2561
2588
  /** Which edge a {@link CollapsiblePanel} docks to. */
2562
2589
  type CollapsiblePanelSide = PanelSide;
@@ -2762,8 +2789,9 @@ export { type SidebarStorage, type WireSidebarOptions, type WireSidebarPanel, wi
2762
2789
  ```ts
2763
2790
  import { SafeHtml } from 'kerfjs';
2764
2791
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2765
- import { PaneAppearance, SurfaceTint, ScrollEdge } from './pane.js';
2792
+ import { PaneAppearance, ScrollEdge } from './pane.js';
2766
2793
  import { CssColor } from './css-values.js';
2794
+ import { S as SurfaceTint } from './surface-tint-DQgYCaP9.js';
2767
2795
  import './scroll-clearance-BikHljTc.js';
2768
2796
 
2769
2797
  interface TabNavigatorTabBase<Id extends string> {
@@ -3092,8 +3120,9 @@ import './sides-BPSWde0A.js';
3092
3120
  import './pane.js';
3093
3121
  import './scroll-clearance-BikHljTc.js';
3094
3122
  import './css-values.js';
3123
+ import './surface-tint-DQgYCaP9.js';
3095
3124
  import './workbench.js';
3096
- import './nav-stack-CU_0dKtI.js';
3125
+ import './nav-stack-CrZF95VO.js';
3097
3126
  import './list.js';
3098
3127
  import './extension-attributes-mu8MFUl0.js';
3099
3128
  import './flex-alignment-4ms8ZbV8.js';
@@ -3247,8 +3276,9 @@ import './sides-BPSWde0A.js';
3247
3276
  import './pane.js';
3248
3277
  import './scroll-clearance-BikHljTc.js';
3249
3278
  import './css-values.js';
3279
+ import './surface-tint-DQgYCaP9.js';
3250
3280
  import './workbench.js';
3251
- import './nav-stack-CU_0dKtI.js';
3281
+ import './nav-stack-CrZF95VO.js';
3252
3282
  import './list.js';
3253
3283
  import './extension-attributes-mu8MFUl0.js';
3254
3284
  import './flex-alignment-4ms8ZbV8.js';
@@ -3302,8 +3332,9 @@ import './sides-BPSWde0A.js';
3302
3332
  import './pane.js';
3303
3333
  import './scroll-clearance-BikHljTc.js';
3304
3334
  import './css-values.js';
3335
+ import './surface-tint-DQgYCaP9.js';
3305
3336
  import './workbench.js';
3306
- import './nav-stack-CU_0dKtI.js';
3337
+ import './nav-stack-CrZF95VO.js';
3307
3338
  import './list.js';
3308
3339
  import './extension-attributes-mu8MFUl0.js';
3309
3340
  import './flex-alignment-4ms8ZbV8.js';
@@ -4545,6 +4576,8 @@ type BackdropImageRootAttributes = StructuralRootAttributes & Readonly<{
4545
4576
  'data-fade'?: never;
4546
4577
  'data-repeat'?: never;
4547
4578
  'data-under-chrome'?: never;
4579
+ 'data-aspect-ratio'?: never;
4580
+ 'data-inline-bleed'?: never;
4548
4581
  }>;
4549
4582
  interface BackdropImageCommonProps {
4550
4583
  /** Image URL. */
@@ -4558,6 +4591,12 @@ interface BackdropImageCommonProps {
4558
4591
  position?: BackdropImagePosition;
4559
4592
  /** Height of the image block. Default 320px (`remify(320)`). */
4560
4593
  height?: CssLength;
4594
+ /** Reserve image height from its width, useful for an inline card. Cannot be combined with `height`. */
4595
+ aspectRatio?: number;
4596
+ /** Native image loading behavior. Omit for the browser default. */
4597
+ loading?: 'eager' | 'lazy';
4598
+ /** Bleed to a Pane's inline content edges. Set false when embedded in a card. Defaults to true. */
4599
+ bleedInline?: boolean;
4561
4600
  /** How far the image lags the scroll. Default `none`. */
4562
4601
  parallax?: BackdropImageParallax;
4563
4602
  /**
@@ -4712,6 +4751,8 @@ type DialogSurfaceFooterPresentation = 'docked' | 'floating';
4712
4751
  * material, so content scrolls visibly beneath the title bar.
4713
4752
  */
4714
4753
  type DialogSurfaceHeaderPresentation = 'docked' | 'floating';
4754
+ /** The built-in close control's visual weight; `normal` keeps the default. */
4755
+ type DialogSurfaceCloseEmphasis = 'normal' | 'quiet';
4715
4756
  /**
4716
4757
  * Application `data-*` identity on the DialogSurface root, such as
4717
4758
  * `data-key`; the surface's own configuration attributes stay protected.
@@ -4767,6 +4808,8 @@ interface DialogSurfaceOptions {
4767
4808
  lightDismiss?: boolean;
4768
4809
  /** The close button's accessible name (default `Close`). */
4769
4810
  closeLabel?: string;
4811
+ /** Soften the built-in close control without changing its 40px target. */
4812
+ closeEmphasis?: DialogSurfaceCloseEmphasis;
4770
4813
  size?: DialogSurfaceSize;
4771
4814
  presentation?: DialogSurfacePresentation;
4772
4815
  /** Modal preferred width. Takes precedence over size, within the viewport cap. */
@@ -4860,7 +4903,7 @@ interface DialogSurfaceOptions {
4860
4903
  * floating header and footer measurement, the bottom sheet's keyboard
4861
4904
  * clearance and grabber, and re-render resilience while open.
4862
4905
  */
4863
- declare function DialogSurface({ id, label, labelledBy, describedBy, alert, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
4906
+ declare function DialogSurface({ id, label, labelledBy, describedBy, alert, heading, headerActions, footer, withoutHeader, lightDismiss, closeLabel, closeEmphasis, children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, height, minHeight, resizable, enterMotion, exitMotion, bodyLayout, headerPresentation, footerPresentation, footerOffset, rootAttributes, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
4864
4907
  /** Where a PopoverDialogSurface appears: the top layer, or in flow. */
4865
4908
  type PopoverDialogSurfacePresentation = 'overlay' | 'embedded';
4866
4909
  /** Application `data-*` metadata on the surface root. */
@@ -4927,7 +4970,7 @@ interface PopupSurfaceProps {
4927
4970
  /** Configure recurring Web Awesome dropdown-menu geometry without consumer ::part() CSS. */
4928
4971
  declare function PopupSurface({ children, inset, className, slot, }: PopupSurfaceProps): kerfjs.SafeHtml;
4929
4972
 
4930
- export { type DialogCloseSource, type DialogRequestCloseDetail, DialogSurface, type DialogSurfaceBodyLayout, type DialogSurfaceFooterPresentation, type DialogSurfaceHeaderPresentation, type DialogSurfaceHeight, type DialogSurfaceMaxHeight, type DialogSurfaceName, type DialogSurfaceOptions, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceRootAttributes, type DialogSurfaceSheetMotion, type DialogSurfaceSize, PopoverDialogSurface, type PopoverDialogSurfaceDataAttributes, type PopoverDialogSurfacePresentation, type PopoverDialogSurfaceProps, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset, closeDialog, openDialog };
4973
+ export { type DialogCloseSource, type DialogRequestCloseDetail, DialogSurface, type DialogSurfaceBodyLayout, type DialogSurfaceCloseEmphasis, type DialogSurfaceFooterPresentation, type DialogSurfaceHeaderPresentation, type DialogSurfaceHeight, type DialogSurfaceMaxHeight, type DialogSurfaceName, type DialogSurfaceOptions, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceRootAttributes, type DialogSurfaceSheetMotion, type DialogSurfaceSize, PopoverDialogSurface, type PopoverDialogSurfaceDataAttributes, type PopoverDialogSurfacePresentation, type PopoverDialogSurfaceProps, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset, closeDialog, openDialog };
4931
4974
  ```
4932
4975
 
4933
4976
  ## `@kerfjs/ui/list-detail-dialog`
@@ -4939,9 +4982,10 @@ import { CssLength } from './css-values.js';
4939
4982
  import { SplitViewListWidth } from './split-view.js';
4940
4983
  import { DialogSurfaceSize, DialogSurfaceHeight, DialogSurfaceMaxHeight, DialogSurfaceRootAttributes } from './surface-scaffold.js';
4941
4984
  export { DialogCloseSource, DialogRequestCloseDetail, closeDialog, openDialog } from './surface-scaffold.js';
4942
- import './nav-stack-CU_0dKtI.js';
4985
+ import './nav-stack-CrZF95VO.js';
4943
4986
  import './pane.js';
4944
4987
  import './scroll-clearance-BikHljTc.js';
4988
+ import './surface-tint-DQgYCaP9.js';
4945
4989
  import './list.js';
4946
4990
  import './extension-attributes-mu8MFUl0.js';
4947
4991
  import './flex-alignment-4ms8ZbV8.js';
@@ -5067,9 +5111,10 @@ import { CssLength } from './css-values.js';
5067
5111
  import { WorkbenchMainBottomToolbar, WorkbenchPanel } from './workbench.js';
5068
5112
  import { DialogSurfaceSize, DialogSurfaceHeight, DialogSurfaceMaxHeight, DialogSurfaceRootAttributes } from './surface-scaffold.js';
5069
5113
  export { DialogCloseSource, DialogRequestCloseDetail, closeDialog, openDialog } from './surface-scaffold.js';
5070
- import './nav-stack-CU_0dKtI.js';
5114
+ import './nav-stack-CrZF95VO.js';
5071
5115
  import './pane.js';
5072
5116
  import './scroll-clearance-BikHljTc.js';
5117
+ import './surface-tint-DQgYCaP9.js';
5073
5118
  import './list.js';
5074
5119
  import './extension-attributes-mu8MFUl0.js';
5075
5120
  import './flex-alignment-4ms8ZbV8.js';
@@ -6662,6 +6707,8 @@ interface ButtonOptions {
6662
6707
  * flips it in its `action` handler. A link (`href`) cannot be a toggle.
6663
6708
  */
6664
6709
  pressed?: boolean;
6710
+ /** Mark the current step in a sequence with `aria-current="step"`. */
6711
+ currentStep?: boolean;
6665
6712
  /**
6666
6713
  * Busy: a spinner replaces the label (which still names it) and the button
6667
6714
  * reports `aria-busy`. Presses still reach it; the application decides
@@ -6716,12 +6763,12 @@ interface ButtonOptions {
6716
6763
  * in one of four tones and three emphases, at the 44px control height or
6717
6764
  * compact, with a label or as an icon-only square, rounded or pill-shaped,
6718
6765
  * sized to its label or filling its parent's width, and optionally a
6719
- * `pressed` toggle. Inside a
6766
+ * `pressed` toggle or `currentStep` marker. Inside a
6720
6767
  * `ToolbarControlGroup`, use a plain `<button>`, which the group styles; use
6721
6768
  * Button for a dialog footer, a form, or a standalone action beside other
6722
6769
  * content.
6723
6770
  */
6724
- declare function Button({ children, tone, emphasis, size, shape, fill, pressed, icon, loading, disabled, type, name, value, form, href, target, action, focusRingInset, badge, badgeLabel, badgeTone, ariaLabel, title, id, className, rootAttributes, slot, }: ButtonProps): kerfjs.SafeHtml;
6771
+ declare function Button({ children, tone, emphasis, size, shape, fill, pressed, currentStep, icon, loading, disabled, type, name, value, form, href, target, action, focusRingInset, badge, badgeLabel, badgeTone, ariaLabel, title, id, className, rootAttributes, slot, }: ButtonProps): kerfjs.SafeHtml;
6725
6772
 
6726
6773
  export { Button, type ButtonContent, type ButtonEmphasis, type ButtonLabel, type ButtonOptions, type ButtonProps, type ButtonRootAttributes, type ButtonShape, type ButtonSize, type ButtonTone, type ButtonType };
6727
6774
  ```
package/ai/skill.md CHANGED
@@ -521,6 +521,8 @@ Common mistakes:
521
521
 
522
522
  For prominent content text, use `Text size="large"`; use `size="xlarge"` for display copy. Both sizes work with semantic `variant`, `tone`, and `font`, so keep heading levels based on the document outline.
523
523
 
524
+ For a setup or progress sequence, use `Button currentStep` on its current step. The native button or link reports `aria-current="step"`, including when disabled; the app owns the label and which step is current. See `docs/button.md`.
525
+
524
526
  For an exclusive choice among several visible options that must wrap (a labeled color palette), use `ChoiceChips` from `@kerfjs/ui/choice-chips`: separate pills backed by native radios, so the group is one Tab stop, arrow keys select, and the checked radio submits under `name`. Name it with `label` or `labelledBy` (a visible `FieldLabel`), lead choices with `swatch` (a `CssColor` such as `fillColor('#3b82f6')`, or `'transparent'` for the struck-through no-color dot) or `icon`, and handle the delegated `action` on `change`. Never wrap a `SegmentedControl` or button-appearance `wa-radio` set instead. When any number of the options may be on, give `value` a `readonly Value[]` and pick a model. `multiple: 'toggle'` (label filters, annotation intents) makes the pills native checkboxes in a `role="group"`: each its own Tab stop, a click or Space toggles it alone, and every checked value submits under `name` (read the checked boxes by name in the change handler). `multiple: true` is the desktop multi-select: an `aria-multiselectable` listbox where a click selects one chip, Cmd (Apple) or Ctrl (elsewhere) toggles one, and Shift extends a range; it is one Tab stop with arrow, Shift+arrow, Space, and Cmd/Ctrl+A selection, and hidden inputs submit under `name`. It requires `wireChoiceChips(root)` from `@kerfjs/ui/wire-choice-chips`, which fires `change` on the group (the group carries the `action`); read the set with `readChoiceChips(group)`. A multiple group takes no `required`. On dark inverse chrome, put the chips in a dark appearance scope (`.wa-dark` or any `color-scheme: dark` ancestor) instead of restyling them.
525
527
 
526
528
  For removable tags use `Chip`, with `removeAction` for delegated handling and a specific `removeLabel` for the native button. `itemId` identifies the tag; the app owns removal. `size="compact"` and `disabled` replace app styling of raw `wa-tag` internals. Use `Badge` for non-interactive metadata. For a downloadable file or a short link, give `Chip` an `href` (optional `download`, `true` or a file name, and `external`) instead of styling an `<a>`: it renders a native link with chip geometry, icon, truncation, focus ring, and hover feedback, and cannot also carry a remove button. Inside a `brand-loud` or `neutral` ContentItem bubble set `tone="inherit"`, which follows the bubble's foreground with a currentColor border and no fill. For a short value the user activates once (a filter token in a search help popover that inserts itself into the query), give `Chip` an `action` and `itemId`: the whole chip becomes a native `<button type="button" data-action data-item-id>` with hover, press, focus-ring, and disabled states, and an optional `actionLabel` that contains the visible label. It cannot also take `href` or `removeAction`. A compact rounded action chip keeps a compact `Badge`'s height in a compact `ValueTable` cell, so use it there instead of a `Badge` the app makes clickable.
@@ -577,4 +579,6 @@ Give sibling `DialogSurface`s that render conditionally a stable `rootAttributes
577
579
 
578
580
  Render a modal dialog as `DialogSurface` itself, a native `<dialog>` (set `alert` for a confirmation before a destructive action, which renders `role="alertdialog"`; pair it with `describedBy` on the message and leave `lightDismiss` off): `label` (or `labelledBy` with a `heading`, such as an xl `ToolbarText` with a description) names it, `describedBy` (the heading ToolbarText's `descriptionId`, or app-owned summary copy) describes it as `aria-describedby`, `children` is its body, `footer` holds its buttons, and `headerActions` sit before its close button (`withoutHeader` drops the header for content that owns its heading and close control). Open it with `openDialog(dialog)` and close it with `closeDialog(dialog)` from `@kerfjs/ui/surface-scaffold`; never toggle its `open` attribute. The close button, any `data-dialog-close` element inside (a Cancel button), Escape, and, with `lightDismiss`, a backdrop press dispatch a cancelable `kui-request-close` (`detail.source`: `close-button`, `escape`, or `backdrop`); cancel it to keep the dialog open (the panel pulses). The native `close` event fires once it closes. `openDialog` installs the listeners and, while the dialog is open, measures its floating header/footer, follows a bottom sheet's keyboard and grabber, and keeps that state across re-renders; import `@kerfjs/ui/surface-scaffold/register` only when a dialog may open some other way. Do not write `wa-dialog` inside a `DialogSurface`.
579
581
 
582
+ Set `DialogSurface closeEmphasis="quiet"` when its built-in close control should recede. It keeps the native 40px button target, `closeLabel`, focus ring, and `kui-request-close` behavior. See `docs/surface-scaffold.md`.
583
+
580
584
  Toolbar visibility measurement probes and invisible state children are helper-owned and survive Kerf rerenders through `data-morph-preserve`. Actual CSS visibility stays stable while rendering removes transient parent markers, preventing geometry-driven render feedback and repeated probe recreation. Toolbar replacement, explicit internal-node removal and disposal retain their existing behavior.
@@ -1,6 +1,6 @@
1
1
  # Web Awesome JSX signatures for the UI authoring corpus
2
2
 
3
- Generated from the emitted `@kerfjs/ui@5.0.0-beta.110` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
3
+ Generated from the emitted `@kerfjs/ui@5.0.0-beta.111` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
4
4
 
5
5
  ```ts
6
6
  import { KerfCustomElement } from 'kerfjs/jsx-runtime';
@@ -50,6 +50,8 @@ type BackdropImageRootAttributes = StructuralRootAttributes & Readonly<{
50
50
  'data-fade'?: never;
51
51
  'data-repeat'?: never;
52
52
  'data-under-chrome'?: never;
53
+ 'data-aspect-ratio'?: never;
54
+ 'data-inline-bleed'?: never;
53
55
  }>;
54
56
  interface BackdropImageCommonProps {
55
57
  /** Image URL. */
@@ -63,6 +65,12 @@ interface BackdropImageCommonProps {
63
65
  position?: BackdropImagePosition;
64
66
  /** Height of the image block. Default 320px (`remify(320)`). */
65
67
  height?: CssLength;
68
+ /** Reserve image height from its width, useful for an inline card. Cannot be combined with `height`. */
69
+ aspectRatio?: number;
70
+ /** Native image loading behavior. Omit for the browser default. */
71
+ loading?: 'eager' | 'lazy';
72
+ /** Bleed to a Pane's inline content edges. Set false when embedded in a card. Defaults to true. */
73
+ bleedInline?: boolean;
66
74
  /** How far the image lags the scroll. Default `none`. */
67
75
  parallax?: BackdropImageParallax;
68
76
  /**
@@ -1,4 +1,4 @@
1
- export { BackdropImage } from './chunk-SCDWURBO.js';
1
+ export { BackdropImage } from './chunk-SOBRGH7T.js';
2
2
 
3
3
 
4
4
 
@@ -1,3 +1,4 @@
1
1
  import '../styles/foundation.css';
2
+ import '../styles/backdrop-image.css';
2
3
  import '../styles/content-item.css';
3
4
  export * from '../content-item.js';
package/dist/button.d.ts CHANGED
@@ -57,6 +57,8 @@ interface ButtonOptions {
57
57
  * flips it in its `action` handler. A link (`href`) cannot be a toggle.
58
58
  */
59
59
  pressed?: boolean;
60
+ /** Mark the current step in a sequence with `aria-current="step"`. */
61
+ currentStep?: boolean;
60
62
  /**
61
63
  * Busy: a spinner replaces the label (which still names it) and the button
62
64
  * reports `aria-busy`. Presses still reach it; the application decides
@@ -111,11 +113,11 @@ interface ButtonOptions {
111
113
  * in one of four tones and three emphases, at the 44px control height or
112
114
  * compact, with a label or as an icon-only square, rounded or pill-shaped,
113
115
  * sized to its label or filling its parent's width, and optionally a
114
- * `pressed` toggle. Inside a
116
+ * `pressed` toggle or `currentStep` marker. Inside a
115
117
  * `ToolbarControlGroup`, use a plain `<button>`, which the group styles; use
116
118
  * Button for a dialog footer, a form, or a standalone action beside other
117
119
  * content.
118
120
  */
119
- declare function Button({ children, tone, emphasis, size, shape, fill, pressed, icon, loading, disabled, type, name, value, form, href, target, action, focusRingInset, badge, badgeLabel, badgeTone, ariaLabel, title, id, className, rootAttributes, slot, }: ButtonProps): kerfjs.SafeHtml;
121
+ declare function Button({ children, tone, emphasis, size, shape, fill, pressed, currentStep, icon, loading, disabled, type, name, value, form, href, target, action, focusRingInset, badge, badgeLabel, badgeTone, ariaLabel, title, id, className, rootAttributes, slot, }: ButtonProps): kerfjs.SafeHtml;
120
122
 
121
123
  export { Button, type ButtonContent, type ButtonEmphasis, type ButtonLabel, type ButtonOptions, type ButtonProps, type ButtonRootAttributes, type ButtonShape, type ButtonSize, type ButtonTone, type ButtonType };