Skip to main content
WireKit

WireKit v2.31.0

Minor release

Minor — a Brazilian Portuguese catalog, a font of your own that no longer moves the page, a CSP audit you can act on without checking it first, and six pages that were teaching the version before this one.

Nothing here changes what an unchanged call site renders. Two commands report more than they used to: wirekit:csp-audit gained a warning class that leaves your exit code alone, and wirekit:verify compares every published bundle instead of four.

Added

  • A tab bar the server drives, with no panels — <x-wirekit::tabs.list> and <x-wirekit::tabs.tab>. tabs assumes the browser already holds each panel's content, which is the wrong shape for the arrangement a Livewire application reaches for first: a row of tabs above content the server renders, where choosing one is a round trip. Two of the things the full component does are actively wrong there — it holds the selection your server has already decided, and it emits aria-controls pointing at panels that do not exist, sending a screen-reader user somewhere there is nothing. The bar on its own holds nothing at all: selected is a plain server-side boolean that drives both aria-selected and the roving tabindex, and the keyboard handlers resolve the tabs from the DOM on every keypress, so Livewire replacing the markup cannot leave focus pointing at an element that no longer exists. Activation is manual — arrows move focus, Enter or Space commits — because selection following focus would fire one request per arrow key and render four pages nobody asked to see. Both bars are styled from one source, so variant and orientation mean the same thing in either. See Tabs.

  • Brazilian Portuguese. pt is European Portuguese, and regional locales resolve through their base language — so pt-BR was rendering fluent Portuguese of the wrong variety. A Brazilian reader saw (abre num novo separador) where they expect (abre em uma nova aba), and A carregar where they expect Carregando. Nothing failed: the page rendered, the screen reader read it out, and only a native speaker looking at the right string would ever have caught it. pt-BR now ships as a delta over pt — it holds only the strings the two varieties spell differently, so the shared wording stays in one place and cannot drift apart. Localization names the variety of every catalog, so you can decide deliberately rather than inherit one.

  • A font you host yourself can get a metric-matched fallback. Every bundled family ships one — a local system font registered under the family's own name with the web font's measured metrics, so text painted before the swap occupies the same box as text painted after it. Your own family got none of that, which is the setup null is for. Declare the four measured values in wirekit.fonts.fallbacks and WireKit emits the face. Empty by default and nothing is invented: a guessed size-adjust moves the layout in the other direction and looks deliberate while doing it. The method, including the two parts that are easy to get wrong, is on the Fonts page.

  • The font component's inline <style> can carry a CSP nonce. It is the only inline style WireKit emits, and it has to stay inline — the three custom properties come from your font configuration, not from our build. Without a nonce you cannot drop 'unsafe-inline' from style-src, and CSP Level 2 makes that a cliff rather than a slope: a nonce anywhere in a directive makes the browser ignore 'unsafe-inline' in that same directive, so this block loses its permission the moment you add a nonce for anything else. Silently — the page renders and your typography falls back to the system font. The value resolves itself from a csp-nonce container binding or Vite::cspNonce(), so an application that already has one needs no configuration; an explicit :nonce still wins.

  • status-tiles items can state the word they report. The visible status word was derived from intent, which has five values — so a domain with more states folds two onto one word, and the pair it folds is usually the one carrying the information. A health check that ran and found a problem and one that crashed are both danger, and both read "Critical": the first points at your application, the second at your monitoring. Pass status per item and the tile says your word, in the caption and in the screen-reader text alike.

  • stream gained replace(text). push() cannot express a rendering derived from the whole stream rather than accumulated from its parts — masked text where a placeholder breaks across a chunk boundary, incremental Markdown where a closing fence changes what came before it, anything that diffs. Setting text directly looks like the workaround and quietly skips the reduced-motion buffer; replace() takes the same path a push takes.

  • The sidebar item's icon size can be personalized. It was a literal in the render call, so the only way to resize it was to take over the surrounding block — and a taken-over block stops inheriting improvements silently, which is the one outcome the closure form exists to avoid.

Changed

  • wirekit:csp-audit no longer reports a bare PASS for an expression it did not fully measure. Blade renders before Alpine reads, so an attribute carrying {{ … }} is checked with an identifier standing in for the part the audit cannot see. That substitution was invisible, and it hid the case that matters: Js::from() — Laravel's attribute-safe encoder, and the documented way to hand data to Alpine — renders to JSON.parse('…'), and JSON is precisely the name the CSP evaluator cannot resolve. The audit rejected that call written out by hand and passed it written the way everybody writes it, in the same file on the same line. A run now counts the expressions resting on a substitution, names the ones that call Js::from() or @js() so you can look at those first, and qualifies its verdict instead of claiming everything resolves in scope. Your exit code does not change — the same encoder emits a plain literal for a number, a boolean, [] or {}, and a violation that turns out to be nothing costs the next hundred their credibility. See CLI reference.

  • wirekit:csp-audit warns when an expression resolves but may not evaluate. Alpine's CSP evaluator refuses a value, not a name: it throws on any property access whose result is one of globalThis's own. So a chain can parse, resolve every identifier, run, and be rejected the moment it touches something global — $el.ownerDocument.location reaches window.location by another route. Reported as a warning that leaves your exit code alone, because the rule approximates a question about runtime values. The output also names what it measured, so a pass is not read as more than it is, and the CLI reference documents the supported shape.

  • wirekit:verify compares every published bundle, and reports a config option by name. The freshness check compared four; every bundle added since inherited nothing, so an upgrade could leave six stale files behind a clean report. It is derived from the package now. The config check also names the missing options and prints each default instead of counting them, and reports an option your file still carries that this version no longer offers.

Fixed

  • wirekit:csp-audit read a bare : on a <livewire:…> tag as an Alpine expression. On a component tag that is Blade's prop binding and its value is PHP, finished on the server. The scanner knew that for <x-…> only. Measured in a real application: 13 of 18 reported violations were false, and 12 of those were this one construct.

  • wirekit:csp-audit judged a Livewire action as written, not as Livewire presents it. wire:click="confirm" is rewritten to a call on the component proxy before Alpine sees it, so a method whose name collides with a browser global was reported as a dead handler while working perfectly.

  • countdown's locale prop did nothing. The x-data object declared the key twice, and a JavaScript object literal keeps the last one — so the line that read the prop was dead and locale="fr-FR" on a German page counted in German.

  • The unknown-prop check now reaches sub-components. It covered the top level and none of the 76 sub-component views — accordion.item, table.th, list.item, field.*, dropdown.panel and the rest, which are exactly the tags you write in a loop.

  • An overlay container you write yourself is finished, not just adopted. WireKit creates #wk-overlay-root and names it in the page's language; one already in your markup was handed straight back without a role or a label. Writing it yourself is the sturdier route — it sits in the markup Livewire morphs — so the localization was reaching everyone except the readers who had built the better setup. A label you wrote is left alone.

  • wirekit:csp-audit said "bridge" when it meant "node". A missing executable does not throw, so the branch written for it never ran and the message never contained the word node. It now says so before the process starts, and the dependency is named in the command's own description.

Documentation

  • Six pages were teaching the version before the one that linked them. All six had the same shape: the reference table was maintained and the prose around it was not.

  • Fonts recommended writing your own font-display rules — the exact hand workaround wirekit.fonts.display had replaced, and rules that land on the same faces as the metric-matched fallbacks that are the actual fix.

  • The CLI reference framed the CSP audit as a grammar check alone, which is precisely the half that does not explain why a passing build had started failing. It now says what the command looks at, and what it deliberately does not.

  • The script-order rule named the wrong invariant. @wirekitScripts must RUN before Alpine starts — which its own defer guarantees in a Livewire layout, so the order of the two directives is not the lever. The troubleshooting table sent you to check a tag order that was already correct, ruling out the wrong hypothesis and keeping the right one.

  • Scroll area documents that start and end are the reading edges, so a horizontal fade follows the document direction.

  • Table documents the edge hint that tells a reader the row continues before they touch it.

  • Integration documents the overlay container, the data-wk-overlay-label attribute, and wirekit.assets.middleware — the last of which the previous release's own entry had linked to a page that never named it.