@kerfjs/ui 5.0.0-beta.54 → 5.0.0-beta.56
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.
- package/README.md +14 -11
- package/ai/compile-time-contracts-v1.json +2 -2
- package/ai/component-catalog-v2.json +206 -39
- package/ai/component-catalog.json +306 -58
- package/ai/public-api-signatures-v1.md +671 -92
- package/ai/skill.md +43 -38
- package/ai/webawesome-jsx-signatures-v1.md +8 -2
- package/dist/app-tab.js +1 -1
- package/dist/badge.d.ts +35 -7
- package/dist/badge.js +1 -1
- package/dist/browser/content-item.js +3 -0
- package/dist/browser/popup-menu.js +3 -0
- package/dist/browser/select.js +1 -0
- package/dist/catalog-resources.d.ts +12 -1
- package/dist/catalog.d.ts +21 -4
- package/dist/catalog.js +260 -308
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-7IUSV4XG.js → chunk-24PHKUR3.js} +2 -2
- package/dist/chunk-24PHKUR3.js.map +1 -0
- package/dist/{chunk-WZ2GLEY4.js → chunk-2G2ZJQYK.js} +2 -14
- package/dist/chunk-2G2ZJQYK.js.map +1 -0
- package/dist/{chunk-D7GWXIAS.js → chunk-5I7QGQVT.js} +1 -1
- package/dist/chunk-5I7QGQVT.js.map +1 -0
- package/dist/{chunk-HJKRCKDR.js → chunk-63ZWBGE3.js} +6 -3
- package/dist/chunk-63ZWBGE3.js.map +1 -0
- package/dist/{chunk-44IG7PFV.js → chunk-6CINWX2E.js} +3 -3
- package/dist/{chunk-44IG7PFV.js.map → chunk-6CINWX2E.js.map} +1 -1
- package/dist/{chunk-C2FAYFDE.js → chunk-AGXQQFKZ.js} +2 -2
- package/dist/{chunk-C2FAYFDE.js.map → chunk-AGXQQFKZ.js.map} +1 -1
- package/dist/{chunk-ATHXW6RP.js → chunk-AHEG27M7.js} +2 -2
- package/dist/{chunk-ATHXW6RP.js.map → chunk-AHEG27M7.js.map} +1 -1
- package/dist/{chunk-7GUQYHKA.js → chunk-AK67F6QN.js} +4 -1
- package/dist/chunk-AK67F6QN.js.map +1 -0
- package/dist/chunk-BFPBS5Y4.js +250 -0
- package/dist/chunk-BFPBS5Y4.js.map +1 -0
- package/dist/chunk-DG6G5EPF.js +35 -0
- package/dist/chunk-DG6G5EPF.js.map +1 -0
- package/dist/{chunk-MTM43SJO.js → chunk-G33XC2NB.js} +1 -1
- package/dist/chunk-G33XC2NB.js.map +1 -0
- package/dist/{chunk-TZ5IHWB2.js → chunk-K6UNVKDK.js} +3 -3
- package/dist/{chunk-TZ5IHWB2.js.map → chunk-K6UNVKDK.js.map} +1 -1
- package/dist/chunk-KH6VUGHU.js +399 -0
- package/dist/chunk-KH6VUGHU.js.map +1 -0
- package/dist/{chunk-HMQ2ZDGG.js → chunk-KROD7KZK.js} +3 -2
- package/dist/chunk-KROD7KZK.js.map +1 -0
- package/dist/{chunk-5NDQABWK.js → chunk-M7QV3IHH.js} +3 -1
- package/dist/chunk-M7QV3IHH.js.map +1 -0
- package/dist/{chunk-RNZAKSME.js → chunk-MA6Z6EIM.js} +3 -3
- package/dist/{chunk-RNZAKSME.js.map → chunk-MA6Z6EIM.js.map} +1 -1
- package/dist/chunk-MA73TA6D.js +42 -0
- package/dist/chunk-MA73TA6D.js.map +1 -0
- package/dist/{chunk-ZMBU522F.js → chunk-MEY33VWB.js} +2 -2
- package/dist/{chunk-ZMBU522F.js.map → chunk-MEY33VWB.js.map} +1 -1
- package/dist/{chunk-T22EM4ET.js → chunk-MOBRFZH3.js} +81 -31
- package/dist/chunk-MOBRFZH3.js.map +1 -0
- package/dist/chunk-MP2GDBQZ.js +50 -0
- package/dist/chunk-MP2GDBQZ.js.map +1 -0
- package/dist/{chunk-3NBL3OUQ.js → chunk-NAPE7WUS.js} +2 -2
- package/dist/chunk-NAPE7WUS.js.map +1 -0
- package/dist/{chunk-CMUIXJZM.js → chunk-NBYTIVHS.js} +2 -2
- package/dist/{chunk-CMUIXJZM.js.map → chunk-NBYTIVHS.js.map} +1 -1
- package/dist/chunk-O3EXIBEZ.js +114 -0
- package/dist/chunk-O3EXIBEZ.js.map +1 -0
- package/dist/{chunk-KPNTVB7A.js → chunk-PK4N5QTO.js} +8 -7
- package/dist/chunk-PK4N5QTO.js.map +1 -0
- package/dist/chunk-QQOTQTDD.js +415 -0
- package/dist/chunk-QQOTQTDD.js.map +1 -0
- package/dist/{chunk-BJJKFVWW.js → chunk-T4UZTNZ4.js} +5 -5
- package/dist/chunk-T4UZTNZ4.js.map +1 -0
- package/dist/chunk-V65EOWNF.js +83 -0
- package/dist/chunk-V65EOWNF.js.map +1 -0
- package/dist/{chunk-GUIHMIT2.js → chunk-VNPQDCQZ.js} +3 -3
- package/dist/{chunk-GUIHMIT2.js.map → chunk-VNPQDCQZ.js.map} +1 -1
- package/dist/{chunk-5I22LMCV.js → chunk-VZOFZPSO.js} +2 -2
- package/dist/chunk-VZOFZPSO.js.map +1 -0
- package/dist/chunk-WLG6SSNT.js +94 -0
- package/dist/chunk-WLG6SSNT.js.map +1 -0
- package/dist/{chunk-NE5JAOQS.js → chunk-WOUF7YXM.js} +3 -3
- package/dist/{chunk-NE5JAOQS.js.map → chunk-WOUF7YXM.js.map} +1 -1
- package/dist/collapsible-panel.d.ts +51 -13
- package/dist/collapsible-panel.js +38 -10
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/content-item.d.ts +46 -0
- package/dist/content-item.js +4 -0
- package/dist/content-item.js.map +1 -0
- package/dist/css-values.d.ts +5 -11
- package/dist/css-values.js +1 -1
- package/dist/disclosure-arrow.d.ts +4 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/floating-toolbar.d.ts +17 -6
- package/dist/floating-toolbar.js +1 -1
- package/dist/grid.js +1 -1
- package/dist/help-tags-register.d.ts +13 -0
- package/dist/help-tags-register.js +9 -0
- package/dist/help-tags-register.js.map +1 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +31 -29
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.d.ts +14 -1
- package/dist/list-header.js +1 -1
- package/dist/list-inset-control.d.ts +1 -1
- package/dist/list-inset-control.js +1 -1
- package/dist/list-inset-text.d.ts +9 -10
- package/dist/list-inset-text.js +2 -1
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +10 -3
- package/dist/list.js +1 -1
- package/dist/nav-stack.d.ts +38 -5
- package/dist/nav-stack.js +4 -1
- package/dist/pane.d.ts +20 -2
- package/dist/pane.js +1 -1
- package/dist/panel-toolbar-C8sF-YZC.d.ts +63 -0
- package/dist/popup-menu-register.d.ts +9 -0
- package/dist/popup-menu-register.js +31 -0
- package/dist/popup-menu-register.js.map +1 -0
- package/dist/popup-menu.d.ts +71 -0
- package/dist/popup-menu.js +3 -0
- package/dist/popup-menu.js.map +1 -0
- package/dist/row.d.ts +1 -1
- package/dist/row.js +1 -1
- package/dist/select-register.js +70 -3
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +46 -8
- package/dist/select.js +2 -1
- package/dist/{divider-sides-BzB6rphT.d.ts → sides-BPSWde0A.d.ts} +1 -3
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.d.ts +31 -3
- package/dist/split-view.js +33 -5
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/badge.css +13 -0
- package/dist/styles/catalog/components/catalog-example.css +1 -17
- package/dist/styles/catalog/components/catalog-stage.css +9 -14
- package/dist/styles/catalog/components/catalog.css +8 -18
- package/dist/styles/catalog.css +0 -5
- package/dist/styles/collapsible-panel.css +192 -39
- package/dist/styles/content-item.css +30 -0
- package/dist/styles/floating-toolbar.css +5 -0
- package/dist/styles/foundation.css +28 -19
- package/dist/styles/layout.css +1 -21
- package/dist/styles/list-action-row.css +13 -1
- package/dist/styles/list-inset-text.css +1 -2
- package/dist/styles/list-item.css +14 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/nav-stack.css +29 -64
- package/dist/styles/pane.css +33 -1
- package/dist/styles/popup-menu.css +90 -0
- package/dist/styles/resizable-region.css +53 -10
- package/dist/styles/select.css +39 -12
- package/dist/styles/split-view.css +21 -25
- package/dist/styles/state-banner.css +13 -4
- package/dist/styles/styles.css +1 -0
- package/dist/styles/tab-bar.css +31 -3
- package/dist/styles/tab-scaffold.css +36 -14
- package/dist/styles/text.css +1 -1
- package/dist/styles/token-search-field.css +4 -4
- package/dist/styles/toolbar-control-group.css +44 -12
- package/dist/styles/toolbar.css +57 -7
- package/dist/styles/value-table.css +19 -2
- package/dist/styles/webawesome.css +3 -1
- package/dist/styles/workbench.css +165 -31
- package/dist/tab-scaffold.d.ts +35 -1
- package/dist/tab-scaffold.js +50 -16
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/text.js +1 -1
- package/dist/token-search-field.d.ts +13 -2
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +12 -4
- package/dist/toolbar.js +1 -1
- package/dist/{types-CEsieBZi.d.ts → types-DXAGogOS.d.ts} +48 -0
- package/dist/value-table.js +1 -1
- package/dist/webawesome.d.ts +7 -1
- package/dist/wire-catalog.d.ts +24 -1
- package/dist/wire-catalog.js +26 -3
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-sidebar.js +6 -3
- package/dist/wire-sidebar.js.map +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/wire-workbench.js +1 -350
- package/dist/wire-workbench.js.map +1 -1
- package/dist/workbench.d.ts +144 -6
- package/dist/workbench.js +11 -208
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +126 -6
- package/docs/app-layouts.md +9 -7
- package/docs/catalog.md +41 -9
- package/docs/collapsible-panel.md +79 -5
- package/docs/component-contract.md +38 -12
- package/docs/component-selection.md +64 -55
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
- package/docs/design/templates/empty-state/with-action.svg +1 -1
- package/docs/design/templates/empty-state-dark.svg +1 -1
- package/docs/design/templates/empty-state.svg +1 -1
- package/docs/design/templates/list/compact-dark.svg +1 -1
- package/docs/design/templates/list/compact.svg +1 -1
- package/docs/design/templates/list/stack-dark.svg +1 -1
- package/docs/design/templates/list/stack.svg +1 -1
- package/docs/design/templates/list-action-row/default-dark.svg +1 -1
- package/docs/design/templates/list-action-row/default.svg +1 -1
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
- package/docs/design/templates/list-action-row/selected.svg +1 -1
- package/docs/design/templates/list-action-row-dark.svg +1 -1
- package/docs/design/templates/list-action-row.svg +1 -1
- package/docs/design/templates/list-dark.svg +1 -1
- package/docs/design/templates/list-item/default-dark.svg +1 -1
- package/docs/design/templates/list-item/default.svg +1 -1
- package/docs/design/templates/list-item/selected-dark.svg +1 -1
- package/docs/design/templates/list-item/selected.svg +1 -1
- package/docs/design/templates/list-item/trailing-dark.svg +1 -1
- package/docs/design/templates/list-item/trailing.svg +1 -1
- package/docs/design/templates/list-item-dark.svg +1 -1
- package/docs/design/templates/list-item.svg +1 -1
- package/docs/design/templates/list.svg +1 -1
- package/docs/design/templates/state-banner/danger-dark.svg +1 -1
- package/docs/design/templates/state-banner/danger.svg +1 -1
- package/docs/design/templates/state-banner/info-dark.svg +1 -1
- package/docs/design/templates/state-banner/info.svg +1 -1
- package/docs/design/templates/state-banner/success-dark.svg +1 -1
- package/docs/design/templates/state-banner/success.svg +1 -1
- package/docs/design/templates/state-banner/warning-dark.svg +1 -1
- package/docs/design/templates/state-banner/warning.svg +1 -1
- package/docs/design/templates/state-banner-dark.svg +1 -1
- package/docs/design/templates/state-banner.svg +1 -1
- package/docs/design/templates/value-table/details-dark.svg +1 -1
- package/docs/design/templates/value-table/details.svg +1 -1
- package/docs/design/templates/value-table-dark.svg +1 -1
- package/docs/design/templates/value-table.svg +1 -1
- package/docs/design-philosophy.md +1 -1
- package/docs/examples/command-palette-adapter.tsx +3 -2
- package/docs/layout.md +74 -29
- package/docs/nav-stack.md +45 -1
- package/docs/recipes.md +48 -40
- package/docs/split-view.md +46 -2
- package/docs/surface-scaffold.md +1 -1
- package/docs/tab-scaffold.md +50 -1
- package/docs/type-contracts.md +17 -17
- package/docs/ux-demo.md +8 -4
- package/docs/webawesome-theme.md +1 -2
- package/docs/workbench.md +131 -10
- package/llms.txt +38 -9
- package/package.json +24 -2
- package/ux-demo/recipes/app-shell.tsx +141 -223
- package/ux-demo/recipes/collapsible-sidebar.tsx +47 -77
- package/ux-demo/recipes/list-detail-dialog.tsx +150 -54
- package/ux-demo/recipes/mount-recipe.ts +0 -5
- package/ux-demo/recipes/navigation-sidebar.tsx +3 -2
- package/ux-demo/recipes/navigation-stack.tsx +3 -2
- package/ux-demo/recipes/types.ts +0 -1
- package/dist/chunk-3NBL3OUQ.js.map +0 -1
- package/dist/chunk-5I22LMCV.js.map +0 -1
- package/dist/chunk-5NDQABWK.js.map +0 -1
- package/dist/chunk-7GUQYHKA.js.map +0 -1
- package/dist/chunk-7IUSV4XG.js.map +0 -1
- package/dist/chunk-BJJKFVWW.js.map +0 -1
- package/dist/chunk-D7GWXIAS.js.map +0 -1
- package/dist/chunk-DQA5FMOX.js +0 -85
- package/dist/chunk-DQA5FMOX.js.map +0 -1
- package/dist/chunk-HJKRCKDR.js.map +0 -1
- package/dist/chunk-HMQ2ZDGG.js.map +0 -1
- package/dist/chunk-KPNTVB7A.js.map +0 -1
- package/dist/chunk-MTM43SJO.js.map +0 -1
- package/dist/chunk-T22EM4ET.js.map +0 -1
- package/dist/chunk-WZ2GLEY4.js.map +0 -1
- package/dist/chunk-XD7DWYXG.js +0 -34
- package/dist/chunk-XD7DWYXG.js.map +0 -1
- package/dist/chunk-ZK5EGVFP.js +0 -27
- package/dist/chunk-ZK5EGVFP.js.map +0 -1
- package/dist/styles/catalog/components/catalog-detail.css +0 -35
- package/dist/styles/catalog/components/catalog-resource-footer.css +0 -37
- package/dist/styles/catalog/components/catalog-secondary-sections.css +0 -38
- package/dist/styles/catalog/components/catalog-section-list.css +0 -22
- package/dist/styles/catalog/components/catalog-sidebar.css +0 -82
package/docs/layout.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Import `Pane` from `@kerfjs/ui/pane`. Browser-aware bundlers receive its CSS
|
|
4
4
|
automatically; other consumers can import `@kerfjs/ui/pane.css`. Import
|
|
5
|
-
`@kerfjs/ui/layout.css` as well when using the
|
|
6
|
-
layout classes
|
|
5
|
+
`@kerfjs/ui/layout.css` as well when using the `.kui-content` stack and control
|
|
6
|
+
layout classes; place `ContentItem` children from `@kerfjs/ui/content-item`. The vocabulary deliberately describes structure rather than
|
|
7
7
|
location: the same pane can be placed anywhere.
|
|
8
8
|
|
|
9
9
|
## Anatomy
|
|
@@ -29,10 +29,17 @@ top toolbar followed by secondary toolbar or status rows. A main area or dialog
|
|
|
29
29
|
often omits the footer; a navigation pane commonly uses all three. Fixed chrome
|
|
30
30
|
stays outside `.kui-pane__content`, which is the pane's only scroll owner.
|
|
31
31
|
|
|
32
|
+
Tall fixed chrome can leave a short pane no room for its content (a small
|
|
33
|
+
window, or text at 200%). `chromePlacement="auto"` keeps the header and footer
|
|
34
|
+
pinned while the pane is at least 480px (30rem, so it scales with the text
|
|
35
|
+
size) tall, and below that lets the whole pane scroll as one column, header,
|
|
36
|
+
content, and footer together, so the content keeps its natural height. The
|
|
37
|
+
default `fixed` always pins them.
|
|
38
|
+
|
|
32
39
|
Pass any combination of logical sides to `separators`: `block-start`,
|
|
33
40
|
`block-end`, `inline-start`, and `inline-end`. Every line is off by default and
|
|
34
41
|
each enabled side uses `--kui-pane-separator-width` (1px) and
|
|
35
|
-
`--kui-pane-separator-color` (`--kui-color-border`). Logical sides keep pane
|
|
42
|
+
`--kui-pane-separator-color` (`--kui-color-neutral-border-normal`). Logical sides keep pane
|
|
36
43
|
boundaries correct in both left-to-right and right-to-left layouts.
|
|
37
44
|
|
|
38
45
|
A pane is safe-area aware. Its background and separators paint through a
|
|
@@ -45,18 +52,8 @@ same logical sides as `separators`; all four by default, `[]` to opt out). See
|
|
|
45
52
|
|
|
46
53
|
`.kui-content` is a vertical stack with a 24px gap between major children.
|
|
47
54
|
Sections may contain adjacent `ListItem` rows without adding another major gap.
|
|
48
|
-
Ordinary surface-like children
|
|
49
|
-
geometry
|
|
50
|
-
|
|
51
|
-
- 8px inline margin from the pane edge
|
|
52
|
-
- 1px border, transparent by default
|
|
53
|
-
- 8px internal padding and 8px internal gap
|
|
54
|
-
- `calc(1px + remify(11px))`, or 12px, rounded corners
|
|
55
|
-
|
|
56
|
-
Use `.kui-content-item--pill` for the 22px pill radius expressed as
|
|
57
|
-
`calc(1px + remify(21px))`. Consumers can make a content item visible without
|
|
58
|
-
changing its geometry by setting `--kui-content-item-border` and
|
|
59
|
-
`--kui-content-item-background`.
|
|
55
|
+
Ordinary surface-like children are `ContentItem`s and own their complete
|
|
56
|
+
geometry (see [Content items](#content-items)).
|
|
60
57
|
|
|
61
58
|
Nested selection and hover highlights stay concentric and match their owning
|
|
62
59
|
control's shape. Their inner radius is the outer radius minus the full inset,
|
|
@@ -72,20 +69,63 @@ inline gutter instead of nesting them inside another padded content item. A
|
|
|
72
69
|
conditional `StateBanner` remains visibly distinct because it communicates
|
|
73
70
|
semantic status.
|
|
74
71
|
|
|
72
|
+
## Content items
|
|
73
|
+
|
|
74
|
+
Render an ordinary surface-like `.kui-content` child with `ContentItem` from
|
|
75
|
+
`@kerfjs/ui/content-item` (browser-aware bundlers receive its CSS automatically;
|
|
76
|
+
`@kerfjs/ui/layout.css` and `@kerfjs/ui/content-item.css` both deliver it). It
|
|
77
|
+
owns its complete geometry:
|
|
78
|
+
|
|
79
|
+
- 8px inline margin from the pane edge
|
|
80
|
+
- 1px border, transparent by default
|
|
81
|
+
- 8px internal padding
|
|
82
|
+
- `calc(1px + remify(11px))`, or 12px, rounded corners
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { ContentItem } from "@kerfjs/ui/content-item";
|
|
86
|
+
|
|
87
|
+
<Pane label="Inspector">
|
|
88
|
+
<ContentItem>{details}</ContentItem>
|
|
89
|
+
<ContentItem frame="framed">{pendingChanges}</ContentItem>
|
|
90
|
+
<ContentItem shape="pill">{summary}</ContentItem>
|
|
91
|
+
</Pane>;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`frame="framed"` paints the standard neutral border in the 1px the item always
|
|
95
|
+
reserves, so framed and unframed items share the same geometry. Frame an item
|
|
96
|
+
only when it marks a real distinction and must read as visibly bounded; do not
|
|
97
|
+
frame it to make it "look contained". `shape="pill"` selects the 22px pill
|
|
98
|
+
radius expressed as `calc(1px + remify(21px))`. There is deliberately no filled
|
|
99
|
+
variant: a lowered, filled surface is `SunkenPanel`, and bare text that only
|
|
100
|
+
needs to align with neighboring items is `ListInsetText`. Safe `data-*`
|
|
101
|
+
metadata goes in `rootAttributes`. Pass `ariaLabel` only for a distinct named
|
|
102
|
+
region (`role="region"`), and `focusTarget` to make the item a programmatic
|
|
103
|
+
focus target (`tabindex="-1"`), for example a NavStack view's `data-nav-focus`
|
|
104
|
+
target.
|
|
105
|
+
|
|
106
|
+
The rendered classes — `.kui-content-item`, `.kui-content-item--framed`, and
|
|
107
|
+
`.kui-content-item--pill` — stay public for the rare element that must carry the
|
|
108
|
+
geometry itself (for example a `<ul>` list or a `Text` paragraph in an
|
|
109
|
+
application-owned adapter). Prefer `ContentItem` everywhere else so the framing
|
|
110
|
+
choice is typed. `npm run check:guidance` rejects a plain
|
|
111
|
+
`<div class="kui-content-item">` in the package's examples, browser fixtures,
|
|
112
|
+
and UX catalog, since that div is exactly what `ContentItem` renders.
|
|
113
|
+
|
|
75
114
|
## Public roles and tokens
|
|
76
115
|
|
|
77
|
-
| Need | Class
|
|
78
|
-
| ---------------------------------------- |
|
|
79
|
-
| Unpadded header/content/footer structure | `Pane`, `.kui-pane`
|
|
80
|
-
| Scrolling pane content | `.kui-pane__content`
|
|
81
|
-
| Optional logical-edge separators | `Pane.separators`
|
|
82
|
-
| Safe-area sides a pane may pad | `Pane.safeAreaEdges`
|
|
83
|
-
| Major vertical rhythm | `.kui-content`
|
|
84
|
-
| Self-contained child geometry |
|
|
85
|
-
| Pill child |
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
116
|
+
| Need | Class | Token / default |
|
|
117
|
+
| ---------------------------------------- | ---------------------------- | ------------------------------------------------------------------ |
|
|
118
|
+
| Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
|
|
119
|
+
| Scrolling pane content | `.kui-pane__content` | — |
|
|
120
|
+
| Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
|
|
121
|
+
| Safe-area sides a pane may pad | `Pane.safeAreaEdges` | `--kui-safe-area-*: env(safe-area-inset-*)`, routed by each layout |
|
|
122
|
+
| Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
|
|
123
|
+
| Self-contained child geometry | `ContentItem` | 8px margin + 1px border + 8px padding |
|
|
124
|
+
| Pill child | `ContentItem shape="pill"` | `--kui-layout-pill-radius: 22px` |
|
|
125
|
+
| Visibly framed child | `ContentItem frame="framed"` | neutral 1px border, same geometry |
|
|
126
|
+
| Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
|
|
127
|
+
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
128
|
+
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
89
129
|
|
|
90
130
|
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
91
131
|
`ListItem`, `Toolbar`, `StateBanner`, `ValueTable`,
|
|
@@ -96,6 +136,11 @@ also keeps 8px of root-scaled block padding independently of its semantic inline
|
|
|
96
136
|
inset. Most interactive rows and toolbar groups are 44px tall.
|
|
97
137
|
Toolbar groups reserve a real 1px outer border around a 42px inner area, even
|
|
98
138
|
when their border and background are transparent.
|
|
139
|
+
A `Toolbar` lays its zones out against one 44px control band at its top: an
|
|
140
|
+
item no taller than the band is centered in it by its own size, and a taller
|
|
141
|
+
item (a wrapped title, a second trailing row) starts at the band's top and
|
|
142
|
+
grows down, so it never moves the other controls. A wrapped `ToolbarText`
|
|
143
|
+
centers its first line in the band.
|
|
99
144
|
Trailing icon actions share one axis: a toolbar control centers its glyph in
|
|
100
145
|
that 44px slot at the 8px inline margin, and a `ListHeader` action centers its
|
|
101
146
|
fitted 36px square in a slot of the same size (a transparent hit layer extends
|
|
@@ -302,8 +347,8 @@ selected sides; the control wrapper applies its 8px outer margin only on
|
|
|
302
347
|
selected sides. Every direct child of `ListInsetControl` grows to fill the row
|
|
303
348
|
(a `Select`, a `wa-input`, or a `wa-button` spans the full inset width), and
|
|
304
349
|
several children share the row with an 8px gap; stack controls vertically with
|
|
305
|
-
a `List` using `controlInsets` instead. `
|
|
306
|
-
|
|
350
|
+
a `List` using `controlInsets` instead. Pass `sides="rl"` for tight text that
|
|
351
|
+
keeps the horizontal inset without adding vertical box space.
|
|
307
352
|
|
|
308
353
|
`--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
|
|
309
354
|
canonical rhythm** — reach for them only as a deliberate exception, never as a
|
package/docs/nav-stack.md
CHANGED
|
@@ -49,11 +49,55 @@ views.value = [
|
|
|
49
49
|
`NavStack` renders every entry stacked, the last one active and the rest kept
|
|
50
50
|
mounted (so their DOM state survives) but hidden. Each entry carries a
|
|
51
51
|
`key` (stable identity), `content`, an optional `title`, optional per-view
|
|
52
|
-
`toolbar`
|
|
52
|
+
`leading`, `center`, and trailing `toolbar` groups for the top toolbar, and an
|
|
53
|
+
optional per-view `bottomToolbar`. The component-level
|
|
53
54
|
`bottomToolbar` remains a persistent fallback for views that do not provide one.
|
|
54
55
|
The back control appears automatically once the stack has more than one entry;
|
|
55
56
|
`wireNavStack`'s `onBack` is where the app pops its own signal.
|
|
56
57
|
|
|
58
|
+
## The top toolbar
|
|
59
|
+
|
|
60
|
+
The top chrome is a real `Toolbar`, so it follows the same zone,
|
|
61
|
+
group, and responsive rules as every other toolbar. Its leading zone holds the
|
|
62
|
+
back control (a borderless `ToolbarControlGroup`, shown once the stack has
|
|
63
|
+
depth), then the active view's `leading` groups, then the view's `title` as a
|
|
64
|
+
`ToolbarText`. The view's `center` fills the center zone and its `toolbar`
|
|
65
|
+
fills the trailing zone. Give each zone `ToolbarControlGroup`s or `ToolbarText`,
|
|
66
|
+
as in any toolbar.
|
|
67
|
+
|
|
68
|
+
Configure the toolbar with `toolbarConfig` instead of styling it:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<NavStack
|
|
72
|
+
id="mail"
|
|
73
|
+
label="Mail"
|
|
74
|
+
views={views.value}
|
|
75
|
+
backText={previousTitle}
|
|
76
|
+
toolbarConfig={{
|
|
77
|
+
headingLevel: 1,
|
|
78
|
+
titleSize: "xlarge",
|
|
79
|
+
dividerSides: "b",
|
|
80
|
+
responsive: "wrap",
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `toolbarConfig` forwards the `Toolbar` configuration (`dividerSides`,
|
|
86
|
+
`centerAlign`, `responsive`, `responsiveAt`, `safeAreaEdges`) and adds
|
|
87
|
+
`label` (the toolbar's accessible name), `titleSize` (the title's
|
|
88
|
+
`ToolbarText` size, default `large`), and `headingLevel` (expose the title as
|
|
89
|
+
a heading; default a plain span). The defaults keep the stack's own chrome:
|
|
90
|
+
no divider, one 44px control band, and the top and side safe-area edges
|
|
91
|
+
claimed.
|
|
92
|
+
- `backIcon` replaces the default chevron-left icon. `backText` adds visible
|
|
93
|
+
text beside it (for example, the previous view's title); visible text names
|
|
94
|
+
the control, so `backLabel` applies only to the icon-only control.
|
|
95
|
+
|
|
96
|
+
The catalog's **Configured toolbar** example shows these together: a level-2
|
|
97
|
+
heading title over a bottom divider, leading and center groups on the root
|
|
98
|
+
view, a trailing group on the pushed view, and `backText` set to the previous
|
|
99
|
+
view's title.
|
|
100
|
+
|
|
57
101
|
Focus follows the controlled stack automatically. On push, `wireNavStack`
|
|
58
102
|
remembers the focused descendant of the departing view and moves focus into the
|
|
59
103
|
new top view. On pop it restores that exact descendant when it still exists. A
|
package/docs/recipes.md
CHANGED
|
@@ -9,9 +9,9 @@ ship no stylesheet of their own and are not new monolithic components.
|
|
|
9
9
|
Copy the recipe source together with the catalog-independent
|
|
10
10
|
[`mount-recipe.ts`](../ux-demo/recipes/mount-recipe.ts) adapter. It mounts the
|
|
11
11
|
controller at one stable application root, uses `delegateActions()` for recipe
|
|
12
|
-
commands, forwards form and dialog lifecycle events,
|
|
13
|
-
|
|
14
|
-
idempotent disposer:
|
|
12
|
+
commands, forwards form and dialog lifecycle events, runs the recipe's own
|
|
13
|
+
`wire()` (for example `wireWorkbench` or `wireSidebar`), retains every
|
|
14
|
+
disposer, and returns one idempotent disposer:
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
17
|
import { createRecipe } from "./navigation-sidebar.js";
|
|
@@ -31,32 +31,34 @@ specific dispatch; either way, wire once at a stable root and retain disposal.
|
|
|
31
31
|
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx)
|
|
32
32
|
|
|
33
33
|
A filling `List` (`fill`, with the recipe's `data-*` markers through
|
|
34
|
-
`rootAttributes`) stacks the app-bar `Toolbar` above a `
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
the
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
`rootAttributes`) stacks the app-bar `Toolbar` above a `Workbench`
|
|
35
|
+
(`@kerfjs/ui/workbench`): resizable navigation and inspector rails around the
|
|
36
|
+
task list. Each rail composes its own panel toolbar — a default-size title and
|
|
37
|
+
the standard hide toggle — and the work area's `mainToolbar` carries the xl
|
|
38
|
+
view title. While a rail is hidden, `Workbench` relocates its show toggle into
|
|
39
|
+
the work-area toolbar on the same logical edge (leading for the navigation
|
|
40
|
+
rail, trailing for the inspector), and `wireWorkbench` hands focus between the
|
|
41
|
+
two positions. Collapse a rail completely rather than preserving an empty icon
|
|
42
|
+
rail. Only the three real panes own a `.kui-pane__content` scroll owner; no
|
|
43
|
+
frame `Pane` wraps the layout in a scroll owner that never scrolls, and the
|
|
44
|
+
app-bar `Toolbar` claims the top edge and both sides with its own
|
|
45
|
+
`safeAreaEdges`.
|
|
46
|
+
|
|
47
|
+
On a narrow container the rails overlay the task list one at a time instead of
|
|
48
|
+
squeezing it, leaving a 44px strip on the far side that dismisses the overlay
|
|
49
|
+
(`responsiveOverlayAt`, on by default; `compactOverlay: 'full'` covers the
|
|
50
|
+
whole width). The app owns each rail's collapsed and size signals — which
|
|
51
|
+
`wireWorkbench` keeps in sync with resizing and overlay dismissal — plus
|
|
52
|
+
routing, persistence, and data. Adapt only public `--kui-layout-*` and
|
|
53
|
+
component variables.
|
|
52
54
|
|
|
53
55
|
## Navigation sidebar
|
|
54
56
|
|
|
55
57
|
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
56
58
|
|
|
57
59
|
One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
|
|
58
|
-
`.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and
|
|
59
|
-
|
|
60
|
+
`.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and
|
|
61
|
+
`ContentItem` children own their 8px margin, 1px border, and 8px padding.
|
|
60
62
|
Rows and footer toolbar groups remain 44px tall. The app owns routes,
|
|
61
63
|
permissions, labels, selection, valid section counts and their localized
|
|
62
64
|
`countLabel` phrases, non-count badge content, disclosure state, and revealed
|
|
@@ -75,19 +77,23 @@ controls relocate without changing focus order.
|
|
|
75
77
|
|
|
76
78
|
[Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
A list-detail dialog is laid out like a `Workbench` with a left sidebar: on
|
|
81
|
+
roomy devices the list is a full-height sidebar and the detail is a full-height
|
|
82
|
+
main column, each a `Pane` with its own top `Toolbar`. The sidebar's toolbar
|
|
83
|
+
carries its quiet default-size title (and it may add a bottom toolbar for
|
|
84
|
+
sidebar-only actions); the main column's toolbar carries the dialog's primary
|
|
85
|
+
extra-large title and the close control, and its footer carries the record
|
|
86
|
+
actions. The Web Awesome dialog therefore renders `without-header` there —
|
|
87
|
+
its label still names the modal — and the close button uses
|
|
88
|
+
`data-dialog="close"`. Web Awesome keeps modal focus and Escape, and the thin
|
|
89
|
+
recipe adapter restores the invoking control after the hide event.
|
|
90
|
+
`DialogSurface` chooses the large modal with no body inset, because the panes
|
|
91
|
+
own their geometry. On compact devices the dialog becomes a full-screen sheet
|
|
92
|
+
with its own labeled header and footer actions, and `SplitView` becomes a
|
|
87
93
|
`NavStack` drill-down whose back control the application pops. The
|
|
88
94
|
application owns open state, selection, the pushed detail, dismissal policy,
|
|
89
|
-
and record actions. Do not
|
|
90
|
-
parts.
|
|
95
|
+
and record actions. Do not put the primary title in a header spanning both
|
|
96
|
+
columns, rebuild the dialog, or reach into private shadow parts.
|
|
91
97
|
|
|
92
98
|
## Composer form
|
|
93
99
|
|
|
@@ -162,11 +168,12 @@ which values are still unknown.
|
|
|
162
168
|
|
|
163
169
|
A mini app frame whose left navigation rail and bottom activity drawer are
|
|
164
170
|
standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
|
|
165
|
-
`wireSidebar` (`@kerfjs/ui/wire-sidebar`). `
|
|
166
|
-
standard per-side
|
|
167
|
-
panel's
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
`wireSidebar` (`@kerfjs/ui/wire-sidebar`). Each panel takes a `toolbar` — a
|
|
172
|
+
title plus the standard per-side toggle — so the hide control lives in the
|
|
173
|
+
panel's own header, and `CollapsiblePanelRelocated` renders the matching show
|
|
174
|
+
control only while the panel is collapsed, so exactly one control owns each
|
|
175
|
+
action and a collapsed panel is still reachable. The rail's relocated toggle
|
|
176
|
+
leads the always-visible main header; the drawer's sits in a
|
|
170
177
|
`FloatingToolbar` passed as the drawer's own `restoreControl`, which the panel
|
|
171
178
|
floats in its column's safe-area-aware bottom-end corner, next to where the
|
|
172
179
|
drawer opens — do not hand-roll a conditional floating control beside the
|
|
@@ -198,7 +205,8 @@ Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.
|
|
|
198
205
|
`@kerfjs/ui/webawesome.css`.
|
|
199
206
|
- Compose, don't style. A recipe ships no stylesheet, sets no inline style, and
|
|
200
207
|
names no class outside the published layout vocabulary (`.kui-content`,
|
|
201
|
-
`.kui-
|
|
208
|
+
`.kui-control-cluster`, …) and themed Web Awesome classes — a content item is
|
|
209
|
+
a `ContentItem`, not a hand-written `.kui-content-item`; it configures
|
|
202
210
|
components through props, `rootAttributes`, and tokens. When a recipe needs a
|
|
203
211
|
capability no component offers, record it as a component gap rather than
|
|
204
212
|
papering over it with CSS. `npm run check:recipes` enforces these rules on
|
package/docs/split-view.md
CHANGED
|
@@ -53,11 +53,55 @@ const selected = signal<string | null>(null);
|
|
|
53
53
|
app's selection). This is the portrait-tablet / handset presentation; as a
|
|
54
54
|
dialog the compact form is a full-screen or large partial-cover modal.
|
|
55
55
|
|
|
56
|
+
## Configuring the list region and the compact stack
|
|
57
|
+
|
|
58
|
+
`resizable` forwards the list's `ResizableRegion` configuration alongside
|
|
59
|
+
`size`, `min`, and `max`: `separator` (`"hidden"` keeps the resize hit target
|
|
60
|
+
but paints no line), `handleIcon`, `contentOverflow`, and collapse —
|
|
61
|
+
`collapsed`, `transitioning`, `collapseMotion`, `restoreControl`, and
|
|
62
|
+
`restorePosition`. A collapsed list snaps to zero width, renders `inert` and
|
|
63
|
+
`aria-hidden`, and the detail fills the split; the app owns the `collapsed`
|
|
64
|
+
flag and renders the restore control (usually a `FloatingToolbar`). Omitted
|
|
65
|
+
options keep the region's defaults.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
resizable={{
|
|
69
|
+
size: listWidth.value,
|
|
70
|
+
min: 220,
|
|
71
|
+
max: 480,
|
|
72
|
+
collapsed: listCollapsed.value,
|
|
73
|
+
restoreControl: <ShowThreadsToolbar />,
|
|
74
|
+
}}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`compactStack` configures the compact `NavStack`: `toolbarConfig`, `backIcon`,
|
|
78
|
+
`backText`, `hideToolbar`, and the persistent `bottomToolbar` forward to the
|
|
79
|
+
stack, and `list` / `detail` give each view its own top-toolbar groups
|
|
80
|
+
(`leading`, `center`, trailing `toolbar`) and `bottomToolbar`:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
compactStack={{
|
|
84
|
+
backText: "Threads",
|
|
85
|
+
toolbarConfig: { headingLevel: 1 },
|
|
86
|
+
list: { toolbar: <ComposeGroup /> },
|
|
87
|
+
detail: { toolbar: <ReplyGroup />, bottomToolbar: <MessageActions /> },
|
|
88
|
+
}}
|
|
89
|
+
```
|
|
90
|
+
|
|
56
91
|
The catalog's **Interactive compact drill-down** example exercises this exact
|
|
57
92
|
controlled flow: a list action sets the selected message, `detailActive` pushes
|
|
58
93
|
its detail, and the wired Back action clears the selection to reveal the
|
|
59
|
-
preserved list.
|
|
60
|
-
|
|
94
|
+
preserved list. It also forwards `compactStack`: a level-2 heading title over
|
|
95
|
+
a bottom divider, a Compose group on the list view, and a Reply group plus a
|
|
96
|
+
bottom toolbar on the detail. Use the same structure in application demos so
|
|
97
|
+
compact examples show the interaction rather than rendering a disconnected
|
|
98
|
+
detail state.
|
|
99
|
+
|
|
100
|
+
The catalog's **Resizable, collapsible list** example forwards `resizable`: a
|
|
101
|
+
hidden separator that still resizes the list (its committed width comes back
|
|
102
|
+
through `wireResizableRegions`' `onCommit` into the app's size signal), a Hide
|
|
103
|
+
control in the list's own toolbar, and a `FloatingToolbar` restore control in
|
|
104
|
+
the split's bottom-start corner.
|
|
61
105
|
|
|
62
106
|
Compose the interactive wiring from the existing helpers — `SplitView` adds no
|
|
63
107
|
wire of its own.
|
package/docs/surface-scaffold.md
CHANGED
|
@@ -18,7 +18,7 @@ padding and aligns with bordered siblings. Reserve `compact` or `comfortable`
|
|
|
18
18
|
body insets for exceptional content that does not already have list/content-item
|
|
19
19
|
geometry.
|
|
20
20
|
|
|
21
|
-
Wrap one `wa-dropdown` with `PopupSurface`. `inset="list-zero"` removes menu
|
|
21
|
+
Wrap one `PopupMenu` (or another single `wa-dropdown`) with `PopupSurface`. `inset="list-zero"` removes menu
|
|
22
22
|
padding for a child that already owns row insets, `compact` uses 4px, and
|
|
23
23
|
`standard` uses the shared 8px surface inset. Keep the dropdown trigger named and
|
|
24
24
|
preserve `data-morph-skip-children` when its upgraded light-DOM items must retain
|
package/docs/tab-scaffold.md
CHANGED
|
@@ -44,7 +44,8 @@ const dispose = wireTabScaffold(root, {
|
|
|
44
44
|
});
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`,
|
|
47
|
+
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, optional `badge`
|
|
48
|
+
and `badgeLabel` (see [Tab badges](#tab-badges)), and `content`. The
|
|
48
49
|
bottom bar paints through the home-indicator safe area and pads for it and for
|
|
49
50
|
the side insets, while each scene pads for the top and side insets (a scene
|
|
50
51
|
whose only child is a `NavStack` or `Pane` lets that child own them; see
|
|
@@ -52,3 +53,51 @@ whose only child is a `NavStack` or `Pane` lets that child own them; see
|
|
|
52
53
|
truncate horizontally with an ellipsis when a destination name exceeds its
|
|
53
54
|
share of the bar. On larger device classes, promote the tab set to a
|
|
54
55
|
`Workbench` rail or a persistent sidebar instead of a bottom bar.
|
|
56
|
+
|
|
57
|
+
## Tab badges
|
|
58
|
+
|
|
59
|
+
Give a tab a `badge` (a count or short string such as `3`, `"99+"`, or
|
|
60
|
+
`"New"`) to show the iOS tab-bar badge: a compact, solid `danger` `Badge` at the
|
|
61
|
+
top-trailing corner of the tab's icon, growing outward as the text lengthens
|
|
62
|
+
(without an icon it sits above the label). The badge is decorative
|
|
63
|
+
(`aria-hidden`); its meaning reaches assistive technology through `badgeLabel`,
|
|
64
|
+
which the tab folds into its accessible name as `"<label>, <badgeLabel>"`:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
{
|
|
68
|
+
id: "inbox",
|
|
69
|
+
label: "Inbox",
|
|
70
|
+
icon: <LucideIcon icon={Inbox} name="inbox" />,
|
|
71
|
+
badge: unread.value,
|
|
72
|
+
badgeLabel: `${unread.value} unread`,
|
|
73
|
+
content: <InboxStack />,
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`badgeLabel` defaults to the badge text itself, so supply a localized phrase
|
|
78
|
+
whenever the bare text would be ambiguous. Omitting `badge` (or passing an
|
|
79
|
+
empty string or a non-finite number) renders no badge and leaves the tab's name
|
|
80
|
+
as its label; `0` renders, so pass `undefined` when a count should disappear.
|
|
81
|
+
Keep the text short — clamp large counts (`"99+"`) in the app. The tone follows
|
|
82
|
+
the platform convention and is fixed; do not restyle the badge.
|
|
83
|
+
|
|
84
|
+
### Dot badges
|
|
85
|
+
|
|
86
|
+
For new content without a count, pass `badge: true`. The tab shows the iOS
|
|
87
|
+
tab-bar dot — an 8px solid `danger` `Badge` (`size="dot"`) centered on the
|
|
88
|
+
icon's top-trailing corner (above the label without an icon). A dot has no
|
|
89
|
+
text to fall back on, so `badgeLabel` is required by the types:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
{
|
|
93
|
+
id: "feed",
|
|
94
|
+
label: "Feed",
|
|
95
|
+
icon: <LucideIcon icon={Rss} name="rss" />,
|
|
96
|
+
badge: hasNewPosts.value ? true : undefined,
|
|
97
|
+
badgeLabel: "New activity",
|
|
98
|
+
content: <FeedStack />,
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The tab's accessible name becomes `"Feed, New activity"`. Pass `undefined` to
|
|
103
|
+
clear the dot; `false` is not a badge value.
|
package/docs/type-contracts.md
CHANGED
|
@@ -11,22 +11,22 @@ public-signature artifact.
|
|
|
11
11
|
|
|
12
12
|
## Strengthened contracts
|
|
13
13
|
|
|
14
|
-
| ID | Compile-time guarantee
|
|
15
|
-
| ---------- |
|
|
16
|
-
| `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected.
|
|
17
|
-
| `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`.
|
|
18
|
-
| `KUI-T003` | A literal `Select.value` belongs to its literal `choices`.
|
|
19
|
-
| `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`.
|
|
20
|
-
| `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`.
|
|
21
|
-
| `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes.
|
|
22
|
-
| `KUI-T007` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code.
|
|
23
|
-
| `KUI-T008` | A literal `TabScaffold.active` id belongs to its literal `tabs`.
|
|
24
|
-
| `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback.
|
|
25
|
-
| `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them.
|
|
26
|
-
| `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception.
|
|
27
|
-
| `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value.
|
|
28
|
-
| `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons use `CssForegroundColor` (foreground tokens via `uiColor()` or `foregroundColorVar()`; fill
|
|
29
|
-
| `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected.
|
|
14
|
+
| ID | Compile-time guarantee |
|
|
15
|
+
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
|
|
17
|
+
| `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
|
|
18
|
+
| `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
|
|
19
|
+
| `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
|
|
20
|
+
| `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
|
|
21
|
+
| `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
|
|
22
|
+
| `KUI-T007` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
|
|
23
|
+
| `KUI-T008` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
|
|
24
|
+
| `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
|
|
25
|
+
| `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
|
|
26
|
+
| `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
|
|
27
|
+
| `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
|
|
28
|
+
| `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons use `CssForegroundColor` (foreground tokens via `uiColor()` or `foregroundColorVar()`; fill tokens such as `uiColor('success-fill-quiet')` are rejected); row declaration strings are absent. |
|
|
29
|
+
| `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected. |
|
|
30
30
|
|
|
31
31
|
`KerfUiContent` lets conditionals and mapped component collections be direct
|
|
32
32
|
siblings without an otherwise-unnecessary `Fragment`:
|
|
@@ -123,7 +123,7 @@ move errors into casts without making the integration safer.
|
|
|
123
123
|
keyword; replace raw Skeleton dimensions with `px`/`rem`/`em`/`pct` builders;
|
|
124
124
|
and replace raw choice colors with a foreground token such as
|
|
125
125
|
`uiColor('success-on-quiet')` or an app-owned `foregroundColorVar()`.
|
|
126
|
-
`SelectChoice.color` rejects fill
|
|
126
|
+
`SelectChoice.color` rejects fill tokens (`uiColor('success-fill-quiet')`) and
|
|
127
127
|
unqualified `colorVar()` values because a fill paints a nearly invisible
|
|
128
128
|
icon.
|
|
129
129
|
- Remove `style` from `ListItem` and `ListActionRow`; use `className`, public
|
package/docs/ux-demo.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UX catalog contract
|
|
2
2
|
|
|
3
|
-
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
|
|
3
|
+
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server reloads component source edits without a restart: component entries still load through their `dist/browser` wrappers, so each component's reachable CSS is exercised as consumers receive it, but the JavaScript those wrappers re-export (and every other `@kerfjs/ui` subpath) resolves to `src/`, and CSS resolves to `src/` as well. A component that gains a new CSS dependency needs a restart, because the wrapper listing its stylesheets is regenerated only by the build. Production demo builds, previews, and the browser tests use `dist/`. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
|
|
4
4
|
|
|
5
5
|
Every Playwright entry point builds current source before the preview server
|
|
6
6
|
starts. `npm run test:e2e -- tests/browser/row.spec.ts` and a direct focused
|
|
@@ -10,7 +10,11 @@ launch Chromium, Firefox, and WebKit. The build emits
|
|
|
10
10
|
`source-freshness.json`; browser coverage compares it with an independent hash
|
|
11
11
|
of the current `src/` and `ux-demo/` inputs, so a stale `dist-demo` cannot pass
|
|
12
12
|
after a route or component edit. `npm run demo:serve` remains a preview-only
|
|
13
|
-
command for an already-built catalog.
|
|
13
|
+
command for an already-built catalog. The demo-conformance preflight also
|
|
14
|
+
rejects a component route that wraps its `CatalogExampleStack` in a card or
|
|
15
|
+
other container (`catalog-demo/wrapped-example-stack`): single-component demos
|
|
16
|
+
sit directly on the catalog grid, and only composition and recipe routes frame
|
|
17
|
+
their content.
|
|
14
18
|
|
|
15
19
|
Ten lazy production-composition routes live under the `Recipes` category.
|
|
16
20
|
Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
@@ -25,7 +29,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
25
29
|
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
26
30
|
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with supporting copy in its own aligned row; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the preview scroll owner rather than a viewport-height stage or nested card, and one explicitly repeated SVG tile keeps the pattern continuous through below-fold content. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
27
31
|
- The composer-form recipe demonstrates one coherent visible form surface with toolbar title/supporting-copy hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
28
|
-
- The list-detail dialog recipe
|
|
32
|
+
- The list-detail dialog recipe is laid out like a `Workbench` on roomy devices: a headerless `DialogSurface` modal whose `SplitView` holds a full-height list sidebar `Pane` with its own toolbar beside a full-height detail `Pane` that carries the primary xl title, the `data-dialog="close"` control, and the record actions in its footer. Compact devices get a full-screen sheet with its own labeled header, footer actions, and a list-to-detail drill-down. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
29
33
|
- Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
|
|
30
34
|
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
31
35
|
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
@@ -47,6 +51,6 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
47
51
|
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
48
52
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
49
53
|
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
50
|
-
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
|
|
54
|
+
- The production build keeps the catalog shell and Kerf routes in the entry chunk and moves the third-party code loaded at startup (the Web Awesome select and popup-menu registrations with their Lit and Floating UI runtime) into an eagerly preloaded `vendor` chunk, so neither nears the largest-chunk limit in `demo-bundle-budget.json`; only modules statically reachable from the entry qualify, so lazy-only dependencies stay in their lazy chunks. It then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
|
|
51
55
|
|
|
52
56
|
Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -176,8 +176,7 @@ their open state uses the independent `rotate` property.
|
|
|
176
176
|
|
|
177
177
|
That scale applies to each Web Awesome component's intrinsic glyph and is
|
|
178
178
|
independent of Kerf `DisclosureArrow`, whose default box is 18px relative to
|
|
179
|
-
the root font size and whose consumer override is
|
|
180
|
-
`--kui-disclosure-arrow-size`.
|
|
179
|
+
the root font size and whose consumer override is its typed `size` prop.
|
|
181
180
|
|
|
182
181
|
Tooltip and Popover use arrowless floating surfaces by default, matching Hot
|
|
183
182
|
Sheet 2. The theme sets Web Awesome's public `--wa-tooltip-arrow-size` token to
|