WireKit v2.3.0
Minor release. Five surface areas touched: form-input accessibility (multi-select, range-slider), shared-prop-vocabulary aliases (feature, card, button, heading, reveal), icon-system completeness (8 new base aliases per preset + bare-name fallthrough resolution), new main max-width default + button forceLoading, two new artisan-command capabilities (wirekit:show <parent>.<child>, wirekit:verify --fix), plus a new static-analysis a11y linter wirekit:doctor:a11y. Documentation site grows the unified /blueprints/ route with /blueprints/ (8 marketing-section building blocks) and /blueprints/ (10 worked-example compositions) under a fresh index page.
Added
ariaLabelprop on<x-wirekit::. Optional explicit override for the screen-reader-announced label of the component's internal comboboxmulti-select> <input>. When unset, the component auto-derives a sensible label from (in order) any passthrougharia-labelattribute, then thelabelprop, then theplaceholder, then finally thename— so the internal input is never unlabeled even if the consuming code does not supply one explicitly.forceLoadingprop on<x-wirekit::. Whenbutton> true, renders the spinner unconditionally and disables the button regardless of anywire:loadinggate. Useful for static documentation demos, non-Livewire contexts, or stories that want to preview the loading state without a backend request in flight. The existingloadingsemantics are unchanged —loading=truealone still auto-detects the wire-action context.maxprop on<x-wirekit::with defaultmain> "2xl". Caps the inner content width to the matching--size-wk-container-*token tier. Default"2xl"matches the existingcontainercomponent default and prevents dashboards from stretching edge-to-edge on 1900 px+ monitors. Opt out withmax="none"to preserve the pre-2.3.0 unbounded behavior, or pick another tier (sm/md/lg/xl/full). The config keywirekit.overrides the default app-wide.components. main. max - 8 new base icon aliases in every preset —
home,moon,sun,book-open,sign-out,megaphone,map,file-text. The four base presets (Heroicons, Lucide, Phosphor, Tabler) now ship 34 aliases each (was 26). Every common first-install icon (theme toggle, sidebar home, sign-out button, file row) resolves without an additional preset. - Icon-alias fallthrough resolution. When
<x-wirekit::references an alias that isn't in the active preset, the resolver now tries the underlying blade-icons identifier (icon name="briefcase"> heroicon-m-briefcase, then-o-, then-s-) before throwingUnknown icon alias. Logs an INFO line so the developer can add the alias to their preset. Catches the bug class where a developer reaches for a heroicons name that ships inblade-ui-kit/but wasn't aliased by WireKit.blade-heroicons php artisan wirekit:show <parent>.<child>resolves dotted sub-component names. Pre-fix,php artisan wirekit:show cardadvertisedSub-components: card.bodybutphp artisan wirekit:show card.bodyreturned "Unknown component" because the component registry tracks only top-level components. The artisan command now resolves sub-components by reading the nested Blade file directly and extracting props via the same parser the top-level command uses. Honors--as=jsonso AI tooling can introspect sub-components the same way as top-level components.wirekit:verify --fixself-heals missing public assets. When the doctor findspublic/orvendor/ wirekit/ wirekit. css wirekit.jsmissing AND--fixis set, it proactively runsvendor:publish --tag=wirekit-assets --forceand re-tests the asset paths so the verify report comes back clean. Closes the fresh-clone-fails-the-doctor gap where the.gitignoredpublic/directory is empty until the publish runs. Withoutvendor/ wirekit/ --fix, the existing "Run: php artisan vendor:publish ..." hint is augmented with an "Or: php artisan wirekit:verify --fix" alternative.wirekit:doctor:a11y— new static-analysis a11y linter. Scans every.blade.phpunder a given path for three high-value WCAG-AA bug classes: icon-only<x-wirekit::withoutbutton> aria-label,role="dialog"/"alertdialog"withoutaria-label/aria-labeledby,role="img"withoutaria-label. CLI:php artisan wirekit:doctor:a11y [path] [--fail-on=error|warning|none]. Exit 1 on any finding ≥ threshold. Pair withwirekit:verifyin CI to gate both integration health AND a11y in one pass.tag_aliasfield inwirekit:export-jsonfor class-based components. Class-based components whose canonical tag uses the single-hyphen form (<x-wirekit-chart>) now also emit atag_aliasfield carrying the double-colon form (<x-wirekit::). Lets downstream tool integrators that grep against the historical shape still match. Normal anonymous components have no alias — the field is omitted entirely so the schema stays minimal for the common case.chart> libraryprop on<x-wirekit-chart>— per-instance chart-library override. Accepts the same values asconfig('wirekit.— the built-in keyscharts. library') "chartjs"/"apexcharts", or a fully qualified class name implementingChartAdapter. Whennull(default), the chart uses whatever the global config resolves to — matching pre-v2.3.0 behavior. When set, this chart instance binds to the named library regardless of the app default, so two charts on the same page can use different libraries. Useful when a specific chart type requires a specific library (boxplot/candlestick/heatmap/treemap/columnare ApexCharts-only and throwTypeagainst the Chart.js adapter) inside an app whose default is the other library. Backwards compatible — existing charts that don't set the prop continue to read from config exactly as before. The wrapper componentsNot Supported Exception <x-wirekit::andchart-mixed> <x-wirekit::accept and forwardsparkline> libraryto the underlying chart, so mixed-library pages work via either tag shape. Example:<x-wirekit-chart type="column" library="apexcharts" ... />renders the ApexCharts column chart even when the app's default ischartjs.
Changed
<x-wirekit::feature> toneprop accepts the canonical intent vocabulary.primaryis now accepted as an alias foraccent(same color role), andinfoas an alias forsoft(same tinted-accent treatment). The pre-2.3.0 names (accent,neutral,soft,success,warning,danger) are unchanged. Developers copyingintent="primary"from a<x-wirekit::no longer get a 500 onbutton> <x-wirekit::.feature> <x-wirekit::heading> sizeprop acceptsmd/4xl/5xl. The size enum now readssm | md | base | lg | xl | 2xl | 3xl | 4xl | 5xl.mdis an alias for the existingbasetier (matches the rest of the WireKit's middle-tier convention);4xl(2.25 rem) and5xl(3 rem) are new tiers for hero copy. The--text-wk-4xland--text-wk-5xldesign tokens are shipped indist/.wirekit. css <x-wirekit::acceptscard> variant="outline"as alias foroutlined.<x-wirekit::acceptsbutton> surface="outlined"as alias foroutline. Same visual treatment, two spellings — the WireKit's pre-2.3.0 vocabulary diverged. Both forms now work on both components; canonical spellings (outlinedfor card,outlinefor button) are unchanged.<x-wirekit::profile> avatarprop acceptsstring | array{src?, initials?, alt?}. Pre-fix the prop required a URL string; passing an array crashed with ahtmlspecialchars(): Argument #1 ($string) must be of type stringerror. Now matches the shape thatmessage.authorand the avatar primitive already accept. The initials fallback renders a rounded-full muted-background span witharia-labelset to the profile'snameso screen readers still announce who the profile belongs to.<x-wirekit::reveal> presetprop acceptsfade-up/fade-down/fade-left/fade-right. Aliases forslide-up-in/slide-down-in/slide-left-in/slide-right-in. Matches thefade-*shorthand naming convention designers reach for first. Existingslide-*-incallers unaffected.animateInprop on every marketing component accepts the samefade-*aliases. Card, feature, hero, stat, callout, alert, empty-state, cta, and footer all route throughWireKit::, which now resolves the fourresolve Animate In() fade-*shorthand aliases identically to<x-wirekit::. Pre-fix,reveal> <x-wirekit::rejected the alias as an unknown enum value (silent no-op in production,card animate In="fade-up"> Invalidin debug). Now byte-identical resolution:Argument Exception fade-up→slide-up-inon every surface. Existing callers unaffected.<x-wirekit::consumessidebar. item> data-currentas fallback for:active. Livewire 4 emitsdata-currentautomatically onwire:navigatelinks. Pre-fix, sidebar.item ignored the attribute and required the developer to manually pass:active="request()->is('posts*')", duplicating routing knowledge already encoded inroutes/web.php. The explicit:activeprop still wins; if unset and the attribute bag carriesdata-current="true"/data-current="page"/data-current="1", the item highlights automatically./blueprints/documentation route —partials/*andrecipes/*sub-sections. The/blueprints/index page lists every entry with per-card previews and copy-paste guidance, organized under two sub-sections:/blueprints/— 8 drop-in marketing-section building blocks: hero, features, pricing, testimonials, logo-cloud, FAQ, CTA, footer. Each partial is a single Blade snippet you paste into a parent layout and customize.partials/* /blueprints/— 10 worked examples composing several WireKit primitives into a complete UI shape: long-form article shell, documentation reader, on-page TOC, marketing landing page (with TOC variant), hero-with-code-aside, live KPI strip, stat-with-sparkline, feature-with-numbered-marker, toolbar-filter-bar.recipes/*
<x-wirekit::acceptsbrand> <x-slot:logo>for custom logo markup. Pre-fix the prop required a URL string for<img src>; passing a slot stringified the slot HTML inside the<img src>attribute and produced broken DOM output (visible attribute-fragment text). The component now detects aComponentSlotinstance and renders the slot directly. URL-string callers unaffected.
Fixed
<x-wirekit::internal comboboxmulti-select> <input>now always carries anaria-label. Previously, the parent<x-wirekit::'s emittedfield label="…"> <label for="$id">did not reach the internal combobox input (whose id is$id-input, not$id), so screen readers + axe'slabelrule reported an unlabeled form element. The component now synthesizes anaria-labelvia the fallback chain above and emits it on the internal input. WCAG 2.1 AA-compliant for any developer that has been wrapping multi-select in a<x-wirekit::with afield> label— no developer-side change required. Backward compatible.<x-wirekit::thumb-circles now sit vertically centered ON the track line. Pre-fix, the two thumbrange-slider> <div>s were siblings of the track div, so theirtop: 50%; transform: translateresolved relative to the FULL component wrapper (track + edge labels + value display) and the thumbs visually dropped BELOW the track line. The thumbs now live INSIDE the track div, so their vertical centering resolves to the 8 px track height and the circles sit pixel-centered on the line.Y(-50%); <x-wirekit::edge labels now show the slider BOUNDS, not the current values. Pre-fix, the bottom-of-component value display renderedrange-slider> minVal/maxVal(the current handle positions) at the container edges viajustify-between. The result was that the visible label "20" sat at position 0 % of the container, while the thumb representing value 20 sat at its proportional position (e.g. 6.7 % on a 0–300 range), and the two looked disconnected — and when the values matched the bounds exactly, the label and the thumb collided at the same pixel. Now: the edge labels render the constant slider min / max ({{ $min }}and{{ $max }}), and the current values surface as a tooltip-style badge ABOVE each thumb that tracks the handle horizontally as it moves. Screen-reader users still get a live announcement of the current range via anaria-live="polite"text node ("Range: 20 to 200") that updates on drag.- Prop validation no longer crashes the whole page in dev HTTP requests. Pre-fix,
<x-wirekit::(or any other invalid prop value) threwheading size="4xl"> InvalidinArgument Exception APP_DEBUG=trueand the whole blade view 500'd. Now: strict-mode is split into two paths. CLI / Pest /wirekit.still throws (fail-fast — the signal a dev wants in a test or artisan command). HTTP dev requests log at ERROR level and render with the first-allowed fallback so a single prop typo doesn't take down the entire page. The dev still sees the typo loudly in the log; iteration continues without a request-response restart.validation. throw_ on_ invalid=true <x-wirekit::warns at log level on unknown prop keys. Pre-fix,button> <x-wirekit::(the prop isbutton variant="ghost"> surface, notvariant) was silently swallowed — the button rendered with defaultsurface="filled"and the developer got no signal that their intendedghosttreatment didn't apply. The newWireKit::helper logs a Levenshtein-ranked Did-you-mean warning for any unknown key not in the canonical Blade-passthrough allowlist (warn Unknown Props() aria-*,data-*,wire:*,x-*,@*,:*, plus reserved HTML attrs). Public helper — opt-in for every component that wants the warning;buttonandmainalready adopt it.<x-wirekit::hover-boundary flicker. Cursor jitter (1–2 px) at the spine's top or left edge fired alternatingreading-spine> mouseenter/mouseleaveevents on every frame, toggling the spine expanded ↔ collapsed once per frame and producing a visible flicker. Hover-collapse now debounces by 120 ms — a re-entry within that window cancels the pending collapse, absorbing the jitter. Expand still fires immediately on initial hover, so the responsive direction is unchanged. Focus-driven expansion (expandOnFocus/collapseOnFocus) is unaffected.<x-wirekit::implicit horizontal scrollbar inside the spine. The spine carriedreading-spine> overflow-y-autobut no explicitoverflow-x. Per CSS spec, setting one axis toautowhile the other defaults tovisiblecomputes BOTH axes toauto— so longwhite-space: nowrapheading labels inside the spine produced a thin horizontal scrollbar gutter even though the ellipsis truncation already clipped them visually. Pinned tooverflow-x-hidden overflow-y-autofor true vertical-only scroll.- Themed scrollbar (
.wk-scrollbar) applied across every component with an internal scroll region.<x-wirekit::body,modal> <x-wirekit::body,drawer> <x-wirekit::,alert-dialog> <x-wirekit::,code-block> <x-wirekit::dropdown list,combobox> <x-wirekit::overlay + inner list,command-palette> <x-wirekit::(horizontal row) +kanban> <x-wirekit::(vertical list),kanban-column> <x-wirekit::,main> <x-wirekit::, andreading-spine> <x-wirekit::previously showed the OS-default scrollbar (thick on Windows, hidden-until-scroll on macOS). Each now applies WireKit's themed scrollbar — thin width, theme-aware thumb / track colors, dark-mode reactive.reading-toc> <x-wirekit::prose> <pre>children inherit the same treatment via a.wk-prose preCSS rule (no template change required).<x-wirekit::,multi-select> <x-wirekit::, andscroll-area> <x-wirekit::already carriedtable> .wk-scrollbarand are unchanged. Override the colors / width via--color-wk-scrollbar-thumb,--color-wk-scrollbar-track,--color-wk-scrollbar-thumb-hover,--size-wk-scrollbarin:root {}.