@kerfjs/ui 5.0.0-beta.60 → 5.0.0-beta.62

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 (142) hide show
  1. package/README.md +1 -0
  2. package/ai/component-catalog.json +141 -24
  3. package/ai/component-composition-rules.json +8 -0
  4. package/ai/component-composition.d.ts +10 -0
  5. package/ai/component-composition.json +117 -8
  6. package/ai/component-composition.schema.json +21 -0
  7. package/ai/components/README.md +5 -4
  8. package/ai/components/chip.md +72 -0
  9. package/ai/components/grid.md +10 -7
  10. package/ai/components/list-item.md +10 -7
  11. package/ai/components/pane.md +3 -2
  12. package/ai/components/select.md +6 -2
  13. package/ai/components/tabs.md +8 -4
  14. package/ai/components/toolbar-text.md +2 -1
  15. package/ai/components/toolbar.md +3 -2
  16. package/ai/public-api-signatures-v1.md +116 -24
  17. package/ai/skill.md +24 -4
  18. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  19. package/dist/app-tab.d.ts +12 -2
  20. package/dist/app-tab.js +1 -1
  21. package/dist/browser/chip.js +3 -0
  22. package/dist/catalog.js +8 -8
  23. package/dist/chip.d.ts +29 -0
  24. package/dist/chip.js +3 -0
  25. package/dist/chip.js.map +1 -0
  26. package/dist/{chunk-WPLIO5WJ.js → chunk-4MG3KXJM.js} +40 -5
  27. package/dist/chunk-4MG3KXJM.js.map +1 -0
  28. package/dist/{chunk-F7F7KRO2.js → chunk-4SQK27MR.js} +33 -18
  29. package/dist/chunk-4SQK27MR.js.map +1 -0
  30. package/dist/chunk-EAC4CDY7.js +57 -0
  31. package/dist/chunk-EAC4CDY7.js.map +1 -0
  32. package/dist/{chunk-T57AXDFM.js → chunk-ENGVYQPG.js} +4 -3
  33. package/dist/chunk-ENGVYQPG.js.map +1 -0
  34. package/dist/{chunk-5PESG2VP.js → chunk-KYDDCJXU.js} +4 -3
  35. package/dist/chunk-KYDDCJXU.js.map +1 -0
  36. package/dist/{chunk-MERWRTBG.js → chunk-N2DRU35S.js} +1 -1
  37. package/dist/chunk-N2DRU35S.js.map +1 -0
  38. package/dist/{chunk-EZVNC4J6.js → chunk-OD2VXAPL.js} +4 -1
  39. package/dist/chunk-OD2VXAPL.js.map +1 -0
  40. package/dist/{chunk-T66UNBXL.js → chunk-Q6YU3BT7.js} +11 -2
  41. package/dist/chunk-Q6YU3BT7.js.map +1 -0
  42. package/dist/{chunk-N3GKMPDW.js → chunk-RKTTF3L4.js} +3 -1
  43. package/dist/chunk-RKTTF3L4.js.map +1 -0
  44. package/dist/chunk-SWVW56CV.js +188 -0
  45. package/dist/chunk-SWVW56CV.js.map +1 -0
  46. package/dist/{chunk-46263QLK.js → chunk-UA6JW52D.js} +53 -7
  47. package/dist/chunk-UA6JW52D.js.map +1 -0
  48. package/dist/{chunk-AGXQQFKZ.js → chunk-UCKKT6KO.js} +13 -4
  49. package/dist/chunk-UCKKT6KO.js.map +1 -0
  50. package/dist/{chunk-3SXMEK3B.js → chunk-V3DX3K65.js} +1 -1
  51. package/dist/chunk-V3DX3K65.js.map +1 -0
  52. package/dist/{chunk-UWONG4QE.js → chunk-VCBTX62E.js} +11 -3
  53. package/dist/chunk-VCBTX62E.js.map +1 -0
  54. package/dist/{chunk-2OXQHM7I.js → chunk-X6ADHJNI.js} +45 -20
  55. package/dist/chunk-X6ADHJNI.js.map +1 -0
  56. package/dist/collapsible-panel.js +2 -2
  57. package/dist/grid.d.ts +13 -5
  58. package/dist/grid.js +1 -1
  59. package/dist/index.d.ts +5 -4
  60. package/dist/index.js +17 -16
  61. package/dist/list-item.d.ts +11 -1
  62. package/dist/list-item.js +1 -1
  63. package/dist/nav-stack.d.ts +3 -1
  64. package/dist/nav-stack.js +1 -1
  65. package/dist/pane.d.ts +8 -3
  66. package/dist/pane.js +1 -1
  67. package/dist/popup-menu.d.ts +2 -2
  68. package/dist/popup-menu.js +1 -1
  69. package/dist/select-register.js +46 -0
  70. package/dist/select-register.js.map +1 -1
  71. package/dist/select.d.ts +16 -0
  72. package/dist/select.js +1 -1
  73. package/dist/split-view.d.ts +1 -1
  74. package/dist/split-view.js +1 -1
  75. package/dist/styles/app-tab.css +119 -0
  76. package/dist/styles/chip.css +118 -0
  77. package/dist/styles/grid.css +7 -0
  78. package/dist/styles/list-item.css +14 -9
  79. package/dist/styles/nav-stack.css +13 -0
  80. package/dist/styles/pane.css +33 -0
  81. package/dist/styles/popup-menu.css +2 -1
  82. package/dist/styles/resizable-region.css +9 -0
  83. package/dist/styles/segmented-control.css +2 -1
  84. package/dist/styles/select.css +45 -0
  85. package/dist/styles/split-view.css +6 -0
  86. package/dist/styles/styles.css +1 -0
  87. package/dist/styles/tab-bar.css +3 -0
  88. package/dist/styles/tab-scaffold.css +13 -0
  89. package/dist/styles/toolbar-text.css +15 -6
  90. package/dist/styles/toolbar.css +15 -5
  91. package/dist/styles/workbench.css +117 -93
  92. package/dist/tab-bar.d.ts +6 -2
  93. package/dist/tab-bar.js +1 -1
  94. package/dist/tab-scaffold.d.ts +3 -0
  95. package/dist/tab-scaffold.js +1 -0
  96. package/dist/tab-scaffold.js.map +1 -1
  97. package/dist/{token-search-field-CCke1yvQ.d.ts → token-search-field-DvgFLhLL.d.ts} +6 -2
  98. package/dist/token-search-field.d.ts +1 -1
  99. package/dist/token-search-model.d.ts +1 -1
  100. package/dist/token-search-model.js +1 -1
  101. package/dist/toolbar-text.d.ts +1 -1
  102. package/dist/toolbar-text.js +1 -1
  103. package/dist/toolbar.d.ts +2 -2
  104. package/dist/toolbar.js +1 -1
  105. package/dist/wire-catalog.js +1 -1
  106. package/dist/wire-tab-bars.js +1 -1
  107. package/dist/wire-token-search-fields.d.ts +1 -1
  108. package/dist/workbench.js +1 -1
  109. package/docs/accessibility.md +29 -3
  110. package/docs/collapsible-panel.md +4 -3
  111. package/docs/component-contract.md +5 -0
  112. package/docs/component-selection.md +92 -11
  113. package/docs/design/templates/segmented-control/equal-dark.svg +1 -1
  114. package/docs/design/templates/segmented-control/equal.svg +1 -1
  115. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -1
  116. package/docs/design/templates/segmented-control/pill-small.svg +1 -1
  117. package/docs/design/templates/segmented-control-dark.svg +1 -1
  118. package/docs/design/templates/segmented-control.svg +1 -1
  119. package/docs/layout.md +51 -9
  120. package/docs/nav-stack.md +5 -0
  121. package/docs/split-view.md +5 -0
  122. package/docs/sunken-panel.md +12 -0
  123. package/docs/tab-scaffold.md +5 -0
  124. package/docs/webawesome-theme.md +62 -0
  125. package/docs/workbench.md +22 -10
  126. package/llms.txt +40 -8
  127. package/package.json +7 -1
  128. package/dist/chunk-2OXQHM7I.js.map +0 -1
  129. package/dist/chunk-3SXMEK3B.js.map +0 -1
  130. package/dist/chunk-46263QLK.js.map +0 -1
  131. package/dist/chunk-5I7OHCIP.js +0 -96
  132. package/dist/chunk-5I7OHCIP.js.map +0 -1
  133. package/dist/chunk-5PESG2VP.js.map +0 -1
  134. package/dist/chunk-AGXQQFKZ.js.map +0 -1
  135. package/dist/chunk-EZVNC4J6.js.map +0 -1
  136. package/dist/chunk-F7F7KRO2.js.map +0 -1
  137. package/dist/chunk-MERWRTBG.js.map +0 -1
  138. package/dist/chunk-N3GKMPDW.js.map +0 -1
  139. package/dist/chunk-T57AXDFM.js.map +0 -1
  140. package/dist/chunk-T66UNBXL.js.map +0 -1
  141. package/dist/chunk-UWONG4QE.js.map +0 -1
  142. package/dist/chunk-WPLIO5WJ.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tab-scaffold.tsx"],"names":[],"mappings":";;;AAkFO,SAAS,WAAA,CAA+B;AAAA,EAC7C,EAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA,GAAiB,QAAA;AAAA,EACjB,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAyB;AACvB,EAAA,uBACE,IAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MAC5C,EAAA;AAAA,MACA,gBAAA,EAAe,cAAA;AAAA,MACf,sBAAA,EAAsB,EAAA;AAAA,MACtB,sBAAA,EACE,cAAA,KAAmB,QAAA,IAAY,cAAA,KAAmB,SAC9C,cAAA,GACA,MAAA;AAAA,MAEN,IAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,SAAI,KAAA,EAAM,0BAAA,EACR,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,qBACT,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,yBAAA;AAAA,YACN,2BAAyB,GAAA,CAAI,EAAA;AAAA,YAC7B,aAAA,EAAa,MAAA,CAAO,GAAA,CAAI,EAAA,KAAO,MAAM,CAAA;AAAA,YACrC,aAAA,EAAa,MAAA,CAAO,GAAA,CAAI,EAAA,KAAO,MAAM,CAAA;AAAA,YAEpC,QAAA,EAAA,GAAA,CAAI;AAAA;AAAA,SAER,CAAA,EACH,CAAA;AAAA,wBACA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EAAwB,IAAA,EAAK,SAAA,EAAU,YAAA,EAAY,KAAA,EAC3D,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACjB,UAAA,MAAM,GAAA,GAAM,IAAI,KAAA,KAAU,IAAA;AAC1B,UAAA,MAAM,SAAA,GAAY,GAAA,GAAM,MAAA,GAAY,cAAA,CAAe,IAAI,KAAK,CAAA;AAC5D,UAAA,MAAM,QAAA,GAAW,OAAO,SAAA,KAAc,MAAA;AACtC,UAAA,MAAM,QAAQ,QAAA,oBACZ,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,6BAAA;AAAA,cACN,iBAAA,EAAiB,MAAM,KAAA,GAAQ,MAAA;AAAA,cAE9B,QAAA,EAAA,SAAA,KAAc,MAAA,mBACb,GAAA,CAAC,KAAA,EAAA,EAAM,IAAA,EAAK,UAAS,IAAA,EAAK,KAAA,EAAM,UAAA,EAAU,IAAA,EAAC,CAAA,mBAE3C,GAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,QAAA;AAAA,kBACL,UAAA,EAAW,OAAA;AAAA,kBACX,IAAA,EAAK,SAAA;AAAA,kBACL,UAAA,EAAU,IAAA;AAAA,kBAET,QAAA,EAAA;AAAA;AAAA;AACH;AAAA,WAEJ;AAIF,UAAA,MAAM,aAAa,QAAA,GACf,GAAA,CAAI,UAAA,EAAY,IAAA,MAAU,SAAA,GAC1B,MAAA;AACJ,UAAA,uBACE,IAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,KAAA,EAAM,uBAAA;AAAA,cACN,IAAA,EAAK,KAAA;AAAA,cACL,yBAAuB,GAAA,CAAI,EAAA;AAAA,cAC3B,eAAA,EAAe,MAAA,CAAO,GAAA,CAAI,EAAA,KAAO,MAAM,CAAA;AAAA,cACvC,QAAA,EAAU,GAAA,CAAI,EAAA,KAAO,MAAA,GAAS,GAAA,GAAM,IAAA;AAAA,cACpC,YAAA,EACE,eAAe,MAAA,GACX,MAAA,GACA,GAAG,GAAA,CAAI,KAAK,KAAK,UAAU,CAAA,CAAA;AAAA,cAEjC,gBAAA,EAAgB,WAAW,MAAA,GAAS,MAAA;AAAA,cAEnC,QAAA,EAAA;AAAA,gBAAA,GAAA,CAAI,uBACH,IAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAM,4BAAA,EAA6B,eAAY,MAAA,EAClD,QAAA,EAAA;AAAA,kBAAA,GAAA,CAAI,IAAA;AAAA,kBACJ;AAAA,iBAAA,EACH,CAAA,GAEA,KAAA;AAAA,gCAEF,GAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAM,6BAAA,EAA+B,cAAI,KAAA,EAAM;AAAA;AAAA;AAAA,WACvD;AAAA,QAEJ,CAAC,CAAA,EACH;AAAA;AAAA;AAAA,GACF;AAEJ;AAEA,SAAS,eACP,KAAA,EACoB;AACpB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA;AACnB,IAAA,OAAO,OAAO,QAAA,CAAS,KAAK,CAAA,GAAI,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA;AAClD,EAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,CAAM,MAAK,EAAG,OAAO,MAAM,IAAA,EAAK;AACjE,EAAA,OAAO,MAAA;AACT","file":"tab-scaffold.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge } from './badge.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\ninterface TabScaffoldTabBase<Id extends string> {\n id: Id;\n label: string;\n /** Decorative icon shown above the label in the bottom bar. */\n icon?: SafeHtml;\n /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */\n content: KerfUiContent;\n}\n\n/** A tab with an optional count or short-status badge. */\ninterface TabScaffoldTabCountBadge {\n /**\n * Optional count or short status shown as a solid danger `Badge` at the\n * top-trailing corner of the tab icon (the iOS tab-bar badge). Omitted, `''`,\n * or non-finite numbers render no badge. The visual badge is `aria-hidden`;\n * its meaning reaches assistive technology through `badgeLabel`.\n *\n * Pass `true` instead for the text-free dot (new content without a count).\n */\n badge?: string | number;\n /**\n * Localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"3 unread\"` → `\"Inbox, 3 unread\"`).\n * Defaults to the badge text itself. Ignored when no badge renders.\n */\n badgeLabel?: string;\n}\n\n/** A tab with the text-free dot badge (new content without a count). */\ninterface TabScaffoldTabDotBadge {\n /**\n * `true` shows a solid danger dot `Badge` at the top-trailing corner of the\n * tab icon — the iOS tab-bar dot for new content without a count.\n */\n badge: true;\n /**\n * Required localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"New activity\"` →\n * `\"Feed, New activity\"`). A dot has no text, so this is its only meaning.\n */\n badgeLabel: string;\n}\n\n/** One bottom-bar destination: its label, optional icon and badge, and content. */\nexport type TabScaffoldTab<Id extends string = string> =\n TabScaffoldTabBase<Id> & (TabScaffoldTabCountBadge | TabScaffoldTabDotBadge);\n\nexport interface TabScaffoldProps<Id extends string = string> {\n id: string;\n /** Accessible name for the tab bar. */\n label: string;\n tabs: readonly TabScaffoldTab<Id>[];\n /** The controlled active tab id (the app owns selection). */\n active: NoInfer<Id>;\n /**\n * The line over the tab bar, where it meets the active scene. `scroll`\n * (default) shows it only while more of the scene's content lies below —\n * never at the scroll end or when the content fits — once\n * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above\n * the scaffold; unwired, it does not show. `always` shows it without the\n * wiring; `none` never, even when wired. The line is the bar's own 1px top\n * border, so no state moves the bar or a tab.\n */\n chromeDividers?: 'scroll' | 'always' | 'none';\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches\n * between major sections, each tab keeping its own content (usually a `NavStack`)\n * mounted so its stack and scroll survive a switch. Controlled — the app owns\n * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.\n * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of\n * a bottom bar. See `docs/23-app-layouts.md` §3.4.\n */\nexport function TabScaffold<Id extends string>({\n id,\n label,\n tabs,\n active,\n chromeDividers = 'scroll',\n className = '',\n slot,\n}: TabScaffoldProps<Id>) {\n return (\n <section\n class={`kui-tab-scaffold ${className}`.trim()}\n id={id}\n data-component=\"tab-scaffold\"\n data-tab-scaffold-id={id}\n data-chrome-dividers={\n chromeDividers === 'always' || chromeDividers === 'none'\n ? chromeDividers\n : undefined\n }\n slot={slot}\n >\n <div class=\"kui-tab-scaffold__scenes\">\n {tabs.map((tab) => (\n <div\n class=\"kui-tab-scaffold__scene\"\n data-tab-scaffold-scene={tab.id}\n data-active={String(tab.id === active)}\n aria-hidden={String(tab.id !== active)}\n >\n {tab.content}\n </div>\n ))}\n </div>\n <nav class=\"kui-tab-scaffold__bar\" role=\"tablist\" aria-label={label}>\n {tabs.map((tab) => {\n const dot = tab.badge === true;\n const badgeText = dot ? undefined : normalizeBadge(tab.badge);\n const hasBadge = dot || badgeText !== undefined;\n const badge = hasBadge && (\n <span\n class=\"kui-tab-scaffold__tab-badge\"\n data-badge-kind={dot ? 'dot' : 'text'}\n >\n {badgeText === undefined ? (\n <Badge tone=\"danger\" size=\"dot\" ariaHidden />\n ) : (\n <Badge\n tone=\"danger\"\n appearance=\"solid\"\n size=\"compact\"\n ariaHidden\n >\n {badgeText}\n </Badge>\n )}\n </span>\n );\n // A dot has no text fallback: without a usable badgeLabel (reachable\n // only from untyped callers) the tab keeps its plain accessible name.\n const badgeLabel = hasBadge\n ? tab.badgeLabel?.trim() || badgeText\n : undefined;\n return (\n <button\n type=\"button\"\n class=\"kui-tab-scaffold__tab\"\n role=\"tab\"\n data-tab-scaffold-tab={tab.id}\n aria-selected={String(tab.id === active)}\n tabindex={tab.id === active ? '0' : '-1'}\n aria-label={\n badgeLabel === undefined\n ? undefined\n : `${tab.label}, ${badgeLabel}`\n }\n data-has-badge={hasBadge ? 'true' : undefined}\n >\n {tab.icon ? (\n <span class=\"kui-tab-scaffold__tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n {badge}\n </span>\n ) : (\n badge\n )}\n <span class=\"kui-tab-scaffold__tab-label\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </section>\n );\n}\n\nfunction normalizeBadge(\n badge: string | number | undefined,\n): string | undefined {\n if (typeof badge === 'number')\n return Number.isFinite(badge) ? String(badge) : undefined;\n if (typeof badge === 'string' && badge.trim()) return badge.trim();\n return undefined;\n}\n"]}
1
+ {"version":3,"sources":["../src/tab-scaffold.tsx"],"names":[],"mappings":"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","file":"tab-scaffold.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge } from './badge.js';\nimport type { PaneAppearance } from './pane.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\ninterface TabScaffoldTabBase<Id extends string> {\n id: Id;\n label: string;\n /** Decorative icon shown above the label in the bottom bar. */\n icon?: SafeHtml;\n /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */\n content: KerfUiContent;\n /** Background of this scene's scrolling work surface. */\n appearance?: PaneAppearance;\n}\n\n/** A tab with an optional count or short-status badge. */\ninterface TabScaffoldTabCountBadge {\n /**\n * Optional count or short status shown as a solid danger `Badge` at the\n * top-trailing corner of the tab icon (the iOS tab-bar badge). Omitted, `''`,\n * or non-finite numbers render no badge. The visual badge is `aria-hidden`;\n * its meaning reaches assistive technology through `badgeLabel`.\n *\n * Pass `true` instead for the text-free dot (new content without a count).\n */\n badge?: string | number;\n /**\n * Localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"3 unread\"` → `\"Inbox, 3 unread\"`).\n * Defaults to the badge text itself. Ignored when no badge renders.\n */\n badgeLabel?: string;\n}\n\n/** A tab with the text-free dot badge (new content without a count). */\ninterface TabScaffoldTabDotBadge {\n /**\n * `true` shows a solid danger dot `Badge` at the top-trailing corner of the\n * tab icon — the iOS tab-bar dot for new content without a count.\n */\n badge: true;\n /**\n * Required localized phrase folded into the tab's accessible name as\n * `\"<label>, <badgeLabel>\"` (for example `\"New activity\"` →\n * `\"Feed, New activity\"`). A dot has no text, so this is its only meaning.\n */\n badgeLabel: string;\n}\n\n/** One bottom-bar destination: its label, optional icon and badge, and content. */\nexport type TabScaffoldTab<Id extends string = string> =\n TabScaffoldTabBase<Id> & (TabScaffoldTabCountBadge | TabScaffoldTabDotBadge);\n\nexport interface TabScaffoldProps<Id extends string = string> {\n id: string;\n /** Accessible name for the tab bar. */\n label: string;\n tabs: readonly TabScaffoldTab<Id>[];\n /** The controlled active tab id (the app owns selection). */\n active: NoInfer<Id>;\n /**\n * The line over the tab bar, where it meets the active scene. `scroll`\n * (default) shows it only while more of the scene's content lies below —\n * never at the scroll end or when the content fits — once\n * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above\n * the scaffold; unwired, it does not show. `always` shows it without the\n * wiring; `none` never, even when wired. The line is the bar's own 1px top\n * border, so no state moves the bar or a tab.\n */\n chromeDividers?: 'scroll' | 'always' | 'none';\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches\n * between major sections, each tab keeping its own content (usually a `NavStack`)\n * mounted so its stack and scroll survive a switch. Controlled — the app owns\n * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.\n * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of\n * a bottom bar. See `docs/23-app-layouts.md` §3.4.\n */\nexport function TabScaffold<Id extends string>({\n id,\n label,\n tabs,\n active,\n chromeDividers = 'scroll',\n className = '',\n slot,\n}: TabScaffoldProps<Id>) {\n return (\n <section\n class={`kui-tab-scaffold ${className}`.trim()}\n id={id}\n data-component=\"tab-scaffold\"\n data-tab-scaffold-id={id}\n data-chrome-dividers={\n chromeDividers === 'always' || chromeDividers === 'none'\n ? chromeDividers\n : undefined\n }\n slot={slot}\n >\n <div class=\"kui-tab-scaffold__scenes\">\n {tabs.map((tab) => (\n <div\n class=\"kui-tab-scaffold__scene\"\n data-tab-scaffold-scene={tab.id}\n data-active={String(tab.id === active)}\n data-appearance={tab.appearance === 'sunken' ? 'sunken' : undefined}\n aria-hidden={String(tab.id !== active)}\n >\n {tab.content}\n </div>\n ))}\n </div>\n <nav class=\"kui-tab-scaffold__bar\" role=\"tablist\" aria-label={label}>\n {tabs.map((tab) => {\n const dot = tab.badge === true;\n const badgeText = dot ? undefined : normalizeBadge(tab.badge);\n const hasBadge = dot || badgeText !== undefined;\n const badge = hasBadge && (\n <span\n class=\"kui-tab-scaffold__tab-badge\"\n data-badge-kind={dot ? 'dot' : 'text'}\n >\n {badgeText === undefined ? (\n <Badge tone=\"danger\" size=\"dot\" ariaHidden />\n ) : (\n <Badge\n tone=\"danger\"\n appearance=\"solid\"\n size=\"compact\"\n ariaHidden\n >\n {badgeText}\n </Badge>\n )}\n </span>\n );\n // A dot has no text fallback: without a usable badgeLabel (reachable\n // only from untyped callers) the tab keeps its plain accessible name.\n const badgeLabel = hasBadge\n ? tab.badgeLabel?.trim() || badgeText\n : undefined;\n return (\n <button\n type=\"button\"\n class=\"kui-tab-scaffold__tab\"\n role=\"tab\"\n data-tab-scaffold-tab={tab.id}\n aria-selected={String(tab.id === active)}\n tabindex={tab.id === active ? '0' : '-1'}\n aria-label={\n badgeLabel === undefined\n ? undefined\n : `${tab.label}, ${badgeLabel}`\n }\n data-has-badge={hasBadge ? 'true' : undefined}\n >\n {tab.icon ? (\n <span class=\"kui-tab-scaffold__tab-icon\" aria-hidden=\"true\">\n {tab.icon}\n {badge}\n </span>\n ) : (\n badge\n )}\n <span class=\"kui-tab-scaffold__tab-label\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </section>\n );\n}\n\nfunction normalizeBadge(\n badge: string | number | undefined,\n): string | undefined {\n if (typeof badge === 'number')\n return Number.isFinite(badge) ? String(badge) : undefined;\n if (typeof badge === 'string' && badge.trim()) return badge.trim();\n return undefined;\n}\n"]}
@@ -8,8 +8,8 @@ interface TokenSearchRule {
8
8
  parse?: (input: string) => string | undefined;
9
9
  /** Label for a committed chip. Defaults to `name:input`. */
10
10
  label?: (value: string, input: string) => string;
11
- /** Suggestions for the unfinished value after `name:`. */
12
- suggest?: (input: string) => readonly (string | {
11
+ /** Suggestions for the unfinished value after `name:`, with current committed tokens. */
12
+ suggest?: (input: string, state: TokenSearchState) => readonly (string | {
13
13
  value: string;
14
14
  label: string;
15
15
  })[];
@@ -33,8 +33,12 @@ interface TokenSearchModel<Result = unknown> {
33
33
  suggestions: Signal<readonly TokenSearchSuggestion[]>;
34
34
  result: Signal<Result | undefined>;
35
35
  edit(value: TokenSearchFieldValue, commit?: boolean): void;
36
+ /** Replace app-owned search state and rebuild the DOM-owned editor text. */
37
+ replace(value: TokenSearchFieldValue): void;
36
38
  submit(value: TokenSearchFieldValue): void;
37
39
  choose(value: string): void;
40
+ /** Commit a value for the active `name:` prefix, even when it is not suggested. */
41
+ commit(value: string): void;
38
42
  expandToken(value: string): void;
39
43
  remove(value: string): void;
40
44
  clear(): void;
@@ -1,3 +1,3 @@
1
1
  import 'kerfjs';
2
2
  import './semantic-content-BbzjvSu9.js';
3
- export { T as TokenSearchEditorAttributes, a as TokenSearchField, b as TokenSearchFieldProps, c as TokenSearchFieldValue, j as TokenSearchToken, p as placeTokenSearchCaret, r as readTokenSearchField } from './token-search-field-CCke1yvQ.js';
3
+ export { T as TokenSearchEditorAttributes, a as TokenSearchField, b as TokenSearchFieldProps, c as TokenSearchFieldValue, j as TokenSearchToken, p as placeTokenSearchCaret, r as readTokenSearchField } from './token-search-field-DvgFLhLL.js';
@@ -1,3 +1,3 @@
1
1
  import 'kerfjs';
2
- export { d as TokenSearchModel, e as TokenSearchModelOptions, f as TokenSearchResolvedToken, g as TokenSearchRule, h as TokenSearchState, i as TokenSearchSuggestion, k as createTokenSearchModel } from './token-search-field-CCke1yvQ.js';
2
+ export { d as TokenSearchModel, e as TokenSearchModelOptions, f as TokenSearchResolvedToken, g as TokenSearchRule, h as TokenSearchState, i as TokenSearchSuggestion, k as createTokenSearchModel } from './token-search-field-DvgFLhLL.js';
3
3
  import './semantic-content-BbzjvSu9.js';
@@ -1,3 +1,3 @@
1
- export { createTokenSearchModel } from './chunk-F7F7KRO2.js';
1
+ export { createTokenSearchModel } from './chunk-4SQK27MR.js';
2
2
 
3
3
  //# sourceMappingURL=token-search-model.js.map
@@ -1,6 +1,6 @@
1
1
  import * as kerfjs from 'kerfjs';
2
2
 
3
- type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small' | 'xsmall';
3
+ type ToolbarTextSize = 'xlarge' | 'xlarge-fixed' | 'large' | 'default' | 'small' | 'xsmall';
4
4
  /** ARIA heading level for a title exposed as a heading landmark. */
5
5
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
6
6
  interface ToolbarTextBaseProps {
@@ -1,4 +1,4 @@
1
- export { ToolbarText } from './chunk-3SXMEK3B.js';
1
+ export { ToolbarText } from './chunk-V3DX3K65.js';
2
2
 
3
3
 
4
4
 
package/dist/toolbar.d.ts CHANGED
@@ -17,8 +17,8 @@ interface ToolbarProps {
17
17
  * then draws its facing side the same way.
18
18
  */
19
19
  dividerSides?: Sides;
20
- /** Horizontal treatment of the center zone. Defaults to centered content. */
21
- centerAlign?: 'center' | 'stretch';
20
+ /** `balanced` gives both side zones equal tracks, centering the middle zone on the toolbar even if only one side has controls. */
21
+ centerAlign?: 'center' | 'stretch' | 'balanced';
22
22
  /**
23
23
  * Component-owned responsive layout; applications choose the policy rather
24
24
  * than restyling toolbar internals. Under every policy the trailing zone
package/dist/toolbar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Toolbar } from './chunk-MERWRTBG.js';
1
+ export { Toolbar } from './chunk-N2DRU35S.js';
2
2
 
3
3
  //# sourceMappingURL=toolbar.js.map
@@ -1,6 +1,6 @@
1
1
  import { wireWorkbench } from './chunk-NNMLEDMD.js';
2
- import { wireScrollDividers } from './chunk-JCV4GXWB.js';
3
2
 
3
+ import { wireScrollDividers } from './chunk-JCV4GXWB.js';
4
4
 
5
5
 
6
6
  import { delegate } from 'kerfjs';
@@ -1,3 +1,3 @@
1
- export { reorderTabs, wireTabBars } from './chunk-46263QLK.js';
1
+ export { reorderTabs, wireTabBars } from './chunk-UA6JW52D.js';
2
2
 
3
3
  //# sourceMappingURL=wire-tab-bars.js.map
@@ -1,5 +1,5 @@
1
1
  import { Signal } from 'kerfjs';
2
- import { d as TokenSearchModel } from './token-search-field-CCke1yvQ.js';
2
+ import { d as TokenSearchModel } from './token-search-field-DvgFLhLL.js';
3
3
  import './semantic-content-BbzjvSu9.js';
4
4
 
5
5
  interface TokenSearchSubmit {
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-2OXQHM7I.js';
1
+ export { Workbench } from './chunk-X6ADHJNI.js';
2
2
 
3
3
 
4
4
 
@@ -7,6 +7,7 @@ These are normative contracts for package components and consuming applications.
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
9
  - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
+ - `ListItemLink` shares that row content and geometry but uses native anchor Enter behavior and requires `href`. It has no `action` or `pressed` state. `external` opens a new tab with `rel="noopener noreferrer"`; the default accessible name announces the new tab for a string label, so an explicit `accessibleLabel` should do the same. Disabled and loading links omit `href`, expose `aria-disabled`, and leave the tab order. Its trailing content remains dormant.
10
11
  - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
12
  - Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
12
13
  - `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
@@ -14,6 +15,7 @@ These are normative contracts for package components and consuming applications.
14
15
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
15
16
  - An unlabeled `Skeleton` (including the value slots a component's `placeholder` prop renders) is decorative loading chrome: it is `aria-hidden`, conveys no information a user needs to act on, and is deliberately exempt from the WCAG 3:1 non-text-contrast target. Inside a solid `Badge` it stays subtle (about 2.1–4.4:1 in light, 2.4–2.8:1 in dark, depending on tone); `tests/browser/tone-contrast.spec.ts` holds those values as a regression floor rather than an accessibility threshold. Announce loading through a labeled `Skeleton`, `LoadingSpinner`, or `aria-busy` on the owning region, never through the skeleton's visibility.
16
17
  - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
18
+ - A removable `Chip` has a native button with a specific `removeLabel`, such as "Remove Urgent tag". The button is absent for a plain chip and disabled for a disabled chip. Tag text remains visible because tone does not convey meaning alone.
17
19
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
18
20
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
19
21
  - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may resize it with the typed `size` prop without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
@@ -38,6 +40,12 @@ chosen option shows its check, and the closed control reads the chosen labels
38
40
  in choice order as one short localized list (the `placeholderText` when none
39
41
  are chosen) instead of a removable tag per choice. `change` and `input` report
40
42
  the value array; in a form, every chosen value is submitted under `name`.
43
+ Disabled choices remain visible with `aria-disabled` and cannot be selected;
44
+ `disabledReason` adds a native tooltip. For a multiple Select, opt-in
45
+ `selectAllLabel` and `clearLabel` render named footer buttons. They keep the
46
+ popup open, select only enabled choices or clear the selection, and emit the
47
+ ordinary `input` and `change` events only when the value changes. Their labels
48
+ come from the application so they can be localized.
41
49
 
42
50
  A toolbar filter menu (a "Filter by label" funnel) is a multiple Select with
43
51
  `selectedPresentation="icon-only"` and a fixed `triggerIcon`; the types accept
@@ -196,7 +204,8 @@ with the Select's current-option fill rather than a second focus ring inside the
196
204
  open popup. A disabled item stays in the menu but cannot be
197
205
  chosen; disable the whole trigger only when every command is unavailable.
198
206
  Nested items use Web Awesome's submenu pointer and Left/Right arrow routing;
199
- checked items expose checkbox menu semantics. `details` supplies optional
207
+ headings and dividers can separate nested groups without becoming keyboard
208
+ choices. Checked items expose checkbox menu semantics. `details` supplies optional
200
209
  trailing visual content without changing a command's accessible label. A
201
210
  `context` menu's invisible anchor is outside the accessibility tree and normal
202
211
  Tab order; opening the menu moves focus into its commands and Escape dismisses
@@ -317,7 +326,11 @@ trailing, and end actions remain visible and do not shrink. The component owns
317
326
  that geometry; applications must not override its anatomy to create the split.
318
327
  `AppTab` provides compact 32px, segmented, truncating-label, and icon-only
319
328
  presentations. Icon-only tabs keep the required `name` as the tab button's
320
- accessible name while visually hiding the duplicate label. A `pending` tab (known,
329
+ accessible name while visually hiding the duplicate label. The default name
330
+ overflow is an ellipsis; `nameOverflow="visible"` lets an inline loading
331
+ treatment show the whole name, without changing the accessible name. The app
332
+ sets `dropTarget` during a content drag-over to paint a brand highlight;
333
+ selection and tab focus stay independent of that visual state. A `pending` tab (known,
321
334
  still opening) keeps its visible name as the tab button's accessible name, so a
322
335
  trailing spinner's own label is not folded into it. It is `aria-busy` and stays
323
336
  selectable (in the roving tab order like a live tab), so the application can
@@ -330,6 +343,12 @@ filtering rejects roles plus case variants of component- or wiring-owned
330
343
  action, identity, selection, drag, drop, and component attributes. An optional
331
344
  `closeIcon` is decorative dormant content inside the already named close button
332
345
  and must not contain interactive descendants.
346
+ The first `AppTab` can set `pinned` to stay at the inline start while its peers
347
+ scroll. It remains a normal tab within the same tablist and roving-focus order;
348
+ focus and controlled selection reveal peers beyond the pinned tab. Pin only the
349
+ leading tab of a strip.
350
+ WebKit currently moves a pinned tab outside an RTL strip as it scrolls;
351
+ `KF-MYFQAS` tracks the browser correction.
333
352
  `wireTabBars()` adds Left/Right wrapping, Home/End, Delete/Backspace close
334
353
  activation, same-bar pointer reordering, `Alt+Shift+ArrowLeft/ArrowRight`
335
354
  reordering, focus restoration, scroll-into-view, and pointer-proximity
@@ -363,7 +382,8 @@ stays in sequential Tab order, matching Hot Sheet 2's compact view and inspector
363
382
  controls; Enter and Space use native button activation. The application handles
364
383
  the supplied `data-action`, reads `data-segment-value`, updates `value`, and
365
384
  re-renders. Keep labels unique and meaningful even when `content` shows only an
366
- icon. Use tabs—not a segmented control—when choices switch page regions that
385
+ icon. The filled track uses the normal neutral text role for readable unselected
386
+ labels in light and dark themes. Use tabs—not a segmented control—when choices switch page regions that
367
387
  need `tab`/`tabpanel` semantics.
368
388
 
369
389
  ## TokenSearchField
@@ -390,6 +410,12 @@ trailing controls share the first text line's fixed vertical center and remain
390
410
  there as the editor wraps. In `collapsible` mode, the closed state is one named
391
411
  iconic search button and the open state is the same named searchbox, whether
392
412
  the field stands alone or is composed inside `ToolbarControlGroup`.
413
+
414
+ For an app-driven query replacement with an unmanaged field, change its
415
+ `revision` along with `query` so the editor text rebuilds. For a model-managed
416
+ field, call `model.replace({ query, tokens })`: it parses the supplied state and
417
+ advances `editorRevision` even when the tokens did not change.
418
+
393
419
  Applications set `expanded` while the field is focused, move focus from the
394
420
  trigger into the revealed editor, and clear `expanded` only after focus leaves
395
421
  the complete component. Text or tokens keep the field expanded even when that
@@ -80,9 +80,10 @@ and that of the components it renders (`Pane`, `Toolbar`, `ToolbarControlGroup`,
80
80
  corners, with typed `restoreInset` for the corner distance. Set
81
81
  `restorePlacement="inline"` when the container should place the control in
82
82
  normal flow. An inline region's surface uses
83
- `--kui-resizable-region-background`; set `contentOverflow="visible"` while
84
- an anchored popup must escape the region's content box, then return to the
85
- normal overflow policy when it closes.
83
+ `--kui-resizable-region-background`; an open descendant `PopupMenu` or Web
84
+ Awesome dropdown releases clipping and raises the popup layer for its
85
+ lifecycle. Set `contentOverflow="visible"` for other anchored content that
86
+ must escape the region's content box.
86
87
  Overlay presentation also clamps fixed-size animated content to the configured
87
88
  responsive overlay maximum, so a remembered desktop size cannot escape a narrow
88
89
  viewport.
@@ -52,6 +52,11 @@ CSS boundaries, diagnostics, and provenance. A wrapper entry may add
52
52
  composition checks treat its elements as each root rather than as an unknown
53
53
  child. A placement with listed parent constraints produces one `KUI-L201`
54
54
  diagnostic even when both the wrapper and a rendered root reject that parent.
55
+ When one catalog entry exports another JSX component with different placement,
56
+ `jsxExports` gives that export its own parent contract. The extra export does
57
+ not stand for the entry's root when a parent zone is checked.
58
+ Generated `boundaries.rootClass` prefers the public `kui-<entry-id>` class when
59
+ present, so a sibling export's class cannot become the entry's root class.
55
60
 
56
61
  `boundaries.placeableClasses` names the subset of `publicClasses` an
57
62
  application may write onto its own elements (layout utilities, the document
@@ -72,6 +72,8 @@ import { Row } from "@kerfjs/ui/row";
72
72
  ```
73
73
 
74
74
  `CssValue` is the common branded base, not an arbitrary-string constructor.
75
+ For a responsive equal-width form, pass `Grid minColumnWidth={px(376)}` instead
76
+ of `columns`; the Grid's container width decides when the tracks collapse.
75
77
  Raw strings such as `"0.25rem"` do not satisfy `Row.gap`, `Grid.gap`, or `List.gap`; this
76
78
  contract lands before the 5.0 stable release so misspelled tokens and incomplete
77
79
  expressions fail during typechecking rather than in the browser.
@@ -141,7 +143,7 @@ an upstream component or recipe request.
141
143
  | Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel`. Sizes below `xlarge` are intentionally quiet identity text with no tone option — do not use them as a normal-color subsection title; use `Text` with an `h2`–`h6` variant, or `ListHeader` for a list section. | Optional `headingLevel` for `role="heading"` + `aria-level`. Defaults to one line, ellipsized when it does not fit; `wrap` flows onto multiple lines, `ellipsis` toggles the trailing …, and `maxLines` caps a wrap. | Text, heading level, overflow behavior, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
142
144
  | Semantic block or inline text — `Text` | Ordinary application copy needs native `h1`–`h6` or `p` semantics plus standard geometry, or inline secondary copy needs a native `span` without box geometry. | Do not choose heading levels for appearance alone or recreate muted, error, small, or code utility classes. Use `ToolbarText` for toolbar identity/title content and `ListInsetText` when list-aligned prose needs full margin/border/padding geometry. | Omit `variant` for the default `p`, choose `h1`–`h6` from the document outline, or choose `span` inside a row, label, or table cell. Independently compose `tone="quiet | danger"`, `size="compact"`, and `font="monospace"`; ordinary native global, `data-_`, and `aria-_` attributes pass through. | Copy, localization, semantic variant, presentation roles, and document heading hierarchy. | `@kerfjs/ui/text` | [Text](./layout.md#text) |
143
145
  | Vertical stack — `List` | Rows, sections, or arbitrary components need a consistent vertical stretch layout, optional standard/custom gap, flex growth, one vertical scroll owner, or edge dividers. | It is layout-only and does not provide `ul`/`ol` semantics. Do not nest scroll owners or use it when a horizontal layout is required. | Choose `gap`, `flex`, `scrollable`, and canonical `dividerSides` (`t`, `r`, `b`, `l` combinations in that order); bound the block size when scrolling. | Child semantics/content, containing height, and which optional layout behaviors apply. | `@kerfjs/ui/list` | [Pane geometry](../README.md#pane-and-content-geometry) |
144
- | Equal-width columns — `Grid` | A fixed positive number of sibling columns must divide the available width evenly, including when child content has different intrinsic widths. | Do not use it for intrinsic, asymmetric, spanning, auto-fit, or masonry tracks; use application-owned CSS grid. Do not use it for a user-operable separator; use `ResizableRegion`. | Choose `columns`, a typed `gap`, and optional typed `flex`; preserve source order when the app changes the count responsively. | Child semantics/content, the column count and responsive policy, and typed gap/flex choices. | `@kerfjs/ui/grid` | [Grid](./layout.md#grid) |
146
+ | Equal-width columns — `Grid` | Equal-width sibling columns need a fixed count or should collapse as their container narrows, while preserving equal tracks despite intrinsic child widths. | Do not use it for intrinsic, asymmetric, spanning, or masonry tracks; use application-owned CSS grid. Do not use it for a user-operable separator; use `ResizableRegion`. | Choose fixed `columns` or typed `minColumnWidth`, plus a typed `gap` and optional `flex`; responsive tracks preserve source order. | Child semantics/content, outer width, fixed count or minimum width, and typed gap/flex choices. | `@kerfjs/ui/grid` | [Grid](./layout.md#grid) |
145
147
  | Navigation row — `ListItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `ListActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. The rounded root clips overflowing descendants; set `multiline` when the primary label should wrap. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
146
148
  | Navigation row with trailing action — `ListActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `ListItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/list-action-row` | [Accessibility](./accessibility.md#listactionrow) |
147
149
  | Navigation section heading — `ListHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Keep its 44px hit target; do not resize its 36px square. Do not use it as a page, panel, or dialog title; use a heading `Toolbar`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/list-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
@@ -174,10 +176,16 @@ an upstream component or recipe request.
174
176
 
175
177
  ## Ambiguous choices
176
178
 
179
+ For a lowered background behind an entire scrolling work area, choose
180
+ `Pane appearance="sunken"` or the appearance option on the layout that owns
181
+ the scroll viewport. Use `SunkenPanel` for a padded content group that does
182
+ not introduce scrolling. See [Lowered work surfaces](./layout.md#lowered-work-surfaces).
183
+
177
184
  Set `ResizableRegion`'s `--kui-resizable-region-background` for an inline
178
- surface. Use `contentOverflow="visible"` while an anchored popup must escape
179
- the region; this also raises the region on its public popup layer. A collapsed
180
- region can place `restoreControl` in a top or bottom corner with
185
+ surface. An open descendant `PopupMenu` or Web Awesome dropdown automatically
186
+ releases clipping and raises the region on its public popup layer. Set
187
+ `contentOverflow="visible"` for other anchored content that must escape the
188
+ region. A collapsed region can place `restoreControl` in a top or bottom corner with
181
189
  `restorePosition`, set its typed `restoreInset`, or use
182
190
  `restorePlacement="inline"` for a control in normal flow.
183
191
 
@@ -194,21 +202,41 @@ whole label. `divider` owns a before or after separator. Set
194
202
  `--kui-list-item-*` tokens control its row, icon, label, and trailing geometry
195
203
  and color. `ListHeader` exposes `--kui-list-header-min-height`, title padding,
196
204
  title minimum height, and border-width tokens for section density.
205
+ For a filled command list, set `--kui-list-item-background` and
206
+ `--kui-list-item-border` on the list or row. The matching
207
+ `--kui-list-item-hover-border` and `--kui-list-item-selected-border` tokens
208
+ set state outlines without replacing ListItem CSS or neutralizing global
209
+ semantic border colors.
210
+
211
+ Use `ListItemLink` from `@kerfjs/ui/list-item` for a navigation row with a
212
+ real `href`. It shares `ListItem` content slots, density, selection, and row
213
+ geometry while rendering a native anchor. `external` opens a new tab with
214
+ `noopener noreferrer` and announces that behavior in the default accessible
215
+ name. Disabled and loading links omit `href` and leave the tab order. Use
216
+ `ListItem` for an application action dispatched through `action`.
197
217
 
198
218
  Use `Toolbar responsive="trailing-priority"` for a collapsible search in the
199
219
  trailing zone that should take a full second row when expanded. `responsiveAt`
200
220
  chooses the compact or narrow container breakpoint. The leading identity stays
201
- in the first row. `--kui-toolbar-leading-min-width`,
221
+ in the first row. Set `centerAlign="balanced"` when the center zone must sit on
222
+ the toolbar's midpoint despite unequal or empty side content; it gives the
223
+ leading and trailing zones equal tracks. `--kui-toolbar-leading-min-width`,
202
224
  `--kui-toolbar-trailing-gap`, `--kui-toolbar-trailing-justify`, and the
203
225
  `--kui-toolbar-*-padding-inline` zone tokens adjust local geometry without
204
226
  selecting toolbar anatomy from application CSS.
227
+ Set `--kui-toolbar-inset: 0px` on an embedded Toolbar that should have no outer
228
+ padding while keeping its `--kui-toolbar-gap` between control zones. The token
229
+ also reduces its minimum height by the removed inset; safe-area compensation
230
+ still adds to the chosen inset on claimed edges.
205
231
 
206
232
  For compact dialog copy, use `Text flush lineHeight="tight"`; block Text still
207
233
  resets native margins. `ValueTable density="compact"` reduces row padding and
208
234
  gap. Its `--kui-value-table-row-columns`, `--kui-value-table-row-padding-block`,
209
235
  and `--kui-value-table-row-gap` tokens support a specific metadata layout.
210
- Use `ToolbarText size="xsmall"` for a dense rail heading. A
211
- `TokenSearchField` editor can shrink to zero minimum width inside a narrow
236
+ Use `ToolbarText size="xsmall"` for a dense rail heading. Use
237
+ `size="xlarge-fixed"` for a toolbar heading at the fixed 20px step; `xlarge`
238
+ retains the responsive page-title clamp. Pass `headingLevel` when the text is
239
+ a section landmark. A `TokenSearchField` editor can shrink to zero minimum width inside a narrow
212
240
  group; set `--kui-token-search-editor-min-width` only when a wider editor is
213
241
  required.
214
242
 
@@ -216,11 +244,25 @@ required.
216
244
  - `TabBar` changes tabpanels and supports overflow/reorder; `SegmentedControl` chooses among a few compact views; `Select` handles a longer value list.
217
245
 
218
246
  Use `AppTab presentation="icon-only"` with its `name` kept as the accessible
219
- tab name, and `labelMaxWidth` for an ellipsized visible name. Set `attention`
220
- to color a tab's name with `--kui-app-tab-attention-color`. A `TabBar` owns its
247
+ tab name. For segmented tabs that should show names in a wide reader and icons
248
+ in a narrower container, set `TabBar iconOnlyAt="wide"` (832px), `"narrow"`
249
+ (704px), or `"compact"` (448px), and give each segmented `AppTab` a leading
250
+ icon. The tablist's own width controls the switch; the visually hidden name
251
+ remains its accessible name. Use `labelMaxWidth` for an ellipsized visible name. Set `attention`
252
+ to color a tab's name with `--kui-app-tab-attention-color`. Set `dropTarget`
253
+ while an app drag is over a tab's content; the tab keeps its selection semantics
254
+ and uses the `--kui-app-tab-drop-target-*` tokens for its highlight. Set
255
+ `nameOverflow="visible"` when an inline loading treatment needs the full name
256
+ instead of the default ellipsis. A `TabBar` owns its
221
257
  scrolling strip; its public `--kui-tab-bar-strip-*` tokens configure strip
222
258
  height, spacing, border, radius, background, and scroll inset without styling
223
259
  its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
260
+ Set `pinned` on the first `AppTab` when that tab must remain visible as peers
261
+ scroll. It stays inside the tablist and keyboard order. Set the public
262
+ `--kui-app-tab-pinned-background` token if the surrounding surface differs from
263
+ the default; the pinned tab must cover peers as they scroll beneath it. Call
264
+ `wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
265
+ scroll correction.
224
266
 
225
267
  - `StateBanner` persists beside affected work; `EmptyState` replaces absent content; `wa-callout` is contextual ecosystem content; `wa-toast` and `wa-toast-item` are transient and must not carry the only copy of important state.
226
268
 
@@ -232,11 +274,21 @@ stacked copy's spacing at an instance boundary.
232
274
 
233
275
  For a compact `Select`, set `triggerWidth` to `"fit-content"`, `"max-content"`,
234
276
  or `"fill"` and cap it with `--kui-select-trigger-max-width` when needed.
277
+ For a round icon-only toolbar trigger, set `selectedPresentation="icon-only"`
278
+ and `caret={false}`. The control keeps its accessible name, current choice help
279
+ tag, and keyboard-operated listbox; the default keeps the visible caret.
235
280
  Custom `renderSelected` text inherits the control color by default; the
236
281
  `--kui-select-selected-color`, `--kui-select-selected-font-size`, and
237
282
  `--kui-select-selected-font-weight` tokens configure its typography. Plain
238
283
  selected text stays on one ellipsized line when the trigger narrows.
239
284
 
285
+ For an unavailable entry, set `SelectChoice.disabled` and optionally
286
+ `disabledReason`; the option stays visible but cannot be chosen. A multiple
287
+ `Select` can opt in to `selectAllLabel` and `clearLabel` footer buttons.
288
+ Select all includes only enabled choices, Clear removes the selection, and
289
+ both report changes through the normal `input` and `change` events. The app
290
+ still owns the controlled `value` and localized action labels.
291
+
240
292
  - `ResizableRegion` is an interactive controlled pane. `Grid` is the right answer for static equal-width columns; application CSS grid remains the answer for asymmetric or intrinsic tracks.
241
293
  - A `ResizableRegion`'s content spans the whole region, like its separator. Give
242
294
  it one child — normally a `Pane` — and that child fills the region, so the
@@ -271,6 +323,9 @@ selected text stays on one ellipsized line when the trigger narrows.
271
323
  - `TokenSearchField` is a structured editor. A native input or `wa-input` is the right answer for ordinary text.
272
324
 
273
325
  For common structured search, use `createTokenSearchModel` from `@kerfjs/ui/token-search-model`, pass the model to `TokenSearchField`, and register it under the field id in `wireTokenSearchFields(root, { models })`. Rules parse `name:value` expressions and provide suggestions; the model owns chips, selection, removal, clear, and an optional evaluation result. Omit `model` and `models` for full manual control.
326
+ `TokenSearchRule.suggest(input, state)` receives the unfinished value and current committed tokens, so a rule can omit values already selected. Existing one-argument suggestion callbacks continue to work.
327
+ For a sibling date picker or other app helper, call `model.commit(value)` while a `name:` prefix is active. It parses and commits that value for the active rule even when it is absent from suggestions, replacing the unfinished prefix text. Invalid values and calls without an active prefix leave the query unchanged; `choose(value)` remains restricted to suggested tokens.
328
+ To apply a saved search or restore a query programmatically, call `model.replace({ query, tokens })`. It publishes parsed state and rebuilds the DOM-owned editor text even when no chip changed. For a field without a model, change the field's `revision` prop when setting `query` yourself.
274
329
  When one root contains both kinds of field, a registered model enables chip keyboard behavior only for its own field. Other fields keep native editing unless the root opts into `keyboard` with an application removal callback.
275
330
 
276
331
  ## Toolbar composition
@@ -338,7 +393,8 @@ menu.
338
393
 
339
394
  Nested decisions use an item with `submenu`. Children keep their own actions,
340
395
  `attributes` metadata, icons, `checked` state, disabled state and
341
- `disabledReason` tooltip. `tone: 'danger'` maps to the Web Awesome destructive
396
+ `disabledReason` tooltip. Nested `heading` and `divider` entries group those
397
+ commands without entering keyboard navigation. `tone: 'danger'` maps to the Web Awesome destructive
342
398
  variant. A parent can sit among ordinary commands and dividers, so the same
343
399
  items shape covers single-line and batch actions; compute each batch child's
344
400
  `disabled` state from the current selection.
@@ -349,8 +405,11 @@ items shape covers single-line and batch actions; compute each batch child's
349
405
  items={[
350
406
  { label: 'Copy reference', action: 'copy-reference', attributes: { 'data-item-id': lineId } },
351
407
  { label: 'Decide', submenu: [
408
+ { kind: 'heading', label: 'Review' },
352
409
  { label: 'Approve', action: 'decide', checked: current === 'approve', attributes: { 'data-item-id': lineId, 'data-decision': 'approve' } },
353
410
  { label: 'Reject', action: 'decide', tone: 'danger', disabled: needsPrice, disabledReason: needsPrice ? 'Add a price first' : undefined, attributes: { 'data-item-id': lineId, 'data-decision': 'reject' } },
411
+ { kind: 'divider' },
412
+ { label: 'Other…', action: 'choose-other' },
354
413
  ] },
355
414
  { kind: 'divider' },
356
415
  { label: 'Remove', action: 'remove', tone: 'danger' },
@@ -421,6 +480,28 @@ forks the package anatomy and spacing contract.
421
480
  </aside>
422
481
  ```
423
482
 
483
+ ## Removable tags
484
+
485
+ Use `Chip` when a short tag has a remove action. `removeAction` becomes the
486
+ native button's `data-action`; `removeLabel` must name the specific tag for
487
+ assistive technology. Use `itemId` for the application's delegated handler.
488
+ The application updates its tag list after the action. `size="compact"` uses a
489
+ 20px chip with a 16px remove button, and `disabled` disables removal. Tone,
490
+ appearance, and shape use the same semantic choices as `Badge`; use `Badge`
491
+ when no removal is needed and the content is status or count metadata.
492
+
493
+ ```tsx
494
+ <Chip
495
+ size="compact"
496
+ tone="brand"
497
+ itemId={tag.id}
498
+ removeAction="remove-tag"
499
+ removeLabel={`Remove ${tag.name} tag`}
500
+ >
501
+ {tag.name}
502
+ </Chip>
503
+ ```
504
+
424
505
  ## Web Awesome overlap policy
425
506
 
426
507
  Web Awesome catalog coverage means supported and themed, not preferred. Import
@@ -437,7 +518,7 @@ replaces it (`PopupMenu` renders `wa-dropdown`; `Select` renders `wa-select` /
437
518
  | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
438
519
  | `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use buttons for actions and `PopupMenu` for a command menu, never raw dropdown markup. Use `ListItem` for a navigation row and `Select` for a value. |
439
520
  | `wa-button-group` | Use only for exceptional grouped actions; use `SegmentedControl` for one-of-many selection. |
440
- | `wa-input`, `wa-tag` | Use for ordinary text and tags; use `TokenSearchField` only when text and ordered filter tokens form one editor. |
521
+ | `wa-input`, `wa-tag` | Use `Chip` for removable or disabled tags; use `TokenSearchField` only when text and ordered filter tokens form one editor. |
441
522
  | `wa-select`, `wa-option` | Use `Select`, which owns Kerf spacing, controlled rendering, icon stability, and explicit registration. |
442
523
  | `wa-tab-group`, `wa-tab`, `wa-tab-panel` | Use `TabBar`/`AppTab` for application tabs or `SegmentedControl` for compact local views. |
443
524
  | `wa-icon` | Use `LucideIcon` in application UI. |
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="210.3" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="208.3" height="42" rx="11"/></clipPath><filter id="sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct3"><rect x="38.7" y="36" width="35.3" height="12"/></clipPath><clipPath id="ct4"><rect x="114.1" y="36" width="22" height="12"/></clipPath><clipPath id="ct5"><rect x="168.5" y="36" width="50.8" height="12"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="210.3" height="44" rx="12" fill="rgb(58,58,60)"/><rect x="20.5" y="20.5" width="209" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#cp0)"><g clip-path="url(#ov1)"><rect x="22" y="23" width="68.8" height="40" rx="10" fill="rgba(0,0,0,0.5)" filter="url(#sh2)"/><rect x="22" y="22" width="68.8" height="40" rx="10" fill="rgb(28,28,30)"/><rect x="22.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="38.7" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(138,203,255)">Board</text><rect x="91.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="114.13" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">List</text><rect x="160.5" y="22.5" width="67" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="168.5" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">Timeline</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="210.3" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="208.3" height="42" rx="11"/></clipPath><filter id="sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct3"><rect x="38.7" y="36" width="35.3" height="12"/></clipPath><clipPath id="ct4"><rect x="114.1" y="36" width="22" height="12"/></clipPath><clipPath id="ct5"><rect x="168.5" y="36" width="50.8" height="12"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="210.3" height="44" rx="12" fill="rgb(58,58,60)"/><rect x="20.5" y="20.5" width="209" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#cp0)"><g clip-path="url(#ov1)"><rect x="22" y="23" width="68.8" height="40" rx="10" fill="rgba(0,0,0,0.5)" filter="url(#sh2)"/><rect x="22" y="22" width="68.8" height="40" rx="10" fill="rgb(28,28,30)"/><rect x="22.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="38.7" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(138,203,255)">Board</text><rect x="91.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="114.13" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(245,245,247)">List</text><rect x="160.5" y="22.5" width="67" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="168.5" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(245,245,247)">Timeline</text></g></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="210.3" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="208.3" height="42" rx="11"/></clipPath><filter id="sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct3"><rect x="38.7" y="36" width="35.3" height="12"/></clipPath><clipPath id="ct4"><rect x="114.1" y="36" width="22" height="12"/></clipPath><clipPath id="ct5"><rect x="168.5" y="36" width="50.8" height="12"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="210.3" height="44" rx="12" fill="rgb(229,229,234)"/><rect x="20.5" y="20.5" width="209" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#cp0)"><g clip-path="url(#ov1)"><rect x="22" y="23" width="68.8" height="40" rx="10" fill="rgba(0,0,0,0.14)" filter="url(#sh2)"/><rect x="22" y="22" width="68.8" height="40" rx="10" fill="rgb(255,255,255)"/><rect x="22.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><text x="38.7" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(12,98,243)">Board</text><rect x="91.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="114.13" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">List</text><rect x="160.5" y="22.5" width="67" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="168.5" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">Timeline</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="210.3" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="208.3" height="42" rx="11"/></clipPath><filter id="sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct3"><rect x="38.7" y="36" width="35.3" height="12"/></clipPath><clipPath id="ct4"><rect x="114.1" y="36" width="22" height="12"/></clipPath><clipPath id="ct5"><rect x="168.5" y="36" width="50.8" height="12"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="210.3" height="44" rx="12" fill="rgb(229,229,234)"/><rect x="20.5" y="20.5" width="209" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#cp0)"><g clip-path="url(#ov1)"><rect x="22" y="23" width="68.8" height="40" rx="10" fill="rgba(0,0,0,0.14)" filter="url(#sh2)"/><rect x="22" y="22" width="68.8" height="40" rx="10" fill="rgb(255,255,255)"/><rect x="22.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><text x="38.7" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(12,98,243)">Board</text><rect x="91.5" y="22.5" width="68" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="114.13" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(29,29,31)">List</text><rect x="160.5" y="22.5" width="67" height="39" rx="9.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="168.5" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(29,29,31)">Timeline</text></g></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="141.4" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="139.4" height="42" rx="21"/></clipPath><clipPath id="ct2"><rect x="31.5" y="36.5" width="21.1" height="11"/></clipPath><filter id="sh3" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct4"><rect x="70" y="36.5" width="30.3" height="11"/></clipPath><clipPath id="ct5"><rect x="116.3" y="36.5" width="35.1" height="11"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="141.4" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="20.5" y="20.5" width="140" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><rect x="22.5" y="22.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="31.47" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(174,174,178)">Day</text><rect x="62" y="23" width="46.3" height="40" rx="20" fill="rgba(0,0,0,0.5)" filter="url(#sh3)"/><rect x="62" y="22" width="46.3" height="40" rx="20" fill="rgb(28,28,30)"/><rect x="62.5" y="22.5" width="45" height="39" rx="19.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="70" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(138,203,255)">Week</text><rect x="108.5" y="22.5" width="50" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="116.28" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(174,174,178)">Month</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="141.4" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="139.4" height="42" rx="21"/></clipPath><clipPath id="ct2"><rect x="31.5" y="36.5" width="21.1" height="11"/></clipPath><filter id="sh3" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct4"><rect x="70" y="36.5" width="30.3" height="11"/></clipPath><clipPath id="ct5"><rect x="116.3" y="36.5" width="35.1" height="11"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="141.4" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="20.5" y="20.5" width="140" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><rect x="22.5" y="22.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="31.47" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(245,245,247)">Day</text><rect x="62" y="23" width="46.3" height="40" rx="20" fill="rgba(0,0,0,0.5)" filter="url(#sh3)"/><rect x="62" y="22" width="46.3" height="40" rx="20" fill="rgb(28,28,30)"/><rect x="62.5" y="22.5" width="45" height="39" rx="19.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="70" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(138,203,255)">Week</text><rect x="108.5" y="22.5" width="50" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="116.28" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(245,245,247)">Month</text></g></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="141.4" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="139.4" height="42" rx="21"/></clipPath><clipPath id="ct2"><rect x="31.5" y="36.5" width="21.1" height="11"/></clipPath><filter id="sh3" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct4"><rect x="70" y="36.5" width="30.3" height="11"/></clipPath><clipPath id="ct5"><rect x="116.3" y="36.5" width="35.1" height="11"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="141.4" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="20.5" y="20.5" width="140" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><rect x="22.5" y="22.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="31.47" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(110,110,115)">Day</text><rect x="62" y="23" width="46.3" height="40" rx="20" fill="rgba(0,0,0,0.14)" filter="url(#sh3)"/><rect x="62" y="22" width="46.3" height="40" rx="20" fill="rgb(255,255,255)"/><rect x="62.5" y="22.5" width="45" height="39" rx="19.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><text x="70" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(12,98,243)">Week</text><rect x="108.5" y="22.5" width="50" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="116.28" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(110,110,115)">Month</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 88" width="460" height="88"><defs><clipPath id="cp0"><rect x="20" y="20" width="141.4" height="44"/></clipPath><clipPath id="ov1"><rect x="21" y="21" width="139.4" height="42" rx="21"/></clipPath><clipPath id="ct2"><rect x="31.5" y="36.5" width="21.1" height="11"/></clipPath><filter id="sh3" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="ct4"><rect x="70" y="36.5" width="30.3" height="11"/></clipPath><clipPath id="ct5"><rect x="116.3" y="36.5" width="35.1" height="11"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="20" y="20" width="141.4" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="20.5" y="20.5" width="140" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><rect x="22.5" y="22.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="31.47" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(29,29,31)">Day</text><rect x="62" y="23" width="46.3" height="40" rx="20" fill="rgba(0,0,0,0.14)" filter="url(#sh3)"/><rect x="62" y="22" width="46.3" height="40" rx="20" fill="rgb(255,255,255)"/><rect x="62.5" y="22.5" width="45" height="39" rx="19.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><text x="70" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(12,98,243)">Week</text><rect x="108.5" y="22.5" width="50" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><text x="116.28" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="11" font-weight="650" fill="rgb(29,29,31)">Month</text></g></g></svg>