Skip to main content

WireKit v1.4.0

Minor release

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::action-bar mode="static">, <x-wirekit::toast-region eventScope>, and the new SandboxRenderer::BODY_WRAPPERS map.

No breaking changes — fully backward-compatible with v1.3.0.

Added

  • <x-wirekit::action-bar mode="static"> — second layout mode for the action bar. The default mode="floating" keeps the existing position: fixed + viewport-centring transforms (fully back-compat). The new mode="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::toast-region eventScope="…"> — 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 existing name parameter (event-name routing) is unchanged and still works in parallel; eventScope is additive. Default null preserves the global-listener behavior.
  • Pushery\WireKit\Sandbox\SandboxRenderer::BODY_WRAPPERS map — auto-wraps the sandbox body slot in a sub-component for primitives whose composition requires it. The Card schema now wraps its body in <x-wirekit::card.body> 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.

Fixed

  • Resizable handle drag no longer flickers from text-selection in adjacent panels. The handle's pointerdown already called setPointerCapture(), but the body user-select was 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"). onPointerDown now captures the prior inline value of body.style.userSelect and sets it to 'none'; onPointerUp restores it. pointercancel already routes through onPointerUp so a tab-switch mid-drag also cleans up — no leak.
  • Vertical <x-wirekit::resizable> no longer collapses to 0 height when the wrapper has no explicit size. [data-wk-resizable][data-wk-direction="vertical"] carries contain: 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 default min-height: 16rem on the vertical wrapper so unstyled containers still render visibly. Authors with explicit inline style="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 required aria-expanded (combobox, multi-select), aria-valuenow (image-compare, range-slider), or aria-checked (rating, segmented-control). Each affected component now ships a static aria-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::brand> in logo-only mode auto-injects aria-label="Home" (caller's aria-label still wins); <x-wirekit::toast-region> adds role="region" so its aria-label="Notifications" is permitted; <x-wirekit::date-picker>, <x-wirekit::color-picker>, <x-wirekit::slider> accept a new label prop and fall back to a sr-only label derived from name when no label / aria-label / aria-labeledby is provided; <x-wirekit::navigation-menu.item> link-mode now falls back to the default slot when no trigger prop is set so the canonical <x-wirekit::navigation-menu.item href="/x">Label</x-wirekit::navigation-menu.item> pattern produces a non-empty link.
  • Card sandbox schema rendered as a bare rounded pill instead of a proper card. The Card sandbox schema declared padded / bordered / elevated boolean 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::card.body>, so slot text pressed flush against the rounded card edges. Schema rewritten to use the real variant prop (outlined / elevated / flat); body slot now auto-wraps via the new BODY_WRAPPERS map.
  • code-block sandbox schema declared lang, but the actual prop is language. The renderer emitted <x-wirekit::code-block lang="php"> which the component ignored — the syntax-highlight language class was never applied. Renamed the schema key to match the component contract.
  • text sandbox schema declared a muted boolean. The Text primitive has no muted prop; it uses a string variant enum with allowed values default / muted / subtle / accent / success / warning / danger. Replaced the boolean with the real variant enum.
  • Callout sandbox schema dropped the un-renderable title reference. title is a named slot in the Callout primitive (@isset($title)), not a @props entry, 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. (alert keeps title because <x-wirekit::alert> declares title as a real @props entry — different shape.)
  • button and badge sandbox schemas: slot key label → body. Earlier iterations declared label as 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 reserved body convention.
  • clipboard-button button-width regression on state change. The first iteration of the stable-width fix used x-show to toggle which label rendered inside the grid cell. x-show sets display: none on 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 static style="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.
  • ticker rendered ++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-shell defaulted to a width that didn't fill its parent. Added w-full to the shell's base classes so it correctly fills its parent container regardless of layout context.
  • code-block defensive styling against inherited inner <pre> / <code> background. Added bg-transparent and radius-none on 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.md icon 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.