WireKit v2.67.0
Minor. A card can fold under its own head, for a page of several cards where only the ones that need the reader stand open. A shop can show the EU energy label and the EU tire label beside a price, and compact classes, opt-in, write the kit's utilities under short names so a long page sends far less markup. A drawer opens from a trigger anywhere on the page, also under a strict Content-Security-Policy, a combobox takes free input for an address, a list draws long lists from data in one render, and a navigation kept with Livewire's @persist follows the page it arrives at. A text inside a control can take the control's look, a label cut in an overflow nav shows its whole name, and WireKit:: hands an application the kit's own token values. A chosen state, a progress track, a presence state and a graphic carrying a value read by more than color, an unsaved field reads as a hint rather than a fault, and the chevron of a combobox is a target of its own size.
Added
- A card folds with
collapsible. Its head is the trigger, a button inside a heading (level, 3 by default) with the text ofheading, adescriptionline under it and ahintsslot beside it, all three in sight while the card is closed; the body and the footer fold away beneath it and stay findable by the browser's find in page.openstarts the card open and wins over a fold the reader made on an earlier visit,persistremembers the reader's choice in the browser, andwirekit:card-toggledreports each choice, so an application can keep it with the account instead. A closed card opens for a field inside it that fails the browser's validation or that a response rejects, for an address that names an element inside it, and forwirekit:card-open; it prints open.open-whennames the reasons a card stands open for, and after a Livewire response only a reason it did not have opens it again, so a card the reader folded stays folded through every render.wirekit:card-openandwirekit:card-closereach a card byname, a list ofnamesorall, which is what buttons to expand and collapse every card dispatch. A card withoutcollapsiblerenders as before, and anopenor anameit is given still reaches its element. See A card that folds. wirekit:focus-invalidtakes the focus to the first field a save was refused for. Dispatch it with the refusal,$this->dispatch('wirekit:focus-invalid')from Livewire, and the focus goes to the first field of the page that carriesaria-invalid="true", as every WireKit field does on its error, after the collapsible card, the wizard step, the accordion item or the collapsible around it has opened. A field that never shows passes the focus on to the next one.withinkeeps the search inside one element. See The Focus After a Refused Save.- New energy label for a price.
<x-wirekit::draws the arrow the EU energy labeling acts ask for beside a price on the web: the product's class in the color of its place on the scale, white with a black edge and in the size of the price, the range of the scale in black, a link from the arrow to the label, and a link to the product information sheet under the name the acts prescribe, in each language WireKit ships. It takes the A to G scale and the older seven-class scales, points either way, and opens the label and the sheet in another tab withenergy-label> new-tab. A class or a scale the label does not have is reported while you develop and draws no arrow in production. The address of the label and of the sheet usually come from a product's data, so one the browser would run as script,javascript:and the like, draws no link. On a touch screen the arrow takes a 44px target, and a label that wraps keeps its rows far enough apart that the target never reaches the link below it. - New tire label for a price.
<x-wirekit::draws the class arrows of the EU tire label beside a price: fuel efficiency, wet grip and external rolling noise, each in the color of its class with the range of its scale and its pictogram, the noise with its decibels, linked to the label and to the product information sheet. It follows the format the European Commission proposed for the nested display of Regulation (EU) 2020/740, which leaves that image open today. Like the energy label, it draws no link for an address the browser would run as script. On a touch screen each arrow takes a 44px target, and a label that wraps keeps its rows far enough apart that no target reaches into the next row.tire-label> - Compact classes write the kit's utilities under short names. With
wirekit.on (styles. compact WIREKIT_), every component writes its Tailwind utilities under short names, andCOMPACT_ CLASSES=true @wirekitStyleslinkswirekit-compact., which declares each one with exactly the rules of its utility. A page looks the same and carries less than half of its class bytes, which a Livewire component sends again with every update. A class you put on a component keeps its name. Compact classes are off by default.css - A drawer opens from a
drawer.triggeranywhere on the page.<x-wirekit::names the drawer it opens and brings its own Alpine scope, so it works in a toolbar, a navbar or above a list with nodrawer. trigger for="filters"> x-dataaround it, also with the CSP bundle under a strict Content Security Policy. A button of your own that sendswirekit-drawer-showfrom outside everyx-datadoes nothing, since Alpine never installs its click handler. Withhrefthe trigger is a link that still reaches its page without JavaScript, and a click on it with Ctrl, Cmd, Shift or Alt opens that page the way any link opens, in a new tab or window. It and amodal.triggerwith afornow carryaria-haspopup="dialog", anaria-expandedthat follows the overlay, and, while the overlay is open, anaria-controlsthat names its dialog. Both takeintent,surfaceandsizefor the button they draw, so a filter trigger above a list can beintent="neutral" surface="outline"; a trigger withhreftakessizefor its link. - A combobox takes free input. With
free-inputthe text in the field is the value:wire:modelbinds it,requiredchecks it,namesubmits it, and the field keeps yourautocompletetoken, so a browser can fill an address in. The suggestions open only while there are some for what was typed and mark none until the arrow keys reach one, so Enter on the typed text submits the form. Taking a suggestion fills the field and dispatcheswirekit:combobox-choosewith the option and thedatayou gave it, to fill the fields beside it.attributionnames the source of the suggestions under them, such as "© OpenStreetMap contributors". - A list draws long lists from data.
itemstakes the rows, each its text or an array withtext,hrefandmeta, and the list draws them all in its own render rather than alist.item, alinkand atextper row. Measured over a thousand rows with a link and a note, that is about 70 ms down to 4 ms. The link and the note carry exactly the classeslinkandtextrender with, your personalization included, and the slot still follows the rows. track-currentkeeps a navigation in Livewire's@persiston the page it arrives at. Livewire puts a persisted navigation back after awire:navigatevisit without rendering it again, so its current entry stayed the page the reader left,aria-currentincluded. Withtrack-currenton a sidebar, a navbar, an app rail, a bottom navigation or a navigation menu, the entry whose link matches the page takes the current state andaria-currentafter each visit: the longest matching path wins, so a section stays current on its sub-pages, and where two entries link to one path and differ by their query,/orders?status=openand/orders?status=closed, the one whose query the page carries is current. Underneath is the directivex-wk-current, which movesaria-currenton any navigation of your own.- A text takes
inheritfor its color, size, weight, family and line height. Inside a link, a tab or a button it then shows in the control's own color and type and follows the control's states, where it kept the theme's text color before. Each axis inherits on its own, so a text can keep the control's color and set its own size. - A label that the overflow nav cuts in its rows shows its whole name in a tooltip while it is cut. It shows on hover and on the keyboard focus of the entry's link, adds no tab stop of its own, and is hidden from screen readers, which read the name from the link once. A label that fits shows none, and one that a wider row lets fit loses it. In the menu a long name wraps as before.
WireKit::andtokens('light') WireKit::return the package's token values for a mode, read from its stylesheet the waytokens('dark') wirekit:doctor:a11y --theme-contrastreads it, with dark laid over light and everyvar()filled in. A color of your own can then be measured against the kit's surfaces withWcagContrastwithout readingdist/yourself. See Measuring against WireKit's own values.wirekit. css - A field set takes one
helpfor its whole group. The question mark stands beside the legend and shows the text in a tooltip, as a field's help does beside its label, so a list of checkboxes that share one explanation carries it once instead of on every checkbox. The group keeps the legend's text alone as its name and takes the help as its description. A mark goes after the legend and its question mark through thelabelAfterslot a field takes (A Mark After the Legend), and on a touch surface the legend stands as tall as a field's label, so a group with help and one without keep their controls level. <x-wirekit::takes a family for one page, andfonts> fonts.publishkeeps such families published.sans,serifandmonoreplace the configured family for that call, its stylesheet and its token, so a shop or a tenant with its own type needs no change to the config per request. A key of another category or one that names no family is reported in development and leaves the configured family in production. List the families your pages may pick inwirekit.:fonts. publish wirekit:publish-fontspublishes them beside the configured ones and--prunekeeps them, so such a page loads a static file with its preload instead of the package route.- An app rail item takes
badge-labelas a list, one sentence per waiting item. Its tooltip shows each sentence on a line of its own, at most five and then how many more wait, andbadge-label-limitsets the number. The link's description names every sentence, so a screen reader hears all of them. A string stays one sentence, as before. - A theme controller stands as tall as the labeled buttons beside it on a touch screen, with
touch-floor. In a bar of labeledsmbuttons, which stand at 44px on a phone, the toggle kept its 32px square and answered the finger through an area around it. With the prop it becomes a 44px square there and drops the area, as an icon-only button withtouch-floordoes, and the trigger of themenuvariant takes the floor the same way. With a mouse nothing changes.
Changed
- A chosen state reads by an edge as well as by its tint. A pressed toggle button, the chosen density of a data table, the checked view of an event calendar and a pressed command of the editor's toolbar draw a line in the muted text color just inside their edge, at 3:1 or more against their tint in every preset, light and dark, as WCAG 1.4.11 asks of a state. An active reaction draws its edge in the accent's text color. Before, the first three differed from their neighbors by a light tint and a darker label alone, and the toolbar's edge stood at 2.85:1. The tints stay as they were.
- The track of a progress indicator shows where it ends. The light track of progress (and of the usage meter, the stat, the stage card and the attachment that show one), the ring of radial progress and an unlit bar of a strength meter or a password input carry an edge in
--color-wk-border-strong, at 3:1 or more against the page in every preset (WCAG 1.4.11). The light track alone stood at 1.09:1, so how far a bar reached could hardly be read without its value as text. The track stays light, so every fill stands at 5:1 or more against it, and a lit bar has no edge. - An avatar's four presence states differ in shape, not only in color. The dot is filled when online, crossed by a bar when busy, half filled when away and hollow when offline; the ring is solid, doubled, dotted and dashed in the same order. Both follow WCAG 1.4.1, which asks for a cue besides color, and the colors are the text tones of their intents.
- A graphic that carries a state or a value takes its intent's text tone. The stars of a rating, the icons of an alert, a callout and a toast, the dot of a badge, the presence of an avatar, the markers of a timeline, an event calendar's agenda and a map, a spinner, the steps of a strength meter and the fills of progress, radial progress and the page and reading progress bars now draw in
--color-wk-{intent}-text, which stands at 3:1 or more against the page and the tints in every preset, light and dark (WCAG 1.4.11). On a light page the warning fill stood at 2.13:1, so warnings and stars turn from gold to a deeper amber, and success turns a little darker. A rating recolored by setting--color-wk-warningon it keeps that color. A timeline marker draws its icon in the page color. - An unsaved field reads as a hint, not as a fault. A field that holds what its server does not have yet (unsaved hint) takes one line of the light warning color and nothing else, no ring and no fill, and the words "Unsaved change" stand beside its label, small, in the muted text color, in the reader's language and outside the field's accessible name. A checkbox, a radio or a switch takes a line of 1px. The hint beside the save button is a soft warning badge without a symbol. An invalid field keeps its error border and takes no words.
--color-wk-border-unsavednow defaults to the light warning color; set it tovar(--color-wk-warning-text)for a line at 3:1.wirekit:doctor:a11y --theme-contrastreports the line as an advisory row with its ratio, since the words carry the state, so a default install still audits clean. - The frozen column of a table wears its row's color, and its lines run through as they run beside it. On a
stripedorhoverabletable the frozen cell takes the stripe and the shade under the pointer, as the data table's does, and inside a card the card's surface. Where the rows sat on fractions of a pixel, the line under the frozen column came out twice in Chromium-based browsers; the table's own line now shows through the frozen cell's edges in every browser. - Outside Livewire, the page-size choice of pagination is a row of links. One link per size, each keeping the query string and the fragment the page links keep and starting the list at its first page; the size in use carries
aria-currentand is not a link. It replaces the select with its Apply button, which was a form of its own: a browser does not nest forms, so inside a form of yours the pager's form was dropped, Apply sent your form, and the controls after the pager fell out of it. The pager now renders no form and can stand inside yours. Bound withwire:model, the choice stays a select. wirekit:mcp-serveruns on the Laravel MCP SDK wherelaravel/mcpis installed. The SDK speaks the protocol revisions it maintains, the newer handshake included, and the server offers the same tools and resources with the same answers as before.laravel/mcpis a suggestion, not a requirement, since it needs Laravel 12.41.1 or newer: without it the command keeps its own loop, which speaks revision2025-06-18only. That loop is deprecated and goes with the next major version; Dependencies has the install line.
Fixed
- The chevron of a searchable combobox is a target of its own size. It was a button of 20 by 20 pixels right beside the field, below the 24 pixels WCAG 2.5.8 asks of a pointer target. It is now 24 pixels under a mouse and takes the 44px touch target on a phone or a touch surface, where the clear button moves aside so the two never overlap. The text of a field that can be cleared ends before the clear button under a mouse as well.
- The list of a combobox or a multi-select that opens under a resting mouse pointer leaves the highlight where the keyboard put it. The option under the pointer took the highlight as the list appeared, though the pointer had not moved, so Enter chose that option instead of the one the arrow keys reached, and with
free-inputa suggestion instead of the typed text. An option now takes the highlight when the pointer moves over it. - A modal trigger with
forandhrefopens its page in a new tab or window on a click with Ctrl, Cmd, Shift or Alt. Every click opened the dialog, so the page behind the link, such as the document a consent sentence names, could not be opened the way any other link opens. A plain click still opens the dialog. - A neutral badge or a soft button inside an element of yours that turns
bg-subtleorbg-mutedunder the pointer stands apart from it. The stylesheet names the surface forhover:bg-[var(--color-wk-bg-subtle)]andhover:bg-[var(--color-wk-bg-muted)], also on a row that is not current or not selected, and for thebg-[color:var(…)]spelling of every surface class, as it already did for the classes the kit paints with. Before, the fill inside kept the shade of the resting surface and sank into the gray. A class you hand to a component through a personalization is followed too, where nodata-wk-surface-hovercan be set. See A gray surface of your own. - The
colorsyou give an ApexCharts chart draw its series, as the theming page promises. The adapter wrote a palette color onto every series, and ApexCharts lets a series' own color win overcolors, so a brand palette drew in WireKit's accent and each row of a one-color heatmap took a color of its own. A series that names its own color keeps it, and withoutcolorsthe palette applies as before. Withpalette="categorical", yourcolorsreplace the palette's outline as well. - An ApexCharts chart is themed again only when its theme changes. The first class change on
htmlorbodyafter a chart was drawn, a menu or a dialog opening, rebuilt every chart of the page with the colors it already had. The chart now remembers the theme it was drawn with. - An ApexCharts chart drawn with
chart.stays still. A theme swap, and a reader allowing motion again, turned its animations on. Less motion still turns them off, and a chart that animates its entrance but not its updates keeps its updates still.animations. enabled: false - A decorative sparkline no longer writes a missing-label line to your log. With
APP_DEBUG=truea chart without anaria-labellogs a reminder to describe its data, and a sparkline markedaria-hidden="true"beside the number it draws triggered it on every render, as did a chart named byaria-labelledby. The sparkline now handsaria-hiddento the chart inside it, and a chart that is hidden or named byaria-labelledbywrites no such line. An unnamed chart still does. - Your Tailwind build no longer compiles an invalid rule out of a comment in the navbar. A comment in its view spelled out an arbitrary-value background class, Tailwind reads every word of the views as a possible class, and lightningcss warned about the rule it produced in every build that scans them, which then carried the rule. The comment says it in words now, and no view or Tailwind source of the kit holds such a placeholder.
- The help button of a field names its label on one line. Where a label was written over several lines in a view, the button's name and the
labelof thewirekit:field-helpevent carried its line breaks and indentation. Both now carry the text with its spaces collapsed. - Two carts on one page that show the same line give its rows an
ideach. A line of the cart list takes its HTMLidfrom its key, theidyou give it or one derived from the product name, so a mini cart beside the cart page repeated everyidof the cart, andgetand anything else that points at a row reached the first cart's. The repeated row'sElement By Id() idgets-2after it now. The cart events still carry the key as it is.
Documentation
-
The cart list and the cart summary have public pages. Both components have shipped since 2.55.0 without one. The cart list page now shows a whole cart in a Livewire component, with each quantity bound and a line removed by its key, and the cart summary page adds the totals beside it. The public component manifests and the examples the MCP server answers with carry
cart-list,cart-summaryandcart-itemas well,cart-itemwith the examples of the cart list page. -
The checkbox page opens its consent documents with a
modal.trigger. Its example dispatched the modal's event from an inline expression, which does nothing under the CSP bundle, and drew a button that a reader without JavaScript could not follow to the document.
Components
Every component this release changes, each linked to its page: Accordion · Alert · Alert Dialog · App Rail · App Shell · Avatar · Badge · Bottom Nav · Button · Calendar · Callout · Card · Cart List · Chart · Checkbox · Collapsible · Color Picker · Combobox · Command Palette · Data Table · Date Picker · Drawer · Dropdown · Editor · Energy Label · Event Calendar · Field · File Upload · Filter Builder · Fonts · Form · Grid · Icon · Indicator · Inline Edit · Input · Label · Lightbox · List · Map · Message · Modal · Multi-Select · Navbar · Navigation Menu · Number Input · OTP Input · Overflow Nav · Page Progress · Pagination · Password Input · Phone · Progress · Progress Radial · Radio · Range Slider · Rating · Reaction · Reading · Resizable · Reveal · Scope Switcher · Select · Shimmer · Sidebar · Slider · Sparkline · Spinner · Strength Meter · Table · Tabs · Tags Input · Text · Textarea · Theme Controller · Time Picker · Timeline · Tire Label · Toast · Toggle · Toggle Button · Tour · Tree View · Unsaved Hint