WireKit v1.4.0
Minor release covering accessibility, sandbox-schema, and component-render fixes that surfaced after v1.3.0 shipped, plus three additive opt-in prop additions: <x-wirekit::, <x-wirekit::, and the new SandboxRenderer:: map.
No breaking changes — fully backward-compatible with v1.3.0.
Added
<x-wirekit::— second layout mode for the action bar. The defaultaction-bar mode="static"> mode="floating"keeps the existingposition: fixed+ viewport-centring transforms (fully back-compat). The newmode="static"flows inline with surrounding content (drops the fixed positioning + the centring transforms; keeps the same chrome — border, shadow, padding, rounded corners). Useful when the bar is part of a card / panel / dashboard rather than a viewport-floating overlay.<x-wirekit::— optional CSS selector that scopes incoming toast events by DOM containment. When set, only events whose dispatching element is inside an ancestor matching the selector are handled. Useful for "per-section toast surfaces" where multiple toast regions on the same page must not cross-talk. The existingtoast-region eventScope="…"> nameparameter (event-name routing) is unchanged and still works in parallel;eventScopeis additive. Defaultnullpreserves the global-listener behavior.Pushery\map — auto-wraps the sandbox body slot in a sub-component for primitives whose composition requires it. The Card schema now wraps its body inWireKit\ Sandbox\ SandboxRenderer:: BODY_ WRAPPERS <x-wirekit::so developer-side sandbox renders carry the full card chrome with padded body content instead of a bare rounded pill. Open extension point: future multi-slot primitives (tabs, accordion, …) can opt in by adding an entry.card. body>
Fixed
- Resizable handle drag no longer flickers from text-selection in adjacent panels. The handle's
pointerdownalready calledset, but the bodyPointer Capture() user-selectwas never disabled during drag — so the cursor crossing a sibling panel highlighted that text and the browser repainted the selection mid-drag (visible as a "flicker").onPointerDownnow captures the prior inline value ofbody.and sets it tostyle. userSelect 'none';onPointerUprestores it.pointercancelalready routes throughonPointerUpso a tab-switch mid-drag also cleans up — no leak. - Vertical
<x-wirekit::no longer collapses to 0 height when the wrapper has no explicit size.resizable> [data-wk-resizable][data-wk-direction="vertical"]carriescontain: size, which requires an explicit container size — without one, the panels' percent heights resolve against a 0-height container and the whole component disappears. Added a defaultmin-height: 16remon the vertical wrapper so unstyled containers still render visibly. Authors with explicit inlinestyle="min-height: …"keep their value (CSS specificity favors the inline rule). - WCAG 4.1.2 (Name, Role, Value) sweep across 11 interactive components. Alpine's reactive
:aria-*bindings only emit the attribute after the JS boots — initial server-rendered HTML lacked the requiredaria-expanded(combobox, multi-select),aria-valuenow(image-compare, range-slider), oraria-checked(rating, segmented-control). Each affected component now ships a staticaria-foo="default-value"immediately before the:aria-foo="reactive-binding"so server-rendered HTML is WCAG-complete from the first paint and Alpine overrides reactively after hydration. Plus:<x-wirekit::in logo-only mode auto-injectsbrand> aria-label="Home"(caller'saria-labelstill wins);<x-wirekit::addstoast-region> role="region"so itsaria-label="Notifications"is permitted;<x-wirekit::,date-picker> <x-wirekit::,color-picker> <x-wirekit::accept a newslider> labelprop and fall back to a sr-only label derived fromnamewhen no label /aria-label/aria-labeledbyis provided;<x-wirekit::link-mode now falls back to the default slot when nonavigation-menu. item> triggerprop is set so the canonical<x-wirekit::pattern produces a non-empty link.navigation-menu. item href="/x">Label</x-wirekit:: navigation-menu. item> - Card sandbox schema rendered as a bare rounded pill instead of a proper card. The Card sandbox schema declared
padded/bordered/elevatedboolean props that the Card primitive never read in its@props([...])block — the renderer emitted them as raw HTML attributes (silently no-op), and the body slot was never wrapped in<x-wirekit::, so slot text pressed flush against the rounded card edges. Schema rewritten to use the realcard. body> variantprop (outlined/elevated/flat); body slot now auto-wraps via the newBODY_WRAPPERSmap. code-blocksandbox schema declaredlang, but the actual prop islanguage. The renderer emitted<x-wirekit::which the component ignored — the syntax-highlight language class was never applied. Renamed the schema key to match the component contract.code-block lang="php"> textsandbox schema declared amutedboolean. The Text primitive has nomutedprop; it uses a stringvariantenum with allowed valuesdefault/muted/subtle/accent/success/warning/danger. Replaced the boolean with the realvariantenum.- Callout sandbox schema dropped the un-renderable
titlereference.titleis a named slot in the Callout primitive (@isset($title)), not a@propsentry, so the renderer's string-prop-as-HTML-attribute path could never populate it. Schema now carries only the props the renderer can actually deliver. (alertkeepstitlebecause<x-wirekit::declaresalert> titleas a real@propsentry — different shape.) buttonandbadgesandbox schemas: slot keylabel→body. Earlier iterations declaredlabelas the slot-content key, which the renderer treated as an HTML attribute and never inserted into the slot — sandbox renders produced empty buttons / badges. Renamed to the renderer's reservedbodyconvention.clipboard-buttonbutton-width regression on state change. The first iteration of the stable-width fix usedx-showto toggle which label rendered inside the grid cell.x-showsetsdisplay: noneon the inactive label, removing it from layout entirely; the grid cell then collapsed to whichever child was visible — defeating the stable-width goal. Replaced with:style="{ visibility: ... }"toggling so both labels stay in layout permanently and the grid cell sizes to the wider one. The copied-state span also carries a staticstyle="visibility: hidden"so its layout slot is reserved before Alpine init evaluates the bindings — no flicker on first paint, no width jump on state change.tickerrendered++8.4%for already-signed-string deltas. The delta-formatting block prepended+to positive deltas, then interpolated the original input verbatim — inputs that already carried an explicit sign rendered with double prefix. Strip leading+from string deltas before re-deriving the sign from the numeric value. Both"8.4"and"+8.4"now render identically as+8.4%. Negative signed strings ("-1.2") keep their leading-. Numeric inputs (int / float) and unsigned strings remain unchanged.app-shelldefaulted to a width that didn't fill its parent. Addedw-fullto the shell's base classes so it correctly fills its parent container regardless of layout context.code-blockdefensive styling against inherited inner<pre>/<code>background. Addedbg-transparentandradius-noneon the inner elements to prevent host-page CSS from bleeding through.- WCAG 1.4.3 contrast sweep for soft-bg foreground tokens. Round-2 polish on calendar week-view chips, finance cells, opacity-overlaid cards, and CTA buttons — every soft-tinted background now pairs with a foreground token that meets the 4.5:1 ratio in both light and dark mode. Plus a P0 fix to
dist/(wirekit. css :root {}instead of@theme {}so a plain<link rel="stylesheet">load works without the build pipeline). - A11y polish on shipped components: center component fills its parent, callout becomes a
<section>for proper landmark semantics, kanban-column scroll body gets the missing scroll-region a11y wiring.
Changed
README.mdicon section now lists both stackable extension presets (heroicons-app,heroicons-marketing) consistently. Earlier the stackable presets only surfaced in the Available-Presets table near the bottom of the icon README block while top-of-page sections (Requirements tip, Configuration block, Switching Presets) referenced base presets only — discoverability gap closed.