Shadow DOM UI primitives built with Lit and driven by shared tokens.
npm install @ismail-elkorchi/ui-primitivesShadow DOM web components backed by the shared token CSS variables from @ismail-elkorchi/ui-tokens. They ship no Tailwind and assume tokens are loaded on :root.
``ts`
import "@ismail-elkorchi/ui-primitives/register"; // defines all elements
// or import individual components for tree-shaking:
import "@ismail-elkorchi/ui-primitives/uik-alert";
import "@ismail-elkorchi/ui-primitives/uik-badge";
import "@ismail-elkorchi/ui-primitives/uik-code-block";
import "@ismail-elkorchi/ui-primitives/uik-box";
import "@ismail-elkorchi/ui-primitives/uik-button";
import "@ismail-elkorchi/ui-primitives/uik-checkbox";
import "@ismail-elkorchi/ui-primitives/uik-description-list";
import "@ismail-elkorchi/ui-primitives/uik-dialog";
import "@ismail-elkorchi/ui-primitives/uik-command-palette";
import "@ismail-elkorchi/ui-primitives/uik-heading";
import "@ismail-elkorchi/ui-primitives/uik-icon";
import "@ismail-elkorchi/ui-primitives/uik-input";
import "@ismail-elkorchi/ui-primitives/uik-listbox";
import "@ismail-elkorchi/ui-primitives/uik-combobox";
import "@ismail-elkorchi/ui-primitives/uik-menu";
import "@ismail-elkorchi/ui-primitives/uik-menu-item";
import "@ismail-elkorchi/ui-primitives/uik-menubar";
import "@ismail-elkorchi/ui-primitives/uik-link";
import "@ismail-elkorchi/ui-primitives/uik-nav";
import "@ismail-elkorchi/ui-primitives/uik-nav-rail";
import "@ismail-elkorchi/ui-primitives/uik-option";
import "@ismail-elkorchi/ui-primitives/uik-tree-view";
import "@ismail-elkorchi/ui-primitives/uik-popover";
import "@ismail-elkorchi/ui-primitives/uik-pagination";
import "@ismail-elkorchi/ui-primitives/uik-progress";
import "@ismail-elkorchi/ui-primitives/uik-radio";
import "@ismail-elkorchi/ui-primitives/uik-radio-group";
import "@ismail-elkorchi/ui-primitives/uik-select";
import "@ismail-elkorchi/ui-primitives/uik-separator";
import "@ismail-elkorchi/ui-primitives/uik-resizable-panels";
import "@ismail-elkorchi/ui-primitives/uik-spinner";
import "@ismail-elkorchi/ui-primitives/uik-stack";
import "@ismail-elkorchi/ui-primitives/uik-surface";
import "@ismail-elkorchi/ui-primitives/uik-switch";
import "@ismail-elkorchi/ui-primitives/uik-tabs";
import "@ismail-elkorchi/ui-primitives/uik-tab";
import "@ismail-elkorchi/ui-primitives/uik-tab-panel";
import "@ismail-elkorchi/ui-primitives/uik-text";
import "@ismail-elkorchi/ui-primitives/uik-textarea";
import "@ismail-elkorchi/ui-primitives/uik-tooltip";
import "@ismail-elkorchi/ui-primitives/uik-visually-hidden";
Ensure your app imports tokens before Tailwind so the theme variables exist:
`css`
@import "@ismail-elkorchi/ui-tokens/index.css";
@import "tailwindcss";
- Most components expose part="base" on the primary element.part="control"
- Form-field primitives expose wrappers plus part="label", part="hint", and part="error" slots when slotted.part="base"
- Layout and text primitives expose on their container or rendered tag.
- Roving focus is implemented with a single tabIndexValue=0 target and tabIndexValue=-1 for the rest of the roving set (usually uik-button inside a composite widget).Home
- Arrow keys move focus within the roving set; /End jump to the first/last enabled item. Enter/Space activate the focused item per APG expectations.uik-listbox
- Focus state is tracked by item id and updated on focus and when the items list changes (falling back to the active or first enabled item).
- and uik-tabs use uik-option and uik-tab as the roving focus targets; uik-listbox can switch to focus-mode="activedescendant" for combobox-style inputs.uik-menu
- uses uik-menu-item for roving focus and returns focus to the trigger on close.uik-nav-rail
- Shell components must delegate roving focus to primitives such as /uik-tree-view instead of re-implementing keyboard logic.
- Overlay primitives emit overlay-close with reason escape | outside | programmatic | toggle.uik-dialog
- Focus origin is captured on open for focus-taking overlays; restores focus to the opener on close. Hover surfaces (uik-tooltip) never move focus.
- Initial focus relies on native
Form-associated primitives use ElementInternals when available. When it isvalue
unavailable, components fall back to reflecting their current value on the host attribute and continue to emit their normal change/input or
component-specific events. In fallback mode, the controls do not participate in
native form submission or constraint validation, so value collection and
validation must be handled by the host.
- Attributes/props: variant (neutral | info | success | warning | danger).title
- Slots: , default slot for message body.base
- Parts: , title, body.role="status"
- Events: none special.
- A11y: by default; role="alert" for warning/danger.--uik-component-alert-{neutral|info|success|warning|danger}-{bg|border|fg}
- Custom properties: .
- Attributes/props: variant (default | secondary | danger | outline).part="base"
- Events: none special; behaves like an inline element.
- Styling hooks: token-driven colors and .
- Attributes/props: padding (0-6), inline (boolean).base
- Slots: default.
- Parts: .--uik-component-box-padding-{0..6}
- Custom properties: , --uik-component-box-bg, --uik-component-box-bg-opacity, --uik-component-box-border-color, --uik-component-box-border-opacity, --uik-component-box-border-width, --uik-component-box-radius.
- Attributes/props: variant (solid | danger | outline | secondary | ghost | link), size (default | sm | lg | icon), type (button | submit | reset - defaults to button), tabIndexValue (number), active (boolean), muted (boolean), disabled (boolean).click
- Events: native button events (, focus/blur) bubble from the internal button; disabled buttons swallow click.:host
- Styling hooks:
- Sizing is enforced on the . The internal button fills the host (100% width/height).active
- /muted props control stateful colors (especially for ghost variant).part="base"
- allows overrides, but avoid fighting the host sizing.aria-label
- A11y: /aria-labelledby/aria-describedby/aria-pressed/aria-haspopup/aria-expanded/aria-controls/role are forwarded to the internal
- Attributes/props: name, value, checked, indeterminate (boolean), disabled, required, invalid, tabIndexValue (number).label
- Slots: , hint, error.base
- Parts: , control, label, hint, error.change
- Events: native bubbles from the internal .aria-label
- A11y: label slot or ; hint/error slots are announced via aria-describedby.FormData
- Forms: form-associated and participates in when checked (see Form association fallback).--uik-component-checkbox-accent
- Custom properties: , --uik-component-checkbox-size.
- Attributes/props: copy (boolean), inline (boolean), copy-label, aria-label, aria-labelledby.copy-label
- Slots: default slot for code content, for the copy button.base
- Parts: , content, copy-button, inline.code-block-copy
- Events: (detail: {value, success}).inline
- Usage: use for short values or commands; default mode renders a scrollable block.aria-label
- A11y: provide /aria-labelledby when the block needs a name; copy button uses copy-label.--uik-component-code-block-
- Custom properties: , --uik-component-code-block-copy-.
- Attributes/props: density (comfortable | compact).dt
- Slots: default slot for /dd pairs.base
- Parts: .dt
- Usage: use one followed by one dd per row; inline values can use uik-code-block.dt
- A11y: use proper /dd pairs so assistive tech announces the term/value relationship.--uik-component-description-list-*
- Custom properties: .
- Attributes/props: open, modal (boolean).title
- Slots: , description, default slot for body, actions.base
- Parts: , panel, title, description, body, actions.close
- Events: native /cancel from
- Attributes/props: open, query, placeholder, loading, disabled, disableFilter, disableAutoClose, highlightMatches, inputLabel, virtualize, itemHeight, viewportHeight, overscan.title
- Slots: , description, label, empty, loading, footer.dialog
- Parts: , panel, header, title, description, label, input, list, group, group-label, item, item-label, item-description, item-shortcut, highlight, empty, loading, footer.command-palette-open-change
- Events: , command-palette-query-change, command-palette-select.aria-activedescendant
- A11y: combobox + listbox with ; input receives focus on open; Escape closes and focus returns to the opener..items
- Items: set to { id, label, description?, value?, keywords?, shortcut?, group?, isDisabled? }[].--uik-component-command-palette-*
- Custom properties: .
- Attributes/props: level (1-6), tone (default | strong | muted | danger | success | warning | info).base
- Slots: default.
- Parts: .--uik-component-heading-size-{1..6}
- Custom properties: , --uik-component-heading-line-height-{1..6}, --uik-component-heading-weight, --uik-component-heading-color-{default|strong|muted|danger|success|warning|info}.
- Attributes/props: size (xs | sm | md | lg), tone (default | muted | danger | success | warning | info | inverse).base
- Slots: default slot for SVG.
- Parts: .aria-label
- A11y: set for meaningful icons or aria-hidden for decorative ones.--uik-component-icon-size-{xs|sm|md|lg}
- Custom properties: , --uik-component-icon-color-{default|muted|danger|success|warning|info|inverse}.
- Attributes/props: type, name, value, placeholder, disabled, required, readonly, invalid, autocomplete, inputmode, enterkeyhint. Optional aria-label/aria-labelledby are forwarded when no label slot is provided.label
- Slots: , hint, error.input
- Events: native and change bubble from the internal (no re-dispatch).for
- A11y: label slot associates via , hint/error slot text is wired to aria-describedby, and aria-invalid is set when invalid or an error slot is present.ElementInternals
- Forms: form-associated via when available and participates in FormData when name is set (see Form association fallback).
- Attributes/props: href, target, rel, download.base
- Slots: default slot for link text.
- Parts: .
- Events: native anchor events bubble from the internal .aria-label
- A11y: forwards /aria-labelledby/aria-describedby to .--uik-component-link-fg-default
- Custom properties: , --uik-component-link-fg-hover, --uik-component-link-underline-offset.
- Attributes/props: value, selectionMode (single | multiple), focusMode (roving | activedescendant), activeId.
- Slots: default slot for .base
- Parts: .listbox-select
- Events: (detail: {value, values, option}), listbox-active (detail: {id, value, option}).role="listbox"
- A11y: with roving focus; aria-multiselectable when selectionMode="multiple".--uik-component-listbox-
- Custom properties: , --uik-component-listbox-item-.
- Attributes/props: value, selected, disabled, active, tabIndexValue (number).base
- Slots: default slot for option label.
- Parts: .role="option"
- A11y: with aria-selected + aria-disabled.--uik-component-listbox-item-*
- Custom properties: .
- Attributes/props: value, open (boolean), items (array), name, placeholder, disabled, required, readonly, invalid.[{id, label, value?, isDisabled?}]
- Items: (value defaults to id).label
- Slots: , hint, error.base
- Parts: , control, label, hint, error, panel.combobox-select
- Events: (detail: {value, item}).role="combobox"
- A11y: input uses with aria-controls + aria-activedescendant; Arrow keys move active option.ElementInternals
- Forms: form-associated via when available (see Form association fallback).--uik-component-combobox-base-*
- Custom properties: , --uik-component-combobox-panel-offset plus listbox tokens.
- Attributes/props: open, placement, popover, value, activeId.trigger
- Slots: , default slot for .control
- Parts: , base.menu-active
- Events: (detail: {id, value, item}), menu-select (detail: {id, value, item}), menu-open, menu-close.role="menu"
- A11y: with roving focus; Escape closes and returns focus to the trigger.--uik-component-menu-
- Custom properties: , --uik-component-menu-item-.
- Attributes/props: value, disabled, active, tabIndexValue (number).base
- Slots: default slot for item label.
- Parts: .role="menuitem"
- A11y: with aria-disabled when disabled.--uik-component-menu-item-*
- Custom properties: .
- Slots: default slot for children.base
- Parts: .role="menubar"
- A11y: with roving focus across triggers; ArrowLeft/ArrowRight + Home/End move; ArrowDown opens.--uik-component-menubar-*
- Custom properties: .
- Attributes/props: activeId, orientation (horizontal | vertical), activation (auto | manual).uik-tab
- Slots: and uik-tab-panel children (slot names are applied automatically).uik-tab
- Pairs: each should share the same value as its uik-tab-panel.tablist
- Parts: , panels.tabs-select
- Events: (detail: {id}).Enter
- A11y: roving focus within the tablist; manual activation uses /Space.--uik-component-tabs-*
- Custom properties: .
- Attributes/props: value on both; selected, disabled, tabIndexValue on uik-tab.base
- Slots: default slots for tab label and panel content.
- Parts: (tab + panel), indicator (tab).
- Attributes/props: items (array), openIds (string[]), currentId.base
- Slots: none.
- Parts: , item, toggle, label.tree-view-activate
- Events: , tree-view-toggle.role="tree"
- A11y: with roving focus; Arrow/Home/End + typeahead navigation; Enter/Space activates; aria-current="page" marks the current item.items
- Notes: typeahead matches labels; selection patterns are app-owned; async loading listens for toggle/activate and updates /openIds.--uik-component-tree-view-item-
- Custom properties: , --uik-component-tree-view-indent, --uik-component-tree-view-text-, --uik-component-tree-view-toggle-fg.
- Attributes/props: items (array), openIds (string[]), currentId.base
- Slots: none.
- Parts: , item, toggle, link, label.nav-select
- Events: , nav-toggle.
- A11y: renders links inside a
- Attributes/props: items (array), activeId, orientation (vertical | horizontal).base
- Slots: none.
- Parts: , item, item-button, item-indicator, item-icon.nav-rail-select
- Events: .--uik-component-nav-rail-bg
- A11y: toolbar role with roving focus; Arrow/Home/End move focus; Enter/Space activate items.
- Custom properties: , --uik-component-nav-rail-fg, --uik-component-nav-rail-width, --uik-component-nav-rail-gap, --uik-component-nav-rail-padding-y, --uik-component-nav-rail-item-size, --uik-component-nav-rail-item-indicator-bg, --uik-component-nav-rail-item-indicator-radius, --uik-component-nav-rail-item-indicator-width, --uik-component-nav-rail-icon-size.
- Attributes/props: open, placement (bottom-start | bottom | bottom-end | top-start | top | top-end), popover (auto | manual | hint).trigger
- Slots: , default slot for panel content.control
- Parts: (trigger wrapper), base (panel).toggle
- Events: native events bubble from slotted trigger; panel listens for when supported; overlay-close (detail: {reason}) with escape | outside | programmatic | toggle.aria-label
- A11y: forwards /aria-labelledby/aria-describedby to the panel.--uik-component-popover-bg
- Behavior: uses the Popover API when available; falls back to a positioned panel in Shadow DOM.
- Custom properties: , --uik-component-popover-fg, --uik-component-popover-border, --uik-component-popover-radius, --uik-component-popover-padding-x, --uik-component-popover-padding-y, --uik-component-popover-shadow, --uik-component-popover-offset.
- Attributes/props: page, page-count, total, max-buttons, aria-label, aria-labelledby.summary
- Slots: .base
- Parts: , list, item, button, summary, ellipsis.pagination-change
- Events: (detail: {page}).page
- Usage: pair with tables or list views and drive , page-count, and total from your data source; override summary for ranges like 1-20 of 240.aria-current="page"
- A11y: marks the active page; label the nav with aria-label/aria-labelledby.--uik-component-pagination-*
- Custom properties: .
- Attributes/props: value, max, indeterminate (boolean).base
- Slots: none.
- Parts: .aria-label
- A11y: forwards /aria-labelledby to the internal
- Attributes/props: name, value, checked, disabled, required, invalid.label
- Slots: , hint, error.base
- Parts: , control, label, hint, error.change
- Events: native bubbles from the internal .aria-label
- A11y: label slot or ; hint/error slots are announced via aria-describedby.uik-radio-group
- Forms: form-associated; when inside , the group manages the submitted value (see Form association fallback).--uik-component-radio-accent
- Custom properties: , --uik-component-radio-size.
- Attributes/props: name, value, orientation (vertical | horizontal), disabled, required, invalid.label
- Slots: , hint, error, default slot for children.base
- Parts: , control, label, hint, error.change
- Events: native bubbles from child radios.aria-labelledby
- A11y: label slot wires to ; hint/error are announced via aria-describedby.ElementInternals
- Keyboard: arrow keys move selection within the group.
- Forms: form-associated via when available (see Form association fallback).--uik-component-radio-group-gap
- Custom properties: .
- Attributes/props: orientation (horizontal | vertical).part="base"
- Events: none; presentational.
- Styling hooks: token-driven color, . Horizontal renders as
, vertical as role="separator".$3
- Attributes/props:
orientation (horizontal | vertical), startSize, minStartSize, minEndSize, step, stepLarge (px).
- Slots: start, end.
- Parts: base, panel-start, panel-end, handle, handle-grip.
- Events: resizable-panels-resize (detail: {startSize, endSize, ratio, source, phase}).
- A11y: handle uses role="separator" with keyboard resizing and aria-valuenow; label defaults to "Resize panels" unless aria-label/aria-labelledby is set.
- Motion/contrast: handle transitions use motion tokens; forced-colors uses system colors.
- Custom properties: --uik-component-resizable-panels-panel-min-size, --uik-component-resizable-panels-panel-start-size, --uik-component-resizable-panels-handle-hit, --uik-component-resizable-panels-handle-size, --uik-component-resizable-panels-handle-bg, --uik-component-resizable-panels-handle-hover-bg, --uik-component-resizable-panels-handle-active-bg, --uik-component-resizable-panels-handle-radius, --uik-component-resizable-panels-step, --uik-component-resizable-panels-step-lg.$3
- Attributes/props:
name, value, disabled, required, invalid.
- Slots: label, hint, error, default slot for