@kerfjs/ui 5.0.0-beta.67 → 5.0.0-beta.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +3 -0
  2. package/ai/application-ui-profile-sync.cjs +20 -1
  3. package/ai/application-ui-profile.d.ts +4 -0
  4. package/ai/application-ui-profile.mjs +27 -0
  5. package/ai/application-ui-profile.schema.json +10 -0
  6. package/ai/component-catalog-extension.schema.json +12 -0
  7. package/ai/component-catalog.json +14 -4
  8. package/ai/component-catalog.schema.json +14 -0
  9. package/ai/component-composition-rules.json +2 -2
  10. package/ai/component-composition.d.ts +2 -0
  11. package/ai/component-composition.json +2 -2
  12. package/ai/component-composition.schema.json +24 -0
  13. package/ai/components/loading-spinner.md +1 -0
  14. package/ai/components/tab-bar.md +5 -4
  15. package/ai/components/toolbar-control-group.md +2 -2
  16. package/ai/public-api-signatures-v1.md +34 -14
  17. package/ai/skill.md +18 -3
  18. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  19. package/analyzer/index.d.mts +1 -0
  20. package/analyzer/index.mjs +108 -12
  21. package/dist/catalog.js +4 -4
  22. package/dist/catalog.js.map +1 -1
  23. package/dist/{chunk-P4UXSYIJ.js → chunk-2ZOLX3IE.js} +2 -2
  24. package/dist/{chunk-P4UXSYIJ.js.map → chunk-2ZOLX3IE.js.map} +1 -1
  25. package/dist/{chunk-E36EVCKG.js → chunk-47CKKLBE.js} +2 -2
  26. package/dist/{chunk-E36EVCKG.js.map → chunk-47CKKLBE.js.map} +1 -1
  27. package/dist/{chunk-JCV4GXWB.js → chunk-6DFDTWGG.js} +100 -61
  28. package/dist/chunk-6DFDTWGG.js.map +1 -0
  29. package/dist/{chunk-IAOAZUOO.js → chunk-7OKX3EF6.js} +2 -2
  30. package/dist/{chunk-IAOAZUOO.js.map → chunk-7OKX3EF6.js.map} +1 -1
  31. package/dist/{chunk-C74LITXE.js → chunk-B3XWUX3A.js} +2 -2
  32. package/dist/{chunk-C74LITXE.js.map → chunk-B3XWUX3A.js.map} +1 -1
  33. package/dist/{chunk-F7OCOBTG.js → chunk-CRVDF4A6.js} +4 -1
  34. package/dist/chunk-CRVDF4A6.js.map +1 -0
  35. package/dist/{chunk-TGM424PI.js → chunk-H7IODXGY.js} +3 -3
  36. package/dist/{chunk-TGM424PI.js.map → chunk-H7IODXGY.js.map} +1 -1
  37. package/dist/chunk-KVBRMEPM.js +44 -0
  38. package/dist/chunk-KVBRMEPM.js.map +1 -0
  39. package/dist/{chunk-EN4LDR5Y.js → chunk-NC4CFOID.js} +2 -2
  40. package/dist/{chunk-EN4LDR5Y.js.map → chunk-NC4CFOID.js.map} +1 -1
  41. package/dist/{chunk-5SYE4CCT.js → chunk-PQZPVBP4.js} +25 -18
  42. package/dist/chunk-PQZPVBP4.js.map +1 -0
  43. package/dist/{chunk-NXWE5S57.js → chunk-QJ5VFCW3.js} +12 -3
  44. package/dist/chunk-QJ5VFCW3.js.map +1 -0
  45. package/dist/{chunk-RKTTF3L4.js → chunk-X3TPECWG.js} +1 -1
  46. package/dist/chunk-X3TPECWG.js.map +1 -0
  47. package/dist/{chunk-VY6YRCTV.js → chunk-X3X4KRDK.js} +2 -2
  48. package/dist/{chunk-VY6YRCTV.js.map → chunk-X3X4KRDK.js.map} +1 -1
  49. package/dist/collapsible-panel.js +2 -2
  50. package/dist/empty-state.js +2 -1
  51. package/dist/index.js +10 -10
  52. package/dist/list-action-row.js +1 -1
  53. package/dist/list-item.js +1 -1
  54. package/dist/loading-spinner.d.ts +7 -2
  55. package/dist/loading-spinner.js +2 -1
  56. package/dist/nav-stack.js +1 -1
  57. package/dist/select.d.ts +4 -2
  58. package/dist/select.js +1 -1
  59. package/dist/split-view.js +1 -1
  60. package/dist/styles/app-tab.css +29 -0
  61. package/dist/styles/loading-spinner.css +4 -0
  62. package/dist/styles/select.css +79 -0
  63. package/dist/styles/tab-bar.css +12 -8
  64. package/dist/styles/token-search-field.css +28 -10
  65. package/dist/styles/toolbar-control-group.css +96 -3
  66. package/dist/styles/webawesome.css +17 -0
  67. package/dist/tab-bar.d.ts +2 -0
  68. package/dist/tab-bar.js +1 -1
  69. package/dist/token-search-field.js +1 -1
  70. package/dist/toolbar-control-group.d.ts +13 -5
  71. package/dist/toolbar-control-group.js +2 -1
  72. package/dist/wire-catalog.js +1 -1
  73. package/dist/wire-scroll-dividers.d.ts +7 -4
  74. package/dist/wire-scroll-dividers.js +1 -1
  75. package/dist/wire-token-search-fields.js +1 -1
  76. package/dist/workbench.js +1 -1
  77. package/docs/accessibility.md +14 -0
  78. package/docs/apple-hig.md +1 -0
  79. package/docs/catalog.md +3 -0
  80. package/docs/component-contract.md +65 -10
  81. package/docs/component-selection.md +24 -6
  82. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  83. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  84. package/docs/design/templates/toolbar-dark.svg +1 -1
  85. package/docs/design/templates/toolbar.svg +1 -1
  86. package/docs/examples/component-catalog-extension.json +1 -0
  87. package/docs/layout.md +6 -0
  88. package/docs/ui-analyzer.md +12 -0
  89. package/docs/ui-doctor.md +15 -3
  90. package/docs/webawesome-theme.md +7 -0
  91. package/doctor/cli.mjs +5 -0
  92. package/doctor/config.schema.json +2 -0
  93. package/doctor/index.d.mts +3 -0
  94. package/doctor/index.mjs +27 -1
  95. package/llms.txt +15 -5
  96. package/package.json +1 -1
  97. package/dist/chunk-5SYE4CCT.js.map +0 -1
  98. package/dist/chunk-F7OCOBTG.js.map +0 -1
  99. package/dist/chunk-JCV4GXWB.js.map +0 -1
  100. package/dist/chunk-NXWE5S57.js.map +0 -1
  101. package/dist/chunk-RKTTF3L4.js.map +0 -1
  102. package/dist/chunk-ZLWQZUGF.js +0 -27
  103. package/dist/chunk-ZLWQZUGF.js.map +0 -1
@@ -92,6 +92,16 @@ the `data-*` attributes it actually writes. First-party entries always carry
92
92
  the list, empty when their helpers write none; it is optional in extension
93
93
  catalogs.
94
94
 
95
+ An extension catalog may add `wiring.sources` entries with `export` and
96
+ `source` for each application-owned helper. The source path is relative to its
97
+ package root; `ui-wiring` resolves helper imports through this file, including
98
+ relative re-export barrels. Every named export must also appear in
99
+ `wiring.helpers`.
100
+
101
+ The selection catalog's `wiringProviders` lists public helpers that install
102
+ other wiring helpers. The `ui-wiring` rule accepts a provider call for each
103
+ helper it declares; `wireCatalog` provides `wireScrollDividers`.
104
+
95
105
  The generator starts with permissive defaults. An `any` mode records that the
96
106
  catalog has no defensible prohibition; it does not claim every composition is
97
107
  recommended. Objective rules live in
@@ -154,7 +164,7 @@ check in `.kerf-ui-profile.json` conforming to
154
164
  [`application-ui-profile.schema.json`](../ai/application-ui-profile.schema.json).
155
165
  Keep package-qualified catalog locations and recurring-concept preferences,
156
166
  allowed color schemes and density, public semantic-token overrides,
157
- layout/responsive conventions, and narrow rule exceptions there. Product
167
+ layout/responsive conventions, application wiring entry paths, and narrow rule exceptions there. Product
158
168
  records, copy, permissions, user preferences, and transport state do not belong
159
169
  in this policy file.
160
170
 
@@ -175,6 +185,12 @@ Discovery and precedence are deterministic:
175
185
  Later scalar and object-map values win. Catalogs merge by package; preferences
176
186
  and token overrides merge by key; exceptions merge by stable id. Lists such as
177
187
  allowed themes/densities replace the earlier list rather than accumulating.
188
+ `wiring.entries` similarly names workspace-relative application entry modules;
189
+ later profile layers replace the entry list. The `ui-wiring` ESLint rule checks
190
+ application-scoped helper calls and registration imports once at each entry
191
+ that reaches a rendering module, while a composition entry with
192
+ `wiring.scope: "module"` keeps a local obligation. See the
193
+ [`ui-wiring` rule](../../eslint-plugin/docs/rules/ui-wiring.md).
178
194
  Every resolved field retains its source file. The shipped
179
195
  `application-ui-profile.mjs` API implements discovery, merge, loading, and
180
196
  validation and reports actionable originating file + JSON-path diagnostics for stale
@@ -246,6 +262,8 @@ claim Kerf delivery paths for them.
246
262
  Extension entries may add `uses` for direct composition dependencies and
247
263
  repository-relative `source` and `demoSource` files. Resolve each dependency
248
264
  against the combined Kerf and consumer catalogs; do not duplicate a Kerf id.
265
+ For Doctor's opt-in component ownership mode, add `styleSources` with the CSS
266
+ paths each entry owns, relative to the consumer package root.
249
267
 
250
268
  - Components own semantic markup, stable anatomy, documented variants, ARIA projection, and package CSS hooks.
251
269
  - Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
@@ -500,20 +518,49 @@ a plain `<button>` — the group styles `> button` fully, and it keeps the group
500
518
  free of a Web Awesome dependency and shadow DOM. Use `ToolbarActionLink` when
501
519
  the action must retain native anchor navigation; the group owns its geometry,
502
520
  hover, and focus treatment. Set `overflow="scroll"` when a row of controls must
503
- stay inside the available toolbar width. Set `visibility="compact-only"` for a
521
+ stay inside the available toolbar width. A disabled native button keeps the
522
+ group's geometry, dims to half opacity, uses the `not-allowed` cursor, and
523
+ paints neither its own nor a lone group's hover surface.
524
+ Set `visibility="compact-only"` for a
504
525
  group that replaces wider toolbar controls below the Toolbar's compact container
505
- breakpoint; do not hide the group with an application class. Reach for
506
- `wa-button` only when you need
507
- a Web Awesome feature; a popup menu is a `PopupMenu`, which renders its own
508
- trigger. Nothing wraps inside a group: an icon sits beside its label on one
509
- row, and a group's controls never break onto a second line — the group sizes
510
- to its content and the enclosing `Toolbar`'s responsive policy relocates whole
511
- groups. For a compact mixed-content group, set `nestedDropdown`; a
526
+ breakpoint; do not hide the group with an application class.
527
+ Use `sizing="grow"` on an expanded search group in a wrapping zone: it grows from
528
+ `growBasis` (19rem by default) into available row space, then takes a whole
529
+ row at 480px container width or less. The collapsed search stays icon-sized.
530
+ Use `sizing="fill"` for a group, including a non-search segmented control,
531
+ that spans its wrapping row. Set `visibility="hide-collapsed-tiny"` on a
532
+ collapsible group to omit its closed icon at 224px or less; expanded content
533
+ remains visible. These sizes use the enclosing Toolbar's container width.
534
+ In a stacked Toolbar, set `placement="end"` on a collapsed group that should
535
+ sit at the trailing edge of its flex row. An expanded search with
536
+ `sizing="fill"` takes a whole row and enters from the row above; reduced-motion
537
+ preferences remove that entrance animation.
538
+ Use `visibility="yield-to-expanded-sibling"` on trailing groups that should
539
+ leave a 480px-or-narrower toolbar while a sibling trailing group is expanded.
540
+ They return when it collapses or the toolbar widens; leading and center zones
541
+ remain visible.
542
+ Reach for `wa-button` only when you need a Web Awesome feature; a popup menu is a `PopupMenu`, which renders its own
543
+ trigger. By default a group stays on one row: each icon sits beside its label,
544
+ and the enclosing `Toolbar`'s responsive policy relocates whole groups. Set
545
+ `overflow="wrap"` only for independent links that should move as whole actions
546
+ to another row within the group; use `overflow="scroll"` for a bounded one-row
547
+ action strip. A wrapping group centers in the toolbar band on one row and
548
+ starts at its top when it grows to multiple rows. For a compact mixed-content
549
+ group, set `nestedDropdown`; a
512
550
  text-and-caret trigger grows to its intrinsic width while an icon-only trigger
513
551
  can remain square. A compact icon `<button>` is a 32px circle, so a selected
514
552
  compact segment never becomes an oval. An icon-only `Select` joins a group as
515
553
  one more segment with its siblings' inset, radius, hover, and focus ring; only a
516
- lone Select grows the group to its icon-and-caret pill. Set `menuInset` to configure the dropdown menu surface
554
+ lone Select grows the group to its icon-and-caret pill.
555
+
556
+ For a rail view heading, use `Select presentation="title"`: its intrinsic
557
+ 36px borderless trigger has a bold large label flush with its leading edge and
558
+ a caret beside that label. Set `focusRingInset` when a NavStack or panel clips
559
+ outside painting; the Select keeps its focus outline 3px inside the trigger
560
+ and drops it while the listbox is open. Both settings belong to Select, not an
561
+ application `::part()` override.
562
+
563
+ Set `menuInset` to configure the dropdown menu surface
517
564
  without application `::part(menu)` CSS; the default `standard` keeps each menu's
518
565
  own inset (8px for a raw `wa-dropdown`, the Select listbox's 4px for a
519
566
  `PopupMenu`), while `compact` and `list-zero` apply to every nested menu. A
@@ -649,6 +696,8 @@ the only shrinking, horizontally scrolling region; `leading`, `trailing`, and
649
696
  trailing action belongs immediately after the final tab and use `end` for a
650
697
  separate action pinned to the far edge. Applications own the action content,
651
698
  but must not restyle TabBar descendants to recreate that allocation.
699
+ When a leading AppTab is pinned, the leading scroll divider appears at that
700
+ tab's trailing edge while peer tabs scroll beneath it, including in RTL.
652
701
 
653
702
  CSS, the generated wrappers that make it
654
703
  reachable, and the registration module are the package's only declared side
@@ -670,6 +719,12 @@ line of its own and centers in its slot. It is a
670
719
  placeholder, not progress: use `LoadingSpinner` for known busy activity, and never
671
720
  animate the skeleton.
672
721
 
722
+ `LoadingSpinner` accepts the same `size` steps as `LucideIcon`: `xs`, `s`, `m`,
723
+ `l`, `xl` (12/16/20/24/32px at a 16px root) or a positive finite pixel
724
+ number converted to rem. Omit `size` to keep its inherited 1em box. A supplied
725
+ size is exposed as `data-size`; callers configure the spinner through this prop
726
+ instead of sizing its SVG from outside.
727
+
673
728
  A placeholder is loading, not unavailable, so it keeps the `disabled` attribute (its
674
729
  controls stay inert and out of the keyboard order) but never shows disabled chrome.
675
730
  `skeleton.css`, which every component that renders a skeleton loads, neutralizes generic
@@ -171,7 +171,7 @@ beside a `ContentItem` value, and connect its id to the preview group's
171
171
  | 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. In a narrow peripheral rail or drawer, where xlarge would truncate at the panel's minimum size, use the default `ToolbarText` size as pane identity. | 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) |
172
172
  | 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) |
173
173
  | 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) |
174
- | 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) |
174
+ | 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. | Pass a label when the spinner conveys status; use size for a named icon step or positive pixel size. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
175
175
  | 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) |
176
176
  | Command menu — `PopupMenu` | A short list of commands sits behind one trigger, such as a toolbar sort or More menu, optionally grouped under headings and dividers with item icons. | Do not use it to choose a persistent value; use `Select`. Do not hand-write `wa-dropdown`/`wa-dropdown-item` markup for an action menu. | Import `@kerfjs/ui/popup-menu/register` once. Place it in a `single` `ToolbarControlGroup` with `nestedDropdown` and a `menuInset`, or wrap it in `PopupSurface` outside a toolbar. Handle item `action`s by delegation. | Commands, enabled state, and reactions to opening or closing. | `@kerfjs/ui/popup-menu` | [Toolbar composition](#toolbar-composition) |
177
177
  | 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. Set `multiple` with an array `value` when the person may choose any number of them; add `selectedPresentation="icon-only"` and a fixed `triggerIcon` for a toolbar filter menu. | Do not use it for commands; use `PopupMenu`. 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 for registration and current-request animation ownership; listen for standard input/change events. Do not add consumer popup timing or positioning repairs. 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) |
@@ -268,10 +268,17 @@ instead of the default ellipsis. A `TabBar` owns its
268
268
  scrolling strip; its public `--kui-tab-bar-strip-*` tokens configure strip
269
269
  height, spacing, border, radius, background, and scroll inset without styling
270
270
  its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
271
+ For a distinct application action, use `end` to pin it at the far edge; use
272
+ `trailingPlacement="adjacent"` to keep a tab-local `trailing` action beside the
273
+ strip. Each zone accepts one `ToolbarControlGroup` or one standalone
274
+ `wa-button` for a primary action with its own chrome. Avoid wrappers and
275
+ application flex overrides.
271
276
  Set `pinned` on the first `AppTab` when that tab must remain visible as peers
272
277
  scroll. It stays inside the tablist and keyboard order. Set the public
273
278
  `--kui-app-tab-pinned-background` token if the surrounding surface differs from
274
- the default; the pinned tab must cover peers as they scroll beneath it. Call
279
+ the default; the pinned tab must cover peers as they scroll beneath it. Its
280
+ background also covers the strip's inline padding and border so scrolled labels cannot
281
+ paint beside the pinned edge, including in right-to-left strips. Call
275
282
  `wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
276
283
  scroll correction.
277
284
 
@@ -343,6 +350,11 @@ search content before saving.
343
350
  The form-field corners and required marker use the same Web Awesome
344
351
  form-control radius, marker color, and offset tokens as a neighboring
345
352
  `wa-input`.
353
+ The four public color tokens (`--kui-token-search-background`,
354
+ `--kui-token-search-border`, `--kui-token-search-token-background`, and
355
+ `--kui-token-search-token-foreground`) inherit from an app ancestor or the
356
+ field's root. `className` marks that root, including the outer wrapper in
357
+ `form-field` presentation, so one app class can configure either form.
346
358
  `TokenSearchRule.suggest(input, state)` receives the unfinished value and current committed tokens, so a rule can omit values already selected. Existing one-argument suggestion callbacks continue to work.
347
359
  For a sibling date picker or other app helper, call `model.commit(value)` while a `name:` prefix is active. It parses and commits that value for the active rule even when it is absent from suggestions, replacing the unfinished prefix text. Invalid values and calls without an active prefix leave the query unchanged; `choose(value)` remains restricted to suggested tokens.
348
360
  To apply a saved search or restore a query programmatically, call `model.replace({ query, tokens })`. It publishes parsed state and rebuilds the DOM-owned editor text even when no chip changed. For a field without a model, change the field's `revision` prop when setting `query` yourself.
@@ -354,6 +366,9 @@ passive content such as a shortcut hint.
354
366
  Set `fill` on a collapsible `TokenSearchField` in a stretched Toolbar center to
355
367
  use the available width when expanded; the field and its search group retain
356
368
  their compact widths when collapsed.
369
+ When the expanded toolbar field has focus, its inset surface uses
370
+ `--kui-token-search-background` or the default surface color if that token is
371
+ unset.
357
372
 
358
373
  ## Toolbar composition
359
374
 
@@ -375,10 +390,13 @@ copy below as app-owned content. Size the title to its track: a narrow
375
390
  peripheral rail or drawer (a navigator, inspector, or console beside a work
376
391
  area that carries the extra-large title, often with its own close control in
377
392
  the header) labels itself with the default size, because extra-large truncates
378
- there — "Inspector" becomes "Ins…" in a 160px rail. Nothing wraps inside a
379
- `ToolbarControlGroup`: an icon sits beside its label on one row and the group
380
- sizes to its content, so a row that does not fit relocates whole groups
381
- through the toolbar's `responsive` policy rather than breaking a group. A
393
+ there — "Inspector" becomes "Ins…" in a 160px rail. By default, nothing wraps
394
+ inside a `ToolbarControlGroup`: an icon sits beside its label on one row and the
395
+ group sizes to its content, so a row that does not fit relocates whole groups
396
+ through the toolbar's `responsive` policy. For independent action links that
397
+ must all remain visible, set `overflow="wrap"`: whole links move to another
398
+ row inside the group without breaking an icon from its label. Use
399
+ `overflow="scroll"` when a single-row action strip is intentional. A
382
400
  toolbar draws no divider by default: pinned over or under scrolling content,
383
401
  it gets one from the scroll state instead — the `Pane` it heads draws the line
384
402
  only while content is scrolled beneath it, once `wireScrollDividers` is wired
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="28" y="28" width="452" height="44"/></clipPath><clipPath id="ov1"><rect x="28" y="28" width="452" height="44" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="28" y="28" width="452" height="44"/></clipPath><clipPath id="ov1"><rect x="28" y="28" width="452" height="44" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="452" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="28" y="28" width="452" height="44" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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></g></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="svgimg3ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" 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(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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></g></g></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="452" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="28" y="28" width="452" height="44" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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></g></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="svgimg3ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family="&quot;ui-sans-serif&quot;, &quot;-apple-system&quot;, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" 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(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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></g></g></g></svg>
@@ -19,6 +19,7 @@
19
19
  "publicTokens": ["--acme-filter-chip-accent"],
20
20
  "uses": ["chip"],
21
21
  "source": "src/filter-chip.tsx",
22
+ "styleSources": ["src/filter-chip.css"],
22
23
  "demoSource": "ux-demo/demos/filter-chip.tsx",
23
24
  "geometry": {
24
25
  "margin": "none",
package/docs/layout.md CHANGED
@@ -111,6 +111,12 @@ scrolls under its pinned header; with a header it draws the line under that
111
111
  header itself, so the stack's chrome never doubles it. It follows re-renders: panes that appear later are paired, and an
112
112
  attribute a morph drops returns before paint.
113
113
 
114
+ Multiple calls in one document share one attribute writer, including calls on
115
+ overlapping roots. Each call contributes its own root and optional named
116
+ targets; its disposer removes only that registration. When the last disposer
117
+ runs, the writer removes the attributes it owns. An app that calls `wireCatalog`
118
+ for a root already has scroll dividers there and does not need another call.
119
+
114
120
  The wiring only reports state; every component draws its own line. It writes
115
121
  `data-scroll-overflow` (the edges with content hidden beyond them, in canonical
116
122
  `t`/`r`/`b`/`l` order) on each scroller and `data-scroll-divider` (the sides to
@@ -144,6 +144,10 @@ which element a selector styles.
144
144
  imported component's JSX (`<Toolbar className="header" />`) is the subject
145
145
  of a rule in a stylesheet that file imports, so the application restyles the
146
146
  component through it. The evidence names the stylesheet, line, and selector.
147
+ For cataloged application components, the import may be direct and relative
148
+ or use an exact or wildcard TypeScript `paths` alias from the nearest
149
+ `tsconfig.json` inside the analyzed root (including inherited options).
150
+ Unresolved aliases remain unclassified; the analyzer does not execute code.
147
151
 
148
152
  Every message names the configuration to use (the component's typed props,
149
153
  public tokens, and variants from its catalog entry) and ends with the same
@@ -188,6 +192,14 @@ findings. `eslint-plugin-kerfjs`'s `ui-component-ownership` rule reports
188
192
  `KUI-L020` and `KUI-L021` in JavaScript and TypeScript (style strings, style
189
193
  objects, and `style.setProperty()`), where this analyzer reads only CSS.
190
194
 
195
+ Pass `ownership: 'component'` to `analyzeUiProject` to enforce the boundary
196
+ between components in one package. In that mode each catalog entry owns only
197
+ its declared `styleSources` for CSS and its selection catalog `source` module
198
+ for JSX; sibling files are foreign. Paths are relative to that entry's package
199
+ root. Declare `styleSources` on the selection catalog extension entry or
200
+ composition entry. Entries without those paths own no files in component mode,
201
+ so add them before adopting it.
202
+
191
203
  ### Retired: `KUI-L005` (forced component dimension)
192
204
 
193
205
  `KUI-L005` used to report, as a review finding, any `width`/`height`/`min-*`/
package/docs/ui-doctor.md CHANGED
@@ -7,6 +7,7 @@ npx kerf-ui-doctor --full
7
7
  npx kerf-ui-doctor --changed
8
8
  npx kerf-ui-doctor --package @acme/admin --changed
9
9
  npx kerf-ui-doctor --full --browser-url http://127.0.0.1:4173
10
+ npx kerf-ui-doctor --full --fail-on warning
10
11
  ```
11
12
 
12
13
  The default terminal output is short and repair-oriented. `--format json` emits the schema-version-1 report; `--output report.json` writes the same representation. The package exports its contract from `@kerfjs/ui/doctor`, its configuration schema from `@kerfjs/ui/doctor/config.schema.json`, and its report schema from `@kerfjs/ui/doctor/report.schema.json`.
@@ -39,6 +40,15 @@ root (`KUI-L022`); the ESLint stage's `ui-component-ownership` rule reports
39
40
  component's configuration, and its message routes a need with no prop to a
40
41
  component-gap report instead of an override (see
41
42
  [the analyzer rule](./ui-analyzer.md#component-ownership-kui-l019kui-l022)).
43
+ The analyzer resolves a cataloged application component imported through an
44
+ exact or wildcard TypeScript `paths` alias from the nearest consumer tsconfig,
45
+ so the same root hook-class check applies in those files.
46
+ Set `"ownership": "component"` in `.kerf-ui-doctor.json` to enforce the same
47
+ boundary between a consumer package's own cataloged components. The default is
48
+ `"package"`. Each local catalog entry then owns only its declared
49
+ `styleSources` (relative to its package root) and its selection catalog
50
+ `source` module. The component mode reports sibling CSS and hook-class
51
+ restyles while still allowing a component's own stylesheet and module.
42
52
 
43
53
  The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
44
54
 
@@ -66,8 +76,10 @@ Place `.kerf-ui-doctor.json` at the workspace root:
66
76
  "$schema": "./node_modules/@kerfjs/ui/doctor/config.schema.json",
67
77
  "schemaVersion": 1,
68
78
  "mode": "full",
79
+ "ownership": "component",
69
80
  "stages": { "browser": false },
70
81
  "cache": true,
82
+ "failOn": "warning",
71
83
  "suppressions": [
72
84
  {
73
85
  "id": "legacy-toolbar",
@@ -91,14 +103,14 @@ Reports replace the workspace's absolute path with `<repo-root>` and express in-
91
103
 
92
104
  Every diagnostic has a stable `id`, `severity`, `stage`, message, and—when applicable—an exact source location with JSON path or DOM context/selector. Analyzer evidence, catalog facts, documentation links, and safe next actions are preserved when the source tool provides them. Identical findings merge with their source stages; same identifiers with conflicting severities remain separate and add `KUI-D003`.
93
105
 
94
- Exit codes are deterministic:
106
+ Exit codes are deterministic. `failOn` defaults to `error`; `--fail-on error|review|warning` overrides the config for a run. `review` fails on errors or review findings; `warning` also fails on warnings. Suppressed diagnostics never count. The effective threshold is part of the cache key.
95
107
 
96
- - `0`: no active error diagnostics;
108
+ - `0`: no active diagnostics at or above the selected threshold;
97
109
  - `1`: repairable application findings;
98
110
  - `2`: malformed configuration, unavailable required tooling, or a failed stage;
99
111
  - `130`: cancellation.
100
112
 
101
- Warnings and review findings remain visible but do not fail unless their originating preset promotes them to errors. A cached report keeps its original exit code and labels previously run stages `cached`.
113
+ Warnings and review findings remain visible at every threshold. A cached report keeps its original exit code and labels previously run stages `cached`.
102
114
 
103
115
  ## Monorepos and repair loops
104
116
 
@@ -86,6 +86,13 @@ exposes `label` instead of `form-control-label`; the theme gives it the same
86
86
  uppercase 12px/650 treatment. Inline control labels (Checkbox, Switch) keep
87
87
  their natural sentence case.
88
88
 
89
+ On coarse touch devices, the editable part of small and extra-small Web Awesome
90
+ Input, Number Input, Time Input, Textarea, and Select has a 16px minimum text
91
+ size. Small field chrome keeps its compact height and desktop typography stays
92
+ small. This prevents Safari from magnifying the page just to focus text; it does
93
+ not constrain a user's own pinch zoom. App-owned native fields need the same
94
+ editable-text audit.
95
+
89
96
  Known Date's field captions and bordered text-like field hints use the same 9px
90
97
  inline inset, keeping secondary text aligned with the value rather than the
91
98
  field's outer border. This covers Input, Known Date, Number Input, OTP Input,
package/doctor/cli.mjs CHANGED
@@ -54,6 +54,7 @@ Options:
54
54
  --browser <list> Comma-separated chromium,firefox,webkit
55
55
  --eslint <preset> recommended-ui or strict-ui
56
56
  --format <format> text or json (default: text)
57
+ --fail-on <severity> error, review, or warning (default: error)
57
58
  --output <path> Also write the merged report
58
59
  --no-cache Disable the content-addressed cache
59
60
 
@@ -68,6 +69,7 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
68
69
  const browserList = take('--browser');
69
70
  const eslintConfig = take('--eslint') ?? 'recommended-ui';
70
71
  const format = take('--format') ?? 'text';
72
+ const failOn = take('--fail-on');
71
73
  const output = take('--output');
72
74
  const full = args.includes('--full');
73
75
  const changed = args.includes('--changed');
@@ -80,6 +82,8 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
80
82
  throw new Error('--full and --changed are mutually exclusive.');
81
83
  if (!['text', 'json'].includes(format))
82
84
  throw new Error('--format must be text or json.');
85
+ if (failOn && !['error', 'review', 'warning'].includes(failOn))
86
+ throw new Error('--fail-on must be error, review, or warning.');
83
87
  if (!['recommended-ui', 'strict-ui'].includes(eslintConfig))
84
88
  throw new Error('--eslint must be recommended-ui or strict-ui.');
85
89
  const abort = new globalThis.AbortController();
@@ -95,6 +99,7 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
95
99
  cache: !noCache,
96
100
  signal: abort.signal,
97
101
  eslintConfig,
102
+ failOn,
98
103
  browser: url
99
104
  ? { url, browsers: browserList?.split(',').map((item) => item.trim()) }
100
105
  : undefined,
@@ -9,6 +9,7 @@
9
9
  "schemaVersion": { "const": 1 },
10
10
  "mode": { "enum": ["full", "changed"] },
11
11
  "package": { "type": "string", "minLength": 1 },
12
+ "ownership": { "enum": ["package", "component"] },
12
13
  "stages": {
13
14
  "type": "object",
14
15
  "properties": {
@@ -37,6 +38,7 @@
37
38
  "additionalProperties": false
38
39
  },
39
40
  "cache": { "type": "boolean" },
41
+ "failOn": { "enum": ["error", "review", "warning"] },
40
42
  "suppressions": {
41
43
  "type": "array",
42
44
  "items": {