Kit catalog¶
24 components, grouped by how they hold state. The gallery (/ui-kit in the demo umbrella) shows every one live, and the browser e2e drives them all.
Pure slots — presentation only¶
No state; the parent projects content.
| Component | Notes |
|---|---|
<Card> |
title / actions slots + default body |
<Field> |
label / help / error around a control — also the substrate declui generates onto |
<PageHeader> |
title + actions slot |
<Stat> |
a KPI tile |
<EmptyState> |
icon + message + action |
<Avatar> |
initials or image; sizes via forwarded class |
<Breadcrumb> |
links with CSS separators |
<Tooltip> |
CSS hover tooltip |
Attribute forwarding applies to every component: class on the host merges into the child's root; style/id/title/role/tabindex/aria-*/data-* are forwarded. A mistyped prop (anything else) fails closed.
Controlled — the parent owns the flag¶
Externally-triggered overlays: the opener lives elsewhere on the page, so the parent holds the Local and the wiring (:open prop + @close emit).
| Component | Contract |
|---|---|
<Dialog> |
:open + @close; footer content via slots |
<Drawer> |
side panel; same contract |
<Toast> |
transient notice; pairs well with :autohide |
<button @click="confirm_open = True" class="ui-btn ui-btn--primary">Delete…</button>
<Dialog :open="confirm_open" @close="close_confirm">
<template slot="title">Delete 3 issues?</template>
<button class="ui-btn ui-btn--danger" @click="do_delete()">Delete</button>
</Dialog>
Uncontrolled — the component owns its state¶
Self-triggered widgets: drop them in with zero parent wiring; each placement is monomorphized into independent state.
| Component | Notes |
|---|---|
<Collapsible> |
an accordion row; N placements, N independent open flags |
<Popover> |
trigger + panel, outside-click close |
<Switch> |
on/off toggle |
<TagInput> |
add/remove chips over a Local[list] (the client :each reconciler) |
<Sortable> |
pointer drag-reorder via :sortable |
Widget primitives — the optional runtime family¶
Complex widgets whose presentation state (open option, sort column, page) nothing else reads. They ship as a binder marker plus a runtime in widgets.js; your data and selection stay in Locals the app reads. Each is a deliberate, labelled runtime stand-in.
| Component | Binder | State the app owns |
|---|---|---|
<Tabs> |
:tabs |
— (bar generated from data-tab panels) |
<DataTable> |
:table="rows" |
a Local[list]; header-click sorting |
<DataGrid> |
:datagrid="rows" + :datagridsel="sel" |
rows Local[list] + selection Local[set] (survives sort/filter/page; tri-state select-all; aria-sort headers) |
<Combobox> |
:combobox |
Local[str] — searchable single-select, full keyboard + ARIA listbox |
<ComboboxMulti> |
:combobox (multi) |
Local[set] — chips, Backspace removes last |
<Menu> |
:menu |
— (roving focus, Escape-to-trigger; each item keeps its compiled @click) |
<CommandPalette> |
:palette |
— (Ctrl/⌘-K overlay, fuzzy filter, activates the item's compiled @click) |
Native-wrapped¶
| Component | Notes |
|---|---|
<Select> |
a real <select class="ui-select"> with :bind forwarding — keyboard and screen-reader behavior for free, zero runtime. The accessible default for a fixed option set; <Combobox> is the searchable upgrade. |
Class-contract variants (no component needed)¶
Buttons (ui-btn, --primary/--danger/--ghost, sizes, loading), inputs (ui-input, invalid state), ui-badge--*, ui-alert--*, tables + pagination, ui-progress, ui-skeleton, ui-kbd. Write the class; there is nothing to instantiate.