@kerfjs/ui 5.0.0-beta.29 → 5.0.0-beta.30
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 +43 -8
- package/ai/application-ui-diagnostic-ids-v1.json +5 -0
- package/ai/compile-time-contracts-v1.json +37 -0
- package/ai/compile-time-contracts-v1.schema.json +3 -1
- package/ai/component-catalog-v2-overrides.json +198 -1
- package/ai/component-catalog-v2.d.ts +22 -0
- package/ai/component-catalog-v2.json +488 -32
- package/ai/component-catalog-v2.schema.json +51 -0
- package/ai/component-catalog.json +251 -36
- package/ai/component-catalog.schema.json +23 -0
- package/ai/public-api-signatures-v1.md +231 -84
- package/ai/skill.md +32 -10
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/index.mjs +247 -5
- package/dist/app-tab.d.ts +3 -2
- package/dist/app-tab.js +2 -1
- package/dist/catalog-resources.d.ts +1 -0
- package/dist/catalog.d.ts +7 -6
- package/dist/catalog.js +8 -7
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-PX3YSS5C.js → chunk-6NDBE2OK.js} +1 -1
- package/dist/chunk-6NDBE2OK.js.map +1 -0
- package/dist/{chunk-B3A3IVO5.js → chunk-AQFWBG23.js} +3 -3
- package/dist/chunk-AQFWBG23.js.map +1 -0
- package/dist/{chunk-WNWD54GR.js → chunk-B6JYRX3P.js} +4 -2
- package/dist/chunk-B6JYRX3P.js.map +1 -0
- package/dist/{chunk-HP2B2L26.js → chunk-CAB6NQ2T.js} +1 -1
- package/dist/chunk-CAB6NQ2T.js.map +1 -0
- package/dist/{chunk-2NMQW7FX.js → chunk-CK6P5AUK.js} +4 -3
- package/dist/chunk-CK6P5AUK.js.map +1 -0
- package/dist/{chunk-VCE4MPVE.js → chunk-DAO2VFTT.js} +5 -6
- package/dist/chunk-DAO2VFTT.js.map +1 -0
- package/dist/{chunk-LPOMRC4C.js → chunk-DYBPI5O2.js} +1 -1
- package/dist/chunk-DYBPI5O2.js.map +1 -0
- package/dist/{chunk-IJBSZ4NX.js → chunk-E34TE2ZI.js} +4 -3
- package/dist/chunk-E34TE2ZI.js.map +1 -0
- package/dist/{chunk-4EF2CMYS.js → chunk-I45QO2IZ.js} +8 -7
- package/dist/chunk-I45QO2IZ.js.map +1 -0
- package/dist/{chunk-JRKSK2HX.js → chunk-I4V3UDSA.js} +1 -1
- package/dist/chunk-I4V3UDSA.js.map +1 -0
- package/dist/{chunk-FEY65TBV.js → chunk-KFUUFNTS.js} +1 -1
- package/dist/chunk-KFUUFNTS.js.map +1 -0
- package/dist/{chunk-QO55FN2Y.js → chunk-MFR2IIFG.js} +1 -1
- package/dist/chunk-MFR2IIFG.js.map +1 -0
- package/dist/{chunk-GWEAPEZI.js → chunk-OC275HHS.js} +4 -3
- package/dist/chunk-OC275HHS.js.map +1 -0
- package/dist/chunk-OL4TC3U2.js +156 -0
- package/dist/chunk-OL4TC3U2.js.map +1 -0
- package/dist/{chunk-OT6RPYAP.js → chunk-OMSRSL3B.js} +4 -3
- package/dist/chunk-OMSRSL3B.js.map +1 -0
- package/dist/{chunk-J5BFYY7Q.js → chunk-RL6COPKF.js} +12 -3
- package/dist/chunk-RL6COPKF.js.map +1 -0
- package/dist/{chunk-TB6DY7H5.js → chunk-SOK5EUF7.js} +1 -1
- package/dist/chunk-SOK5EUF7.js.map +1 -0
- package/dist/{chunk-RKVQEH4J.js → chunk-T7DOWS6T.js} +3 -3
- package/dist/chunk-T7DOWS6T.js.map +1 -0
- package/dist/{chunk-7KEUJIIC.js → chunk-TIGSG6BX.js} +1 -1
- package/dist/chunk-TIGSG6BX.js.map +1 -0
- package/dist/{chunk-W5L2JSBY.js → chunk-UDHHKZWO.js} +6 -5
- package/dist/chunk-UDHHKZWO.js.map +1 -0
- package/dist/{chunk-R7ZWUN64.js → chunk-V6GQKOX2.js} +4 -3
- package/dist/chunk-V6GQKOX2.js.map +1 -0
- package/dist/{chunk-KWWR5VMS.js → chunk-V6HCTCVD.js} +4 -3
- package/dist/chunk-V6HCTCVD.js.map +1 -0
- package/dist/{chunk-JFGXQBIP.js → chunk-VDUJZF5G.js} +76 -13
- package/dist/chunk-VDUJZF5G.js.map +1 -0
- package/dist/{chunk-SYDMYBPG.js → chunk-VXFGMDA7.js} +1 -1
- package/dist/chunk-VXFGMDA7.js.map +1 -0
- package/dist/{chunk-YMKH5XTF.js → chunk-WN52USHV.js} +2 -2
- package/dist/{chunk-YMKH5XTF.js.map → chunk-WN52USHV.js.map} +1 -1
- package/dist/{chunk-OBLTAKSX.js → chunk-WSXUPSID.js} +1 -1
- package/dist/chunk-WSXUPSID.js.map +1 -0
- package/dist/{chunk-X2U3QJCJ.js → chunk-ZPWZIVI6.js} +1 -1
- package/dist/chunk-ZPWZIVI6.js.map +1 -0
- package/dist/collapsible-panel.d.ts +5 -2
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/css-values.d.ts +75 -0
- package/dist/css-values.js +3 -0
- package/dist/css-values.js.map +1 -0
- package/dist/empty-state.d.ts +2 -1
- package/dist/empty-state.js +1 -1
- package/dist/floating-toolbar.d.ts +4 -3
- package/dist/floating-toolbar.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +26 -25
- package/dist/list-action-row.d.ts +1 -2
- package/dist/list-action-row.js +2 -1
- package/dist/list-header.d.ts +1 -1
- package/dist/list-header.js +2 -1
- package/dist/list-inset-control.d.ts +4 -3
- package/dist/list-inset-control.js +1 -1
- package/dist/list-inset-text.d.ts +4 -3
- package/dist/list-inset-text.js +1 -1
- package/dist/list-item.d.ts +4 -3
- package/dist/list-item.js +2 -1
- package/dist/list.d.ts +10 -8
- package/dist/list.js +2 -1
- package/dist/nav-stack.d.ts +9 -6
- package/dist/nav-stack.js +2 -1
- package/dist/pane.d.ts +6 -5
- package/dist/pane.js +1 -1
- package/dist/resizable-region.d.ts +2 -1
- package/dist/resizable-region.js +1 -1
- package/dist/segmented-control.d.ts +4 -3
- package/dist/segmented-control.js +2 -1
- package/dist/select.d.ts +3 -1
- package/dist/select.js +2 -1
- package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
- package/dist/skeleton.d.ts +7 -6
- package/dist/skeleton.js +2 -1
- package/dist/split-view.d.ts +5 -4
- package/dist/split-view.js +3 -2
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +3 -2
- package/dist/state-banner.js +2 -1
- package/dist/styles/catalog.css +8 -1
- package/dist/styles/collapsible-panel.css +6 -2
- package/dist/styles/foundation.css +44 -0
- package/dist/styles/list-header.css +6 -0
- package/dist/styles/list-item.css +9 -0
- package/dist/styles/nav-stack.css +32 -2
- package/dist/styles/segmented-control.css +8 -1
- package/dist/styles/select.css +2 -1
- package/dist/styles/state-banner.css +14 -0
- package/dist/styles/tab-scaffold.css +1 -0
- package/dist/styles/token-search-field.css +4 -0
- package/dist/styles/toolbar-control-group.css +98 -16
- package/dist/styles/webawesome.css +22 -0
- package/dist/styles/workbench.css +4 -0
- package/dist/sunken-panel.d.ts +4 -3
- package/dist/sunken-panel.js +1 -1
- package/dist/surface-scaffold.d.ts +6 -5
- package/dist/surface-scaffold.js +1 -1
- package/dist/tab-bar.d.ts +6 -5
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +2 -1
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/token-search-field.d.ts +5 -4
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +10 -4
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.js +2 -1
- package/dist/toolbar.d.ts +6 -5
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +4 -2
- package/dist/value-table.js +2 -1
- package/dist/wire-nav-stack.d.ts +4 -1
- package/dist/wire-nav-stack.js +125 -0
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-tab-bars.d.ts +1 -0
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +7 -4
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +9 -3
- package/docs/collapsible-panel.md +4 -1
- package/docs/component-contract.md +56 -8
- package/docs/component-selection.md +45 -8
- 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/multiline-dark.svg +1 -1
- package/docs/design/templates/list-item/multiline.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-philosophy.md +4 -2
- package/docs/examples/component-catalog-extension-v2.json +2 -0
- package/docs/layout.md +12 -4
- package/docs/nav-stack.md +29 -8
- package/docs/recipes.md +8 -4
- package/docs/split-view.md +6 -0
- package/docs/surface-scaffold.md +8 -0
- package/docs/tab-scaffold.md +3 -1
- package/docs/type-contracts.md +39 -12
- package/docs/ui-analyzer.md +17 -3
- package/docs/ui-doctor.md +7 -0
- package/docs/ux-demo.md +10 -4
- package/docs/webawesome-theme.md +6 -0
- package/docs/workbench.md +6 -4
- package/llms.txt +46 -6
- package/package.json +5 -1
- package/ux-demo/recipes/compact-toolbar.tsx +10 -9
- package/ux-demo/recipes/composer-form.tsx +6 -3
- package/ux-demo/recipes/loading-inspector.tsx +2 -1
- package/dist/chunk-2NMQW7FX.js.map +0 -1
- package/dist/chunk-4EF2CMYS.js.map +0 -1
- package/dist/chunk-7KEUJIIC.js.map +0 -1
- package/dist/chunk-B3A3IVO5.js.map +0 -1
- package/dist/chunk-FEY65TBV.js.map +0 -1
- package/dist/chunk-GWEAPEZI.js.map +0 -1
- package/dist/chunk-HP2B2L26.js.map +0 -1
- package/dist/chunk-IJBSZ4NX.js.map +0 -1
- package/dist/chunk-J5BFYY7Q.js.map +0 -1
- package/dist/chunk-JFGXQBIP.js.map +0 -1
- package/dist/chunk-JRKSK2HX.js.map +0 -1
- package/dist/chunk-KWWR5VMS.js.map +0 -1
- package/dist/chunk-LPOMRC4C.js.map +0 -1
- package/dist/chunk-OBLTAKSX.js.map +0 -1
- package/dist/chunk-OT6RPYAP.js.map +0 -1
- package/dist/chunk-PX3YSS5C.js.map +0 -1
- package/dist/chunk-QO55FN2Y.js.map +0 -1
- package/dist/chunk-R7ZWUN64.js.map +0 -1
- package/dist/chunk-RKVQEH4J.js.map +0 -1
- package/dist/chunk-SYDMYBPG.js.map +0 -1
- package/dist/chunk-TB6DY7H5.js.map +0 -1
- package/dist/chunk-VCE4MPVE.js.map +0 -1
- package/dist/chunk-W5L2JSBY.js.map +0 -1
- package/dist/chunk-WNWD54GR.js.map +0 -1
- package/dist/chunk-X2U3QJCJ.js.map +0 -1
|
@@ -24,10 +24,18 @@ remains authored rather than generated.
|
|
|
24
24
|
machine-evaluable composition layer without changing the v1 selection and
|
|
25
25
|
delivery contract. Every v1 entry projects once, in order, under the stable
|
|
26
26
|
qualified key `package:id`. Each v2 entry explicitly covers parents/contexts,
|
|
27
|
-
named zones and cardinality, child concepts, state ownership, required wiring,
|
|
27
|
+
named zones and cardinality, optional explicit JSX prop bindings, child concepts, state ownership, required wiring,
|
|
28
28
|
responsive ownership, layout and geometry, accessibility obligations, public
|
|
29
29
|
CSS boundaries, diagnostics, and provenance.
|
|
30
30
|
|
|
31
|
+
An entry's optional `cssValueProps` makes CSS-adjacent prop grammar equally
|
|
32
|
+
machine-evaluable. Each path records the property grammar, finite shorthands,
|
|
33
|
+
canonical versus exceptional spacing choices, standalone and expression-only
|
|
34
|
+
helpers, raw/unsafe policy, and serialization examples. Nested paths use the
|
|
35
|
+
bounded `items[].property` form. Consumer catalogs can publish the same facts;
|
|
36
|
+
ESLint, `kerf-ui-analyze`, and `kerf-ui-doctor` then issue the same stable
|
|
37
|
+
`KUI-L013`–`KUI-L017` feedback for application and third-party components.
|
|
38
|
+
|
|
31
39
|
The generator starts with permissive defaults. An `any` mode records that the
|
|
32
40
|
catalog has no defensible prohibition; it does not claim every composition is
|
|
33
41
|
recommended. Objective rules live in
|
|
@@ -35,6 +43,17 @@ recommended. Objective rules live in
|
|
|
35
43
|
diagnostics. A tool reports a diagnostic only after proving its exact `when`
|
|
36
44
|
condition. Subjective choice, product policy, and visual taste remain prose.
|
|
37
45
|
|
|
46
|
+
Kerf UI calls component content positions **named `SafeHtml` prop slots**.
|
|
47
|
+
Use `children` for one homogeneous primary region; use explicit named
|
|
48
|
+
`SafeHtml` props for semantic positions or replacement content such as
|
|
49
|
+
`header`, `footer`, `leading`, `trailing`, `icon`, and `action`. These are typed
|
|
50
|
+
function-component props, not native web-component `<slot>` elements and not a
|
|
51
|
+
generic `slots={{...}}` object. A v2 zone declares `jsx.prop` only when tooling
|
|
52
|
+
can soundly bind that zone to one public JSX prop; `children` is spelled
|
|
53
|
+
explicitly. Zones without that binding remain guidance and are never guessed
|
|
54
|
+
from their ids. Static tools inspect only visible JSX structure and leave
|
|
55
|
+
dynamic `SafeHtml` expressions to runtime and accessibility evaluation.
|
|
56
|
+
|
|
38
57
|
V1 consumers continue unchanged. V2 consumers read v1 for selection/delivery
|
|
39
58
|
and v2 for composition. `npm run catalog:sync` projects every new component,
|
|
40
59
|
recipe, and supported Web Awesome entry, and the completeness gate prevents
|
|
@@ -140,7 +159,7 @@ claim Kerf delivery paths for them.
|
|
|
140
159
|
- Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
|
|
141
160
|
- Actions are `data-action` strings. Wire them at a stable root with `delegate()` or `delegateActions()` and retain the disposer.
|
|
142
161
|
- A reusable component never owns per-instance mutable module state.
|
|
143
|
-
- Consumers style through `--kui-*` semantic tokens and public component classes. Foundation tokens provide opinionated neutral, brand/info, success, warning, and danger fill/border/foreground roles. Stateful components expose local override variables; prefer an equivalent prop or token before writing a selector.
|
|
162
|
+
- Consumers style through `--kui-*` semantic tokens and public component classes. Foundation tokens provide opinionated neutral, brand/info, non-status pop, success, warning, and danger fill/border/foreground roles. Stateful components expose local override variables; prefer an equivalent prop or token before writing a selector.
|
|
144
163
|
|
|
145
164
|
`ListItem.rootAttributes`, `ListActionRow.rootAttributes`,
|
|
146
165
|
`ListHeader.rootAttributes`, `AppTab.rootAttributes`,
|
|
@@ -195,7 +214,11 @@ need, request a supported hook instead of inferring one from rendered markup.
|
|
|
195
214
|
|
|
196
215
|
`ListItem` renders its leading icon and nested SVG at a root-scaled 18px by
|
|
197
216
|
default while retaining the row's 44px minimum interactive target. Multiline
|
|
198
|
-
rows align that 18px visual with the first inherited text line.
|
|
217
|
+
rows align that 18px visual with the first inherited text line. The row clips
|
|
218
|
+
descendant painting to its rounded root so labels, status, and dormant trailing
|
|
219
|
+
content cannot escape its visible boundary. Rows with descriptions grow enough
|
|
220
|
+
to keep their owned two-line label stack readable; use `multiline` when the
|
|
221
|
+
primary label should wrap rather than truncate.
|
|
199
222
|
|
|
200
223
|
`DisclosureArrow` uses an 18px root-scaled visual by default. Override
|
|
201
224
|
`--kui-disclosure-arrow-size` on the component or its containing scope when a
|
|
@@ -224,6 +247,13 @@ segmented-control, app-tab, and tab-bar colors likewise use their public
|
|
|
224
247
|
hover, and selected-state variables, so rounded, pill, and toolbar presentations
|
|
225
248
|
remain opinionated but locally overridable.
|
|
226
249
|
|
|
250
|
+
Inset hover and selection surfaces follow the outer control shape instead of
|
|
251
|
+
choosing an independent radius. `--kui-layout-highlight-inset` is the full
|
|
252
|
+
distance between those edges; the inner radius is the outer radius minus that
|
|
253
|
+
inset. `ToolbarControlGroup` publishes its derived radius to nested toolbar
|
|
254
|
+
`SegmentedControl` choices, keeping both direct buttons and exclusive-choice
|
|
255
|
+
highlights concentric when the group switches between pill and rounded shapes.
|
|
256
|
+
|
|
227
257
|
`ResizableRegion` owns the Hot Sheet 2 split treatment: a persistent 1px
|
|
228
258
|
separator with a compact grip that appears on hover or keyboard focus. Override
|
|
229
259
|
`--kui-resizable-region-separator-color`,
|
|
@@ -238,12 +268,24 @@ Logical-edge separator lines are independently opt-in and default off.
|
|
|
238
268
|
`@kerfjs/ui/layout.css` retains the pane roles and supplies the related content
|
|
239
269
|
geometry classes.
|
|
240
270
|
`List` is the corresponding layout-only vertical stack: its children stretch by
|
|
241
|
-
default, while `gap`, `flex`, and `scrollable` opt into
|
|
271
|
+
default, while `gap`, `flex`, and `scrollable` opt into typed spacing,
|
|
242
272
|
flex growth, and vertical scroll ownership. `dividerSides` accepts canonical
|
|
243
273
|
physical top/right/bottom/left combinations such as `tr` and `trbl`; the stack
|
|
244
|
-
adds no list semantics, margin, or padding of its own.
|
|
245
|
-
|
|
246
|
-
|
|
274
|
+
adds no list semantics, margin, or padding of its own. Use a direct finite
|
|
275
|
+
spacing shorthand such as `gap="2xs"`, or pass a `CssLength` created by the
|
|
276
|
+
CSS-free `@kerfjs/ui/css-values` helpers: `space('m')`, `rem(0.25)`,
|
|
277
|
+
`lengthVar('--app-gap')`, or `calc(plus(rem(0.25), pct(10)))`. `plus` returns a
|
|
278
|
+
non-standalone `CssLengthExpression`; only `calc` turns it into a complete
|
|
279
|
+
length. Raw CSS strings and source-only `remify(4px)` are not valid runtime
|
|
280
|
+
props. The `CssLength` name intentionally includes percentages for UI dimension
|
|
281
|
+
props. Its opaque string brand improves authoring correctness; it is not a CSS
|
|
282
|
+
sanitizer or security boundary.
|
|
283
|
+
`List.flex` likewise accepts its boolean default, finite keywords, or `CssFlex`
|
|
284
|
+
from `flex(grow, shrink, basis)`. `Skeleton` width/height use `CssSize`, radius
|
|
285
|
+
uses `CssLength`, and `SelectChoice.color` uses `CssColor` from `uiColor()` or
|
|
286
|
+
`colorVar()`. Never exchange these property grammars. List rows expose
|
|
287
|
+
`className`, public tokens, and semantic props instead of unrestricted `style`
|
|
288
|
+
declarations.
|
|
247
289
|
`.kui-content` gives major children 24px vertical separation.
|
|
248
290
|
`.kui-content-item` gives one child 8px inline margin, a real 1px border,
|
|
249
291
|
8px padding, and 12px corners; border and background may be transparent without
|
|
@@ -255,7 +297,13 @@ groups and inside items. For an ordinary icon/action control inside a group, use
|
|
|
255
297
|
a plain `<button>` — the group styles `> button` fully, and it keeps the group
|
|
256
298
|
free of a Web Awesome dependency and shadow DOM. Reach for `wa-button` only when
|
|
257
299
|
you need a Web Awesome feature, chiefly the `slot="trigger"` button of a
|
|
258
|
-
`wa-dropdown` popup menu.
|
|
300
|
+
`wa-dropdown` popup menu. For a compact mixed-content group, set
|
|
301
|
+
`nestedDropdown`; a text-and-caret trigger grows to its intrinsic width while an
|
|
302
|
+
icon-only trigger can remain square, keeping the separator and caret inside the
|
|
303
|
+
group. The short text trigger has an engine-stable 66px floor because WebKit
|
|
304
|
+
does not include the shadow caret in the custom-element host's intrinsic width;
|
|
305
|
+
raise `--kui-toolbar-dropdown-trigger-width` for longer localized copy.
|
|
306
|
+
`ListHeader` similarly separates its dormant title and
|
|
259
307
|
optional count or badge from its optional 44px action. Use the mutually
|
|
260
308
|
exclusive `count`/`countLabel` pair for non-negative safe-integer section
|
|
261
309
|
quantities; reserve `badge` for non-count `SafeHtml`. Do not concatenate counts
|
|
@@ -36,6 +36,42 @@ useful subtree. Prefer a component prop or component-specific token when the
|
|
|
36
36
|
change belongs to one component or instance, and preserve the documented
|
|
37
37
|
foreground/background contrast relationships and visible focus ring.
|
|
38
38
|
|
|
39
|
+
### Typed runtime dimensions
|
|
40
|
+
|
|
41
|
+
Use `List` spacing shorthands for the common case: `gap="xs"`, `gap="m"`, and
|
|
42
|
+
the other `UiSpaceName` values resolve to their matching `--kui-space-*` token.
|
|
43
|
+
The canonical rhythm is `none`, `2xs`, `xs`, `m`, and `l`; use `s` and `xl`
|
|
44
|
+
only as deliberate exceptions.
|
|
45
|
+
|
|
46
|
+
When a value cannot be expressed by one spacing step, import the CSS-free
|
|
47
|
+
`@kerfjs/ui/css-values` subpath. `px`, `rem`, `em`, and `pct` create typed
|
|
48
|
+
`CssLength` values; `space` and `lengthVar` reference spacing or
|
|
49
|
+
application-owned length tokens. `plus` creates a non-standalone
|
|
50
|
+
`CssLengthExpression`, so wrap it with `calc` before passing it to `List.gap`:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { calc, pct, plus, rem, space } from "@kerfjs/ui/css-values";
|
|
54
|
+
import { List } from "@kerfjs/ui/list";
|
|
55
|
+
|
|
56
|
+
<List gap="xs">...</List>;
|
|
57
|
+
<List gap={space("m")}>...</List>;
|
|
58
|
+
<List gap={calc(plus(rem(0.25), pct(10)))}>...</List>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`CssValue` is the common branded base, not an arbitrary-string constructor.
|
|
62
|
+
Raw strings such as `"0.25rem"` no longer satisfy `List.gap`; this breaking
|
|
63
|
+
contract lands before the 5.0 stable release so misspelled tokens and incomplete
|
|
64
|
+
expressions fail during typechecking rather than in the browser.
|
|
65
|
+
|
|
66
|
+
Other CSS-valued props use distinct contracts: `flex()` creates `CssFlex` for
|
|
67
|
+
`List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
|
|
68
|
+
keywords for width/height); and `uiColor()`/`colorVar()` create `CssColor` for
|
|
69
|
+
`SelectChoice.color`. Do not substitute one brand for another. Row components
|
|
70
|
+
have no declaration-string `style` prop; use `className`, documented public
|
|
71
|
+
tokens, and semantic component props. Catalog media queries remain strings and
|
|
72
|
+
semantic pixel measurements remain numbers because those are different APIs.
|
|
73
|
+
The corresponding public builders are `flex`, `uiColor`, and `colorVar`.
|
|
74
|
+
|
|
39
75
|
## Production recipes
|
|
40
76
|
|
|
41
77
|
Use the [complete recipe guide](./recipes.md) when several primitives form one
|
|
@@ -80,17 +116,17 @@ an upstream component or recipe request.
|
|
|
80
116
|
| Icon — `LucideIcon` | A decorative or explicitly labeled Lucide-compatible icon belongs in app UI. | Do not use an icon as the only name of an unfamiliar action; add visible or accessible text. Prefer it over Web Awesome `wa-icon`. | None. | Icon choice and meaningful label. | `@kerfjs/ui/lucide-icon` | [Accessibility](./accessibility.md#shared-rules) |
|
|
81
117
|
| Disclosure indicator — `DisclosureArrow` | A control needs one animated 18px root-scaled visual for open and closed state, including configurable directions or a replacement icon. | Do not use it as the interactive control or accessible name; place it inside the button or control that exposes expanded state. | Pass the controlled `open` state, render it inside the owning control, author replacement icon content facing right before transforms, and override `--kui-disclosure-arrow-size` only when another visual size is required. Direction changes use the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. | Open state, interaction, accessible name, size override, replacement glyph, direction choices, and shortest-path rotation. | `@kerfjs/ui/disclosure-arrow` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
82
118
|
| Application toolbar or heading — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar or a page/panel/dialog heading. | Do not add bespoke heading geometry or a private wrapper; use the same zone contract everywhere. | Put identity/title text directly in a zone as `ToolbarText`; group controls and icon tiles in `ToolbarControlGroup`. | Actions, command availability, responsive relocation, state, and supporting copy below a heading. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
|
|
83
|
-
| Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained/borderless chrome, compact/tight geometry, icon/text/mixed/avatar anatomy, selected treatment, or nested dropdown sizing. | Do not use it merely to align unrelated buttons; use
|
|
119
|
+
| Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained/borderless chrome, compact/tight geometry, icon/text/mixed/avatar anatomy, selected treatment, or nested dropdown sizing. | Do not use it merely to align unrelated buttons; use Toolbar's named `SafeHtml` prop slots or ordinary layout. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; declare typed content and appearance instead of overriding descendants or `::part(base)`; use `avatarImage` instead of inserting an `img` (single groups paint the outer group; multi-button groups paint the pressed highlight); use `SegmentedControl` for an exclusive choice. | Actions, pressed/expanded state, content, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
84
120
|
| Floating controls over content — `FloatingToolbar` | A small cluster of controls (e.g. a drawer restore) must float over scrolling content — distinct and forced-dark — inside a positioned container. | Do not use it for a primary page or panel toolbar (use `Toolbar`), or for anything that must sit above dialogs/overlays — it is not top-layer. | Compose `ToolbarControlGroup`s and delegate their actions; the app owns visibility and position. | Controls, visibility, and position. | `@kerfjs/ui/floating-toolbar` | [Component subpaths](../README.md#component-subpaths) |
|
|
85
121
|
| 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`. | 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) |
|
|
86
122
|
| 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) |
|
|
87
|
-
| 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.
|
|
123
|
+
| 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) |
|
|
88
124
|
| 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) |
|
|
89
125
|
| 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. Do not shrink its 44px action target to the 18px visual. 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) |
|
|
90
126
|
| Inset self-bordered control — `ListInsetControl` | A control that owns its own border and padding but no outer margin (a search input, a `SegmentedControl`) must line up inside a `.kui-content` list with the standard 8px inline margins and stretch to fill the row. | Do not wrap a `.kui-content-item` or a `ListItem`/`ListHeader` that already owns its inline margin — that double-insets it. Do not add ad-hoc `margin`/`padding` around a bare control to align it; use this instead. | Place the self-bordered control(s) as children; they stretch to fill. It applies only the inline margin, flex stretch, and 8px gap — the child owns its own border and padding. | The control(s), their state, and action wiring. | `@kerfjs/ui/list-inset-control` | [Pane geometry](../README.md#component-subpaths) |
|
|
91
127
|
| Inset bare text — `ListInsetText` | A plain string or inline content with no margin, border, or padding of its own must sit in a `.kui-content` list with its text edge aligned to the bordered items around it. | Do not use it to wrap a component that already owns content-item geometry (`ListItem`, `StateBanner`, a `.kui-content-item`) — that double-insets it. Do not hand-roll the 8px margin / 1px border / 8px padding. | Pass the text or inline `SafeHtml` as children; it supplies the 8px inline margin, 1px transparent border, and 8px padding so the text edge lands at the standard 17px inset. Pass `horizontalOnly` to keep that horizontal inset but drop the vertical margin, border, and padding for tight text layout. | The text, copy, and localization. | `@kerfjs/ui/list-inset-text` | [Pane geometry](../README.md#component-subpaths) |
|
|
92
|
-
| Application column — `Pane` | A sidebar, main area, inspector, or dialog needs shared vertical header/content/footer organization, one scroll owner, and optional edge separators. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Import `Pane`,
|
|
93
|
-
| One-pane navigation — `NavStack` | A compact flow pushes into details while keeping earlier views mounted, or a single-pane screen needs the same stable layout contract. | Do not use it when list and detail should remain visible together; use `SplitView`. Do not use it alone for co-equal top-level destinations; use one stack per `TabScaffold` tab. | Keep the ordered view array controlled, import its CSS explicitly, and call `wireNavStack` for back delegation, transitions, and focus
|
|
128
|
+
| Application column — `Pane` | A sidebar, main area, inspector, or dialog needs shared vertical header/content/footer organization, one scroll owner, and optional edge separators. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Import `Pane`, use `children` for its homogeneous primary content and the named `SafeHtml` prop slots `header`/`footer` for optional fixed chrome, then opt into only the needed logical-edge separators; add `.kui-content-item` as needed. A visible pane owns collapse in its toolbar; move a hidden inline-start pane's restore control to the main toolbar leading edge and an inline-end pane's restore control to its trailing edge. | Layout hierarchy, reading width, responsive relocation, pane visibility state, slot semantics, labels, and separator placement. | `@kerfjs/ui/pane` | [Pane anatomy](./layout.md#anatomy) |
|
|
129
|
+
| One-pane navigation — `NavStack` | A compact flow pushes into details while keeping earlier views mounted, or a single-pane screen needs the same stable layout contract. | Do not use it when list and detail should remain visible together; use `SplitView`. Do not use it alone for co-equal top-level destinations; use one stack per `TabScaffold` tab. | Keep the ordered view array controlled, put view-specific bottom chrome on each view (or use the component fallback), import its CSS explicitly, and call `wireNavStack` for back delegation, content-slide/chrome-cross-fade transitions, and focus move/restore. Mark a preferred initial target with `data-nav-focus` when DOM order is insufficient. | View keys/content/titles, push and pop actions, preferred initial focus targets, and per-view or persistent bottom toolbar content. | `@kerfjs/ui/nav-stack` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
|
|
94
130
|
| Responsive list-detail — `SplitView` | A primary list and related detail belong together on roomy devices but become a one-pane drill-down on compact devices. | Do not use it for a single linear flow or a multi-panel editor shell. | Provide controlled selection/detail state and device class; opt into resize wiring only when the separator is user adjustable. | Selection, detail content, compact back action, device policy, and optional persisted width. | `@kerfjs/ui/split-view` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
|
|
95
131
|
| Compact top-level destinations — `TabScaffold` | A handset or portrait-tablet app has two to five co-equal destinations whose scenes and nested navigation must remain mounted across switches. | Do not use it for document tabs, reorderable work items, or a desktop navigation rail. | Keep the active id controlled, import its CSS explicitly, and call `wireTabScaffold`; commonly place one `NavStack` in each tab scene. | Destination definitions, active id, routing, persistence, and roomy-device replacement. | `@kerfjs/ui/tab-scaffold` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
|
|
96
132
|
| Desktop multi-panel workspace — `Workbench` | A desktop-class tool needs a central work area plus independently collapsible navigator, inspector, or console regions. | Do not shrink the full shell onto compact devices or use it for one standalone panel. | Render only the required rails/drawer, keep collapse state controlled, and replace peripheral regions with stacks or overlays below desktop sizes. | Panel presence/content, collapsed states, sizes, persistence, responsive replacement, and controls. | `@kerfjs/ui/workbench` | [Workbench](./workbench.md) |
|
|
@@ -101,14 +137,14 @@ an upstream component or recipe request.
|
|
|
101
137
|
| One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, compact/segmented/icon-only presentation, label truncation, safe domain metadata, or a product-specific close glyph. | Do not render it alone or use it for a small settings choice; compose in `TabBar`, or use `SegmentedControl`. Keep `closeIcon` noninteractive. | Compose in `TabBar`; let `wireTabBars` manage interaction. Put only domain `data-*` values in `rootAttributes`; use typed appearance props instead of descendant CSS. | Tab identity, order, selection, close policy, content, domain metadata values, and outer placement. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
|
|
102
138
|
| Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, reorder, fill a segmented inspector strip, or sit beside a trailing action. | Do not use it for a compact local view toggle; use `SegmentedControl`. Do not use it for a long choice list; use `Select`. Prefer it over Web Awesome `wa-tab-group`, `wa-tab`, and `wa-tab-panel` for Kerf app tabs. | Call `wireTabBars` once, retain the disposer, and apply `onReorder` synchronously; `reorderTabs` is the default array helper. Choose typed presentation/allocation props while the parent owns outer placement. | Ordered tabs, selection, panels, routing, closing, persistence, and outer placement. | `@kerfjs/ui/tab-bar` plus `@kerfjs/ui/wire-tab-bars` | [Tabs contract](./accessibility.md#tabs) |
|
|
103
139
|
| Panel, dialog, or page heading — `Toolbar` + `ToolbarText` | A panel, dialog, or page needs an extra-large title, optional icon, grouped trailing actions, and optional supporting copy. | Do not add a private heading wrapper or bespoke layout. The toolbar supplies structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. | Connect title/supporting-copy ids to the host, group controls, and delegate actions. **For a page or view title set `headingLevel` (usually `1`)** on `ToolbarText`; omit it for a dialog title referenced by `aria-labelledby`. | Open state, focus lifecycle, dismissal, controls, ids, copy, and heading level. | `@kerfjs/ui/toolbar` + `@kerfjs/ui/toolbar-text` | [Toolbar headings](./accessibility.md#toolbar-headings) |
|
|
104
|
-
| Dialog or popup geometry — `DialogSurface`, `PopupSurface` | Recurring Web Awesome dialogs need standard sizes/presentations/body/footer insets, or dropdown menus need list-compatible inset ownership. | Do not use these wrappers as generic layout or to replace native dialog/dropdown behavior. Avoid consumer `::part()` overrides
|
|
140
|
+
| Dialog or popup geometry — `DialogSurface`, `PopupSurface` | Recurring Web Awesome dialogs need standard sizes/presentations/body/footer insets, or dropdown menus need list-compatible inset ownership. | Do not use these wrappers as generic layout or to replace native dialog/dropdown behavior. Avoid consumer `::part()` overrides and do not stack a dialog body inset around list-owned child geometry. | Wrap one `wa-dialog` or `wa-dropdown`; choose the typed geometry while retaining native labeling, open state, focus, dismissal, and menu semantics. Compose a dialog body as a `List` by default, use `bodyInset="none"` when its children own list geometry, and wrap bare prose in `ListInsetText`. | State, focus lifecycle, dismissal, labels, content, and outer placement. | `@kerfjs/ui/surface-scaffold` | [Surface scaffolds](./surface-scaffold.md) |
|
|
105
141
|
| Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
106
142
|
| Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. | None; pass a label when the spinner conveys status. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
|
|
107
143
|
| Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`, which the pure-Kerf primitives avoid to stay Web-Awesome-free. | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render skeletons in its value slots with interactivity disabled; use the standalone `Skeleton` for a custom slot. | Loading lifecycle, which slots are unknown, and announcing the loading region. | `@kerfjs/ui/skeleton` | [Accessibility](./accessibility.md#shared-rules) |
|
|
108
144
|
| Value selection — `Select` | A controlled form, toolbar, or compact navigation value comes from a moderate or long choice list, possibly grouped, icon-bearing, icon-only when closed, or truncating. | Do not use it for commands; use a real action menu. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once; listen for standard input/change events. Use typed presentation props rather than consumer `::part()` overrides; let the parent own placement. | Controlled value, validation, choices, domain mapping, and outer placement. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
|
|
109
145
|
| Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `Select`. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
|
|
110
146
|
| Structured search editor — `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields` | Free text and ordered, editable, removable filter tokens share one searchbox; enable `collapsible` when an empty, unfocused field should reduce to one iconic action, standalone or in a toolbar group. | Do not use it for ordinary text entry; use a native input or Web Awesome `wa-input`. Do not use it when filters belong in separate form controls. | Read DOM-owned text on input, empty `textContent` on clear, and use `placeTokenSearchCaret` after explicit controlled focus changes. Call `wireTokenSearchFields` from `@kerfjs/ui/wire-token-search-fields` once so Enter submits without adding a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. In `collapsible` mode it also manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse); bind the field's `expanded` to the signal on the returned handle (`handle.expanded(id)`) or adopt your own via `collapsible.signals`, and opt out per behavior only when the app must own it. | Parsing, suggestions, tokens, query execution, results, announcements, and — only if overriding the default — the collapsible `expanded` signal. | `@kerfjs/ui/token-search-field` | [TokenSearchField contract](./accessibility.md#tokensearchfield) |
|
|
111
|
-
| Persistent inline status — `StateBanner` | A neutral, info, success, warning, or danger message belongs next to the affected work.
|
|
147
|
+
| Persistent inline status — `StateBanner` | A neutral, info, pop, success, warning, or danger message belongs next to the affected work; pop highlights featured or novel content without implying status. | Do not use pop as a success, warning, or danger substitute. Do not use StateBanner for a no-content screen; use `EmptyState`. Do not use it for transient confirmation; use a toast. Web Awesome `wa-callout` is the ecosystem alternative for Web Awesome-owned content. | Delegate an optional action; choose alert urgency only for attention-requiring failure. Use the optional tone-tinted `badge` for a terse count or status beside the title, not as the only expression of meaning. | State mapping, message lifetime, retry/action behavior, badge content, and copy. | `@kerfjs/ui/state-banner` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
112
148
|
| Empty or busy content area — `EmptyState` | A content region has no items, cannot proceed, or is loading and needs explanation plus an optional action. | Do not use it for an inline status update; use `StateBanner`. Do not use it for transient success; use `wa-toast`/`wa-toast-item` or the application's toast system. | Delegate its optional action; it composes `LoadingSpinner` when busy. | Empty/busy policy, recovery action, illustration, and copy. | `@kerfjs/ui/empty-state` | [Feedback ownership](./component-contract.md#extracted-versus-application-specific) |
|
|
113
149
|
|
|
114
150
|
## Ambiguous choices
|
|
@@ -153,7 +189,7 @@ Common toolbar patterns:
|
|
|
153
189
|
| Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
|
|
154
190
|
| One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
|
|
155
191
|
| An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
|
|
156
|
-
| A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once
|
|
192
|
+
| A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
|
|
157
193
|
| A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
|
|
158
194
|
|
|
159
195
|
A **popup menu in a toolbar** is a `single` `ToolbarControlGroup` wrapping a Web
|
|
@@ -183,7 +219,8 @@ Configure the List family instead of selecting its descendant classes. Use
|
|
|
183
219
|
label line, `status` for dormant state text, and `busy` for known progress that
|
|
184
220
|
keeps current content visible. `divider` marks group boundaries without an
|
|
185
221
|
app-owned separator rule. `ListHeader.indicatorTone` gives count, badge, or
|
|
186
|
-
status content neutral, accent, or danger attention.
|
|
222
|
+
status content neutral, accent, pop, or danger attention. Pop marks featured or
|
|
223
|
+
novel content without claiming a status transition.
|
|
187
224
|
|
|
188
225
|
When a row needs a context action, use `ListActionRow`; its two native buttons
|
|
189
226
|
remain siblings. `trailingActionVisibility="interaction"` keeps the action
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 190" width="420" height="190"><defs><clipPath id="cp0"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 190" width="420" height="190"><defs><clipPath id="cp0"><rect x="28" y="20" width="324" height="44"/></clipPath><clipPath id="ov1"><rect x="29" y="21" width="322" height="42" rx="11"/></clipPath><clipPath id="cp2"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="cp4"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="ov5"><rect x="63" y="33" width="272" height="18" rx="0"/></clipPath><clipPath id="ct6"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="cp7"><rect x="28" y="64" width="324" height="44"/></clipPath><clipPath id="ov8"><rect x="29" y="65" width="322" height="42" rx="11"/></clipPath><clipPath id="cp9"><rect x="37" y="77" width="18" height="18"/></clipPath><clipPath id="cp11"><rect x="63" y="77" width="272" height="18"/></clipPath><clipPath id="ov12"><rect x="63" y="77" width="272" height="18" rx="0"/></clipPath><clipPath id="ct13"><rect x="63" y="77" width="272" height="18"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><g clip-path="url(#cp2)"><g color="rgb(245, 245, 247)"><clipPath id="svgic3"><rect x="37" y="33" width="18" height="18"/></clipPath><g clip-path="url(#svgic3)"><g transform="matrix(.75 0 0 .75 37 33)"><path d="M11.5 2.3a.5.5 0 0 1 1 0L14.8 7a2 2 0 0 0 1.6 1l5.1.8a.5.5 0 0 1 .3.9l-3.7 3.6a2 2 0 0 0-.6 2l.9 5a.5.5 0 0 1-.8.6L13 18.6a2 2 0 0 0-2 0L6.4 21a.5.5 0 0 1-.8-.6l1-5a2 2 0 0 0-.7-2L2.2 9.8a.5.5 0 0 1 .3-1l5.1-.7A2 2 0 0 0 9.2 7Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp4)"><g clip-path="url(#ov5)"><text x="63" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(245,245,247)">Priority</text></g></g></g></g><g clip-path="url(#cp7)"><rect x="28.5" y="64.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov8)"><g clip-path="url(#cp9)"><g color="rgb(245, 245, 247)"><clipPath id="svgic10"><rect x="37" y="77" width="18" height="18"/></clipPath><g clip-path="url(#svgic10)"><g transform="matrix(.75 0 0 .75 37 77)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp11)"><g clip-path="url(#ov12)"><text x="63" y="92" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(245,245,247)">All work</text></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 190" width="420" height="190"><defs><clipPath id="cp0"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 190" width="420" height="190"><defs><clipPath id="cp0"><rect x="28" y="20" width="324" height="44"/></clipPath><clipPath id="ov1"><rect x="29" y="21" width="322" height="42" rx="11"/></clipPath><clipPath id="cp2"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="cp4"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="ov5"><rect x="63" y="33" width="272" height="18" rx="0"/></clipPath><clipPath id="ct6"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="cp7"><rect x="28" y="64" width="324" height="44"/></clipPath><clipPath id="ov8"><rect x="29" y="65" width="322" height="42" rx="11"/></clipPath><clipPath id="cp9"><rect x="37" y="77" width="18" height="18"/></clipPath><clipPath id="cp11"><rect x="63" y="77" width="272" height="18"/></clipPath><clipPath id="ov12"><rect x="63" y="77" width="272" height="18" rx="0"/></clipPath><clipPath id="ct13"><rect x="63" y="77" width="272" height="18"/></clipPath></defs><g clip-path="url(#cp0)"><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov1)"><g clip-path="url(#cp2)"><g color="rgb(29, 29, 31)"><clipPath id="svgic3"><rect x="37" y="33" width="18" height="18"/></clipPath><g clip-path="url(#svgic3)"><g transform="matrix(.75 0 0 .75 37 33)"><path d="M11.5 2.3a.5.5 0 0 1 1 0L14.8 7a2 2 0 0 0 1.6 1l5.1.8a.5.5 0 0 1 .3.9l-3.7 3.6a2 2 0 0 0-.6 2l.9 5a.5.5 0 0 1-.8.6L13 18.6a2 2 0 0 0-2 0L6.4 21a.5.5 0 0 1-.8-.6l1-5a2 2 0 0 0-.7-2L2.2 9.8a.5.5 0 0 1 .3-1l5.1-.7A2 2 0 0 0 9.2 7Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp4)"><g clip-path="url(#ov5)"><text x="63" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(29,29,31)">Priority</text></g></g></g></g><g clip-path="url(#cp7)"><rect x="28.5" y="64.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov8)"><g clip-path="url(#cp9)"><g color="rgb(29, 29, 31)"><clipPath id="svgic10"><rect x="37" y="77" width="18" height="18"/></clipPath><g clip-path="url(#svgic10)"><g transform="matrix(.75 0 0 .75 37 77)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp11)"><g clip-path="url(#ov12)"><text x="63" y="92" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(29,29,31)">All work</text></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" width="420" height="230"><defs><clipPath id="cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="cp3"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" width="420" height="230"><defs><clipPath id="cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="cp3"><rect x="28" y="72" width="324" height="44"/></clipPath><clipPath id="ov4"><rect x="29" y="73" width="322" height="42" rx="11"/></clipPath><clipPath id="cp5"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="cp7"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="ov8"><rect x="63" y="85" width="272" height="18" rx="0"/></clipPath><clipPath id="ct9"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="cp10"><rect x="28" y="124" width="324" height="44"/></clipPath><clipPath id="ov11"><rect x="29" y="125" width="322" height="42" rx="11"/></clipPath><clipPath id="cp12"><rect x="37" y="137" width="18" height="18"/></clipPath><clipPath id="cp14"><rect x="63" y="137" width="272" height="18"/></clipPath><clipPath id="ov15"><rect x="63" y="137" width="272" height="18" rx="0"/></clipPath><clipPath id="ct16"><rect x="63" y="137" width="272" height="18"/></clipPath></defs><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g clip-path="url(#ct1)"><text x="37" y="46.5" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">WORKSPACE</text></g></g><rect x="126.8" y="31.2" width="21.6" height="21.6" rx="10.8" fill="rgb(17,17,19)"/><text x="134.33" y="46" font-family="system-ui, "-apple-system", sans-serif" font-size="10" font-weight="700" fill="rgb(174,174,178)">2</text><g clip-path="url(#cp3)"><rect x="28" y="72" width="324" height="44" rx="12" fill="rgb(40,57,66)"/><rect x="28.5" y="72.5" width="323" height="43" rx="11.5" fill="none" stroke="rgb(53,92,109)" stroke-width="1"/><g clip-path="url(#ov4)"><g clip-path="url(#cp5)"><g color="rgb(245, 245, 247)"><clipPath id="svgic6"><rect x="37" y="85" width="18" height="18"/></clipPath><g clip-path="url(#svgic6)"><g transform="matrix(.75 0 0 .75 37 85)"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp7)"><g clip-path="url(#ov8)"><text x="63" y="100" font-family="system-ui, "-apple-system", sans-serif" font-size="16" font-weight="650" fill="rgb(245,245,247)">Inbox</text></g></g></g></g><g clip-path="url(#cp10)"><rect x="28.5" y="124.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov11)"><g clip-path="url(#cp12)"><g color="rgb(245, 245, 247)"><clipPath id="svgic13"><rect x="37" y="137" width="18" height="18"/></clipPath><g clip-path="url(#svgic13)"><g transform="matrix(.75 0 0 .75 37 137)"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.7-.9l-.8-1.2A2 2 0 0 0 7.9 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp14)"><g clip-path="url(#ov15)"><text x="63" y="152" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(245,245,247)">Projects</text></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" width="420" height="230"><defs><clipPath id="cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="cp3"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" width="420" height="230"><defs><clipPath id="cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="cp3"><rect x="28" y="72" width="324" height="44"/></clipPath><clipPath id="ov4"><rect x="29" y="73" width="322" height="42" rx="11"/></clipPath><clipPath id="cp5"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="cp7"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="ov8"><rect x="63" y="85" width="272" height="18" rx="0"/></clipPath><clipPath id="ct9"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="cp10"><rect x="28" y="124" width="324" height="44"/></clipPath><clipPath id="ov11"><rect x="29" y="125" width="322" height="42" rx="11"/></clipPath><clipPath id="cp12"><rect x="37" y="137" width="18" height="18"/></clipPath><clipPath id="cp14"><rect x="63" y="137" width="272" height="18"/></clipPath><clipPath id="ov15"><rect x="63" y="137" width="272" height="18" rx="0"/></clipPath><clipPath id="ct16"><rect x="63" y="137" width="272" height="18"/></clipPath></defs><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g clip-path="url(#ct1)"><text x="37" y="46.5" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">WORKSPACE</text></g></g><rect x="126.8" y="31.2" width="21.6" height="21.6" rx="10.8" fill="rgb(242,242,247)"/><text x="134.33" y="46" font-family="system-ui, "-apple-system", sans-serif" font-size="10" font-weight="700" fill="rgb(110,110,115)">2</text><g clip-path="url(#cp3)"><rect x="28" y="72" width="324" height="44" rx="12" fill="rgb(214,236,255)"/><rect x="28.5" y="72.5" width="323" height="43" rx="11.5" fill="none" stroke="rgb(166,213,255)" stroke-width="1"/><g clip-path="url(#ov4)"><g clip-path="url(#cp5)"><g color="rgb(29, 29, 31)"><clipPath id="svgic6"><rect x="37" y="85" width="18" height="18"/></clipPath><g clip-path="url(#svgic6)"><g transform="matrix(.75 0 0 .75 37 85)"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp7)"><g clip-path="url(#ov8)"><text x="63" y="100" font-family="system-ui, "-apple-system", sans-serif" font-size="16" font-weight="650" fill="rgb(29,29,31)">Inbox</text></g></g></g></g><g clip-path="url(#cp10)"><rect x="28.5" y="124.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#ov11)"><g clip-path="url(#cp12)"><g color="rgb(29, 29, 31)"><clipPath id="svgic13"><rect x="37" y="137" width="18" height="18"/></clipPath><g clip-path="url(#svgic13)"><g transform="matrix(.75 0 0 .75 37 137)"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.7-.9l-.8-1.2A2 2 0 0 0 7.9 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp14)"><g clip-path="url(#ov15)"><text x="63" y="152" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(29,29,31)">Projects</text></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="ov4"><rect x="69" y="28" width="271" height="28" rx="0"/></clipPath><clipPath id="ct5"><rect x="69" y="33" width="271" height="18"/></clipPath><clipPath id="cp6"><rect x="37" y="30" width="24" height="24"/></clipPath></defs><g clip-path="url(#ishc0)"><path d="M24 16h372v52H24zm16 5h340a11 11 0 0 1 11 11v20a11 11 0 0 1-11 11H40a11 11 0 0 1-11-11V32a11 11 0 0 1 11-11" fill="rgba(0,0,0,0)" fill-rule="evenodd"/></g><g clip-path="url(#cp1)"><g color="rgb(245, 245, 247)"><clipPath id="svgic2"><rect x="362" y="34" width="16" height="16"/></clipPath><g clip-path="url(#svgic2)"><g transform="translate(362 34)scale(.66667)"><path d="M10 11v6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 11v6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 6h18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp3)"><g clip-path="url(#ov4)"><text x="69" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(245,245,247)">staging.example.com</text></g></g><g clip-path="url(#cp6)"><g color="rgb(245, 245, 247)"><clipPath id="svgic7"><rect x="37" y="30" width="24" height="24"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(37 30)"><path d="M12 15v5s3-.6 4-2c1-1.6 0-5 0-5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M4.5 16.5c-1.5 1.3-2 5-2 5s3.7-.5 5-2c.7-.8.7-2.1 0-3a2 2 0 0 0-3 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12a22 22 0 0 1 2-4 13 13 0 0 1 11-6c0 2.7-.8 7.5-6 11a22 22 0 0 1-4 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12H4s.5-3 2-4c1.6-1 5 0 5 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="ov4"><rect x="69" y="28" width="271" height="28" rx="0"/></clipPath><clipPath id="ct5"><rect x="69" y="33" width="271" height="18"/></clipPath><clipPath id="cp6"><rect x="37" y="30" width="24" height="24"/></clipPath></defs><g clip-path="url(#ishc0)"><path d="M24 16h372v52H24zm16 5h340a11 11 0 0 1 11 11v20a11 11 0 0 1-11 11H40a11 11 0 0 1-11-11V32a11 11 0 0 1 11-11" fill="rgba(0,0,0,0)" fill-rule="evenodd"/></g><g clip-path="url(#cp1)"><g color="rgb(29, 29, 31)"><clipPath id="svgic2"><rect x="362" y="34" width="16" height="16"/></clipPath><g clip-path="url(#svgic2)"><g transform="translate(362 34)scale(.66667)"><path d="M10 11v6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 11v6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 6h18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp3)"><g clip-path="url(#ov4)"><text x="69" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" fill="rgb(29,29,31)">staging.example.com</text></g></g><g clip-path="url(#cp6)"><g color="rgb(29, 29, 31)"><clipPath id="svgic7"><rect x="37" y="30" width="24" height="24"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(37 30)"><path d="M12 15v5s3-.6 4-2c1-1.6 0-5 0-5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M4.5 16.5c-1.5 1.3-2 5-2 5s3.7-.5 5-2c.7-.8.7-2.1 0-3a2 2 0 0 0-3 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12a22 22 0 0 1 2-4 13 13 0 0 1 11-6c0 2.7-.8 7.5-6 11a22 22 0 0 1-4 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12H4s.5-3 2-4c1.6-1 5 0 5 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 92" width="420" height="92"><defs><clipPath id="ishc0"><rect x="28" y="20" width="364" height="44" rx="12"/></clipPath><clipPath id="cp1"><rect x="362" y="34" width="16" height="16"/></clipPath><clipPath id="cp3"><rect x="69" y="28" width="271" height="28"/></clipPath><clipPath id="ov4"><rect x="69" y="28" width="271" height="28" rx="0"/></clipPath><clipPath id="ct5"><rect x="69" y="33" width="271" height="18"/></clipPath><clipPath id="cp6"><rect x="37" y="30" width="24" height="24"/></clipPath></defs><rect x="28" y="20" width="364" height="44" rx="12" fill="rgb(40,57,66)"/><g clip-path="url(#ishc0)"><path d="M24 16h372v52H24zm16 5h340a11 11 0 0 1 11 11v20a11 11 0 0 1-11 11H40a11 11 0 0 1-11-11V32a11 11 0 0 1 11-11" fill="rgb(53,92,109)" fill-rule="evenodd"/></g><g clip-path="url(#cp1)"><g color="rgb(245, 245, 247)"><clipPath id="svgic2"><rect x="362" y="34" width="16" height="16"/></clipPath><g clip-path="url(#svgic2)"><g transform="translate(362 34)scale(.66667)"><path d="M21.2 6.8a1 1 0 0 0-4-4L3.8 16.2l-.5.8L2 21.4a.5.5 0 0 0 .6.6L7 20.7l.8-.5Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="m15 5 4 4" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp3)"><g clip-path="url(#ov4)"><text x="69" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" font-weight="650" fill="rgb(245,245,247)">production.example.com</text></g></g><g clip-path="url(#cp6)"><g color="rgb(245, 245, 247)"><clipPath id="svgic7"><rect x="37" y="30" width="24" height="24"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(37 30)"><path d="M12 15v5s3-.6 4-2c1-1.6 0-5 0-5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M4.5 16.5c-1.5 1.3-2 5-2 5s3.7-.5 5-2c.7-.8.7-2.1 0-3a2 2 0 0 0-3 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12a22 22 0 0 1 2-4 13 13 0 0 1 11-6c0 2.7-.8 7.5-6 11a22 22 0 0 1-4 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 12H4s.5-3 2-4c1.6-1 5 0 5 0" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(245, 245, 247)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></svg>
|