@kerfjs/ui 5.0.0-beta.34 → 5.0.0-beta.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,15 @@ Accessible, composable UI primitives for [kerf](https://github.com/brianwestphal
4
4
 
5
5
  Start interface work with the [need-first component selection matrix](./docs/component-selection.md). It distinguishes direct reuse, composition, thin application adapters, and genuinely custom semantics, with imports, required wiring, application ownership, alternatives, and recipes for every public primitive.
6
6
 
7
+ When adding a first-party public component, follow the
8
+ [component integration workflow](./docs/component-integration.md). The catalog
9
+ entry drives one validator across package, build, barrel, CSS, demo, route, and
10
+ AI signature projections, with a non-mutating dry-run report.
11
+
12
+ Browser verification always builds current source: both `npm run test:e2e`
13
+ and direct focused Playwright commands run the catalog/conformance preflights
14
+ and production build before preview. See the [UX catalog contract](./docs/ux-demo.md).
15
+
7
16
  When authoring a component gallery, follow the single
8
17
  [Catalog demo authoring contract](./docs/catalog.md#catalog-demo-authoring-contract)
9
18
  for focused-versus-composition modes, helper nesting, specimen selection,
@@ -132,7 +141,7 @@ defaults to left/full alignment, the `xs` gap, and `wrap={false}`; List keeps
132
141
  its existing full/top alignment and zero-gap defaults.
133
142
 
134
143
  The same property-specific boundary applies beyond spacing: use `flex()` (or a
135
- finite keyword) for `List.flex`; length builders and intrinsic-size keywords for
144
+ finite keyword) for `Row.flex` and `List.flex`; length builders and intrinsic-size keywords for
136
145
  `Skeleton.width`/`height`; length builders for `Skeleton.radius`; and
137
146
  `uiColor()` or `colorVar()` for `SelectChoice.color`. These grammars are not
138
147
  interchangeable. `ListItem` and `ListActionRow` deliberately expose no raw
@@ -1424,7 +1424,7 @@
1424
1424
  "required": false
1425
1425
  },
1426
1426
  {
1427
- "id": "typed gap overrides",
1427
+ "id": "typed gap and flex choices",
1428
1428
  "owner": "application",
1429
1429
  "required": false
1430
1430
  }
@@ -1438,7 +1438,7 @@
1438
1438
  "border": "none",
1439
1439
  "padding": "none",
1440
1440
  "notes": [
1441
- "Row adds no margin, border, or padding; it owns only flex alignment, wrapping, and the gap between children."
1441
+ "Row adds no margin, border, or padding; it owns only flex participation, alignment, wrapping, and the gap between children."
1442
1442
  ]
1443
1443
  }
1444
1444
  },
@@ -1463,6 +1463,16 @@
1463
1463
  "gap={space(\"m\")}",
1464
1464
  "gap={calc(plus(rem(0.25), pct(10)))}"
1465
1465
  ]
1466
+ },
1467
+ {
1468
+ "path": "flex",
1469
+ "grammar": "flex",
1470
+ "helpers": ["flex"],
1471
+ "shorthands": ["none", "auto", "initial"],
1472
+ "canonicalShorthands": ["none", "auto", "initial"],
1473
+ "exceptionalShorthands": [],
1474
+ "rawPolicy": "forbid",
1475
+ "examples": ["flex", "flex=\"none\"", "flex={flex(1, 1, rem(20))}"]
1466
1476
  }
1467
1477
  ],
1468
1478
  "boundaries": {
@@ -1642,7 +1642,7 @@
1642
1642
  "category": "Structure",
1643
1643
  "kind": "component",
1644
1644
  "source": "kerf",
1645
- "description": "A horizontal flex row with physical-axis alignment, a typed xs gap by default, and opt-in wrapping.",
1645
+ "description": "A horizontal flex row with physical-axis alignment, a typed xs gap by default, flex participation, and opt-in wrapping.",
1646
1646
  "uses": [],
1647
1647
  "purpose": "Arrange related content horizontally with consistent spacing and explicit left/center/right/full plus top/middle/bottom/full alignment instead of application-owned flex wrappers.",
1648
1648
  "publicExports": ["Row"],
@@ -1657,16 +1657,27 @@
1657
1657
  "exceptionalShorthands": ["s", "xl"],
1658
1658
  "rawPolicy": "forbid",
1659
1659
  "examples": ["gap=\"xs\"", "gap={space(\"m\")}", "gap={calc(plus(rem(0.25), pct(10)))}"]
1660
+ },
1661
+ {
1662
+ "path": "flex",
1663
+ "grammar": "flex",
1664
+ "helpers": ["flex"],
1665
+ "shorthands": ["none", "auto", "initial"],
1666
+ "canonicalShorthands": ["none", "auto", "initial"],
1667
+ "exceptionalShorthands": [],
1668
+ "rawPolicy": "forbid",
1669
+ "examples": ["flex", "flex=\"none\"", "flex={flex(1, 1, rem(20))}"]
1660
1670
  }
1661
1671
  ],
1662
1672
  "useWhen": [
1663
1673
  "Arrange a group of sibling components horizontally.",
1664
1674
  "Choose physical horizontal distribution and vertical alignment without writing one-off flex CSS.",
1675
+ "Use a finite flex keyword or the flex() builder when Row participates in a flexible parent layout.",
1665
1676
  "Allow a bounded group to wrap while retaining the same typed gap and alignment contract."
1666
1677
  ],
1667
1678
  "avoidWhen": [
1668
1679
  "Do not use Row when content belongs in a semantic list or when a named-zone component such as Toolbar owns the composition.",
1669
- "Do not pass raw CSS strings to gap or use s/xl spacing steps without a deliberate exception.",
1680
+ "Do not pass raw CSS strings to gap or flex, mix their distinct grammars, or use s/xl spacing steps without a deliberate exception.",
1670
1681
  "Do not use alignment to replace semantic grouping or responsive relocation."
1671
1682
  ],
1672
1683
  "alternatives": [
@@ -1684,13 +1695,14 @@
1684
1695
  "appOwns": [
1685
1696
  "child semantics and content",
1686
1697
  "alignment and wrapping policy",
1687
- "typed gap overrides"
1698
+ "typed gap and flex choices"
1688
1699
  ],
1689
1700
  "variants": [
1690
1701
  "left/center/right/full horizontal alignment with l/c/r/f and flex aliases",
1691
1702
  "top/middle/bottom/full vertical alignment with t/m/c/b/f and flex aliases",
1692
1703
  "named none/2xs/xs/s/m/l/xl token gap",
1693
1704
  "typed CssLength gap",
1705
+ "boolean, finite-keyword, or typed CssFlex flex",
1694
1706
  "nowrap or wrapping"
1695
1707
  ],
1696
1708
  "accessibility": [
@@ -1702,7 +1714,7 @@
1702
1714
  "border": "none",
1703
1715
  "padding": "none",
1704
1716
  "notes": [
1705
- "Row adds no margin, border, or padding; it owns only flex alignment, wrapping, and the gap between children."
1717
+ "Row adds no margin, border, or padding; it owns only flex participation, alignment, wrapping, and the gap between children."
1706
1718
  ]
1707
1719
  },
1708
1720
  "delivery": {
@@ -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.34` and `kerfjs@5.0.0-beta.34`. 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.35` and `kerfjs@5.0.0-beta.35`. 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
 
@@ -1940,3 +1940,81 @@ declare function delegateActions<E extends Element = Element>(root: HTMLElement,
1940
1940
 
1941
1941
  export { type ActionHandler, type DelegateActionsOptions, action, delegateActions };
1942
1942
  ```
1943
+
1944
+ ## `@kerfjs/ui/lucide-icon`
1945
+
1946
+ ```ts
1947
+ import * as kerfjs from 'kerfjs';
1948
+ import { IconNode } from 'lucide';
1949
+
1950
+ type LucideNode = IconNode;
1951
+ interface LucideIconProps {
1952
+ icon: LucideNode;
1953
+ name: string;
1954
+ className?: string;
1955
+ label?: string;
1956
+ }
1957
+ /** Render a Lucide-compatible icon node without copying icon SVG strings. */
1958
+ declare function LucideIcon({ icon, name, className, label }: LucideIconProps): kerfjs.SafeHtml;
1959
+
1960
+ export { LucideIcon, type LucideIconProps, type LucideNode };
1961
+ ```
1962
+
1963
+ ## `@kerfjs/ui/surface-scaffold`
1964
+
1965
+ ```ts
1966
+ import * as kerfjs from 'kerfjs';
1967
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1968
+
1969
+ type DialogSurfaceSize = 'small' | 'medium' | 'large';
1970
+ type DialogSurfacePresentation = 'modal' | 'side-sheet' | 'fullscreen';
1971
+ type SurfaceInset = 'none' | 'compact' | 'comfortable';
1972
+ interface DialogSurfaceProps {
1973
+ children: KerfUiContent;
1974
+ size?: DialogSurfaceSize;
1975
+ presentation?: DialogSurfacePresentation;
1976
+ bodyInset?: SurfaceInset;
1977
+ footerInset?: SurfaceInset;
1978
+ className?: string;
1979
+ }
1980
+ /** Configure recurring Web Awesome dialog geometry without consumer ::part() CSS. */
1981
+ declare function DialogSurface({ children, size, presentation, bodyInset, footerInset, className, }: DialogSurfaceProps): kerfjs.SafeHtml;
1982
+ type PopupSurfaceInset = 'standard' | 'compact' | 'list-zero';
1983
+ interface PopupSurfaceProps {
1984
+ children: KerfUiContent;
1985
+ inset?: PopupSurfaceInset;
1986
+ className?: string;
1987
+ }
1988
+ /** Configure recurring Web Awesome dropdown-menu geometry without consumer ::part() CSS. */
1989
+ declare function PopupSurface({ children, inset, className, }: PopupSurfaceProps): kerfjs.SafeHtml;
1990
+
1991
+ export { DialogSurface, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceSize, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset };
1992
+ ```
1993
+
1994
+ ## `@kerfjs/ui/row`
1995
+
1996
+ ```ts
1997
+ import * as kerfjs from 'kerfjs';
1998
+ import { UiSpaceName, CssLength, CssFlexKeyword, CssFlex } from './css-values.js';
1999
+ import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-CF8NLj7i.js';
2000
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2001
+
2002
+ interface RowProps {
2003
+ children?: KerfUiContent;
2004
+ /** Horizontal distribution. Defaults to left. */
2005
+ hAlign?: HorizontalAlignment;
2006
+ /** Vertical alignment and wrapped-line distribution. Defaults to full. */
2007
+ vAlign?: VerticalAlignment;
2008
+ /** A named UI spacing token or typed CSS length. Defaults to xs. */
2009
+ gap?: UiSpaceName | CssLength;
2010
+ /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
2011
+ flex?: boolean | CssFlexKeyword | CssFlex;
2012
+ /** Allow children to wrap onto additional lines. */
2013
+ wrap?: boolean;
2014
+ className?: string;
2015
+ }
2016
+ /** A horizontal flex row with explicit physical-axis alignment and spacing. */
2017
+ declare function Row({ children, hAlign, vAlign, gap, flex, wrap, className, }: RowProps): kerfjs.SafeHtml;
2018
+
2019
+ export { CssFlex, CssFlexKeyword, CssLength, HorizontalAlignment, Row, type RowProps, UiSpaceName, VerticalAlignment };
2020
+ ```
@@ -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.34` 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.35` 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';
@@ -16,10 +16,16 @@ function Row({
16
16
  hAlign = "left",
17
17
  vAlign = "full",
18
18
  gap = "xs",
19
+ flex = false,
19
20
  wrap = false,
20
21
  className = ""
21
22
  }) {
22
23
  const gapValue = spaceNames.includes(gap) ? space(gap) : gap;
24
+ const flexValue = flex === true ? "1 1 auto" : flex || void 0;
25
+ const style = [
26
+ `--_kui-row-gap:${gapValue}`,
27
+ flexValue ? `--_kui-row-flex:${flexValue}` : ""
28
+ ].filter(Boolean).join(";");
23
29
  return /* @__PURE__ */ jsx(
24
30
  "div",
25
31
  {
@@ -27,8 +33,9 @@ function Row({
27
33
  "data-component": "row",
28
34
  "data-h-align": horizontalAlignment(hAlign),
29
35
  "data-v-align": verticalAlignment(vAlign),
36
+ "data-flex": String(Boolean(flex)),
30
37
  "data-wrap": String(wrap),
31
- style: `--_kui-row-gap:${gapValue}`,
38
+ style,
32
39
  children
33
40
  }
34
41
  );
@@ -36,4 +43,4 @@ function Row({
36
43
 
37
44
  export { Row };
38
45
 
39
- //# sourceMappingURL=chunk-OKRCKIVO.js.map
46
+ //# sourceMappingURL=chunk-QHCBZTDK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/row.tsx"],"names":[],"mappings":";;;;AAeA,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAkBO,SAAS,GAAA,CAAI;AAAA,EAClB,QAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,MAAA,GAAS,MAAA;AAAA,EACT,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,SAAA,GAAY;AACd,CAAA,EAAa;AACX,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AACJ,EAAA,MAAM,SAAA,GAAY,IAAA,KAAS,IAAA,GAAO,UAAA,GAAa,IAAA,IAAQ,MAAA;AACvD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,kBAAkB,QAAQ,CAAA,CAAA;AAAA,IAC1B,SAAA,GAAY,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,GAAK;AAAA,GAC/C,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEX,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACnC,gBAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAc,oBAAoB,MAAM,CAAA;AAAA,MACxC,cAAA,EAAc,kBAAkB,MAAM,CAAA;AAAA,MACtC,WAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MAC/B,WAAA,EAAW,OAAO,IAAI,CAAA;AAAA,MACtB,KAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-QHCBZTDK.js","sourcesContent":["import {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from './css-values.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type VerticalAlignment,\n verticalAlignment,\n} from './flex-alignment.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface RowProps {\n children?: KerfUiContent;\n /** Horizontal distribution. Defaults to left. */\n hAlign?: HorizontalAlignment;\n /** Vertical alignment and wrapped-line distribution. Defaults to full. */\n vAlign?: VerticalAlignment;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n /** Allow children to wrap onto additional lines. */\n wrap?: boolean;\n className?: string;\n}\n\n/** A horizontal flex row with explicit physical-axis alignment and spacing. */\nexport function Row({\n children,\n hAlign = 'left',\n vAlign = 'full',\n gap = 'xs',\n flex = false,\n wrap = false,\n className = '',\n}: RowProps) {\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n `--_kui-row-gap:${gapValue}`,\n flexValue ? `--_kui-row-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n class={`kui-row ${className}`.trim()}\n data-component=\"row\"\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-flex={String(Boolean(flex))}\n data-wrap={String(wrap)}\n style={style}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from './css-values.js';\nexport type {\n HorizontalAlignment,\n VerticalAlignment,\n} from './flex-alignment.js';\n"]}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export { DialogSurface, PopupSurface } from './chunk-WSXUPSID.js';
10
10
  export { TabBar } from './chunk-ZPWZIVI6.js';
11
11
  export { ToolbarText } from './chunk-V6GQKOX2.js';
12
12
  export { ResizableRegion, clampRegionSize, resizeRegionFromPointer } from './chunk-CAB6NQ2T.js';
13
- export { Row } from './chunk-OKRCKIVO.js';
13
+ export { Row } from './chunk-QHCBZTDK.js';
14
14
  export { SegmentedControl } from './chunk-E34TE2ZI.js';
15
15
  export { FloatingToolbar } from './chunk-DYBPI5O2.js';
16
16
  export { ListActionRow } from './chunk-DAO2VFTT.js';
package/dist/row.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as kerfjs from 'kerfjs';
2
- import { UiSpaceName, CssLength } from './css-values.js';
2
+ import { UiSpaceName, CssLength, CssFlexKeyword, CssFlex } from './css-values.js';
3
3
  import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-CF8NLj7i.js';
4
4
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
5
5
 
@@ -11,11 +11,13 @@ interface RowProps {
11
11
  vAlign?: VerticalAlignment;
12
12
  /** A named UI spacing token or typed CSS length. Defaults to xs. */
13
13
  gap?: UiSpaceName | CssLength;
14
+ /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
15
+ flex?: boolean | CssFlexKeyword | CssFlex;
14
16
  /** Allow children to wrap onto additional lines. */
15
17
  wrap?: boolean;
16
18
  className?: string;
17
19
  }
18
20
  /** A horizontal flex row with explicit physical-axis alignment and spacing. */
19
- declare function Row({ children, hAlign, vAlign, gap, wrap, className, }: RowProps): kerfjs.SafeHtml;
21
+ declare function Row({ children, hAlign, vAlign, gap, flex, wrap, className, }: RowProps): kerfjs.SafeHtml;
20
22
 
21
- export { CssLength, HorizontalAlignment, Row, type RowProps, UiSpaceName, VerticalAlignment };
23
+ export { CssFlex, CssFlexKeyword, CssLength, HorizontalAlignment, Row, type RowProps, UiSpaceName, VerticalAlignment };
package/dist/row.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Row } from './chunk-OKRCKIVO.js';
1
+ export { Row } from './chunk-QHCBZTDK.js';
2
2
 
3
3
 
4
4
 
@@ -5,6 +5,7 @@
5
5
  min-width: 0;
6
6
  min-height: 0;
7
7
  box-sizing: border-box;
8
+ flex: var(--_kui-row-flex, initial);
8
9
  flex-direction: row;
9
10
  flex-wrap: nowrap;
10
11
  align-items: stretch;
@@ -269,7 +269,8 @@ Logical-edge separator lines are independently opt-in and default off.
269
269
  geometry classes.
270
270
  `List` is the corresponding layout-only vertical stack: its children stretch by
271
271
  default, while `gap`, `flex`, and `scrollable` opt into typed spacing,
272
- flex growth, and vertical scroll ownership. `dividerSides` accepts canonical
272
+ flex growth, and vertical scroll ownership. `Row.flex` uses the same typed flex
273
+ participation contract on the horizontal counterpart. `dividerSides` accepts canonical
273
274
  physical top/right/bottom/left combinations such as `tr` and `trbl`; the stack
274
275
  adds no list semantics, margin, or padding of its own. Use a direct finite
275
276
  spacing shorthand such as `gap="2xs"`, or pass a `CssLength` created by the
@@ -280,7 +281,7 @@ length. Raw CSS strings and source-only `remify(4px)` are not valid runtime
280
281
  props. The `CssLength` name intentionally includes percentages for UI dimension
281
282
  props. Its opaque string brand improves authoring correctness; it is not a CSS
282
283
  sanitizer or security boundary.
283
- `List.flex` likewise accepts its boolean default, finite keywords, or `CssFlex`
284
+ `Row.flex` and `List.flex` likewise accept their boolean default, finite keywords, or `CssFlex`
284
285
  from `flex(grow, shrink, basis)`. `Skeleton` width/height use `CssSize`, radius
285
286
  uses `CssLength`, and `SelectChoice.color` uses `CssColor` from `uiColor()` or
286
287
  `colorVar()`. Never exchange these property grammars. List rows expose
@@ -0,0 +1,63 @@
1
+ # Public component integration workflow
2
+
3
+ The canonical `ai/component-catalog.json` is also the integration manifest for
4
+ first-party public components. Every entry with `source: "kerf"` and
5
+ `kind: "component"` projects into the package, build, root API, catalog demo,
6
+ and AI authoring surfaces. This keeps a new List-like primitive from reaching a
7
+ late build or release gate before an omitted surface is discovered.
8
+
9
+ Author the implementation, CSS, catalog facts, demo, and behavior tests first.
10
+ Then run:
11
+
12
+ ```bash
13
+ npm run component-integrations:dry-run
14
+ ```
15
+
16
+ The dry run reports every mismatched projection together, using diff-like
17
+ `expected` and `actual` lines without changing files or failing the command.
18
+ Resolve the complete report, build the package, regenerate signatures with
19
+ `npm run ai:signatures:sync`, and refresh reviewed compatibility digests with
20
+ `npm run ai:compatibility:sync`. `npm run check:component-integrations` runs the
21
+ same validator in failing CI mode, and the package `check` gate includes it.
22
+
23
+ For each catalog component, the validator checks:
24
+
25
+ - the source module and stylesheet;
26
+ - the tsup entry and typed package subpath, including the browser-conditioned
27
+ entry when the catalog declares `browserImport`;
28
+ - the CSS package export;
29
+ - root-barrel exposure for browser-conditioned components;
30
+ - the stable catalog route, demo module, and exhaustive demo registry; and
31
+ - inclusion of the public subpath in the generated AI signature reference.
32
+
33
+ The existing catalog sync and compatibility gates remain responsible for the
34
+ typed catalog projections, catalog count/order, and artifact digests. Public
35
+ API signatures derive component subpaths from the catalog, so a newly cataloged
36
+ component can no longer be silently omitted from that corpus.
37
+
38
+ Non-component helpers, wiring modules, compositions, recipes, and development
39
+ tools remain explicit package surfaces. They do not masquerade as visual
40
+ components merely to enter this workflow.
41
+
42
+ ## Change-local verification and bundle review
43
+
44
+ Run `npm run check:change` after changing a public component. It synchronizes
45
+ both catalog projections, declarations, AI signatures, and compatibility
46
+ digests; names every checked-in projection changed by that synchronization;
47
+ runs the catalog/component contracts, unit coverage, consumer bundle tests,
48
+ source and packed type contracts; builds the production catalog; and reports
49
+ the exact gzip byte delta from the last reviewed baseline. The broader
50
+ `npm run check` remains the release gate.
51
+
52
+ Intentional bundle changes use the same workflow with an explicit review
53
+ reason:
54
+
55
+ ```bash
56
+ npm run check:change -- --update-bundle-budget \
57
+ --reason "Added the reviewed List detail states and keyboard behavior"
58
+ ```
59
+
60
+ That mode writes `demo-bundle-budget.json`, records the previous and new exact
61
+ measurements and budgets with the reason and timestamp, and rounds the total
62
+ budget to the next 100 bytes. Do not edit the budget or add a history comment
63
+ by hand.
@@ -66,7 +66,7 @@ contract lands before the 5.0 stable release so misspelled tokens and incomplete
66
66
  expressions fail during typechecking rather than in the browser.
67
67
 
68
68
  Other CSS-valued props use distinct contracts: `flex()` creates `CssFlex` for
69
- `List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
69
+ `Row.flex` and `List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
70
70
  keywords for width/height); and `uiColor()`/`colorVar()` create `CssColor` for
71
71
  `SelectChoice.color`. Do not substitute one brand for another. Row components
72
72
  have no declaration-string `style` prop; use `className`, documented public
package/docs/layout.md CHANGED
@@ -136,6 +136,13 @@ items, while applying the requested distribution to `align-content` when a
136
136
  Row wraps. Prefer the descriptive values in application code; the short and
137
137
  CSS-shaped aliases are provided for compact or migrated call sites.
138
138
 
139
+ `Row.flex` and `List.flex` share one typed participation contract. Omit the prop
140
+ for the CSS initial value, pass `true` for `1 1 auto`, select the finite
141
+ `"none"`, `"auto"`, or `"initial"` keyword, or use
142
+ `flex(grow, shrink, basis)` from `@kerfjs/ui/css-values` for a complete branded
143
+ shorthand. A `CssLength` is not a flex shorthand, and arbitrary strings are
144
+ rejected by the type contract.
145
+
139
146
  `--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
140
147
  canonical rhythm** — reach for them only as a deliberate exception, never as a
141
148
  default step. Prefer the five canonical tokens so spacing stays legible and
package/docs/ux-demo.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
4
4
 
5
+ Every Playwright entry point builds current source before the preview server
6
+ starts. `npm run test:e2e -- tests/browser/row.spec.ts` and a direct focused
7
+ `npx playwright test tests/browser/row.spec.ts` both run catalog generation and
8
+ demo-conformance preflights, build the package and production catalog, then
9
+ launch Chromium, Firefox, and WebKit. The build emits
10
+ `source-freshness.json`; browser coverage compares it with an independent hash
11
+ of the current `src/` and `ux-demo/` inputs, so a stale `dist-demo` cannot pass
12
+ after a route or component edit. `npm run demo:serve` remains a preview-only
13
+ command for an already-built catalog.
14
+
5
15
  Seven lazy production-composition routes live under the `Recipes` category.
6
16
  Each `?component=recipe-*` route uses public package primitives, real wiring,
7
17
  deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.34",
3
+ "version": "5.0.0-beta.35",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -329,11 +329,15 @@
329
329
  "dev": "npm run build && vite --config ux-demo/vite.config.ts",
330
330
  "demo:build": "npm run build && vite build --config ux-demo/vite.config.ts && node scripts/check-demo-bundle.mjs",
331
331
  "demo:serve": "vite preview --config ux-demo/vite.config.ts",
332
+ "test:e2e:serve": "npm run check:catalog && npm run check:demo-conformance && npm run demo:build && npm run demo:serve",
332
333
  "format": "prettier --write . --ignore-unknown",
333
334
  "format:css": "prettier --write \"src/**/*.css\" \"ux-demo/**/*.css\"",
334
335
  "check:format": "prettier --check . --ignore-unknown",
335
336
  "catalog:sync": "node scripts/sync-component-catalog.mjs && node scripts/sync-component-catalog-v2.mjs",
336
337
  "catalog:v2:sync": "node scripts/sync-component-catalog-v2.mjs",
338
+ "check:component-integrations": "node scripts/check-component-integrations.mjs",
339
+ "component-integrations:dry-run": "node scripts/check-component-integrations.mjs --dry-run",
340
+ "check:change": "node scripts/check-ui-change.mjs",
337
341
  "check:catalog": "node scripts/sync-component-catalog.mjs --check && node scripts/sync-component-catalog-v2.mjs --check && node scripts/check-component-catalog.mjs",
338
342
  "check:profile": "node scripts/check-application-ui-profile.mjs",
339
343
  "check:demo-conformance": "node scripts/check-catalog-demo-conformance.mjs",
@@ -341,6 +345,8 @@
341
345
  "check:ai-regressions": "node scripts/check-ai-regressions.mjs && node scripts/audit-ai-regression-results.mjs && node scripts/audit-ai-regression-results-v3.mjs",
342
346
  "check:ai-signatures": "node scripts/sync-ai-public-signatures.mjs --check",
343
347
  "ai:signatures:sync": "npm run build && node scripts/sync-ai-public-signatures.mjs",
348
+ "check:ai-compatibility": "node scripts/sync-ai-compatibility.mjs",
349
+ "ai:compatibility:sync": "node scripts/sync-ai-compatibility.mjs --write",
344
350
  "ai:regressions:compile": "node scripts/compile-ai-regression-response.mjs",
345
351
  "ai:regressions:prepare": "node scripts/prepare-ai-regression.mjs",
346
352
  "ai:regressions:record": "node scripts/record-ai-regression-run.mjs",
@@ -360,8 +366,8 @@
360
366
  "test:contract-types:source": "node node_modules/typescript7/bin/tsc -p tests/consumer-types/contracts/tsconfig.source.json",
361
367
  "test:contract-types:packed": "node scripts/check-packed-type-contracts.mjs",
362
368
  "test:consumer-types": "npm run test:contract-types:source && npm run build && node node_modules/typescript7/bin/tsc -p tests/consumer-types/webawesome/tsconfig.json && npm run test:contract-types:packed",
363
- "test:e2e": "npm run demo:build && playwright test",
364
- "check": "npm run check:catalog && npm run check:profile && npm run check:demo-conformance && npm run check:recipes && npm run check:guidance && npm run check:ai-regressions && npm run lint && npm run typecheck && npm test && npm run test:consumer-types && npm run check:ai-signatures && npm run demo:build && npm run check:design-templates && npm pack --dry-run",
369
+ "test:e2e": "playwright test",
370
+ "check": "npm run check:catalog && npm run check:component-integrations && npm run check:profile && npm run check:demo-conformance && npm run check:recipes && npm run check:guidance && npm run check:ai-compatibility && npm run check:ai-regressions && npm run lint && npm run typecheck && npm test && npm run test:consumer-types && npm run check:ai-signatures && npm run demo:build && npm run check:design-templates && npm pack --dry-run",
365
371
  "prepublishOnly": "npm run check",
366
372
  "check:design-templates": "node scripts/check-design-templates.mjs",
367
373
  "check:design-templates:drift": "node scripts/check-design-templates-drift.mjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/row.tsx"],"names":[],"mappings":";;;;AASA,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAgBO,SAAS,GAAA,CAAI;AAAA,EAClB,QAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,MAAA,GAAS,MAAA;AAAA,EACT,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,SAAA,GAAY;AACd,CAAA,EAAa;AACX,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AAEJ,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACnC,gBAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAc,oBAAoB,MAAM,CAAA;AAAA,MACxC,cAAA,EAAc,kBAAkB,MAAM,CAAA;AAAA,MACtC,WAAA,EAAW,OAAO,IAAI,CAAA;AAAA,MACtB,KAAA,EAAO,kBAAkB,QAAQ,CAAA,CAAA;AAAA,MAEhC;AAAA;AAAA,GACH;AAEJ","file":"chunk-OKRCKIVO.js","sourcesContent":["import { type CssLength, space, type UiSpaceName } from './css-values.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type VerticalAlignment,\n verticalAlignment,\n} from './flex-alignment.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface RowProps {\n children?: KerfUiContent;\n /** Horizontal distribution. Defaults to left. */\n hAlign?: HorizontalAlignment;\n /** Vertical alignment and wrapped-line distribution. Defaults to full. */\n vAlign?: VerticalAlignment;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow children to wrap onto additional lines. */\n wrap?: boolean;\n className?: string;\n}\n\n/** A horizontal flex row with explicit physical-axis alignment and spacing. */\nexport function Row({\n children,\n hAlign = 'left',\n vAlign = 'full',\n gap = 'xs',\n wrap = false,\n className = '',\n}: RowProps) {\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n\n return (\n <div\n class={`kui-row ${className}`.trim()}\n data-component=\"row\"\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-wrap={String(wrap)}\n style={`--_kui-row-gap:${gapValue}`}\n >\n {children}\n </div>\n );\n}\n\nexport type { CssLength, UiSpaceName } from './css-values.js';\nexport type {\n HorizontalAlignment,\n VerticalAlignment,\n} from './flex-alignment.js';\n"]}