Skip to main content
WireKit

WireKit v2.29.0

Minor release

Minor — the navigation that took the page with it, and four checks that could not fail.

Nothing here changes what an unchanged call site renders. The additions are things the browser and the screen reader were told nothing about until now.

Added

  • Native widgets follow your dark theme. The <select> popup, scrollbars, number spinners, date and color pickers, autofill and the spellcheck menu are painted by the browser rather than by any stylesheet, and the browser had no way to know the page was dark — so it painted them light, on a page that was not. WireKit now declares color-scheme, so they follow. The native <select> is the one this matters most for, because using it is a deliberate choice here: it costs no JavaScript and gives a real picker on a real phone, and its popup was the one surface a screenshot of our own components would never show. Theming

  • A regional locale reaches its language. pt-PT, pt-BR, de-AT, es-MX, fr-CA — every regional variant previously saw none of the shipped translations, because the JSON loader matches a locale's filename exactly and there is no step from pt-PT to pt. An application running regional locales got English chrome inside a fully translated page, with nothing to indicate why. They now resolve through the base language, and your own catalog still wins per key, so you can reword one regional phrase without re-translating everything around it. Localization

  • The overlay landmark is announced in the reader's language. Every teleported panel lives inside one labeled region, and that label was the English word Overlays in every locale — invisible on screen, and therefore visible only to the readers the landmark was built for. It now follows the page's locale. A language WireKit does not yet ship keeps the English name rather than losing it: a region announced as "region" and nothing else helps nobody.

  • Two words the commerce vocabulary was missing. stack, and cash-register — the till the receipts come from, next to receipt, barcode and truck which were all already there. A name outside the vocabulary resolves through whichever icon set happens to be installed, so it works until the day you change one; stack did not even do that, because two of the four sets ship no glyph of that name and the icon package throws on a name it does not know. Icon

  • Self-hosted fonts no longer move the page when they arrive. Every bundled family now ships a metric-matched fallback face: a local system font registered under its own name with the web font's own measurements, named in the stack ahead of the generic families. Measured on a 400px column of prose, the unadjusted fallback rendered the block a full line shorter than the web font — that shift is now zero. The four override percentages are read out of the shipped font files rather than estimated, on both sides of every ratio. Fonts

  • font-display is yours to choose. wirekit.fonts.display sets what every bundled @font-face is served with; the default stays swap, which now costs no layout shift. Pick optional if you would rather some readers never see the web font than see it arrive late — that is a real trade, and it is your application's to make. It reaches the package route and wirekit:publish-fonts; a plain vendor:publish copies files verbatim, so when that leaves a published copy disagreeing with your config, the fonts component says so in the page source and wirekit:verify reports it.

  • <x-wirekit::tabs> can be driven from the server. The new active prop is the active tab as the server sees it, and unlike default it keeps arriving: a tab restored from a URL, a validation error whose field lives in another panel, or a permission that just changed can all move the tablist. Previously the only route was to hand-build the widget, which meant re-implementing its keyboard and ARIA behavior. Tabs

  • <x-wirekit::pricing-table> tells your application which interval was chosen. A hidden input carries the choice into a surrounding form, wire:model binds it, and the new interval prop lets the server set it — so a checkout decided server-side can both read the choice and correct it. Pricing table

  • <x-wirekit::stream> can render your markup instead of its own. The new output slot replaces the component's output block while keeping the state machine and the announcement contract, so a chat bubble or a syntax-highlighted pane can use it as a pure transport. The default slot is unchanged and still additive. Stream

  • <x-wirekit::status-tiles> can let a caption wrap. wrapMeta turns the one-line clamp on the meta line into wrapping text, for the case where the caption is the message rather than a two-word count. Off by default, because a wrapping caption grows the whole grid row. Status tiles

  • Seven words the icon vocabulary was missing. system names the third theme state beside sun and moon; code, key, rocket-launch, chart-line, folders and phone fill gaps that showed up as a name with no answer. key and rocket-launch were previously reachable only by stacking an extension preset, so they are now available on every icon set. Icon

  • Disclosure animations work outside Livewire. Four components ask for Alpine's collapse directive and nothing registered it, so in an Alpine-only application the region snapped open instead of animating and each element logged a warning. It is now registered by every full-catalog bundle, at a cost of roughly 600 gzipped bytes.

Changed

  • wirekit:csp-audit now checks more, and will fail builds it used to pass. Read this one before upgrading: nothing about your templates has changed, but the command was reporting a pass over two things it never looked at.

    It checked whether an expression parses under Alpine's grammar, and stopped there. Under a strict policy an identifier is resolved against the Alpine scope alone — there is no window fallback — so an expression can be flawless grammar and still throw on the first name it reads. JSON.parse('…'), which is what Laravel's Js::from() emits for anything but a scalar, is exactly that shape: it passed the audit and threw in the browser, leaving the element with an empty scope and every directive on it silently dead. It also scanned no wire: attributes at all, though Livewire hands most of their values to the same evaluator.

    Both are now covered. A pipeline that is green today can go red on upgrade — it was green about something that does not run.

  • wirekit:doctor:props and wirekit:doctor:a11y refuse to pass over nothing. A mistyped path produced "no issues found across 0 templates" and exit 0. Reading zero files is now a failure, because a linter that read nothing and reported clean is worse than no linter — you stop looking. Templates that exist but use no WireKit component are a different, honest result and still succeed, saying the count out loud.

Fixed

  • A wire:navigate no longer takes the whole page with it. Since 2.27.0 every overlay panel teleports into a landmark container built by the bundle. A navigation replaces the document body, the container left with it, and the framework treats a teleport whose target is missing as fatal — so initialization stopped at the first overlay on the arriving page and everything after it was never wired up. The result is the expensive kind of broken: the page renders completely, every control is visible, and not one of them does anything. There is no error a reader would see and nothing that looks wrong in a screenshot.

    The obvious repair does not work, and finding that out was most of the fix. The event that announces a completed navigation fires after the framework has already walked the new document, so rebuilding the container there restores it for the next overlay and leaves the failure exactly where it was. The container is now rebuilt in the window between the body swap and that walk. Covered by a case that navigates twice and asserts the arriving page is still interactive, not merely quiet.

  • A page-wide script failure could no longer start with a color. Several places assumed a document body was present before touching it — the overlay container, the chart adapters' theme probe, its color-variable resolver, the optimistic announcer, and two class-attribute observers. An uncaught error there does not cost the thing that failed; it ends the evaluation of the bundle, and everything registered after it stops existing. Each of these now yields a sensible default and stays quiet, and a check holds the whole class rather than the sites that were reported.

  • A menu that stayed dead after an update, in three more components. 2.28.0 fixed this for the dropdown. The same shape survived on the tooltip, the combobox and the multi-select: a teleported panel identified by an id that changes on every render is replaced rather than patched by a Livewire update, the replacement has lost its component scope, and the expression deciding whether it is open resolves against the browser's global object — where open is a real function. Calling it raises Illegal invocation from a stack that names nothing. All four panels now carry a stable key, and a check fails the build on a teleported panel that has an id and no key.

  • wirekit:doctor no longer reports a token asymmetry that is not there — and can now report one that is. The symmetry check located :root and .dark by searching for the text, so .dark also matched inside html.dark and .dark-mode. An application that writes its theme class on <html> — the usual arrangement for setting it before the first paint — was told all of its color tokens were missing from dark mode, and advised to duplicate declarations it already had. Worse in the other direction: the @custom-variant dark line the integration guide asks every application to write also matched, and made the two sides look identical, so a real gap could never be reported at all. The selector is now matched as a selector, a shared :root, .dark { … } head counts for both sides, and a rule nested in a layer or a media query is still found. The check also says when it skipped, instead of falling silent in a way that reads like a pass.

  • wirekit:csp-audit no longer crashes on an ordinary comment — and no longer misses expressions because of one. An apostrophe inside a Blade comment placed among a tag's attributes was read as the start of a quoted value. With an odd number the scan ran past the end of the file and the command failed with an internal error, before it could even tell you the CSP build was not installed. With an even number there was no error at all: the span between them was swallowed and every Alpine expression inside it went unreported — silence from a command whose entire purpose is finding failures that are silent. Both halves came from one hand-written scanner that a sister scanner had already been fixed for; there is now one, and all three places that read Blade tags use it. wirekit:show --validate-against was the third, and it was quietly wrong in its own way: it read words out of expression text as if they were attributes.

  • vendor:publish --tag=wirekit-lang hands over every catalog. It named English and German because those were the two that existed when it was written; five more shipped and it kept handing over two, while the documentation promised seven. Nothing failed — a publish list cannot notice a file it was never told about. It is now derived from what ships, so the next language needs no edit.

  • A blank reserved for a validation message is no longer selectable. The spacer that keeps a field from shoving its neighbors around, and the reserved message row on input, select and textarea, each render a non-breaking space that exists only to hold height. Dragging a selection across a form collected one of them per reserved field and carried them into the clipboard. They are marked unselectable now, which is the pointer half of the decision that already hid them from screen readers.

  • wirekit:csp-audit reads what the browser reads, not what Blade left behind. Server-side constructs are removed before the expression is parsed — a comment outright, an escaped or raw echo and @js(…) as a placeholder, with the parentheses of @js(…) balanced across strings so a payload containing ) does not truncate the rest. Before this, @js(…) — the pattern this very command recommends in its own advice — was reported as blocked with a syntax error at the @, and a raw echo carrying JSON was reported as a broken object literal. Neither is anything a reader could act on: by the time Alpine sees the attribute, the server text is gone.

    Two more corrections in the same area. x-teleport is no longer scanned at all: its value goes straight to document.querySelector(), so it is a CSS selector, and a selector beginning with # was being reported as an expression beginning with an operator. And a rejection now counts as a finding only when the parser saw what the browser sees — an expression the audit could not read is listed separately and does not fail the run, because "the tool could not check this" and "Alpine will refuse this" are different facts and only one of them is yours to fix. CLI reference

  • The setup guide listed five CSS features as safe to rely on that are not. @starting-style, field-sizing: content, text-wrap: balance, native CSS nesting and round() all require browsers newer than the supported floor, so an application taking the sentence at face value would ship something a reader on a supported browser does not get. They are now named for what they are — worth using behind a feature test — and where WireKit uses one itself, it already sits behind one. Every version in the correction was read from browser-compatibility data rather than recalled. Integration

  • Two field-composition recipes in the documentation did not do what they said. The pattern for putting a button beside a labeled field described a wrapper that produces a different vertical rhythm than the one the field itself uses, so following it left the control a hair off. Corrected, and pinned by a check so the page and the component cannot drift apart again. Field

  • A table that scrolls sideways shows its edge hint before you scroll. The hint is the only sign that more columns exist, and it was appearing only after the first scroll — after the moment it is for. Its two markers sat on the wrong axis. Table

  • wirekit:doctor:props no longer invents props that are not there. A Blade echo inside a quoted attribute value — the shape any translated attribute built from a variable takes — was read as more attributes, so a translation key surfaced as a prop the component never declared.

  • wirekit:verify recognizes every real form of the ApexCharts global assignment. window.ApexCharts ??= …, globalThis.ApexCharts = … and the bracket form were reported as missing, TypeScript entry points were not read at all, and a commented-out line counted. In the other direction, window.ApexCharts == null was read as an assignment — a feature detect passing as a completed setup.

  • wirekit:verify no longer recommends putting a chart library on every page. Its ApexCharts advice named only the global entry point, which is roughly 850 KB on pages without a chart. It now names the route-scoped alternative too, with the weight attached.

  • Six documentation pages named icons that do not exist on a default installation. They rendered a hole for anyone following them without an extension preset. Two of them wanted a telephone, which is why phone is now a word.

  • The design-token reference credited the wrong spacing ladder. The gap prop of the layout primitives resolves to the --space-wk-* scale, not --gap-wk-* — so overriding the latter to widen a grid changed nothing, and the page said to look there. Design tokens

  • A 2.27.0 note credited wirekit:verify with a check that lives elsewhere. Duplicate adapter registration is reported — by the adapter, as a browser console warning — so running the command and seeing nothing meant nothing. The entry now says where the report comes from.