@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.
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/ai/skill.md +38 -0
- package/dist/app-tab.d.ts +18 -0
- package/dist/app-tab.js +3 -0
- package/dist/app-tab.js.map +1 -0
- package/dist/browser/app-tab.js +3 -0
- package/dist/browser/dialog-header.js +3 -0
- package/dist/browser/empty-state.js +4 -0
- package/dist/browser/loading-spinner.js +3 -0
- package/dist/browser/lucide-icon.js +3 -0
- package/dist/browser/menu-header.js +3 -0
- package/dist/browser/menu-item.js +3 -0
- package/dist/browser/page-header.js +3 -0
- package/dist/browser/resizable-region.js +3 -0
- package/dist/browser/segmented-control.js +3 -0
- package/dist/browser/select.js +4 -0
- package/dist/browser/state-banner.js +3 -0
- package/dist/browser/tab-bar.js +3 -0
- package/dist/browser/toolbar-control-group.js +3 -0
- package/dist/browser/toolbar-text.js +3 -0
- package/dist/browser/toolbar.js +3 -0
- package/dist/browser/value-table.js +3 -0
- package/dist/chunk-2PES33HS.js +13 -0
- package/dist/chunk-2PES33HS.js.map +1 -0
- package/dist/chunk-3O7ZEUW2.js +27 -0
- package/dist/chunk-3O7ZEUW2.js.map +1 -0
- package/dist/chunk-4HHCYSGT.js +79 -0
- package/dist/chunk-4HHCYSGT.js.map +1 -0
- package/dist/chunk-55DATV3A.js +10 -0
- package/dist/chunk-55DATV3A.js.map +1 -0
- package/dist/chunk-723YNJBH.js +17 -0
- package/dist/chunk-723YNJBH.js.map +1 -0
- package/dist/chunk-DAFFDO33.js +14 -0
- package/dist/chunk-DAFFDO33.js.map +1 -0
- package/dist/chunk-GY5WH7TO.js +10 -0
- package/dist/chunk-GY5WH7TO.js.map +1 -0
- package/dist/chunk-MBR3TYNL.js +14 -0
- package/dist/chunk-MBR3TYNL.js.map +1 -0
- package/dist/chunk-MFKMHK5W.js +10 -0
- package/dist/chunk-MFKMHK5W.js.map +1 -0
- package/dist/chunk-MQIZAD4A.js +227 -0
- package/dist/chunk-MQIZAD4A.js.map +1 -0
- package/dist/chunk-NHXX4JI5.js +28 -0
- package/dist/chunk-NHXX4JI5.js.map +1 -0
- package/dist/chunk-OPCJRCYO.js +19 -0
- package/dist/chunk-OPCJRCYO.js.map +1 -0
- package/dist/chunk-T5XNMVOI.js +24 -0
- package/dist/chunk-T5XNMVOI.js.map +1 -0
- package/dist/chunk-TRNBHPNW.js +54 -0
- package/dist/chunk-TRNBHPNW.js.map +1 -0
- package/dist/chunk-VHW5YNIB.js +17 -0
- package/dist/chunk-VHW5YNIB.js.map +1 -0
- package/dist/chunk-VKH2JLDZ.js +10 -0
- package/dist/chunk-VKH2JLDZ.js.map +1 -0
- package/dist/chunk-W2FK7ZNY.js +26 -0
- package/dist/chunk-W2FK7ZNY.js.map +1 -0
- package/dist/chunk-WVPYVDXY.js +15 -0
- package/dist/chunk-WVPYVDXY.js.map +1 -0
- package/dist/chunk-WY4CAEPG.js +14 -0
- package/dist/chunk-WY4CAEPG.js.map +1 -0
- package/dist/dialog-header.d.ts +14 -0
- package/dist/dialog-header.js +3 -0
- package/dist/dialog-header.js.map +1 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +4 -0
- package/dist/empty-state.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/loading-spinner.d.ts +10 -0
- package/dist/loading-spinner.js +3 -0
- package/dist/loading-spinner.js.map +1 -0
- package/dist/lucide-icon.d.ts +14 -0
- package/dist/lucide-icon.js +3 -0
- package/dist/lucide-icon.js.map +1 -0
- package/dist/menu-header.d.ts +15 -0
- package/dist/menu-header.js +3 -0
- package/dist/menu-header.js.map +1 -0
- package/dist/menu-item.d.ts +22 -0
- package/dist/menu-item.js +3 -0
- package/dist/menu-item.js.map +1 -0
- package/dist/page-header.d.ts +9 -0
- package/dist/page-header.js +3 -0
- package/dist/page-header.js.map +1 -0
- package/dist/resizable-region.d.ts +21 -0
- package/dist/resizable-region.js +3 -0
- package/dist/resizable-region.js.map +1 -0
- package/dist/segmented-control.d.ts +28 -0
- package/dist/segmented-control.js +3 -0
- package/dist/segmented-control.js.map +1 -0
- package/dist/select-register.d.ts +4 -0
- package/dist/select-register.js +10 -0
- package/dist/select-register.js.map +1 -0
- package/dist/select.d.ts +28 -0
- package/dist/select.js +4 -0
- package/dist/select.js.map +1 -0
- package/dist/state-banner.d.ts +15 -0
- package/dist/state-banner.js +3 -0
- package/dist/state-banner.js.map +1 -0
- package/dist/tab-bar.d.ts +14 -0
- package/dist/tab-bar.js +3 -0
- package/dist/tab-bar.js.map +1 -0
- package/dist/toolbar-control-group.d.ts +15 -0
- package/dist/toolbar-control-group.js +3 -0
- package/dist/toolbar-control-group.js.map +1 -0
- package/dist/toolbar-text.d.ts +11 -0
- package/dist/toolbar-text.js +3 -0
- package/dist/toolbar-text.js.map +1 -0
- package/dist/toolbar.d.ts +13 -0
- package/dist/toolbar.js +3 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/value-table.d.ts +10 -0
- package/dist/value-table.js +3 -0
- package/dist/value-table.js.map +1 -0
- package/dist/wire-resizable-regions.d.ts +15 -0
- package/dist/wire-resizable-regions.js +4 -0
- package/dist/wire-resizable-regions.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +17 -0
- package/dist/wire-tab-bars.js +3 -0
- package/dist/wire-tab-bars.js.map +1 -0
- package/docs/accessibility.md +56 -0
- package/docs/apple-hig.md +40 -0
- package/docs/component-contract.md +66 -0
- package/docs/design-philosophy.md +63 -0
- package/docs/ux-demo.md +18 -0
- package/docs/webawesome-theme.md +181 -0
- package/llms.txt +22 -0
- package/package.json +218 -0
- package/src/app-tab.css +20 -0
- package/src/dialog-header.css +9 -0
- package/src/empty-state.css +6 -0
- package/src/foundation.css +106 -0
- package/src/loading-spinner.css +4 -0
- package/src/lucide-icon.css +1 -0
- package/src/menu-header.css +13 -0
- package/src/menu-item.css +12 -0
- package/src/page-header.css +3 -0
- package/src/resizable-region.css +19 -0
- package/src/segmented-control.css +138 -0
- package/src/select.css +18 -0
- package/src/state-banner.css +31 -0
- package/src/styles.css +18 -0
- package/src/tab-bar.css +4 -0
- package/src/toolbar-control-group.css +25 -0
- package/src/toolbar-text.css +4 -0
- package/src/toolbar.css +9 -0
- package/src/value-table.css +5 -0
- 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.
|
package/docs/ux-demo.md
ADDED
|
@@ -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
|
+
}
|
package/src/app-tab.css
ADDED
|
@@ -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); }
|