@kerfjs/ui 4.4.1

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 (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/ai/skill.md +38 -0
  4. package/dist/app-tab.d.ts +18 -0
  5. package/dist/app-tab.js +3 -0
  6. package/dist/app-tab.js.map +1 -0
  7. package/dist/browser/app-tab.js +3 -0
  8. package/dist/browser/dialog-header.js +3 -0
  9. package/dist/browser/empty-state.js +4 -0
  10. package/dist/browser/loading-spinner.js +3 -0
  11. package/dist/browser/lucide-icon.js +3 -0
  12. package/dist/browser/menu-header.js +3 -0
  13. package/dist/browser/menu-item.js +3 -0
  14. package/dist/browser/page-header.js +3 -0
  15. package/dist/browser/resizable-region.js +3 -0
  16. package/dist/browser/segmented-control.js +3 -0
  17. package/dist/browser/select.js +4 -0
  18. package/dist/browser/state-banner.js +3 -0
  19. package/dist/browser/tab-bar.js +3 -0
  20. package/dist/browser/toolbar-control-group.js +3 -0
  21. package/dist/browser/toolbar-text.js +3 -0
  22. package/dist/browser/toolbar.js +3 -0
  23. package/dist/browser/value-table.js +3 -0
  24. package/dist/chunk-2PES33HS.js +13 -0
  25. package/dist/chunk-2PES33HS.js.map +1 -0
  26. package/dist/chunk-3O7ZEUW2.js +27 -0
  27. package/dist/chunk-3O7ZEUW2.js.map +1 -0
  28. package/dist/chunk-4HHCYSGT.js +79 -0
  29. package/dist/chunk-4HHCYSGT.js.map +1 -0
  30. package/dist/chunk-55DATV3A.js +10 -0
  31. package/dist/chunk-55DATV3A.js.map +1 -0
  32. package/dist/chunk-723YNJBH.js +17 -0
  33. package/dist/chunk-723YNJBH.js.map +1 -0
  34. package/dist/chunk-DAFFDO33.js +14 -0
  35. package/dist/chunk-DAFFDO33.js.map +1 -0
  36. package/dist/chunk-GY5WH7TO.js +10 -0
  37. package/dist/chunk-GY5WH7TO.js.map +1 -0
  38. package/dist/chunk-MBR3TYNL.js +14 -0
  39. package/dist/chunk-MBR3TYNL.js.map +1 -0
  40. package/dist/chunk-MFKMHK5W.js +10 -0
  41. package/dist/chunk-MFKMHK5W.js.map +1 -0
  42. package/dist/chunk-MQIZAD4A.js +227 -0
  43. package/dist/chunk-MQIZAD4A.js.map +1 -0
  44. package/dist/chunk-NHXX4JI5.js +28 -0
  45. package/dist/chunk-NHXX4JI5.js.map +1 -0
  46. package/dist/chunk-OPCJRCYO.js +19 -0
  47. package/dist/chunk-OPCJRCYO.js.map +1 -0
  48. package/dist/chunk-T5XNMVOI.js +24 -0
  49. package/dist/chunk-T5XNMVOI.js.map +1 -0
  50. package/dist/chunk-TRNBHPNW.js +54 -0
  51. package/dist/chunk-TRNBHPNW.js.map +1 -0
  52. package/dist/chunk-VHW5YNIB.js +17 -0
  53. package/dist/chunk-VHW5YNIB.js.map +1 -0
  54. package/dist/chunk-VKH2JLDZ.js +10 -0
  55. package/dist/chunk-VKH2JLDZ.js.map +1 -0
  56. package/dist/chunk-W2FK7ZNY.js +26 -0
  57. package/dist/chunk-W2FK7ZNY.js.map +1 -0
  58. package/dist/chunk-WVPYVDXY.js +15 -0
  59. package/dist/chunk-WVPYVDXY.js.map +1 -0
  60. package/dist/chunk-WY4CAEPG.js +14 -0
  61. package/dist/chunk-WY4CAEPG.js.map +1 -0
  62. package/dist/dialog-header.d.ts +14 -0
  63. package/dist/dialog-header.js +3 -0
  64. package/dist/dialog-header.js.map +1 -0
  65. package/dist/empty-state.d.ts +13 -0
  66. package/dist/empty-state.js +4 -0
  67. package/dist/empty-state.js.map +1 -0
  68. package/dist/index.d.ts +21 -0
  69. package/dist/index.js +21 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/loading-spinner.d.ts +10 -0
  72. package/dist/loading-spinner.js +3 -0
  73. package/dist/loading-spinner.js.map +1 -0
  74. package/dist/lucide-icon.d.ts +14 -0
  75. package/dist/lucide-icon.js +3 -0
  76. package/dist/lucide-icon.js.map +1 -0
  77. package/dist/menu-header.d.ts +15 -0
  78. package/dist/menu-header.js +3 -0
  79. package/dist/menu-header.js.map +1 -0
  80. package/dist/menu-item.d.ts +22 -0
  81. package/dist/menu-item.js +3 -0
  82. package/dist/menu-item.js.map +1 -0
  83. package/dist/page-header.d.ts +9 -0
  84. package/dist/page-header.js +3 -0
  85. package/dist/page-header.js.map +1 -0
  86. package/dist/resizable-region.d.ts +21 -0
  87. package/dist/resizable-region.js +3 -0
  88. package/dist/resizable-region.js.map +1 -0
  89. package/dist/segmented-control.d.ts +28 -0
  90. package/dist/segmented-control.js +3 -0
  91. package/dist/segmented-control.js.map +1 -0
  92. package/dist/select-register.d.ts +4 -0
  93. package/dist/select-register.js +10 -0
  94. package/dist/select-register.js.map +1 -0
  95. package/dist/select.d.ts +28 -0
  96. package/dist/select.js +4 -0
  97. package/dist/select.js.map +1 -0
  98. package/dist/state-banner.d.ts +15 -0
  99. package/dist/state-banner.js +3 -0
  100. package/dist/state-banner.js.map +1 -0
  101. package/dist/tab-bar.d.ts +14 -0
  102. package/dist/tab-bar.js +3 -0
  103. package/dist/tab-bar.js.map +1 -0
  104. package/dist/toolbar-control-group.d.ts +15 -0
  105. package/dist/toolbar-control-group.js +3 -0
  106. package/dist/toolbar-control-group.js.map +1 -0
  107. package/dist/toolbar-text.d.ts +11 -0
  108. package/dist/toolbar-text.js +3 -0
  109. package/dist/toolbar-text.js.map +1 -0
  110. package/dist/toolbar.d.ts +13 -0
  111. package/dist/toolbar.js +3 -0
  112. package/dist/toolbar.js.map +1 -0
  113. package/dist/value-table.d.ts +10 -0
  114. package/dist/value-table.js +3 -0
  115. package/dist/value-table.js.map +1 -0
  116. package/dist/wire-resizable-regions.d.ts +15 -0
  117. package/dist/wire-resizable-regions.js +4 -0
  118. package/dist/wire-resizable-regions.js.map +1 -0
  119. package/dist/wire-tab-bars.d.ts +17 -0
  120. package/dist/wire-tab-bars.js +3 -0
  121. package/dist/wire-tab-bars.js.map +1 -0
  122. package/docs/accessibility.md +56 -0
  123. package/docs/apple-hig.md +40 -0
  124. package/docs/component-contract.md +66 -0
  125. package/docs/design-philosophy.md +63 -0
  126. package/docs/ux-demo.md +18 -0
  127. package/docs/webawesome-theme.md +181 -0
  128. package/llms.txt +22 -0
  129. package/package.json +218 -0
  130. package/src/app-tab.css +20 -0
  131. package/src/dialog-header.css +9 -0
  132. package/src/empty-state.css +6 -0
  133. package/src/foundation.css +106 -0
  134. package/src/loading-spinner.css +4 -0
  135. package/src/lucide-icon.css +1 -0
  136. package/src/menu-header.css +13 -0
  137. package/src/menu-item.css +12 -0
  138. package/src/page-header.css +3 -0
  139. package/src/resizable-region.css +19 -0
  140. package/src/segmented-control.css +138 -0
  141. package/src/select.css +18 -0
  142. package/src/state-banner.css +31 -0
  143. package/src/styles.css +18 -0
  144. package/src/tab-bar.css +4 -0
  145. package/src/toolbar-control-group.css +25 -0
  146. package/src/toolbar-text.css +4 -0
  147. package/src/toolbar.css +9 -0
  148. package/src/value-table.css +5 -0
  149. package/src/webawesome.css +178 -0
@@ -0,0 +1,66 @@
1
+ # Component and integration contract
2
+
3
+ `@kerfjs/ui` components are plain functions that return Kerf `SafeHtml`. They have no component instance or lifecycle.
4
+
5
+ ## Ownership boundaries
6
+
7
+ - Components own semantic markup, stable anatomy, documented variants, ARIA projection, and package CSS hooks.
8
+ - Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
9
+ - Actions are `data-action` strings. Wire them at a stable root with `delegate()` or `delegateActions()` and retain the disposer.
10
+ - A reusable component never owns per-instance mutable module state.
11
+ - Consumers style through `--kui-*` semantic tokens and public component classes. Foundation tokens provide opinionated neutral, brand/info, success, warning, and danger fill/border/foreground roles. Stateful components expose local override variables; do not target private descendants when a documented variant or token exists.
12
+
13
+ `StateBanner` exposes instance-level `--kui-state-banner-background`,
14
+ `--kui-state-banner-border`, `--kui-state-banner-foreground`,
15
+ `--kui-state-banner-detail`, and action background variables. Its five built-in
16
+ tones can be rethemed globally with
17
+ `--kui-state-banner-{tone}-{background|border|foreground}`. Toolbar control,
18
+ segmented-control, app-tab, and tab-bar colors likewise use their public
19
+ `--kui-*-*` variables. `SegmentedControl` exposes surface, border, foreground,
20
+ hover, and selected-state variables, so rounded, pill, and toolbar presentations
21
+ remain opinionated but locally overridable.
22
+
23
+ `ResizableRegion` owns the Hot Sheet 2 split treatment: a persistent 1px
24
+ separator with a compact grip that appears on hover or keyboard focus. Override
25
+ `--kui-resizable-region-separator-color`,
26
+ `--kui-resizable-region-handle-color`, or
27
+ `--kui-resizable-region-handle-active-color` on a region when its containing
28
+ surface needs a different contrast level.
29
+
30
+ ## Imports and side effects
31
+
32
+ Every component has an explicit JS and CSS subpath. In CSS-aware browser builds,
33
+ the JS component subpath resolves to a generated wrapper that imports the
34
+ foundation, the component stylesheet, and styles for its reachable UI
35
+ subcomponents. The source graph derives those transitive styles at build time;
36
+ an application root never maintains that list. Unused component subpaths and
37
+ their CSS remain unreachable. The root barrel and `@kerfjs/ui/unstyled` stay
38
+ CSS-free for Node, SSR, and custom styling pipelines; pair a root-barrel import
39
+ with `styles.css` when the complete component layer is intended. Application
40
+ overrides load later in the cascade or set scoped `--kui-*` variables. JavaScript
41
+ modules are pure except the browser style wrappers and `@kerfjs/ui/select/register`,
42
+ which registers exactly the Web Awesome elements used by `Select`. Eventful
43
+ helpers such as `wireResizableRegions` and `wireTabBars` attach listeners only
44
+ when called and return disposers. CSS, the generated wrappers that make it
45
+ reachable, and the registration module are the package's only declared side
46
+ effects.
47
+
48
+ `@kerfjs/ui/webawesome.css` is a separate, optional CSS boundary. It imports
49
+ Web Awesome's public base stylesheet and overrides its semantic theme layer to
50
+ match the Kerf/Hot Sheet 2 system. It never imports component JavaScript.
51
+ Consumers register only the individual Web Awesome modules they render and can
52
+ override `--wa-*` values after the theme import. The shared `--wa-*` values feed
53
+ both Web Awesome controls and Kerf's `--kui-*` foundation aliases, preventing a
54
+ second application palette.
55
+
56
+ `kerfjs` is a peer dependency and remains external in every build. Importing a toolbar must not bundle a second Kerf runtime, another UI component, Web Awesome registration, the UX catalog, or development tooling.
57
+
58
+ ## Extracted versus application-specific
59
+
60
+ The package set is intentionally domain-neutral: icon rendering, toolbar primitives, controlled segmented choices, menu rows/headers, resizable regions and wiring, controlled reorderable tab bars, headers, loading, select, banners, empty states, dialog headers, and value tables.
61
+
62
+ Keep product adapters outside the package: connection-state maps, ticket empty-state copy, project/terminal/chat tab actions, saved pane sizes, provider or repository models, and application-specific palettes. An adapter may compose these primitives, map product state into their props, and override semantic CSS variables.
63
+
64
+ ## Testing contract
65
+
66
+ Each behavior has focused unit coverage and a real-browser flow through the production-backed catalog. Consumer bundle tests enforce subpath CSS reachability, transitive component styles, root/SSR isolation, optional registration boundaries, and the CSS-only Web Awesome theme boundary. Visual evidence supplements—never replaces—keyboard, focus, state, and event assertions.
@@ -0,0 +1,63 @@
1
+ # Kerf UI design philosophy
2
+
3
+ Kerf interfaces should feel calm, legible, stable, and honest. They protect the user's train of thought while data and automation continue around them. Visual emphasis is spent only where it helps a person decide or act.
4
+
5
+ ## Decision order
6
+
7
+ When concerns compete, use this order:
8
+
9
+ 1. Explicit product intent and user direction.
10
+ 2. Continuity, data safety, and truthful behavior.
11
+ 3. Accessibility and platform conventions.
12
+ 4. Shared semantic systems and component contracts.
13
+ 5. Local visual polish.
14
+
15
+ A reference image defines hierarchy, relationships, and intended feel. It does not require brittle pixel copying. Responsive adaptations may change presentation while preserving reading order, grouping, emphasis, and actions.
16
+
17
+ ## Principles
18
+
19
+ ### Continuity is correctness
20
+
21
+ Unrelated updates must not close controls, move focus, reset a draft, replace a selection, or jump scroll. Give every durable and transient state an explicit owner. Preserve DOM identity when meaning has not changed. Distinguish blocking foreground work from background synchronization.
22
+
23
+ ### Hierarchy precedes decoration
24
+
25
+ Establish one reading order and one dominant action per decision point. Group first with alignment, spacing, typography, and shared surfaces. A border, fill, badge, or nested card must communicate a real distinction.
26
+
27
+ ### Prefer directness
28
+
29
+ Use the shortest understandable, recoverable interaction. Avoid modes, dialogs, confirmations, and explicit saves when direct manipulation can safely express the same result. Low ceremony still requires a discoverable affordance, visible focus, and honest feedback.
30
+
31
+ ### Components encode meaning
32
+
33
+ Reuse a component when surfaces share its purpose, anatomy, state model, and interaction behavior—not merely its appearance. Components expose semantic actions and explicit variants. Product-specific copy, transport, persistence, and domain states stay in application adapters.
34
+
35
+ ### Use a finite vocabulary
36
+
37
+ Color, type, spacing, radius, elevation, and motion express semantic roles. Prefer package tokens over raw values in consuming code. State is never communicated by color alone.
38
+
39
+ The default vocabulary is opinionated rather than empty: neutral, brand/info,
40
+ success, warning, and danger provide quiet/normal/loud fills, borders, and
41
+ foregrounds compatible with Hot Sheet 2 and Web Awesome. Consumers may replace
42
+ the global palette, one semantic role, or one component instance through
43
+ documented custom properties without rewriting component CSS.
44
+
45
+ ### Responsive design reprioritizes
46
+
47
+ Protect primary content, readable type, recognizable icons, and usable targets. Relocate secondary information before compressing it below a usable scale. Keep one clear scroll owner per region and test narrow, wide, zoomed, and intermediate layouts.
48
+
49
+ ### Production behavior is proof
50
+
51
+ The catalog uses the same exports and CSS consumers receive. Its category-grouped master/detail layout keeps one component under review at a time, and its decorative shell consumes the same semantic palette as the component stage. Every public visual primitive has a focused route; composition demos declare their direct dependencies so reviewers can navigate both `Uses` and `Used by` relationships. Every enabled control works. Demonstrations cover meaningful variants and adverse states with realistic enough content to expose wrapping, clipping, density, and state-transition defects.
52
+
53
+ ### Be platform-fluent, not platform-costumed
54
+
55
+ Use honest web primitives in the browser. Translate platform behavior and accessibility conventions; do not counterfeit native title bars, materials, or controls that cannot behave natively. The web package is HIG-shaped, not HIG-skinned.
56
+
57
+ ### Performance is interaction design
58
+
59
+ Acknowledge actions within the interaction frame. Keep blocking work out of pointer, keyboard, resize, drag, and animation frames. Bound observers, listeners, caches, and retained resources; dispose them with their owning surface.
60
+
61
+ ### AI remains accountable to people
62
+
63
+ AI-generated or AI-modified content needs persistent factual attribution where it matters, clear uncertainty, and adjacent human controls such as Edit, Undo, Retry, or Reject. Never use humanlike decoration to substitute for provenance or review.
@@ -0,0 +1,18 @@
1
+ # UX catalog contract
2
+
3
+ Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation.
4
+
5
+ - It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
6
+ - Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
7
+ - Catalog metadata has a unique id, category, kind (`component` or `composition`), source (`kerf` or `webawesome`), name, description, and direct `uses` dependencies.
8
+ - The master/detail shell groups first-party routes by category with production `MenuHeader` and `MenuItem` components. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts constrain the expanded list to its own scroll region.
9
+ - A detail with relationships renders one grouped selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The footer is absent when both groups are empty; no duplicate relationship summary is rendered.
10
+ - Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
11
+ - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
12
+ - Settings exercise light/dark, increased contrast, and reduced motion without changing component code, and expose each preference through `aria-pressed`.
13
+ - The action log is an `aria-live` region, making interactions visible to both people and browser tests.
14
+ - The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
15
+ - Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup behavior, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
16
+ - The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route (or the broad Web Awesome theme route) is selected. Directly addressed ecosystem URLs pass through the same loader. `npm run demo:build` enforces a 150 kB gzip ceiling for any JavaScript chunk and a 235 kB gzip ceiling for the complete JavaScript payload, so both accidental chunk collapse and aggregate growth fail the build.
17
+
18
+ Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
@@ -0,0 +1,181 @@
1
+ # Web Awesome theme contract
2
+
3
+ `@kerfjs/ui/webawesome.css` is the optional bridge between Web Awesome 3.12's
4
+ free components and the Kerf/Hot Sheet 2 design system.
5
+
6
+ ```ts
7
+ import '@kerfjs/ui/webawesome.css';
8
+ import '@awesome.me/webawesome/dist/components/button/button.js';
9
+ import '@awesome.me/webawesome/dist/components/input/input.js';
10
+ ```
11
+
12
+ The CSS entry includes Web Awesome's native, utility, palette, and default-theme
13
+ styles, then overrides the public semantic contract in
14
+ `@layer wa-theme-overrides`. It does not import or register component
15
+ JavaScript. Importing or removing an individual component module therefore
16
+ still controls whether that component and its JavaScript dependencies enter the
17
+ bundle.
18
+
19
+ ## Coverage
20
+
21
+ The theme supplies shared surfaces, normal/quiet/link text, neutral/brand/
22
+ success/warning/danger fill-border-foreground ramps, focus, overlays, form
23
+ controls, panel and tooltip geometry, radii, and shadows. Those values reach all
24
+ free visual component families through Web Awesome's own styles:
25
+
26
+ - Actions: Button, Button Group, Copy Button, Dropdown, and Dropdown Item.
27
+ - Forms: Checkbox and Checkbox Group, Color Picker, Input, Known Date, Number
28
+ Input, OTP Input, Radio and Radio Group, Rating, Select and Option, Slider,
29
+ Switch, Textarea, and Time Input.
30
+ - Layout: Accordion and Accordion Item, Card, Details, Dialog, Divider, Drawer,
31
+ Page, Scroller, Split Panel.
32
+ - Navigation: Breadcrumb and Breadcrumb Item, Pagination, Tab Group, Tab and Tab
33
+ Panel, Tree and Tree Item.
34
+ - Feedback: Badge, Callout, Progress Bar, Progress Ring, Skeleton, Spinner, Tag,
35
+ Toast and Toast Item, Tooltip.
36
+ - Media: Animated Image, Avatar, Carousel and Carousel Item, Comparison, Icon,
37
+ Markdown, QR Code, and Zoomable Frame.
38
+ - Helpers: Animation, Format Bytes/Date/Number, Include, Intersection Observer,
39
+ Mutation Observer, Popover, Popup, Random Content, Relative Time, and Resize
40
+ Observer. Helpers without their own visual chrome naturally inherit only the
41
+ applicable typography and color values.
42
+
43
+ Carousel navigation follows the same compact geometry as Kerf disclosure
44
+ controls: 16px arrows in 28px controls and 7px visible page dots in 20px
45
+ pointer targets. Override `--kui-wa-carousel-icon-size`,
46
+ `--kui-wa-carousel-control-size`, `--kui-wa-carousel-dot-size`, or
47
+ `--kui-wa-carousel-dot-hit-size` on a carousel or containing scope when a
48
+ product needs different geometry.
49
+
50
+ Accordion, Details, Breadcrumb, and Kerf Select share
51
+ `--kui-disclosure-icon-scale` (default `.5`) so disclosure and traversal
52
+ chevrons keep the same visual weight. The Accordion and Details components
53
+ retain their own rotation behavior because scaling uses `transform` while
54
+ their open state uses the independent `rotate` property.
55
+
56
+ Tooltip and Popover use arrowless floating surfaces by default, matching Hot
57
+ Sheet 2. The theme sets Web Awesome's public `--wa-tooltip-arrow-size` token to
58
+ `0px` and maps each popover's public `--arrow-size` property from
59
+ `--kui-wa-popover-arrow-size` (also `0px`). Restore arrows globally, for a
60
+ subtree, or for one instance after the theme import:
61
+
62
+ ```css
63
+ :root {
64
+ --wa-tooltip-arrow-size: .375rem;
65
+ --kui-wa-popover-arrow-size: .375rem;
66
+ }
67
+
68
+ .pointed-popover {
69
+ --arrow-size: .5rem;
70
+ }
71
+ ```
72
+
73
+ The native `without-arrow` attribute remains useful when an individual
74
+ component should declare the no-arrow choice independent of theme context.
75
+
76
+ The UX catalog includes a focused, addressable specimen for every component in
77
+ this list. Because these are themed ecosystem components rather than
78
+ first-class `@kerfjs/ui` exports, the sidebar keeps them under a collapsible
79
+ `Web Awesome` heading with category subgroups. The aggregate theme route
80
+ remains a broad visual-regression surface, while modal, drawer, toast, media,
81
+ and helper routes make their own behavior inspectable without opening every
82
+ state at once.
83
+
84
+ ## Badge and tag guidance
85
+
86
+ Use Web Awesome's stable `wa-badge` and `wa-tag` directly instead of adding
87
+ generic Kerf wrappers around them. Their variants and appearances already read
88
+ the semantic palette above, and importing their component modules remains
89
+ independent and tree-shakeable.
90
+
91
+ - Use `wa-badge pill` for compact status, count, or activity labels. Hot Sheet
92
+ 2's pill-shaped ticket status treatment is the reference shape.
93
+ - Use the default, non-pill `wa-tag` for categories, filters, and removable
94
+ selections. Its medium-radius rounded rectangle keeps tags distinct from
95
+ status badges. Handle the bubbling `wa-remove` event in the feature that owns
96
+ the underlying selection.
97
+ - Keep domain components when they add domain behavior. Hot Sheet 2's
98
+ `StatusBadge` owns the ticket-status-to-label/icon mapping, while its
99
+ `TagChip` is already a small domain adapter around `wa-tag`. Those are useful
100
+ application components, not missing general-purpose primitives in
101
+ `@kerfjs/ui`.
102
+
103
+ The `pill`, `variant`, `appearance`, and size APIs remain available per
104
+ instance, and applications can override the same `--wa-*` semantic tokens when
105
+ their domain needs a different palette.
106
+
107
+ ## Markdown trust boundary
108
+
109
+ > **Security:** `wa-markdown` is only appropriate for trusted static Markdown.
110
+ > Do not pass unsanitized user input or any other untrusted Markdown to it.
111
+
112
+ Web Awesome sends Marked's HTML output directly into the component's light DOM
113
+ without sanitization. Untrusted content can therefore create cross-site
114
+ scripting vulnerabilities. Sanitize content with an appropriate, separately
115
+ maintained HTML sanitization pipeline before it reaches the component, or use a
116
+ renderer whose trust boundary fits the application.
117
+
118
+ `wa-markdown` is also client-only: it requires the DOM at runtime, cannot render
119
+ during SSR, and should not carry SEO-critical content. All connected instances
120
+ share one mutable Marked instance. Calling `marked.use()` through any instance
121
+ changes shared parser configuration; use `WaMarkdown.updateAll()` deliberately
122
+ when every connected instance should be rerendered. Do not treat per-instance
123
+ configuration as isolated.
124
+
125
+ The UX catalog specimen intentionally contains only trusted, source-controlled
126
+ static content and labels that constraint next to the rendered output.
127
+
128
+ ## Component selection guidance
129
+
130
+ The catalog lists every free Web Awesome component so support can be inspected;
131
+ listing does not make each component the preferred Kerf application pattern.
132
+
133
+ - Consider `wa-popup` for low-level anchored positioning when its flip, shift,
134
+ or placement behavior avoids custom positioning code. Prefer the higher-level
135
+ Tooltip or Popover when their interaction semantics already fit.
136
+ - For ordinary app value selection, use Kerf `Select` instead of directly
137
+ composing Web Awesome Dropdown, Dropdown Item, Select, or Option. Kerf Select
138
+ owns the compact spacing and disclosure-arrow treatment while registering
139
+ only its reachable Web Awesome dependencies. Use an action-menu pattern only
140
+ when the choices truly perform commands rather than select a value.
141
+ - Avoid Web Awesome Button Group as a general visual pattern. Use controlled
142
+ `SegmentedControl` for a small exclusive choice set, including lightweight
143
+ view selection inside an inspector. Reserve Button Group for an exceptional
144
+ grouped-action requirement that is not a selection.
145
+ - Prefer `TabBar` for reorderable or horizontally overflowing application tabs,
146
+ and `SegmentedControl` for compact local views, instead of Web Awesome Tab,
147
+ Tab Group, and Tab Panel.
148
+ - Use Web Awesome Tree, Tree Item, Animated Image, and Comparison only for a
149
+ specific product requirement that needs their specialized behavior.
150
+ - Avoid Web Awesome Zoomable Frame and Icon. Use application-owned media
151
+ presentation and Kerf `LucideIcon`, respectively.
152
+ - Prefer Kerf `ResizableRegion` over Web Awesome Split Panel for Hot Sheet-style
153
+ application panes. It provides the persistent 1px separator, hover/focus
154
+ grip, accessible keyboard and pointer resizing, controlled size, collapse
155
+ support, and application-owned persistence. Use Split Panel only when its
156
+ distinct Web Awesome API is itself required.
157
+
158
+ ## Customization
159
+
160
+ Load application overrides after the package theme, or scope them to the
161
+ smallest subtree that needs a different identity:
162
+
163
+ ```css
164
+ :root {
165
+ --wa-color-brand-fill-loud: #7540a8;
166
+ --wa-color-focus: #7540a8;
167
+ --wa-form-control-border-radius: .5rem;
168
+ }
169
+
170
+ .billing-workspace {
171
+ --wa-color-success-fill-loud: #10a86b;
172
+ }
173
+ ```
174
+
175
+ Use `.wa-light`, `.wa-dark`, and `.wa-invert` for explicit appearance scopes.
176
+ Component-specific custom properties and documented `::part()` selectors from
177
+ Web Awesome remain available when a semantic theme token is not sufficiently
178
+ specific. Prefer those public hooks over shadow-tree implementation details.
179
+
180
+ Kerf's `foundation.css` reads the same `--wa-*` values into `--kui-*` aliases,
181
+ so a scoped Web Awesome override also keeps adjacent Kerf primitives coherent.
package/llms.txt ADDED
@@ -0,0 +1,22 @@
1
+ # @kerfjs/ui
2
+
3
+ Accessible, production-backed UI primitives for kerf applications.
4
+
5
+ - [Package guide](./README.md)
6
+ - [AI skill](./ai/skill.md)
7
+ - [Component contract](./docs/component-contract.md)
8
+ - [Web Awesome theme contract](./docs/webawesome-theme.md)
9
+ - [Accessibility and keyboard contracts](./docs/accessibility.md)
10
+ - [Design philosophy](./docs/design-philosophy.md)
11
+ - [Apple HIG translation](./docs/apple-hig.md)
12
+ - [UX catalog](./docs/ux-demo.md)
13
+
14
+ The UX catalog is a category-grouped master/detail review surface with a focused route for every public visual component plus composition demos. Its collapsible ecosystem section also lists all 70 free Web Awesome 3.12 components, each with a focused themed route. Related entries use one grouped `Uses` / `Used by` selector across both sets; the shell, display preferences, and selected component share one semantic theme.
15
+
16
+ Public components: `LucideIcon`, `Toolbar`, `ToolbarControlGroup`, `ToolbarText`, `SegmentedControl`, `MenuItem`, `MenuHeader`, `ResizableRegion`, `wireResizableRegions`, `AppTab`, `TabBar`, `wireTabBars`, `reorderTabs`, `PageHeader`, `LoadingSpinner`, `Select`, `StateBanner`, `EmptyState`, `DialogHeader`, and `ValueTable`. `SegmentedControl` is controlled and supports toolbar, rounded-rectangle, and pill presentations with native pressed buttons in sequential Tab order. Semantic state palettes are opinionated, Web Awesome-compatible, and overridable globally, by tone, or per instance.
17
+
18
+ Import visual components from explicit JavaScript subpaths. A CSS-aware browser bundler selects generated wrappers that bring in the foundation, the component CSS, and styles for reachable UI subcomponents; unrelated CSS stays out. The root barrel and `@kerfjs/ui/unstyled` are CSS-free; use `styles.css` with the root barrel only when the complete layer is intentional. Manual CSS exports remain available for custom pipelines, and application overrides follow package styles in the cascade. `@kerfjs/ui/webawesome.css` is an optional CSS-only theme for Web Awesome's free components; it includes Web Awesome's base stylesheet, registers no elements, and remains overridable through public `--wa-*` tokens. CSS, its generated browser wrappers, and `@kerfjs/ui/select/register` are the declared side effects. `kerfjs` is an external peer. The Web Awesome peer is optional and needed for its components. `wireTabBars` includes proximity-based horizontal edge autoscroll during same-bar drag reordering.
19
+
20
+ The Web Awesome theme renders Tooltip and Popover without pointer arrows by default. Restore them only when the anchor relationship needs the pointer, using `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or one popover's public `--arrow-size`; `without-arrow` remains available for explicit local intent.
21
+
22
+ Web Awesome catalog coverage means themed and inspectable, not preferred. Consider Popup when it replaces custom anchored positioning. Prefer Kerf Select over direct Dropdown/Dropdown Item/Select/Option composition, SegmentedControl over Button Group, TabBar or SegmentedControl over Web Awesome Tabs, LucideIcon over Web Awesome Icon, and ResizableRegion over Split Panel. ResizableRegion carries Hot Sheet 2's 1px separator plus hover/focus grip. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
package/package.json ADDED
@@ -0,0 +1,218 @@
1
+ {
2
+ "name": "@kerfjs/ui",
3
+ "version": "4.4.1",
4
+ "description": "Accessible, composable UI primitives for kerf applications.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Brian Westphal <brian.westphal@bleugris.com>",
8
+ "homepage": "https://brianwestphal.github.io/kerf/docs/ui-package/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/brianwestphal/kerf",
12
+ "directory": "ui"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/brianwestphal/kerf/issues"
16
+ },
17
+ "keywords": [
18
+ "kerf",
19
+ "kerfjs",
20
+ "components",
21
+ "design-system",
22
+ "accessibility"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "sideEffects": [
33
+ "**/*.css",
34
+ "./dist/browser/*.js",
35
+ "./dist/select-register.js"
36
+ ],
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js"
41
+ },
42
+ "./lucide-icon": {
43
+ "types": "./dist/lucide-icon.d.ts",
44
+ "browser": "./dist/browser/lucide-icon.js",
45
+ "import": "./dist/lucide-icon.js"
46
+ },
47
+ "./toolbar": {
48
+ "types": "./dist/toolbar.d.ts",
49
+ "browser": "./dist/browser/toolbar.js",
50
+ "import": "./dist/toolbar.js"
51
+ },
52
+ "./toolbar-control-group": {
53
+ "types": "./dist/toolbar-control-group.d.ts",
54
+ "browser": "./dist/browser/toolbar-control-group.js",
55
+ "import": "./dist/toolbar-control-group.js"
56
+ },
57
+ "./toolbar-text": {
58
+ "types": "./dist/toolbar-text.d.ts",
59
+ "browser": "./dist/browser/toolbar-text.js",
60
+ "import": "./dist/toolbar-text.js"
61
+ },
62
+ "./menu-item": {
63
+ "types": "./dist/menu-item.d.ts",
64
+ "browser": "./dist/browser/menu-item.js",
65
+ "import": "./dist/menu-item.js"
66
+ },
67
+ "./menu-header": {
68
+ "types": "./dist/menu-header.d.ts",
69
+ "browser": "./dist/browser/menu-header.js",
70
+ "import": "./dist/menu-header.js"
71
+ },
72
+ "./resizable-region": {
73
+ "types": "./dist/resizable-region.d.ts",
74
+ "browser": "./dist/browser/resizable-region.js",
75
+ "import": "./dist/resizable-region.js"
76
+ },
77
+ "./wire-resizable-regions": {
78
+ "types": "./dist/wire-resizable-regions.d.ts",
79
+ "import": "./dist/wire-resizable-regions.js"
80
+ },
81
+ "./app-tab": {
82
+ "types": "./dist/app-tab.d.ts",
83
+ "browser": "./dist/browser/app-tab.js",
84
+ "import": "./dist/app-tab.js"
85
+ },
86
+ "./tab-bar": {
87
+ "types": "./dist/tab-bar.d.ts",
88
+ "browser": "./dist/browser/tab-bar.js",
89
+ "import": "./dist/tab-bar.js"
90
+ },
91
+ "./wire-tab-bars": {
92
+ "types": "./dist/wire-tab-bars.d.ts",
93
+ "import": "./dist/wire-tab-bars.js"
94
+ },
95
+ "./page-header": {
96
+ "types": "./dist/page-header.d.ts",
97
+ "browser": "./dist/browser/page-header.js",
98
+ "import": "./dist/page-header.js"
99
+ },
100
+ "./loading-spinner": {
101
+ "types": "./dist/loading-spinner.d.ts",
102
+ "browser": "./dist/browser/loading-spinner.js",
103
+ "import": "./dist/loading-spinner.js"
104
+ },
105
+ "./select": {
106
+ "types": "./dist/select.d.ts",
107
+ "browser": "./dist/browser/select.js",
108
+ "import": "./dist/select.js"
109
+ },
110
+ "./segmented-control": {
111
+ "types": "./dist/segmented-control.d.ts",
112
+ "browser": "./dist/browser/segmented-control.js",
113
+ "import": "./dist/segmented-control.js"
114
+ },
115
+ "./select/register": {
116
+ "types": "./dist/select-register.d.ts",
117
+ "import": "./dist/select-register.js"
118
+ },
119
+ "./state-banner": {
120
+ "types": "./dist/state-banner.d.ts",
121
+ "browser": "./dist/browser/state-banner.js",
122
+ "import": "./dist/state-banner.js"
123
+ },
124
+ "./empty-state": {
125
+ "types": "./dist/empty-state.d.ts",
126
+ "browser": "./dist/browser/empty-state.js",
127
+ "import": "./dist/empty-state.js"
128
+ },
129
+ "./dialog-header": {
130
+ "types": "./dist/dialog-header.d.ts",
131
+ "browser": "./dist/browser/dialog-header.js",
132
+ "import": "./dist/dialog-header.js"
133
+ },
134
+ "./value-table": {
135
+ "types": "./dist/value-table.d.ts",
136
+ "browser": "./dist/browser/value-table.js",
137
+ "import": "./dist/value-table.js"
138
+ },
139
+ "./unstyled": {
140
+ "types": "./dist/index.d.ts",
141
+ "import": "./dist/index.js"
142
+ },
143
+ "./foundation.css": "./src/foundation.css",
144
+ "./webawesome.css": "./src/webawesome.css",
145
+ "./styles.css": "./src/styles.css",
146
+ "./lucide-icon.css": "./src/lucide-icon.css",
147
+ "./toolbar.css": "./src/toolbar.css",
148
+ "./toolbar-control-group.css": "./src/toolbar-control-group.css",
149
+ "./toolbar-text.css": "./src/toolbar-text.css",
150
+ "./menu-item.css": "./src/menu-item.css",
151
+ "./menu-header.css": "./src/menu-header.css",
152
+ "./resizable-region.css": "./src/resizable-region.css",
153
+ "./app-tab.css": "./src/app-tab.css",
154
+ "./tab-bar.css": "./src/tab-bar.css",
155
+ "./page-header.css": "./src/page-header.css",
156
+ "./loading-spinner.css": "./src/loading-spinner.css",
157
+ "./select.css": "./src/select.css",
158
+ "./segmented-control.css": "./src/segmented-control.css",
159
+ "./state-banner.css": "./src/state-banner.css",
160
+ "./empty-state.css": "./src/empty-state.css",
161
+ "./dialog-header.css": "./src/dialog-header.css",
162
+ "./value-table.css": "./src/value-table.css",
163
+ "./ai/*": "./ai/*",
164
+ "./docs/*": "./docs/*"
165
+ },
166
+ "files": [
167
+ "dist",
168
+ "src/*.css",
169
+ "ai",
170
+ "docs",
171
+ "llms.txt",
172
+ "README.md",
173
+ "LICENSE"
174
+ ],
175
+ "scripts": {
176
+ "build": "tsup && node ../scripts/clean-dist-output.mjs dist && node scripts/build-browser-entries.mjs",
177
+ "dev": "npm run build && vite --config ux-demo/vite.config.ts",
178
+ "demo:build": "npm run build && vite build --config ux-demo/vite.config.ts && node scripts/check-demo-bundle.mjs",
179
+ "demo:serve": "vite preview --config ux-demo/vite.config.ts",
180
+ "lint": "eslint src tests ux-demo --max-warnings 0",
181
+ "typecheck": "node node_modules/typescript7/bin/tsc --noEmit",
182
+ "test": "npm run test:unit && npm run test:bundle",
183
+ "test:unit": "vitest run tests/unit --coverage",
184
+ "test:bundle": "npm run build && vitest run tests/bundle",
185
+ "test:e2e": "npm run demo:build && playwright test",
186
+ "check": "npm run lint && npm run typecheck && npm test && npm run demo:build && npm pack --dry-run",
187
+ "prepublishOnly": "npm run check"
188
+ },
189
+ "peerDependencies": {
190
+ "@awesome.me/webawesome": "^3.12.0",
191
+ "kerfjs": "^4.0.0",
192
+ "lucide": "^1.43.0"
193
+ },
194
+ "peerDependenciesMeta": {
195
+ "@awesome.me/webawesome": {
196
+ "optional": true
197
+ }
198
+ },
199
+ "devDependencies": {
200
+ "@awesome.me/webawesome": "3.12.0",
201
+ "@eslint/js": "^10.0.1",
202
+ "@playwright/test": "^1.63.0",
203
+ "@types/node": "^22.10.0",
204
+ "@typescript-eslint/eslint-plugin": "^8.65.0",
205
+ "@typescript-eslint/parser": "^8.65.0",
206
+ "@vitest/coverage-v8": "4.1.11",
207
+ "esbuild": "^0.25.0",
208
+ "eslint": "^10.8.0",
209
+ "eslint-plugin-simple-import-sort": "^12.1.1",
210
+ "happy-dom": "^20.9.0",
211
+ "lucide": "1.43.0",
212
+ "tsup": "^8.3.0",
213
+ "typescript": "^6.0.3",
214
+ "typescript7": "npm:typescript@^7.0.2",
215
+ "vite": "^7.1.0",
216
+ "vitest": "4.1.11"
217
+ }
218
+ }
@@ -0,0 +1,20 @@
1
+ .kui-app-tab { --kui-app-tab-color: var(--kui-color-neutral-on-quiet); --kui-app-tab-hover-background: color-mix(in srgb, var(--kui-color-surface) 72%, transparent); --kui-app-tab-selected-color: var(--kui-color-brand-on-quiet); --kui-app-tab-selected-background: var(--kui-color-surface); --kui-app-tab-selected-border: color-mix(in srgb, var(--kui-color-neutral-border-normal) 80%, transparent); --kui-app-tab-drop-indicator: var(--kui-color-brand-border-normal); position: relative; display: flex; width: max-content; min-width: max-content; height: 2rem; border: 0; border-radius: var(--kui-radius-pill); color: var(--kui-app-tab-color); background: transparent; align-items: center; flex: none; }
2
+ .kui-app-tab:hover { background: var(--kui-app-tab-hover-background); }
3
+ .kui-app-tab[data-selected="true"] { color: var(--kui-app-tab-selected-color); background: var(--kui-app-tab-selected-background); box-shadow: var(--kui-shadow-s), inset 0 0 0 1px var(--kui-app-tab-selected-border); }
4
+ .kui-app-tab[data-tab-dragging="true"] { opacity: .55; }
5
+ .kui-app-tab[data-tab-drop-position]::after { position: absolute; z-index: 2; top: .2rem; bottom: .2rem; width: 2px; border-radius: var(--kui-radius-pill); background: var(--kui-app-tab-drop-indicator); content: ""; pointer-events: none; }
6
+ .kui-app-tab[data-tab-drop-position="before"]::after { inset-inline-start: calc(var(--kui-space-2xs) * -1); }
7
+ .kui-app-tab[data-tab-drop-position="after"]::after { inset-inline-end: calc(var(--kui-space-2xs) * -1); }
8
+ .kui-app-tab__select { display: flex; min-width: 0; height: 100%; padding: 0 var(--kui-space-s); border: 0; appearance: none; color: inherit; background: transparent; cursor: pointer; font: inherit; align-items: center; gap: var(--kui-space-xs); text-align: start; }
9
+ .kui-app-tab:has(.kui-app-tab__close) .kui-app-tab__select { padding-inline-start: var(--kui-space-2xs); }
10
+ .kui-app-tab:has(.kui-app-tab__select:focus-visible) { outline: var(--kui-focus-ring); outline-offset: -2px; }
11
+ .kui-app-tab__select:focus-visible { outline: none; }
12
+ .kui-app-tab__close:focus-visible { border-radius: var(--kui-radius-pill); outline: var(--kui-focus-ring); outline-offset: -2px; }
13
+ .kui-app-tab__select > svg { width: 1rem; height: 1rem; flex: none; }
14
+ .kui-app-tab__name { overflow: hidden; font-size: var(--kui-font-xs); font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
15
+ .kui-app-tab__trailing { display: grid; min-width: 1.275rem; min-height: 1.275rem; flex: none; place-items: center; }
16
+ .kui-app-tab__close { display: grid; width: 1.275rem; height: 1.275rem; margin-inline-start: var(--kui-space-xs); padding: 0; border: 0; appearance: none; color: var(--kui-color-neutral-on-quiet); background: transparent; cursor: pointer; flex: none; place-items: center; }
17
+ .kui-app-tab__close:hover { color: var(--kui-color-text); }
18
+ .kui-app-tab__close svg { width: .6375rem; height: .6375rem; }
19
+ @media (hover: hover) and (pointer: fine) { .kui-app-tab__close { opacity: 0; pointer-events: none; transition: opacity .12s ease; } .kui-app-tab:hover .kui-app-tab__close, .kui-app-tab:focus-within .kui-app-tab__close { opacity: 1; pointer-events: auto; } }
20
+ @media (prefers-reduced-motion: reduce) { .kui-app-tab__close { transition: none; } }
@@ -0,0 +1,9 @@
1
+ .kui-dialog-header { --kui-dialog-header-border: var(--kui-color-border); --kui-dialog-header-icon-background: var(--kui-color-brand-fill-quiet); --kui-dialog-header-icon-foreground: var(--kui-color-brand-on-quiet); display: grid; min-height: 4.9rem; padding: var(--kui-space-m) var(--kui-space-l); box-sizing: border-box; border-bottom: 1px solid var(--kui-dialog-header-border); grid-template-columns: 2rem minmax(0, 1fr) auto; align-items: start; gap: var(--kui-space-m); }
2
+ .kui-dialog-header[data-has-icon="false"] { grid-template-columns: minmax(0, 1fr) auto; }
3
+ .kui-dialog-header__icon { display: grid; width: 2rem; height: 2rem; border-radius: var(--kui-radius-circle); color: var(--kui-dialog-header-icon-foreground); background: var(--kui-dialog-header-icon-background); place-items: center; }
4
+ .kui-dialog-header__icon svg { width: 1.25rem; height: 1.25rem; }
5
+ .kui-dialog-header__copy { min-width: 0; }
6
+ .kui-dialog-header h2, .kui-dialog-header p { margin: 0; }
7
+ .kui-dialog-header h2 { color: var(--kui-color-text); font-size: var(--kui-font-l); line-height: 1.2; }
8
+ .kui-dialog-header p { margin-block-start: var(--kui-space-2xs); color: var(--kui-color-text-quiet); }
9
+ .kui-dialog-header__actions { display: flex; align-items: center; gap: var(--kui-space-xs); }
@@ -0,0 +1,6 @@
1
+ .kui-empty-state { --kui-empty-state-foreground: var(--kui-color-neutral-on-quiet); --kui-empty-state-title: var(--kui-color-text); display: grid; width: min(100%, 28rem); min-height: 12rem; margin: auto; padding: var(--kui-space-xl); box-sizing: border-box; color: var(--kui-empty-state-foreground); place-items: center; place-content: center; gap: var(--kui-space-xs); text-align: center; }
2
+ .kui-empty-state__icon { display: grid; min-width: 2rem; min-height: 2rem; margin-block-end: var(--kui-space-xs); color: inherit; place-items: center; }
3
+ .kui-empty-state__icon svg { width: 2rem; height: 2rem; stroke-width: 1.5; }
4
+ .kui-empty-state strong { color: var(--kui-empty-state-title); font-size: var(--kui-font-l); font-weight: 650; }
5
+ .kui-empty-state > span { max-width: 24rem; font-size: var(--kui-font-s); line-height: 1.45; }
6
+ .kui-empty-state__action { margin-block-start: var(--kui-space-xs); }