@kerfjs/ui 5.0.0-beta.66 → 5.0.0-beta.68

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 (45) hide show
  1. package/README.md +1 -1
  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.json +12 -7
  7. package/ai/component-composition-rules.json +1 -1
  8. package/ai/component-composition.json +13 -4
  9. package/ai/component-composition.schema.json +1 -0
  10. package/ai/components/text.md +8 -6
  11. package/ai/components/toolbar-control-group.md +1 -1
  12. package/ai/public-api-signatures-v1.md +9 -2
  13. package/ai/skill.md +2 -0
  14. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  15. package/analyzer/component-ownership.d.mts +1 -1
  16. package/analyzer/component-ownership.mjs +24 -5
  17. package/analyzer/index.mjs +3 -1
  18. package/dist/catalog.js +2 -2
  19. package/dist/{chunk-5IJ6UGNZ.js → chunk-3BCJGM36.js} +2 -2
  20. package/dist/{chunk-5IJ6UGNZ.js.map → chunk-3BCJGM36.js.map} +1 -1
  21. package/dist/{chunk-CQHN45XR.js → chunk-YDGNU2OD.js} +11 -2
  22. package/dist/chunk-YDGNU2OD.js.map +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/list-header.js +1 -1
  26. package/dist/styles/app-tab.css +12 -0
  27. package/dist/styles/text.css +18 -0
  28. package/dist/styles/token-search-field.css +9 -0
  29. package/dist/styles/webawesome.css +17 -0
  30. package/dist/text.d.ts +8 -1
  31. package/dist/text.js +1 -1
  32. package/docs/accessibility.md +4 -0
  33. package/docs/apple-hig.md +1 -0
  34. package/docs/component-contract.md +7 -1
  35. package/docs/component-selection.md +13 -1
  36. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  37. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  38. package/docs/design/templates/toolbar-dark.svg +1 -1
  39. package/docs/design/templates/toolbar.svg +1 -1
  40. package/docs/layout.md +21 -0
  41. package/docs/ui-analyzer.md +5 -1
  42. package/docs/webawesome-theme.md +7 -0
  43. package/llms.txt +4 -0
  44. package/package.json +1 -1
  45. package/dist/chunk-CQHN45XR.js.map +0 -1
package/README.md CHANGED
@@ -189,7 +189,7 @@ interchangeable. `ListItem` and `ListActionRow` deliberately expose no raw
189
189
  | `FloatingToolbar` | `@kerfjs/ui/floating-toolbar` | `@kerfjs/ui/floating-toolbar.css` |
190
190
  | `PopupMenu` (register with `@kerfjs/ui/popup-menu/register`) | `@kerfjs/ui/popup-menu` | `@kerfjs/ui/popup-menu.css` |
191
191
  | `ToolbarText` | `@kerfjs/ui/toolbar-text` | `@kerfjs/ui/toolbar-text.css` |
192
- | `Text` | `@kerfjs/ui/text` | `@kerfjs/ui/text.css` |
192
+ | `Text`, `FieldLabel` | `@kerfjs/ui/text` | `@kerfjs/ui/text.css` |
193
193
  | `List` | `@kerfjs/ui/list` | `@kerfjs/ui/list.css` |
194
194
  | `Row` | `@kerfjs/ui/row` | `@kerfjs/ui/row.css` |
195
195
  | `Grid` | `@kerfjs/ui/grid` | `@kerfjs/ui/grid.css` |
@@ -28,7 +28,7 @@ function mergeApplicationUiProfiles(layers) {
28
28
  catalogs.set(catalog.package, catalog);
29
29
  provenance[`$catalogs.${catalog.package}`] = source;
30
30
  }
31
- for (const field of ['preferences', 'theme', 'tokens', 'layout'])
31
+ for (const field of ['preferences', 'theme', 'tokens', 'layout', 'wiring'])
32
32
  if (
33
33
  layer[field] &&
34
34
  typeof layer[field] === 'object' &&
@@ -166,6 +166,7 @@ function validateApplicationUiProfile(profile, options = {}) {
166
166
  'theme',
167
167
  'tokens',
168
168
  'layout',
169
+ 'wiring',
169
170
  'exceptions',
170
171
  ],
171
172
  '$',
@@ -183,7 +184,25 @@ function validateApplicationUiProfile(profile, options = {}) {
183
184
  const theme = objectAt(profile.theme, '$.theme');
184
185
  const tokens = objectAt(profile.tokens, '$.tokens') ?? {};
185
186
  const layout = objectAt(profile.layout, '$.layout');
187
+ const wiring = objectAt(profile.wiring, '$.wiring');
186
188
  const exceptions = arrayAt(profile.exceptions, '$.exceptions');
189
+ rejectUnknown(wiring, ['entries'], '$.wiring');
190
+ if (wiring?.entries !== undefined) {
191
+ if (
192
+ !stringList(wiring.entries) ||
193
+ wiring.entries.some(
194
+ (entry) =>
195
+ isAbsolute(entry) ||
196
+ entry.split('/').includes('..') ||
197
+ /[*?]/.test(entry),
198
+ )
199
+ )
200
+ add(
201
+ 'KUI-P030',
202
+ '$.wiring.entries',
203
+ 'Wiring entries must be unique workspace-relative paths without traversal or wildcards.',
204
+ );
205
+ }
187
206
  const packages = new Set();
188
207
  for (const [index, catalog] of catalogs.entries()) {
189
208
  const path = `$.catalogs[${index}]`;
@@ -39,6 +39,10 @@ export interface ApplicationUiProfile {
39
39
  scrollOwnership?: 'one-per-pane' | 'application';
40
40
  spacingScale?: 'kerf-five-step' | 'application';
41
41
  };
42
+ wiring?: {
43
+ /** Static application entry modules, relative to the workspace root. */
44
+ entries?: string[];
45
+ };
42
46
  exceptions?: Array<{
43
47
  id: string;
44
48
  rules: string[];
@@ -123,6 +123,14 @@ export function mergeApplicationUiProfiles(layers) {
123
123
  profile.layout = { ...profile.layout, ...layer.layout };
124
124
  recordProvenance(provenance, source, '$layout', layer.layout);
125
125
  }
126
+ if (
127
+ layer.wiring &&
128
+ typeof layer.wiring === 'object' &&
129
+ !Array.isArray(layer.wiring)
130
+ ) {
131
+ profile.wiring = { ...profile.wiring, ...layer.wiring };
132
+ recordProvenance(provenance, source, '$wiring', layer.wiring);
133
+ }
126
134
  for (const exception of Array.isArray(layer.exceptions)
127
135
  ? layer.exceptions
128
136
  : []) {
@@ -250,6 +258,7 @@ export function validateApplicationUiProfile(
250
258
  'theme',
251
259
  'tokens',
252
260
  'layout',
261
+ 'wiring',
253
262
  'exceptions',
254
263
  ],
255
264
  '$',
@@ -269,7 +278,25 @@ export function validateApplicationUiProfile(
269
278
  const theme = objectAt(profile?.theme, '$.theme');
270
279
  const tokens = objectAt(profile?.tokens, '$.tokens') ?? {};
271
280
  const layout = objectAt(profile?.layout, '$.layout');
281
+ const wiring = objectAt(profile?.wiring, '$.wiring');
272
282
  const exceptions = arrayAt(profile?.exceptions, '$.exceptions');
283
+ rejectUnknown(wiring, ['entries'], '$.wiring');
284
+ if (wiring?.entries !== undefined) {
285
+ if (
286
+ !stringList(wiring.entries) ||
287
+ wiring.entries.some(
288
+ (entry) =>
289
+ isAbsolute(entry) ||
290
+ entry.split('/').includes('..') ||
291
+ /[*?]/.test(entry),
292
+ )
293
+ )
294
+ add(
295
+ 'KUI-P030',
296
+ '$.wiring.entries',
297
+ 'Wiring entries must be unique workspace-relative paths without traversal or wildcards.',
298
+ );
299
+ }
273
300
 
274
301
  const catalogPackages = new Set();
275
302
  for (const [index, catalog] of catalogs.entries()) {
@@ -25,6 +25,16 @@
25
25
  "additionalProperties": { "type": "string", "minLength": 1 }
26
26
  },
27
27
  "layout": { "$ref": "#/$defs/layout" },
28
+ "wiring": {
29
+ "type": "object",
30
+ "properties": {
31
+ "entries": {
32
+ "description": "Application entry modules, relative to the workspace root, where application-scoped wiring is installed.",
33
+ "$ref": "#/$defs/stringList"
34
+ }
35
+ },
36
+ "additionalProperties": false
37
+ },
28
38
  "exceptions": {
29
39
  "type": "array",
30
40
  "items": { "$ref": "#/$defs/exception" }
@@ -1942,14 +1942,16 @@
1942
1942
  "category": "Structure",
1943
1943
  "kind": "component",
1944
1944
  "source": "kerf",
1945
- "description": "Semantic native paragraph text by default, with heading and inline-span variants plus composable semantic presentation roles.",
1945
+ "description": "Semantic native text plus a FieldLabel heading for read-only field previews that matches themed Web Awesome form labels.",
1946
1946
  "uses": [],
1947
1947
  "purpose": "Render paragraph semantics by default, h1 through h6 when selected, or geometry-free inline secondary text as a span while independently configuring quiet or danger tone, compact through extra-large size, or monospace font.",
1948
1948
  "publicExports": [
1949
- "Text"
1949
+ "Text",
1950
+ "FieldLabel"
1950
1951
  ],
1951
1952
  "useWhen": [
1952
- "Render ordinary application headings or paragraphs with native document semantics, or inline secondary text inside rows, labels, and table cells."
1953
+ "Render ordinary application headings or paragraphs with native document semantics, or inline secondary text inside rows, labels, and table cells.",
1954
+ "Render FieldLabel over a read-only or click-to-edit preview that replaces a Web Awesome form control."
1953
1955
  ],
1954
1956
  "avoidWhen": [
1955
1957
  "Do not use Text for toolbar identity or title content; use ToolbarText inside a Toolbar zone.",
@@ -1971,7 +1973,8 @@
1971
1973
  "size",
1972
1974
  "font",
1973
1975
  "copy",
1974
- "document heading hierarchy"
1976
+ "document heading hierarchy",
1977
+ "FieldLabel copy, preview/edit state, and aria-labelledby association"
1975
1978
  ],
1976
1979
  "variants": [
1977
1980
  "p, h1 through h6, or inline span semantic variant",
@@ -1983,14 +1986,15 @@
1983
1986
  "accessibility": [
1984
1987
  "Text renders a native paragraph by default; choose an h1 through h6 variant by document outline rather than appearance, or span for inline secondary text.",
1985
1988
  "Ordinary global, data, and ARIA attributes pass through to the native element.",
1986
- "Choose tone, size, and font independently of the heading level so appearance does not distort the document outline."
1989
+ "Choose tone, size, and font independently of the heading level so appearance does not distort the document outline.",
1990
+ "FieldLabel is plain text, not a native label; give it an id and associate the preview group with aria-labelledby."
1987
1991
  ],
1988
1992
  "geometry": {
1989
1993
  "margin": "none",
1990
1994
  "border": "self",
1991
1995
  "padding": "self",
1992
1996
  "notes": [
1993
- "Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding."
1997
+ "Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding. FieldLabel mirrors the themed Web Awesome label typography, 9px inline inset, and bottom gap."
1994
1998
  ]
1995
1999
  },
1996
2000
  "delivery": {
@@ -2002,7 +2006,8 @@
2002
2006
  },
2003
2007
  "wiring": [],
2004
2008
  "publicClasses": [
2005
- "kui-text"
2009
+ "kui-text",
2010
+ "kui-text__field-label"
2006
2011
  ],
2007
2012
  "publicTokens": [
2008
2013
  "--kui-color-danger-on-quiet",
@@ -89,7 +89,7 @@
89
89
  },
90
90
  "parents": {
91
91
  "mode": "listed",
92
- "entries": ["@kerfjs/ui:toolbar"]
92
+ "entries": ["@kerfjs/ui:toolbar", "@kerfjs/ui:floating-toolbar"]
93
93
  },
94
94
  "contexts": ["toolbar-zone"],
95
95
  "zones": [
@@ -1521,7 +1521,10 @@
1521
1521
  "name": "ToolbarControlGroup",
1522
1522
  "kind": "component",
1523
1523
  "source": "kerf",
1524
- "parents": { "mode": "listed", "entries": ["@kerfjs/ui:toolbar"] },
1524
+ "parents": {
1525
+ "mode": "listed",
1526
+ "entries": ["@kerfjs/ui:toolbar", "@kerfjs/ui:floating-toolbar"]
1527
+ },
1525
1528
  "contexts": ["toolbar-zone"],
1526
1529
  "zones": [
1527
1530
  {
@@ -1805,6 +1808,11 @@
1805
1808
  "id": "document heading hierarchy",
1806
1809
  "owner": "application",
1807
1810
  "required": false
1811
+ },
1812
+ {
1813
+ "id": "FieldLabel copy, preview/edit state, and aria-labelledby association",
1814
+ "owner": "application",
1815
+ "required": false
1808
1816
  }
1809
1817
  ],
1810
1818
  "wiring": {
@@ -1821,7 +1829,7 @@
1821
1829
  "border": "self",
1822
1830
  "padding": "self",
1823
1831
  "notes": [
1824
- "Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding."
1832
+ "Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding. FieldLabel mirrors the themed Web Awesome label typography, 9px inline inset, and bottom gap."
1825
1833
  ]
1826
1834
  }
1827
1835
  },
@@ -1829,13 +1837,14 @@
1829
1837
  "obligations": [
1830
1838
  "Text renders a native paragraph by default; choose an h1 through h6 variant by document outline rather than appearance, or span for inline secondary text.",
1831
1839
  "Ordinary global, data, and ARIA attributes pass through to the native element.",
1832
- "Choose tone, size, and font independently of the heading level so appearance does not distort the document outline."
1840
+ "Choose tone, size, and font independently of the heading level so appearance does not distort the document outline.",
1841
+ "FieldLabel is plain text, not a native label; give it an id and associate the preview group with aria-labelledby."
1833
1842
  ]
1834
1843
  },
1835
1844
  "cssValueProps": [],
1836
1845
  "boundaries": {
1837
1846
  "rootClass": "kui-text",
1838
- "publicClasses": ["kui-text"],
1847
+ "publicClasses": ["kui-text", "kui-text__field-label"],
1839
1848
  "publicTokens": [
1840
1849
  "--kui-color-danger-on-quiet",
1841
1850
  "--kui-color-text-quiet",
@@ -214,6 +214,7 @@
214
214
  "required": ["required", "helpers", "obligations"],
215
215
  "properties": {
216
216
  "required": { "type": "boolean" },
217
+ "scope": { "enum": ["application", "module"] },
217
218
  "helpers": { "$ref": "#/$defs/stringList" },
218
219
  "obligations": { "$ref": "#/$defs/stringList" },
219
220
  "stateAttributes": {
@@ -4,13 +4,14 @@
4
4
 
5
5
  `@kerfjs/ui:text` · Kerf UI · component · Structure
6
6
 
7
- Semantic native paragraph text by default, with heading and inline-span variants plus composable semantic presentation roles.
7
+ Semantic native text plus a FieldLabel heading for read-only field previews that matches themed Web Awesome form labels.
8
8
 
9
9
  ## When to use
10
10
 
11
11
  Render paragraph semantics by default, h1 through h6 when selected, or geometry-free inline secondary text as a span while independently configuring quiet or danger tone, compact through extra-large size, or monospace font.
12
12
 
13
13
  - Render ordinary application headings or paragraphs with native document semantics, or inline secondary text inside rows, labels, and table cells.
14
+ - Render FieldLabel over a read-only or click-to-edit preview that replaces a Web Awesome form control.
14
15
 
15
16
  **Not when:**
16
17
 
@@ -24,7 +25,7 @@ Render paragraph semantics by default, h1 through h6 when selected, or geometry-
24
25
 
25
26
  ## Imports
26
27
 
27
- - JavaScript: `@kerfjs/ui/text` — exports `Text`.
28
+ - JavaScript: `@kerfjs/ui/text` — exports `Text`, `FieldLabel`.
28
29
  - CSS: the browser build of `@kerfjs/ui/text` pulls its CSS automatically; import `@kerfjs/ui/text.css` manually only without the `browser` export condition.
29
30
  - Declared side effects: browser-condition-css.
30
31
 
@@ -48,7 +49,7 @@ Exact prop names and types: [`@kerfjs/ui/text`](../public-api-signatures-v1.md#k
48
49
 
49
50
  ## State and wiring
50
51
 
51
- **The app owns:** semantic variant; tone; size; font; copy; document heading hierarchy.
52
+ **The app owns:** semantic variant; tone; size; font; copy; document heading hierarchy; FieldLabel copy, preview/edit state, and aria-labelledby association.
52
53
 
53
54
  No wiring helper.
54
55
 
@@ -56,21 +57,22 @@ No wiring helper.
56
57
 
57
58
  Margin: none · border: self · padding: self (layout role: structure). `self` means the component already owns it — do not add wrapper padding, margin, or borders around it.
58
59
 
59
- - Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding.
60
+ - Block Text variants reset native margins and add a 1px transparent border plus the standard 8px item padding; flush removes the border and padding. Tight line height is available for compact copy. Span has no margin, border, or padding. FieldLabel mirrors the themed Web Awesome label typography, 9px inline inset, and bottom gap.
60
61
 
61
62
  ## Accessibility
62
63
 
63
64
  - Text renders a native paragraph by default; choose an h1 through h6 variant by document outline rather than appearance, or span for inline secondary text.
64
65
  - Ordinary global, data, and ARIA attributes pass through to the native element.
65
66
  - Choose tone, size, and font independently of the heading level so appearance does not distort the document outline.
67
+ - FieldLabel is plain text, not a native label; give it an id and associate the preview group with aria-labelledby.
66
68
 
67
69
  ## Styling boundary
68
70
 
69
71
  The component owns its own styles. Configure it through its props and variants; do not override its internals.
70
72
 
71
- Public class hooks (select for layout placement only, never to change the component's look): `kui-text`.
73
+ Public class hooks (select for layout placement only, never to change the component's look): `kui-text`, `kui-text__field-label`.
72
74
 
73
- Never put `kui-text` on an element you write; render `Text` instead (`KUI-L103`).
75
+ Never put `kui-text`, `kui-text__field-label` on an element you write; render `Text` instead (`KUI-L103`).
74
76
 
75
77
  Public tokens it reads: `--kui-color-danger-on-quiet`, `--kui-color-text-quiet`, `--kui-font-2xl`, `--kui-font-l`, `--kui-font-mono`, `--kui-font-xs`, `--kui-layout-item-padding`. Set a token only where its public contract allows; prefer a prop.
76
78
 
@@ -57,7 +57,7 @@ Exact prop names and types: [`@kerfjs/ui/toolbar-control-group`](../public-api-s
57
57
 
58
58
  ## Composition
59
59
 
60
- - Parents: only inside [Toolbar](./toolbar.md).
60
+ - Parents: only inside [Toolbar](./toolbar.md) or [FloatingToolbar](./floating-toolbar.md).
61
61
  - Contexts: `toolbar-zone`.
62
62
  - Children: only `control` (required: `control`).
63
63
  - Built from: [LucideIcon](./lucide-icon.md), [SegmentedControl](./segmented-control.md).
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.66` and `kerfjs@5.0.0-beta.66`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.68` and `kerfjs@5.0.0-beta.68`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/css-values`
6
6
 
@@ -3301,6 +3301,13 @@ type TextBorder = 'transparent' | 'none';
3301
3301
  type TextLineHeight = 'default' | 'tight';
3302
3302
  type TextWrap = 'normal' | 'anywhere' | 'nowrap' | 'truncate';
3303
3303
  type TextContent = KerfUiContent | string | number | readonly TextContent[];
3304
+ type FieldLabelProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
3305
+ children: string;
3306
+ class?: string;
3307
+ className?: string;
3308
+ };
3309
+ /** Visible field heading for a read-only preview; use its id with aria-labelledby on the preview group. */
3310
+ declare function FieldLabel({ children, class: classValue, className, ...attributes }: FieldLabelProps): kerfjs.SafeHtml;
3304
3311
  type TextCommonProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
3305
3312
  /** Native heading, paragraph, or inline span element to render. Defaults to `p`. */
3306
3313
  variant?: TextVariant;
@@ -3337,7 +3344,7 @@ type TextProps = TextCommonProps & ({
3337
3344
  */
3338
3345
  declare function Text({ variant: Variant, tone, size, font, border, flush, lineHeight, wrap, maxLines, children, class: classValue, className, ...attributes }: TextProps): kerfjs.SafeHtml;
3339
3346
 
3340
- export { Text, type TextBorder, type TextContent, type TextFont, type TextLineHeight, type TextProps, type TextSize, type TextTone, type TextVariant, type TextWrap };
3347
+ export { FieldLabel, type FieldLabelProps, Text, type TextBorder, type TextContent, type TextFont, type TextLineHeight, type TextProps, type TextSize, type TextTone, type TextVariant, type TextWrap };
3341
3348
  ```
3342
3349
 
3343
3350
  ## `@kerfjs/ui/row`
package/ai/skill.md CHANGED
@@ -246,6 +246,8 @@ Set `--kui-toolbar-inset: 0px` on an embedded Toolbar to remove its outer paddin
246
246
 
247
247
  For compact dialog copy, use `Text flush lineHeight="tight"`; for compact metadata, use `ValueTable density="compact"` and its public row columns/padding/gap tokens. `ToolbarText size="xsmall"` fits a dense rail heading. A `TokenSearchField` editor shrinks inside a narrow group by default; set `--kui-token-search-editor-min-width` when a minimum is needed. A full-height shell uses the one `.kui-app-root` mount container documented in `docs/document-baseline.md`.
248
248
 
249
+ For a read-only field preview that replaces a Web Awesome input, use `FieldLabel` from `@kerfjs/ui/text` above the preview. Put it in `ListInsetControl sides="trl"` beside a `ContentItem` value; give it an id and use `aria-labelledby` on the preview group. The app owns preview/edit state and hint copy.
250
+
249
251
  Common mistakes:
250
252
 
251
253
  | Mistake | Fix |
@@ -1,6 +1,6 @@
1
1
  # Web Awesome JSX signatures for the UI authoring corpus
2
2
 
3
- Generated from the emitted `@kerfjs/ui@5.0.0-beta.66` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
3
+ Generated from the emitted `@kerfjs/ui@5.0.0-beta.68` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
4
4
 
5
5
  ```ts
6
6
  import { KerfCustomElement } from 'kerfjs/jsx-runtime';
@@ -22,7 +22,7 @@ export interface ComponentOwnershipFacts {
22
22
 
23
23
  export interface RestyledComponent {
24
24
  entry: OwnershipCatalogEntry;
25
- via: 'class' | 'data-component' | 'tag';
25
+ via: 'class' | 'data-component' | 'tag' | 'descendant';
26
26
  name: string;
27
27
  }
28
28
 
@@ -87,10 +87,11 @@ export function componentOwnershipFacts(entries) {
87
87
  }
88
88
 
89
89
  /**
90
- * The cataloged components a selector list makes its subject: the rightmost
91
- * compound of each complex selector, with `:has()` / `:not()` arguments
92
- * removed because keying on or excluding a component does not style it. A
93
- * subject that is a `::part()` is left to the shadow-part rule (`KUI-L011`).
90
+ * The cataloged components a selector list restyles: the rightmost compound
91
+ * of each complex selector, or an unclassed descendant reached through a
92
+ * cataloged anatomy class. `:has()` / `:not()` arguments are removed because
93
+ * keying on or excluding a component does not style it. A subject that is a
94
+ * `::part()` is left to the shadow-part rule (`KUI-L011`).
94
95
  * Returns `{ entry, via, name }` records; `isForeign(entry)` decides which
95
96
  * entries the stylesheet does not own.
96
97
  */
@@ -108,6 +109,7 @@ export function restyledComponents(selectorList, facts, isForeign) {
108
109
  const subject = parts.at(-1)?.compound;
109
110
  if (!subject || /::part\(/i.test(subject)) continue;
110
111
  const bare = withoutRelationalArguments(subject);
112
+ const subjectClasses = classNames(bare);
111
113
  for (const className of classNames(bare))
112
114
  add(facts.classOwners.get(className), 'class', `.${className}`);
113
115
  for (const value of dataComponents(bare))
@@ -118,9 +120,26 @@ export function restyledComponents(selectorList, facts, isForeign) {
118
120
  );
119
121
  // An application class on the subject scopes a Web Awesome tag to the
120
122
  // application's own element; a component package never places one there.
121
- if (classNames(bare).some((name) => !facts.classOwners.has(name))) continue;
123
+ if (subjectClasses.some((name) => !facts.classOwners.has(name))) continue;
122
124
  for (const type of subjectTypes(subject) ?? [])
123
125
  add(facts.tagOwners.get(type), 'tag', type);
126
+ // A raw descendant of a component's cataloged anatomy is still inside
127
+ // that component. Do not infer ownership through its root, where an app
128
+ // may place its own children, or through an app-named intermediate node.
129
+ if (subjectClasses.length || parts.length < 2) continue;
130
+ for (let index = parts.length - 2; index >= 0; index -= 1) {
131
+ const classes = classNames(
132
+ withoutRelationalArguments(parts[index].compound),
133
+ );
134
+ if (classes.some((name) => !facts.classOwners.has(name))) break;
135
+ const anatomy = classes.find(
136
+ (name) => name.includes('__') && facts.classOwners.has(name),
137
+ );
138
+ if (anatomy) {
139
+ add(facts.classOwners.get(anatomy), 'descendant', `.${anatomy}`);
140
+ break;
141
+ }
142
+ }
124
143
  }
125
144
  return found;
126
145
  }
@@ -435,7 +435,9 @@ function inspectComponentOwnership(
435
435
  diagnostic(
436
436
  'KUI-L019',
437
437
  location(file, rule),
438
- `\`${name}\` makes ${componentLabel(entry)} the subject of an application rule (${properties.join(', ')}); components own their styles. Configure it through ${configurationFor(entry)}. To place your own content in its context, style your own element (\`${name} > .your-element\`). ${reportGap(entry)}`,
438
+ via === 'descendant'
439
+ ? `\`${name}\` reaches an unclassed descendant inside ${componentLabel(entry)} (${properties.join(', ')}); components own their styles. Configure it through ${configurationFor(entry)}. To place your own content in its context, style your own element. ${reportGap(entry)}`
440
+ : `\`${name}\` makes ${componentLabel(entry)} the subject of an application rule (${properties.join(', ')}); components own their styles. Configure it through ${configurationFor(entry)}. To place your own content in its context, style your own element (\`${name} > .your-element\`). ${reportGap(entry)}`,
439
441
  {
440
442
  selector: rule.selector,
441
443
  component: entry.key,
package/dist/catalog.js CHANGED
@@ -4,8 +4,8 @@ import { Workbench } from './chunk-TGM424PI.js';
4
4
  import { PopupMenu } from './chunk-J3KGJ6P6.js';
5
5
 
6
6
  import { Row } from './chunk-VZOFZPSO.js';
7
- import { ListHeader } from './chunk-5IJ6UGNZ.js';
8
- import { Text } from './chunk-CQHN45XR.js';
7
+ import { ListHeader } from './chunk-3BCJGM36.js';
8
+ import { Text } from './chunk-YDGNU2OD.js';
9
9
  import { ListInsetText } from './chunk-DG6G5EPF.js';
10
10
  import { ListItem } from './chunk-EN4LDR5Y.js';
11
11
  import { ToolbarText } from './chunk-V3DX3K65.js';
@@ -1,4 +1,4 @@
1
- import { Text } from './chunk-CQHN45XR.js';
1
+ import { Text } from './chunk-YDGNU2OD.js';
2
2
  import { DisclosureArrow } from './chunk-AUVG54KC.js';
3
3
  import { Skeleton } from './chunk-AHEG27M7.js';
4
4
  import { Badge } from './chunk-MP2GDBQZ.js';
@@ -153,4 +153,4 @@ function ListHeader({
153
153
 
154
154
  export { ListHeader };
155
155
 
156
- //# sourceMappingURL=chunk-5IJ6UGNZ.js.map
156
+ //# sourceMappingURL=chunk-3BCJGM36.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/list-header.tsx"],"names":[],"mappings":"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","file":"chunk-5IJ6UGNZ.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge, type BadgeTone } from './badge.js';\nimport { em } from './css-values.js';\nimport { DisclosureArrow } from './disclosure-arrow.js';\nimport {\n filterControlAttributes,\n filterDataAttributes,\n} from './extension-attributes.js';\nimport { Skeleton } from './skeleton.js';\nimport { Text } from './text.js';\nimport type { HeadingLevel } from './toolbar-text.js';\n\nconst PROTECTED_ROOT_DATA_ATTRIBUTES = new Set([\n 'data-component',\n 'data-action',\n 'data-has-badge',\n 'data-has-count',\n 'data-density',\n 'data-divider',\n 'data-inline',\n 'data-width',\n 'data-indicator-tone',\n 'data-toggle',\n]);\nconst PROTECTED_TRIGGER_DATA_ATTRIBUTES = new Set([\n 'data-action',\n 'data-kui-disabled',\n]);\n\ntype ListHeaderRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-action'?: never;\n 'data-has-badge'?: never;\n 'data-has-count'?: never;\n 'data-density'?: never;\n 'data-divider'?: never;\n 'data-inline'?: never;\n 'data-width'?: never;\n 'data-indicator-tone'?: never;\n 'data-toggle'?: never;\n }\n>;\n\ntype ListHeaderTriggerAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-action'?: never;\n 'data-kui-disabled'?: never;\n popoverTarget?: string;\n popoverTargetAction?: 'toggle' | 'show' | 'hide';\n 'aria-controls'?: string;\n 'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';\n }\n>;\n\ninterface ListHeaderBaseProps {\n label: string;\n density?: 'standard' | 'compact';\n divider?: 'none' | 'before' | 'after' | 'both';\n /** Shrink-wrap the header without its default outer margin, border, or padding. */\n inline?: boolean;\n /** Fill the available row or shrink-wrap while retaining normal header geometry. */\n width?: 'fill' | 'content';\n indicatorTone?: 'neutral' | 'accent' | 'pop' | 'danger';\n /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */\n placeholder?: boolean;\n rootAttributes?: ListHeaderRootAttributes;\n triggerAttributes?: ListHeaderTriggerAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\ntype ListHeaderModeProps =\n | {\n /** Render the title as a controlled disclosure trigger. */\n toggle: true;\n /** A toggle's label is its button text, not a heading. */\n headingLevel?: never;\n action: string;\n expanded: boolean;\n actionIcon?: SafeHtml;\n actionLabel?: never;\n actionDisabled?: boolean;\n disabledReason?: string;\n }\n | {\n /** Render a separately named trailing action. */\n toggle?: false;\n /**\n * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the\n * document outline; the visual treatment does not change.\n */\n headingLevel?: HeadingLevel;\n action: string;\n actionLabel: string;\n actionIcon: SafeHtml;\n expanded?: never;\n actionDisabled?: boolean;\n disabledReason?: string;\n }\n | {\n /** Render a passive section heading. */\n toggle?: false;\n /**\n * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the\n * document outline; the visual treatment does not change.\n */\n headingLevel?: HeadingLevel;\n action?: never;\n actionLabel?: never;\n actionIcon?: never;\n expanded?: never;\n actionDisabled?: never;\n disabledReason?: never;\n };\n\ntype ListHeaderIndicatorProps =\n | { count: number; countLabel: string; badge?: never; status?: never }\n | { count?: never; countLabel?: never; badge: SafeHtml; status?: never }\n | { count?: never; countLabel?: never; badge?: never; status?: SafeHtml };\n\nexport type ListHeaderProps = ListHeaderBaseProps &\n ListHeaderIndicatorProps &\n ListHeaderModeProps;\n\nexport function ListHeader({\n label,\n headingLevel = 2,\n count,\n countLabel,\n badge,\n status,\n density = 'standard',\n divider = 'none',\n inline = false,\n width = 'fill',\n indicatorTone = 'neutral',\n action,\n actionLabel,\n actionIcon,\n actionDisabled = false,\n disabledReason,\n expanded,\n toggle = false,\n placeholder = false,\n rootAttributes = {},\n triggerAttributes = {},\n slot,\n}: ListHeaderProps) {\n const extensionRootAttributes = filterDataAttributes(\n rootAttributes,\n PROTECTED_ROOT_DATA_ATTRIBUTES,\n );\n const extensionTriggerAttributes = filterControlAttributes(\n triggerAttributes,\n PROTECTED_TRIGGER_DATA_ATTRIBUTES,\n );\n const normalizedCount =\n typeof count === 'number' && Number.isSafeInteger(count) && count >= 0\n ? count\n : undefined;\n const normalizedCountLabel =\n normalizedCount === undefined\n ? undefined\n : typeof countLabel === 'string' && countLabel.trim()\n ? countLabel\n : String(normalizedCount);\n const renderedBadge =\n normalizedCount === undefined ? (status ?? badge) : undefined;\n const renderedActionIcon =\n actionIcon ??\n (toggle ? <DisclosureArrow open={Boolean(expanded)} /> : undefined);\n const accessibleLabel =\n normalizedCount === undefined\n ? undefined\n : `${label}, ${normalizedCountLabel}`;\n const badgeTone: BadgeTone =\n indicatorTone === 'accent' ? 'brand' : indicatorTone;\n const indicator = placeholder ? (\n (normalizedCount !== undefined || renderedBadge) && (\n <Badge size=\"compact\" tone={badgeTone}>\n <Skeleton width={em(1.75)} />\n </Badge>\n )\n ) : normalizedCount === undefined ? (\n renderedBadge && (\n <Badge size=\"compact\" tone={badgeTone}>\n {renderedBadge}\n </Badge>\n )\n ) : (\n <Badge size=\"compact\" tone={badgeTone} ariaHidden>\n {normalizedCount}\n </Badge>\n );\n const busy = placeholder ? ('true' as const) : undefined;\n if (toggle) {\n return (\n <header\n {...extensionRootAttributes}\n class=\"kui-list-header\"\n data-component=\"list-header\"\n data-has-badge={String(Boolean(renderedBadge))}\n data-has-count={String(normalizedCount !== undefined)}\n data-density={density}\n data-divider={divider}\n data-inline={String(inline)}\n data-width={width}\n data-indicator-tone={indicatorTone}\n data-toggle=\"true\"\n data-placeholder={placeholder ? 'true' : undefined}\n aria-busy={busy}\n slot={slot}\n >\n <button\n {...extensionTriggerAttributes}\n type=\"button\"\n class=\"kui-list-header__title kui-list-header__toggle\"\n data-action={placeholder ? undefined : action}\n title={actionDisabled ? disabledReason : undefined}\n disabled={actionDisabled || placeholder || undefined}\n data-kui-disabled={actionDisabled ? 'true' : undefined}\n aria-label={accessibleLabel}\n aria-expanded={String(Boolean(expanded))}\n >\n <Text variant=\"span\">{label}</Text>\n {indicator}\n {renderedActionIcon && (\n <span class=\"kui-list-header__action-layer\">\n {renderedActionIcon}\n </span>\n )}\n </button>\n </header>\n );\n }\n return (\n <header\n {...extensionRootAttributes}\n class=\"kui-list-header\"\n data-component=\"list-header\"\n data-has-badge={String(Boolean(renderedBadge))}\n data-has-count={String(normalizedCount !== undefined)}\n data-density={density}\n data-divider={divider}\n data-inline={String(inline)}\n data-width={width}\n data-indicator-tone={indicatorTone}\n data-toggle=\"false\"\n data-placeholder={placeholder ? 'true' : undefined}\n aria-busy={busy}\n slot={slot}\n >\n <div class=\"kui-list-header__title\">\n <Text\n variant={`h${headingLevel}`}\n border=\"none\"\n aria-label={accessibleLabel}\n >\n {label}\n </Text>\n {indicator}\n </div>\n {action && (\n <button\n {...extensionTriggerAttributes}\n type=\"button\"\n class=\"kui-list-header__action\"\n data-action={placeholder ? undefined : action}\n aria-label={actionLabel}\n title={actionDisabled ? disabledReason : actionLabel}\n disabled={actionDisabled || placeholder || undefined}\n data-kui-disabled={actionDisabled ? 'true' : undefined}\n >\n {renderedActionIcon}\n </button>\n )}\n </header>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/list-header.tsx"],"names":[],"mappings":"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","file":"chunk-3BCJGM36.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Badge, type BadgeTone } from './badge.js';\nimport { em } from './css-values.js';\nimport { DisclosureArrow } from './disclosure-arrow.js';\nimport {\n filterControlAttributes,\n filterDataAttributes,\n} from './extension-attributes.js';\nimport { Skeleton } from './skeleton.js';\nimport { Text } from './text.js';\nimport type { HeadingLevel } from './toolbar-text.js';\n\nconst PROTECTED_ROOT_DATA_ATTRIBUTES = new Set([\n 'data-component',\n 'data-action',\n 'data-has-badge',\n 'data-has-count',\n 'data-density',\n 'data-divider',\n 'data-inline',\n 'data-width',\n 'data-indicator-tone',\n 'data-toggle',\n]);\nconst PROTECTED_TRIGGER_DATA_ATTRIBUTES = new Set([\n 'data-action',\n 'data-kui-disabled',\n]);\n\ntype ListHeaderRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-action'?: never;\n 'data-has-badge'?: never;\n 'data-has-count'?: never;\n 'data-density'?: never;\n 'data-divider'?: never;\n 'data-inline'?: never;\n 'data-width'?: never;\n 'data-indicator-tone'?: never;\n 'data-toggle'?: never;\n }\n>;\n\ntype ListHeaderTriggerAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-action'?: never;\n 'data-kui-disabled'?: never;\n popoverTarget?: string;\n popoverTargetAction?: 'toggle' | 'show' | 'hide';\n 'aria-controls'?: string;\n 'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';\n }\n>;\n\ninterface ListHeaderBaseProps {\n label: string;\n density?: 'standard' | 'compact';\n divider?: 'none' | 'before' | 'after' | 'both';\n /** Shrink-wrap the header without its default outer margin, border, or padding. */\n inline?: boolean;\n /** Fill the available row or shrink-wrap while retaining normal header geometry. */\n width?: 'fill' | 'content';\n indicatorTone?: 'neutral' | 'accent' | 'pop' | 'danger';\n /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */\n placeholder?: boolean;\n rootAttributes?: ListHeaderRootAttributes;\n triggerAttributes?: ListHeaderTriggerAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\ntype ListHeaderModeProps =\n | {\n /** Render the title as a controlled disclosure trigger. */\n toggle: true;\n /** A toggle's label is its button text, not a heading. */\n headingLevel?: never;\n action: string;\n expanded: boolean;\n actionIcon?: SafeHtml;\n actionLabel?: never;\n actionDisabled?: boolean;\n disabledReason?: string;\n }\n | {\n /** Render a separately named trailing action. */\n toggle?: false;\n /**\n * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the\n * document outline; the visual treatment does not change.\n */\n headingLevel?: HeadingLevel;\n action: string;\n actionLabel: string;\n actionIcon: SafeHtml;\n expanded?: never;\n actionDisabled?: boolean;\n disabledReason?: string;\n }\n | {\n /** Render a passive section heading. */\n toggle?: false;\n /**\n * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the\n * document outline; the visual treatment does not change.\n */\n headingLevel?: HeadingLevel;\n action?: never;\n actionLabel?: never;\n actionIcon?: never;\n expanded?: never;\n actionDisabled?: never;\n disabledReason?: never;\n };\n\ntype ListHeaderIndicatorProps =\n | { count: number; countLabel: string; badge?: never; status?: never }\n | { count?: never; countLabel?: never; badge: SafeHtml; status?: never }\n | { count?: never; countLabel?: never; badge?: never; status?: SafeHtml };\n\nexport type ListHeaderProps = ListHeaderBaseProps &\n ListHeaderIndicatorProps &\n ListHeaderModeProps;\n\nexport function ListHeader({\n label,\n headingLevel = 2,\n count,\n countLabel,\n badge,\n status,\n density = 'standard',\n divider = 'none',\n inline = false,\n width = 'fill',\n indicatorTone = 'neutral',\n action,\n actionLabel,\n actionIcon,\n actionDisabled = false,\n disabledReason,\n expanded,\n toggle = false,\n placeholder = false,\n rootAttributes = {},\n triggerAttributes = {},\n slot,\n}: ListHeaderProps) {\n const extensionRootAttributes = filterDataAttributes(\n rootAttributes,\n PROTECTED_ROOT_DATA_ATTRIBUTES,\n );\n const extensionTriggerAttributes = filterControlAttributes(\n triggerAttributes,\n PROTECTED_TRIGGER_DATA_ATTRIBUTES,\n );\n const normalizedCount =\n typeof count === 'number' && Number.isSafeInteger(count) && count >= 0\n ? count\n : undefined;\n const normalizedCountLabel =\n normalizedCount === undefined\n ? undefined\n : typeof countLabel === 'string' && countLabel.trim()\n ? countLabel\n : String(normalizedCount);\n const renderedBadge =\n normalizedCount === undefined ? (status ?? badge) : undefined;\n const renderedActionIcon =\n actionIcon ??\n (toggle ? <DisclosureArrow open={Boolean(expanded)} /> : undefined);\n const accessibleLabel =\n normalizedCount === undefined\n ? undefined\n : `${label}, ${normalizedCountLabel}`;\n const badgeTone: BadgeTone =\n indicatorTone === 'accent' ? 'brand' : indicatorTone;\n const indicator = placeholder ? (\n (normalizedCount !== undefined || renderedBadge) && (\n <Badge size=\"compact\" tone={badgeTone}>\n <Skeleton width={em(1.75)} />\n </Badge>\n )\n ) : normalizedCount === undefined ? (\n renderedBadge && (\n <Badge size=\"compact\" tone={badgeTone}>\n {renderedBadge}\n </Badge>\n )\n ) : (\n <Badge size=\"compact\" tone={badgeTone} ariaHidden>\n {normalizedCount}\n </Badge>\n );\n const busy = placeholder ? ('true' as const) : undefined;\n if (toggle) {\n return (\n <header\n {...extensionRootAttributes}\n class=\"kui-list-header\"\n data-component=\"list-header\"\n data-has-badge={String(Boolean(renderedBadge))}\n data-has-count={String(normalizedCount !== undefined)}\n data-density={density}\n data-divider={divider}\n data-inline={String(inline)}\n data-width={width}\n data-indicator-tone={indicatorTone}\n data-toggle=\"true\"\n data-placeholder={placeholder ? 'true' : undefined}\n aria-busy={busy}\n slot={slot}\n >\n <button\n {...extensionTriggerAttributes}\n type=\"button\"\n class=\"kui-list-header__title kui-list-header__toggle\"\n data-action={placeholder ? undefined : action}\n title={actionDisabled ? disabledReason : undefined}\n disabled={actionDisabled || placeholder || undefined}\n data-kui-disabled={actionDisabled ? 'true' : undefined}\n aria-label={accessibleLabel}\n aria-expanded={String(Boolean(expanded))}\n >\n <Text variant=\"span\">{label}</Text>\n {indicator}\n {renderedActionIcon && (\n <span class=\"kui-list-header__action-layer\">\n {renderedActionIcon}\n </span>\n )}\n </button>\n </header>\n );\n }\n return (\n <header\n {...extensionRootAttributes}\n class=\"kui-list-header\"\n data-component=\"list-header\"\n data-has-badge={String(Boolean(renderedBadge))}\n data-has-count={String(normalizedCount !== undefined)}\n data-density={density}\n data-divider={divider}\n data-inline={String(inline)}\n data-width={width}\n data-indicator-tone={indicatorTone}\n data-toggle=\"false\"\n data-placeholder={placeholder ? 'true' : undefined}\n aria-busy={busy}\n slot={slot}\n >\n <div class=\"kui-list-header__title\">\n <Text\n variant={`h${headingLevel}`}\n border=\"none\"\n aria-label={accessibleLabel}\n >\n {label}\n </Text>\n {indicator}\n </div>\n {action && (\n <button\n {...extensionTriggerAttributes}\n type=\"button\"\n class=\"kui-list-header__action\"\n data-action={placeholder ? undefined : action}\n aria-label={actionLabel}\n title={actionDisabled ? disabledReason : actionLabel}\n disabled={actionDisabled || placeholder || undefined}\n data-kui-disabled={actionDisabled ? 'true' : undefined}\n >\n {renderedActionIcon}\n </button>\n )}\n </header>\n );\n}\n"]}
@@ -1,6 +1,15 @@
1
1
  import { jsx } from 'kerfjs/jsx-runtime';
2
2
 
3
3
  // src/text.tsx
4
+ function FieldLabel({
5
+ children,
6
+ class: classValue = "",
7
+ className = "",
8
+ ...attributes
9
+ }) {
10
+ const classes = ["kui-text__field-label", classValue, className].filter(Boolean).join(" ");
11
+ return /* @__PURE__ */ jsx("div", { ...attributes, class: classes, "data-component": "field-label", children });
12
+ }
4
13
  function Text({
5
14
  variant: Variant = "p",
6
15
  tone = "default",
@@ -47,6 +56,6 @@ function Text({
47
56
  );
48
57
  }
49
58
 
50
- export { Text };
59
+ export { FieldLabel, Text };
51
60
 
52
- //# sourceMappingURL=chunk-CQHN45XR.js.map
61
+ //# sourceMappingURL=chunk-YDGNU2OD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/text.tsx"],"names":[],"mappings":";;;AA0BO,SAAS,UAAA,CAAW;AAAA,EACzB,QAAA;AAAA,EACA,OAAO,UAAA,GAAa,EAAA;AAAA,EACpB,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAoB;AAClB,EAAA,MAAM,OAAA,GAAU,CAAC,uBAAA,EAAyB,UAAA,EAAY,SAAS,EAC5D,MAAA,CAAO,OAAO,CAAA,CACd,IAAA,CAAK,GAAG,CAAA;AACX,EAAA,uBACE,GAAA,CAAC,SAAK,GAAG,UAAA,EAAY,OAAO,OAAA,EAAS,gBAAA,EAAe,eACjD,QAAA,EACH,CAAA;AAEJ;AA6CO,SAAS,IAAA,CAAK;AAAA,EACnB,SAAS,OAAA,GAAU,GAAA;AAAA,EACnB,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,MAAA,GAAS,aAAA;AAAA,EACT,KAAA,GAAQ,KAAA;AAAA,EACR,UAAA,GAAa,SAAA;AAAA,EACb,IAAA,GAAO,QAAA;AAAA,EACP,QAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAO,UAAA,GAAa,EAAA;AAAA,EACpB,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAc;AACZ,EAAA,IACE,QAAA,KAAa,MAAA,KACZ,CAAC,MAAA,CAAO,aAAA,CAAc,QAAQ,CAAA,IAC7B,QAAA,GAAW,CAAA,IACX,IAAA,KAAS,QAAA,IACT,IAAA,KAAS,UAAA,CAAA;AAEX,IAAA,MAAM,IAAI,UAAA;AAAA,MACR;AAAA,KACF;AACF,EAAA,MAAM,OAAA,GAAU,CAAC,UAAA,EAAY,UAAA,EAAY,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC5E,EAAA,uBACE,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,KAAA,EAAO,OAAA;AAAA,MACP,gBAAA,EAAe,MAAA;AAAA,MACf,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,IAAA;AAAA,MACX,aAAA,EAAa,MAAA;AAAA,MACb,YAAA,EAAY,QAAQ,MAAA,GAAS,MAAA;AAAA,MAC7B,kBAAA,EAAkB,UAAA,KAAe,OAAA,GAAU,OAAA,GAAU,MAAA;AAAA,MACrD,WAAA,EAAW,IAAA,KAAS,QAAA,GAAW,MAAA,GAAY,IAAA;AAAA,MAC3C,gBAAA,EAAgB,QAAA,KAAa,MAAA,GAAY,MAAA,GAAY,OAAO,QAAQ,CAAA;AAAA,MAEnE,QAAA,EAAA,QAAA,KAAa,SACZ,QAAA,mBAEA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAM,iBAAA;AAAA,UACN,KAAA,EAAO,yBAAyB,QAAQ,CAAA,CAAA;AAAA,UAEvC;AAAA;AAAA;AACH;AAAA,GAEJ;AAEJ","file":"chunk-YDGNU2OD.js","sourcesContent":["import type { KerfBaseAttrs } from 'kerfjs/jsx-runtime';\n\nimport type { KerfUiContent } from './semantic-content.js';\n\nexport type TextVariant =\n 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';\nexport type TextTone = 'default' | 'quiet' | 'danger';\nexport type TextSize = 'compact' | 'default' | 'large' | 'xlarge';\nexport type TextFont = 'default' | 'monospace';\nexport type TextBorder = 'transparent' | 'none';\nexport type TextLineHeight = 'default' | 'tight';\nexport type TextWrap = 'normal' | 'anywhere' | 'nowrap' | 'truncate';\n\nexport type TextContent =\n KerfUiContent | string | number | readonly TextContent[];\n\nexport type FieldLabelProps = Omit<\n KerfBaseAttrs,\n 'children' | 'class' | 'className'\n> & {\n children: string;\n class?: string;\n className?: string;\n};\n\n/** Visible field heading for a read-only preview; use its id with aria-labelledby on the preview group. */\nexport function FieldLabel({\n children,\n class: classValue = '',\n className = '',\n ...attributes\n}: FieldLabelProps) {\n const classes = ['kui-text__field-label', classValue, className]\n .filter(Boolean)\n .join(' ');\n return (\n <div {...attributes} class={classes} data-component=\"field-label\">\n {children}\n </div>\n );\n}\n\ntype TextCommonProps = Omit<\n KerfBaseAttrs,\n 'children' | 'class' | 'className'\n> & {\n /** Native heading, paragraph, or inline span element to render. Defaults to `p`. */\n variant?: TextVariant;\n /** Semantic foreground treatment. Defaults to the inherited foreground. */\n tone?: TextTone;\n /** Text sizing independent of the native semantic element. */\n size?: TextSize;\n /** Font family independent of the native semantic element. */\n font?: TextFont;\n /** Transparent alignment border or no border when embedded in owner chrome. */\n border?: TextBorder;\n /** Remove the block variant's item padding and border in compact content. */\n flush?: boolean;\n /** Use compact leading for short dialog or metadata copy. */\n lineHeight?: TextLineHeight;\n children: TextContent;\n class?: string;\n className?: string;\n};\n\nexport type TextProps = TextCommonProps &\n (\n | {\n /** Wrap normally or break long unbroken strings. Defaults to normal. */\n wrap?: 'normal' | 'anywhere';\n /** Cap wrapped text to this positive number of lines. */\n maxLines?: number;\n }\n | {\n /** Keep one line, with or without an ellipsis. */\n wrap: 'nowrap' | 'truncate';\n maxLines?: never;\n }\n );\n\n/**\n * Semantic heading, paragraph, or inline text. Block variants use the standard\n * content-item padding; `span` adds no box geometry.\n * All ordinary native heading/paragraph attributes pass through to the element.\n */\nexport function Text({\n variant: Variant = 'p',\n tone = 'default',\n size = 'default',\n font = 'default',\n border = 'transparent',\n flush = false,\n lineHeight = 'default',\n wrap = 'normal',\n maxLines,\n children,\n class: classValue = '',\n className = '',\n ...attributes\n}: TextProps) {\n if (\n maxLines !== undefined &&\n (!Number.isSafeInteger(maxLines) ||\n maxLines < 1 ||\n wrap === 'nowrap' ||\n wrap === 'truncate')\n )\n throw new RangeError(\n 'Text maxLines requires a positive integer and wrapping',\n );\n const classes = ['kui-text', classValue, className].filter(Boolean).join(' ');\n return (\n <Variant\n {...attributes}\n class={classes}\n data-component=\"text\"\n data-tone={tone}\n data-size={size}\n data-font={font}\n data-border={border}\n data-flush={flush ? 'true' : undefined}\n data-line-height={lineHeight === 'tight' ? 'tight' : undefined}\n data-wrap={wrap === 'normal' ? undefined : wrap}\n data-max-lines={maxLines === undefined ? undefined : String(maxLines)}\n >\n {maxLines === undefined ? (\n children\n ) : (\n <span\n class=\"kui-text__clamp\"\n style={`--_kui-text-max-lines:${maxLines}`}\n >\n {children}\n </span>\n )}\n </Variant>\n );\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@ export { StateBanner, StateBannerProps, StateBannerTone, StateBannerUrgency } fr
28
28
  export { SunkenPanel, SunkenPanelProps, SunkenPanelShape } from './sunken-panel.js';
29
29
  export { DialogSurface, DialogSurfacePresentation, DialogSurfaceProps, DialogSurfaceSize, PopupSurface, PopupSurfaceInset, PopupSurfaceProps, SurfaceInset } from './surface-scaffold.js';
30
30
  export { TabActivation, TabBar, TabBarAllocation, TabBarPresentation, TabBarProps, TabBarTrailingPlacement } from './tab-bar.js';
31
- export { Text, TextContent, TextFont, TextProps, TextSize, TextTone, TextVariant } from './text.js';
31
+ export { FieldLabel, FieldLabelProps, Text, TextContent, TextFont, TextProps, TextSize, TextTone, TextVariant } from './text.js';
32
32
  export { T as TokenSearchEditorAttributes, a as TokenSearchField, b as TokenSearchFieldProps, c as TokenSearchFieldValue, d as TokenSearchModel, e as TokenSearchModelOptions, f as TokenSearchResolvedToken, g as TokenSearchRule, h as TokenSearchState, i as TokenSearchSuggestion, j as TokenSearchToken, k as TokenSearchTrailingAction, l as createTokenSearchModel, p as placeTokenSearchCaret, r as readTokenSearchField } from './token-search-field-CVGwDn2y.js';
33
33
  export { Toolbar, ToolbarConfig, ToolbarPosition, ToolbarProps } from './toolbar.js';
34
34
  export { ToolbarActionLink, ToolbarActionLinkProps, ToolbarControlGroup, ToolbarControlGroupAppearance, ToolbarControlGroupButtonAppearance, ToolbarControlGroupContent, ToolbarControlGroupDensity, ToolbarControlGroupMenuInset, ToolbarControlGroupOverflow, ToolbarControlGroupProps, ToolbarControlGroupSelectedChrome, ToolbarControlGroupSelectedTone, ToolbarControlGroupShape, ToolbarControlGroupSize, ToolbarControlGroupTone, ToolbarControlGroupVisibility } from './toolbar-control-group.js';
package/dist/index.js CHANGED
@@ -21,8 +21,8 @@ export { AppTab } from './chunk-Q6YU3BT7.js';
21
21
  export { PopupMenu, closePopupMenu, openPopupMenuAt } from './chunk-J3KGJ6P6.js';
22
22
  export { ResizableRegion, clampRegionSize, resizeRegionFromPointer } from './chunk-KLJKU6ZL.js';
23
23
  export { Row } from './chunk-VZOFZPSO.js';
24
- export { ListHeader } from './chunk-5IJ6UGNZ.js';
25
- export { Text } from './chunk-CQHN45XR.js';
24
+ export { ListHeader } from './chunk-3BCJGM36.js';
25
+ export { FieldLabel, Text } from './chunk-YDGNU2OD.js';
26
26
  export { ListInsetText } from './chunk-DG6G5EPF.js';
27
27
  export { ListItem, ListItemLink } from './chunk-EN4LDR5Y.js';
28
28
  export { ToolbarText } from './chunk-V3DX3K65.js';
@@ -1,4 +1,4 @@
1
- export { ListHeader } from './chunk-5IJ6UGNZ.js';
1
+ export { ListHeader } from './chunk-3BCJGM36.js';
2
2
 
3
3
 
4
4
 
@@ -38,6 +38,18 @@
38
38
  var(--_kui-app-tab-pinned-default-background, var(--kui-color-surface))
39
39
  );
40
40
  }
41
+ .kui-app-tab[data-pinned="true"]::before {
42
+ content: "";
43
+ position: absolute;
44
+ z-index: -1;
45
+ inset-block: 0;
46
+ inset-inline-start: calc(-1 * var(--kui-tab-bar-strip-padding, 1px));
47
+ inset-inline-end: 0;
48
+ background: var(
49
+ --kui-app-tab-pinned-background,
50
+ var(--_kui-app-tab-pinned-default-background, var(--kui-color-surface))
51
+ );
52
+ }
41
53
  .kui-tab-bar[data-presentation="segmented"] .kui-app-tab[data-pinned="true"],
42
54
  .kui-tab-bar[data-presentation="inspector"] .kui-app-tab[data-pinned="true"] {
43
55
  --_kui-app-tab-pinned-default-background: var(
@@ -7,6 +7,24 @@
7
7
  overflow-wrap: normal;
8
8
  }
9
9
 
10
+ /* Match the themed Web Awesome form-control label without introducing a
11
+ native <label> that would falsely point at a non-form preview. */
12
+ .kui-text__field-label {
13
+ box-sizing: border-box;
14
+ min-width: 0;
15
+ padding-inline: calc(
16
+ var(--wa-form-control-border-width, 1px) +
17
+ var(--wa-form-control-padding-inline, 0.5rem)
18
+ );
19
+ margin: 0 0 0.5em;
20
+ color: var(--kui-color-neutral-on-quiet, var(--wa-color-text-quiet));
21
+ font-size: var(--kui-font-xs, var(--wa-font-size-xs));
22
+ font-weight: 650;
23
+ letter-spacing: 0.04em;
24
+ line-height: var(--wa-line-height-condensed);
25
+ text-transform: uppercase;
26
+ }
27
+
10
28
  .kui-text[data-wrap="anywhere"] {
11
29
  overflow-wrap: anywhere;
12
30
  }
@@ -98,6 +98,15 @@
98
98
  cursor: text;
99
99
  }
100
100
 
101
+ /* iOS can magnify a small editor on focus and leave the page magnified after
102
+ blur. Keep the desktop small-text rhythm, but give touch editors legible text
103
+ without restricting the user's own page zoom. */
104
+ @media (hover: none) and (pointer: coarse) {
105
+ .kui-token-search__editor {
106
+ font-size: max(16px, var(--kui-font-s));
107
+ }
108
+ }
109
+
101
110
  .kui-token-search__editor:empty::before {
102
111
  color: var(--kui-color-text-quiet);
103
112
  content: attr(data-placeholder);
@@ -591,6 +591,23 @@
591
591
  }
592
592
  }
593
593
 
594
+ /* Small Web Awesome fields have 14px hosts by default. Raise only the editable
595
+ shadow part on touch devices so focusing them need not magnify the page;
596
+ their compact chrome and desktop typography keep their normal sizes. */
597
+ @media (hover: none) and (pointer: coarse) {
598
+ @layer wa-theme-overrides {
599
+ :is(wa-input, wa-number-input, wa-time-input):is(
600
+ [size="xs"],
601
+ [size="s"],
602
+ [size="small"]
603
+ )::part(input),
604
+ wa-textarea:is([size="xs"], [size="s"], [size="small"])::part(textarea),
605
+ wa-select:is([size="xs"], [size="s"], [size="small"])::part(display-input) {
606
+ font-size: max(16px, var(--wa-font-size-m, 16px));
607
+ }
608
+ }
609
+ }
610
+
594
611
  @media (prefers-contrast: more) {
595
612
  @layer wa-theme-overrides {
596
613
  :where(:root),
package/dist/text.d.ts CHANGED
@@ -10,6 +10,13 @@ type TextBorder = 'transparent' | 'none';
10
10
  type TextLineHeight = 'default' | 'tight';
11
11
  type TextWrap = 'normal' | 'anywhere' | 'nowrap' | 'truncate';
12
12
  type TextContent = KerfUiContent | string | number | readonly TextContent[];
13
+ type FieldLabelProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
14
+ children: string;
15
+ class?: string;
16
+ className?: string;
17
+ };
18
+ /** Visible field heading for a read-only preview; use its id with aria-labelledby on the preview group. */
19
+ declare function FieldLabel({ children, class: classValue, className, ...attributes }: FieldLabelProps): kerfjs.SafeHtml;
13
20
  type TextCommonProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
14
21
  /** Native heading, paragraph, or inline span element to render. Defaults to `p`. */
15
22
  variant?: TextVariant;
@@ -46,4 +53,4 @@ type TextProps = TextCommonProps & ({
46
53
  */
47
54
  declare function Text({ variant: Variant, tone, size, font, border, flush, lineHeight, wrap, maxLines, children, class: classValue, className, ...attributes }: TextProps): kerfjs.SafeHtml;
48
55
 
49
- export { Text, type TextBorder, type TextContent, type TextFont, type TextLineHeight, type TextProps, type TextSize, type TextTone, type TextVariant, type TextWrap };
56
+ export { FieldLabel, type FieldLabelProps, Text, type TextBorder, type TextContent, type TextFont, type TextLineHeight, type TextProps, type TextSize, type TextTone, type TextVariant, type TextWrap };
package/dist/text.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Text } from './chunk-CQHN45XR.js';
1
+ export { FieldLabel, Text } from './chunk-YDGNU2OD.js';
2
2
 
3
3
  //# sourceMappingURL=text.js.map
@@ -444,6 +444,10 @@ there as the editor wraps. In `collapsible` mode, the closed state is one named
444
444
  iconic search button and the open state is the same named searchbox, whether
445
445
  the field stands alone or is composed inside `ToolbarControlGroup`.
446
446
 
447
+ On coarse touch devices, the editor's computed text size has a 16px floor. This
448
+ avoids iOS focus magnification of the small-text editor while preserving the
449
+ user's ability to pinch zoom; other field chrome and desktop density are unchanged.
450
+
447
451
  For an app-driven query replacement with an unmanaged field, change its
448
452
  `revision` along with `query` so the editor text rebuilds. For a model-managed
449
453
  field, call `model.replace({ query, tokens })`: it parses the supplied state and
package/docs/apple-hig.md CHANGED
@@ -7,6 +7,7 @@ Native Apple applications should use native platform controls and follow the Hum
7
7
  - Accessibility is behavior: every workflow is keyboard-operable; focus is visible; DOM and reading order agree; controls have accessible names; decorative icons are hidden.
8
8
  - Use adaptive semantic colors. Every custom role needs usable light, dark, and increased-contrast behavior. Never rely on color alone.
9
9
  - Use the system UI font stack and allow browser zoom through 200%. Avoid thin weights and hard minimums that clip enlarged text.
10
+ - On iOS, prevent unwanted focus magnification by keeping editable text legible at touch widths. Do not force the page scale back on blur: that would also undo a person's deliberate pinch zoom.
10
11
  - Group with space and alignment before adding borders. Keep essential content in the leading reading path and secondary detail in progressive disclosure.
11
12
  - Use one icon family and one metaphor per action. Directional icons may mirror in RTL; checkmarks, logos, and real-world objects do not.
12
13
  - Motion is brief, causal, and optional. Honor reduced-motion and reduced-transparency preferences; repeating indicators stop moving under reduced motion.
@@ -154,7 +154,7 @@ check in `.kerf-ui-profile.json` conforming to
154
154
  [`application-ui-profile.schema.json`](../ai/application-ui-profile.schema.json).
155
155
  Keep package-qualified catalog locations and recurring-concept preferences,
156
156
  allowed color schemes and density, public semantic-token overrides,
157
- layout/responsive conventions, and narrow rule exceptions there. Product
157
+ layout/responsive conventions, application wiring entry paths, and narrow rule exceptions there. Product
158
158
  records, copy, permissions, user preferences, and transport state do not belong
159
159
  in this policy file.
160
160
 
@@ -175,6 +175,12 @@ Discovery and precedence are deterministic:
175
175
  Later scalar and object-map values win. Catalogs merge by package; preferences
176
176
  and token overrides merge by key; exceptions merge by stable id. Lists such as
177
177
  allowed themes/densities replace the earlier list rather than accumulating.
178
+ `wiring.entries` similarly names workspace-relative application entry modules;
179
+ later profile layers replace the entry list. The `ui-wiring` ESLint rule checks
180
+ application-scoped helper calls and registration imports once at each entry
181
+ that reaches a rendering module, while a composition entry with
182
+ `wiring.scope: "module"` keeps a local obligation. See the
183
+ [`ui-wiring` rule](../../eslint-plugin/docs/rules/ui-wiring.md).
178
184
  Every resolved field retains its source file. The shipped
179
185
  `application-ui-profile.mjs` API implements discovery, merge, loading, and
180
186
  validation and reports actionable originating file + JSON-path diagnostics for stale
@@ -132,6 +132,12 @@ an upstream component or recipe request.
132
132
 
133
133
  ## Problem-to-component matrix
134
134
 
135
+ For a read-only or click-to-edit field preview, use `FieldLabel` from
136
+ `@kerfjs/ui/text` above the preview instead of a `ListHeader`. It matches the
137
+ themed Web Awesome form-control label. Put it in `ListInsetControl sides="trl"`
138
+ beside a `ContentItem` value, and connect its id to the preview group's
139
+ `aria-labelledby`; see [Field labels](./layout.md#text).
140
+
135
141
  | Interface need | Use when | Do not use when; nearest alternative | Required wiring | Application owns | Import | Recipe |
136
142
  | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
137
143
  | 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) |
@@ -265,7 +271,9 @@ its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
265
271
  Set `pinned` on the first `AppTab` when that tab must remain visible as peers
266
272
  scroll. It stays inside the tablist and keyboard order. Set the public
267
273
  `--kui-app-tab-pinned-background` token if the surrounding surface differs from
268
- the default; the pinned tab must cover peers as they scroll beneath it. Call
274
+ the default; the pinned tab must cover peers as they scroll beneath it. Its
275
+ background also covers the strip's inline padding so scrolled labels cannot
276
+ paint beside the pinned edge, including in right-to-left strips. Call
269
277
  `wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
270
278
  scroll correction.
271
279
 
@@ -351,6 +359,10 @@ their compact widths when collapsed.
351
359
 
352
360
  ## Toolbar composition
353
361
 
362
+ A `FloatingToolbar` accepts `ToolbarControlGroup` directly as its children in
363
+ both floating and inline placement. Name the toolbar with `label`; group its
364
+ controls as you would in a `Toolbar` zone.
365
+
354
366
  A `Toolbar` has three zones — `leading`, `center`, and `trailing`. In almost
355
367
  every case the only things that go **directly** in a zone are `ToolbarText`
356
368
  (identity/title text) and `ToolbarControlGroup` (any control or cluster of
@@ -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>
package/docs/layout.md CHANGED
@@ -331,6 +331,27 @@ CSS strings.
331
331
 
332
332
  ## Text
333
333
 
334
+ `FieldLabel` from `@kerfjs/ui/text` gives a read-only or click-to-edit field
335
+ preview the same visible heading as a themed Web Awesome form control. Put it
336
+ in `ListInsetControl sides="trl"` above the preview so its text starts at the
337
+ same 17px content column as a `ContentItem` value. Unlike a native `<label>`, it
338
+ is a plain element: give it an `id` and name the preview's group with
339
+ `aria-labelledby`. Keep the app's preview/edit switching and hint copy beside
340
+ the preview; use the live control's own `label` when editing.
341
+
342
+ ```tsx
343
+ import { ContentItem } from "@kerfjs/ui/content-item";
344
+ import { ListInsetControl } from "@kerfjs/ui/list-inset-control";
345
+ import { FieldLabel } from "@kerfjs/ui/text";
346
+
347
+ <div role="group" aria-labelledby="needed-by-label">
348
+ <ListInsetControl sides="trl">
349
+ <FieldLabel id="needed-by-label">Needed by</FieldLabel>
350
+ </ListInsetControl>
351
+ <ContentItem>October 16, 2026</ContentItem>
352
+ </div>;
353
+ ```
354
+
334
355
  Use `Text` from `@kerfjs/ui/text` for ordinary semantic headings, paragraphs,
335
356
  and inline secondary text. It renders a native `p` by default; set `variant` to
336
357
  `h1`–`h6` when the document outline calls for a heading, or `span` for inline
@@ -125,7 +125,11 @@ which element a selector styles.
125
125
  arguments) names a public class of a cataloged component (`.kui-toolbar`,
126
126
  `.kui-list-item__label`, `.acme-meter`), its `[data-component="…"]` root, or
127
127
  a Web Awesome tag the package themes (`wa-button`, `wa-select`), and the rule
128
- sets at least one ordinary property. A `::part()` subject is left to
128
+ sets at least one ordinary property. It also catches a raw descendant of
129
+ cataloged component anatomy, such as `.kui-app-tab__select > svg`, even when
130
+ the component class is not the final compound. A component root used only as
131
+ context (`.kui-toolbar > .my-widget`) and app-named descendants remain
132
+ available for app-owned content. A `::part()` subject is left to
129
133
  `KUI-L011`; a rule that only sets custom properties is configuration, judged
130
134
  by the token rules. Forcing a component's dimensions is one such restyle
131
135
  and reports here once: `.kui-pane { width: 300px }`.
@@ -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/llms.txt CHANGED
@@ -157,6 +157,10 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
157
157
  `ToolbarText size="xsmall"` suits a dense rail heading. The
158
158
  `TokenSearchField` editor shrinks in narrow groups; its minimum width can be
159
159
  set with `--kui-token-search-editor-min-width`.
160
+ - **Label read-only field previews.** Use `FieldLabel` from `@kerfjs/ui/text`
161
+ in `ListInsetControl sides="trl"` above a `ContentItem` value. Connect its
162
+ id to the preview group's `aria-labelledby`; use the live control's own
163
+ `label` while editing.
160
164
  - **Use Toolbar zone options.** `responsive="trailing-priority"` moves an
161
165
  expanded trailing control to a full second row at `responsiveAt`; the leading
162
166
  title remains above it. Public leading-min-width, trailing-gap/justify, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.66",
3
+ "version": "5.0.0-beta.68",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/text.tsx"],"names":[],"mappings":";;;AA2DO,SAAS,IAAA,CAAK;AAAA,EACnB,SAAS,OAAA,GAAU,GAAA;AAAA,EACnB,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,IAAA,GAAO,SAAA;AAAA,EACP,MAAA,GAAS,aAAA;AAAA,EACT,KAAA,GAAQ,KAAA;AAAA,EACR,UAAA,GAAa,SAAA;AAAA,EACb,IAAA,GAAO,QAAA;AAAA,EACP,QAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAO,UAAA,GAAa,EAAA;AAAA,EACpB,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAc;AACZ,EAAA,IACE,QAAA,KAAa,MAAA,KACZ,CAAC,MAAA,CAAO,aAAA,CAAc,QAAQ,CAAA,IAC7B,QAAA,GAAW,CAAA,IACX,IAAA,KAAS,QAAA,IACT,IAAA,KAAS,UAAA,CAAA;AAEX,IAAA,MAAM,IAAI,UAAA;AAAA,MACR;AAAA,KACF;AACF,EAAA,MAAM,OAAA,GAAU,CAAC,UAAA,EAAY,UAAA,EAAY,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC5E,EAAA,uBACE,GAAA;AAAA,IAAC,OAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,KAAA,EAAO,OAAA;AAAA,MACP,gBAAA,EAAe,MAAA;AAAA,MACf,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,IAAA;AAAA,MACX,aAAA,EAAa,MAAA;AAAA,MACb,YAAA,EAAY,QAAQ,MAAA,GAAS,MAAA;AAAA,MAC7B,kBAAA,EAAkB,UAAA,KAAe,OAAA,GAAU,OAAA,GAAU,MAAA;AAAA,MACrD,WAAA,EAAW,IAAA,KAAS,QAAA,GAAW,MAAA,GAAY,IAAA;AAAA,MAC3C,gBAAA,EAAgB,QAAA,KAAa,MAAA,GAAY,MAAA,GAAY,OAAO,QAAQ,CAAA;AAAA,MAEnE,QAAA,EAAA,QAAA,KAAa,SACZ,QAAA,mBAEA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAM,iBAAA;AAAA,UACN,KAAA,EAAO,yBAAyB,QAAQ,CAAA,CAAA;AAAA,UAEvC;AAAA;AAAA;AACH;AAAA,GAEJ;AAEJ","file":"chunk-CQHN45XR.js","sourcesContent":["import type { KerfBaseAttrs } from 'kerfjs/jsx-runtime';\n\nimport type { KerfUiContent } from './semantic-content.js';\n\nexport type TextVariant =\n 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';\nexport type TextTone = 'default' | 'quiet' | 'danger';\nexport type TextSize = 'compact' | 'default' | 'large' | 'xlarge';\nexport type TextFont = 'default' | 'monospace';\nexport type TextBorder = 'transparent' | 'none';\nexport type TextLineHeight = 'default' | 'tight';\nexport type TextWrap = 'normal' | 'anywhere' | 'nowrap' | 'truncate';\n\nexport type TextContent =\n KerfUiContent | string | number | readonly TextContent[];\n\ntype TextCommonProps = Omit<\n KerfBaseAttrs,\n 'children' | 'class' | 'className'\n> & {\n /** Native heading, paragraph, or inline span element to render. Defaults to `p`. */\n variant?: TextVariant;\n /** Semantic foreground treatment. Defaults to the inherited foreground. */\n tone?: TextTone;\n /** Text sizing independent of the native semantic element. */\n size?: TextSize;\n /** Font family independent of the native semantic element. */\n font?: TextFont;\n /** Transparent alignment border or no border when embedded in owner chrome. */\n border?: TextBorder;\n /** Remove the block variant's item padding and border in compact content. */\n flush?: boolean;\n /** Use compact leading for short dialog or metadata copy. */\n lineHeight?: TextLineHeight;\n children: TextContent;\n class?: string;\n className?: string;\n};\n\nexport type TextProps = TextCommonProps &\n (\n | {\n /** Wrap normally or break long unbroken strings. Defaults to normal. */\n wrap?: 'normal' | 'anywhere';\n /** Cap wrapped text to this positive number of lines. */\n maxLines?: number;\n }\n | {\n /** Keep one line, with or without an ellipsis. */\n wrap: 'nowrap' | 'truncate';\n maxLines?: never;\n }\n );\n\n/**\n * Semantic heading, paragraph, or inline text. Block variants use the standard\n * content-item padding; `span` adds no box geometry.\n * All ordinary native heading/paragraph attributes pass through to the element.\n */\nexport function Text({\n variant: Variant = 'p',\n tone = 'default',\n size = 'default',\n font = 'default',\n border = 'transparent',\n flush = false,\n lineHeight = 'default',\n wrap = 'normal',\n maxLines,\n children,\n class: classValue = '',\n className = '',\n ...attributes\n}: TextProps) {\n if (\n maxLines !== undefined &&\n (!Number.isSafeInteger(maxLines) ||\n maxLines < 1 ||\n wrap === 'nowrap' ||\n wrap === 'truncate')\n )\n throw new RangeError(\n 'Text maxLines requires a positive integer and wrapping',\n );\n const classes = ['kui-text', classValue, className].filter(Boolean).join(' ');\n return (\n <Variant\n {...attributes}\n class={classes}\n data-component=\"text\"\n data-tone={tone}\n data-size={size}\n data-font={font}\n data-border={border}\n data-flush={flush ? 'true' : undefined}\n data-line-height={lineHeight === 'tight' ? 'tight' : undefined}\n data-wrap={wrap === 'normal' ? undefined : wrap}\n data-max-lines={maxLines === undefined ? undefined : String(maxLines)}\n >\n {maxLines === undefined ? (\n children\n ) : (\n <span\n class=\"kui-text__clamp\"\n style={`--_kui-text-max-lines:${maxLines}`}\n >\n {children}\n </span>\n )}\n </Variant>\n );\n}\n"]}