Skip to main content

WireKit v2.58.0

Minor release

Minor. An image can carry srcset, sizes, width, height and fetchpriority, Laravel Boost loads WireKit's own guidance, a clearable input names its clear button and says when it cleared, and date fields take a date object. Two ways a value from your data could run script are closed, in the editor's first paint and in link targets. Most of the rest is fixes: a prop value WireKit rejects renders the fallback it names, @wirekitScripts brings Livewire's scripts to a page without a Livewire component, a pager scrolls to its target under the CSP build, wirekit:install reports what failed, and diagnostic log lines stop repeating on every request.

Security

  • The editor's first paint runs nothing from its value. With format="html" the value was written into the page as it came, as the preview shown until the editor mounts, and the editor never reads that copy: an <img src=x onerror=…> in the value, from old('body') for instance, ran as the page loaded. The preview is now rebuilt from an allowlist with PHP's HTML5 parser. It keeps the formatting elements and a few inert attributes, escapes all text, checks href and src, and drops elements whose content is not document text. Without PHP's DOM extension, which Laravel does not require, the preview stays empty until the editor mounts.
  • A link target that comes from your data can no longer run script. Citations in an assistant message, the rows of a notification center, from the server and in real time, status tiles, the rows and the create action of a scope switcher, and overflow-nav entries put their target into href unchecked. They now keep http, https, mailto, tel and relative URLs, read the scheme the way a browser's URL parser does, and render an entry whose target is refused without a link: a static tile, a source as text, a row as a button.
  • The sandbox renderer tells its caller only that a render failed. It returned the exception's message, and a Blade error carries the view's absolute path on your server. The caller now gets render failed, and the exception goes to your application's exception handler.
  • The sandbox audit log no longer keeps a reversible fingerprint of the client's address. It wrote a short unkeyed hash of the IP address, which a lookup table reverses. It now writes a digest of the client's network, an IPv4 /24 or an IPv6 /48, keyed with your application key, and - when no key is set. Daily files older than wirekit.sandbox.audit_log_retention_days are deleted, 14 days by default; null keeps them all, and WIREKIT_SANDBOX_AUDIT_RETENTION_DAYS sets it from the environment.
  • A prop value WireKit rejects reaches your log quoted. Line breaks and other control characters are replaced and a value longer than 64 characters is shortened, so a value bound to user input cannot start a log line of its own. The same holds for an unknown icon alias and an unknown flag code. A rejection is logged once per request instead of once per render, so a component in a loop writes one line, not one per row.

Added

  • image writes srcset, sizes, width, height and fetchpriority on the <img>. They used to fall through to the <figure>, where they do nothing: a responsive source loaded at full size, and a box without ratio reserved no space while the image loaded. fetchpriority takes high, low or auto.
  • Laravel Boost loads WireKit's guidance. The package ships a Boost guideline with its conventions and a wirekit-development skill with worked examples, and boost:install lists pushery/wirekit among the third-party packages it found, so there is nothing to publish. wirekit:boost-skills still writes its manifest with the same options, and its description and success message say that Boost does not read that file. See Laravel Boost.
  • A clearable input names its clear button and says when it cleared. clear-label names the button and its tooltip, and the field dispatches wirekit:input-cleared once after a clear, with the field's name in detail, so a search can drop the record it found. The events page lists it.
  • calendar, date-picker and time-picker take a date object for value, min and max, and for both ends of a range. A Carbon instance from an Eloquent date or datetime cast is written the way the field reads it, where it used to arrive as Y-m-d H:i:s, which the calendar read as an invalid date and a native field as no value at all. The calendar also takes a range as its two ends, ['start' => …, 'end' => …] or [$start, $end], as the date picker does.
  • toggle-button takes active-intent="primary", as the accent it is everywhere else in the catalog.

Changed

  • A dropdown's entries are concentric with their panel. An entry's corner radius is the panel's radius minus its padding, as the rail's and the sidebar's already were; the dropdown panel sits in the overlay root, where that rule did not reach, so its entries kept the full radius. At the default scale an entry's corners go from 8px to 4px, and a submenu's entries sit 4px in from its walls.
  • Diagnostic log lines appear once per hour, not once per request. The warning about a configured font preset whose CSS is not published, and the one about flag artwork that cannot be found, were meant to be logged once; under PHP-FPM every request starts afresh, so a lasting misconfiguration logged on every request. Both remember that they were logged in the application cache for an hour, and they still log when no cache is available.
  • wirekit:verify reports a published config that only lacks newer options as information. Every release that added an option turned a --fail-on=warning run red, although the new option already applied through the merge with the package's defaults. A key this version no longer reads is still a warning.
  • Control-scale boxes read the --size-wk-* tokens. Avatars, the avatar group, the brand mark, the testimonial mark, feature and step markers, the OTP cells, the color-picker trigger and more used fixed Tailwind steps equal to a component height, so a theme that moved the tokens moved buttons and inputs and left them behind. They follow the tokens now, and the 24px pointer targets read --size-wk-target-min, so a denser theme cannot take them below the accessible minimum. At the default values nothing moves. The design tokens page names what each size token drives.
  • The standalone FAB button and the bottom navigation stack through --z-wk-sticky, as the FAB already did, so a theme that moves the layer moves all three together.
  • The indeterminate progress bar animates transform instead of left, on the same path across the track, so the browser no longer lays it out on every frame.
  • The editor's notice about window.tiptapEditor no longer announces a removal in 3.0.0. The name keeps working, and no removal is scheduled.
  • composer.json names the documentation and the product site. support.docs points at https://docs.wirekit.app, and homepage at https://www.wirekit.app instead of repeating the source link.

Fixed

  • A prop value WireKit rejects renders what its fallback renders. Where a rejected value does not throw, as on a web request by default, it is logged and replaced by the component's first allowed value, and in many components that value's name was written into the markup instead of what it stands for: hero variant="nope" rendered a class named default and no background, an avatar lost its size, a container its width cap, and text with an unsupported line clamp clamped nothing. The fallback now renders exactly as if you had written it. hero and cta fall back to their default sizes, lg and md, where a typo gave the smallest.
  • @wirekitScripts brings Livewire's scripts to a page without a Livewire component. Livewire adds its scripts, and the Alpine they carry, only to a page that renders a component, unless it is asked to. @wirekitScripts meant to ask and never did, so on a page with neither a Livewire component nor @livewireScripts WireKit's interactive components did nothing. It asks now, except when wirekit.scripts.bundle is csp: that bundle brings its own Alpine, and a second one from Livewire would need unsafe-eval.
  • Personalizations, scopes and the classes config reach five more components. chart, icon, the reading shell, replay-button and the table's reorder header rendered their classes past WireKit's class resolver, so a personalization or a scope for them changed nothing. They go through it now. WireKit::personalize() logs a warning in debug mode when it names a component that renders no class block of its own: faq-item, fonts, glass, structured-data or toggle-button.
  • wirekit:install reports every failed flag as a failure, and records an aborted run. --apex-license reported success over a config file it had not written, and under --no-strict --ignore-failed-flags a failed --preset ended with "installed successfully". An install that stops after changing files now records the run, and --rollback reverses that aborted run instead of the one before it. When wirekit:verify fails at the end of an install, the install says so: without --ignore-failed-flags it ends with "Install incomplete" and exit code 1; with it, the exit code stays 0 and the closing line names the problem.
  • wirekit:install and wirekit:verify read the Tailwind version that is installed, not the lower bound of a range. >=3.4 or ^3.4 || ^4.0 read as v3, so the install aborted and verify failed on a project that runs v4. The installed package decides now; without one, only a constraint that names a single major version counts, and any other does not block.
  • wirekit:install and wirekit:verify read an app.css whose @source globs look like comments. A glob such as views/**/*.blade.php contains comment delimiters, and verify stripped the file from there to the next real comment, so it reported tokens and blocks after it as missing. The install skipped WireKit's @source whenever wirekit and @source appeared anywhere in the file, and verify then reported the source missing. Both read the file with one parser that knows strings and comments.
  • A pager in Livewire mode scrolls to its target under Alpine's CSP build. With a scrollTo target outside the pager, such as a heading above the list, every page turn threw an error and scrolled nowhere.
  • Setting expanded on an app rail or collapsed on a sidebar wins over the state it stored. A rail rendered with :expanded="false" opened after an earlier visit had stored it open, and with the cookie driver the server's answer was replaced a frame after the render. When you set the state, the component no longer reads the store; it still writes to it.
  • Every folding sidebar section names its own panel. Two sidebar.group or sidebar.collapsible sections with the same label, or two groups without one, rendered the same id, so the second trigger's aria-controls pointed at the first section. A repeated id now gets a suffix, and an id you pass is used as given.
  • A sidebar label of one long word wraps inside the column instead of running past its edge, on sidebar.item and on a sidebar.collapsible trigger.
  • An open FAB speed dial shows its action names on a touch screen. They appeared on hover and keyboard focus only, so on a phone the open dial was a column of unlabeled circles. hideLabel on an action still hides its name.
  • align="start" and align="end" on a toolbar place the items of its default slot. The slot's wrapper always spread its items apart, so a single button under align="end" sat at the start.
  • A stepper's connecting lines follow a larger circle. The page taught a larger circle by changing the circle's size classes, while the lines stayed placed for the default circle and ran beside it. --wk-stepper-circle now sizes the circle, both connecting lines and the compact form's step width together; set it on one stepper or in a scope. It defaults to --size-wk-sm.
  • An animated radial progress moves its fill to a new value in every supported browser. The fill's transition relied on the browser interpolating between two gradients; it now transitions the value the gradient reads.
  • The dark tint of the Liquid Glass wk-glass-tier2 layer applies. It looked for the dark class and the glass detector's class on two different elements, while WireKit sets both on <html>, so in dark mode the layer kept its light tint. Publish the glass assets again with php artisan wirekit:glass install --force to take the fix.
  • Escape on an app shell drawer that has just opened prevents its default, as it does once the drawer's focus trap is active, so a listener of yours finds the default prevented however quickly the reader presses it.
  • A tool call announces when it settles. It promised a status announcement and had no status region. The region is now there from the first render, empty while the call is pending or running, and reads the tool's name and state once the call is done or has failed.
  • A QR code reads every CSS hex form, and reports a color or error-correction level it does not know. background="#fff" painted the quiet zone red-orange, color="red" drew a wrong color with a PHP deprecation notice, and an unknown errorCorrection fell back to L in silence. #rgb, #rgba, #rrggbb and #rrggbbaa are read, an alpha becomes the fill's opacity, and any other value is reported like an invalid prop.
  • structured-data keeps its block valid when a value holds a bad byte, and renders none when JSON cannot hold a value. Invalid UTF-8, NAN or INF made the encoder fail, and the page carried an empty ld+json block. A bad byte now becomes U+FFFD, and a failed encode renders no tag and logs the JSON error in debug mode. The schemas of breadcrumb and FAQ render through it and take the fix.
  • highlight matches letters beyond ASCII regardless of case, and never inside an HTML entity. "über" did not find "Über", and a query of amp matched inside &amp;, so the reader saw the entity as text. A query with an & now finds Tom & Jerry.
  • Tour step numbers and FAQ schemas start fresh after each request and before each queued job. A render that stopped between a tour step and its tour left the next tour numbering from there, and under a queue worker an orphaned FAQ question could reach the next job's FAQPage schema.
  • Values WireKit hands to Alpine read back the same in every CSP build. The CSP builds of Alpine and Livewire read some JSON escapes back as plain letters, so a value containing a form feed or a line separator arrived changed. Line and paragraph separators are now written as themselves and other control characters as U+FFFD; newline, tab and carriage return keep their escapes.
  • A treemap tooltip stays at the hovered cell with ApexCharts, where it sat about 130px away and stayed there wherever the pointer moved.
  • A custom chart adapter's scripts load. ChartAdapter::scripts() promised that its URLs would be loaded, and nothing read it, so a chart drew nothing and said nothing. The chart component now writes one deferred <script> per URL per response, with the page's CSP nonce. A script that arrives with a Livewire update does not run, as the contracts page says.
  • An unsupported chart type suggests switching libraries only where the other library draws it, and otherwise the nearest type names of the active library, such as pie, line for lnie.
  • A map passes MapLibre's errors on. Its listener had silenced MapLibre's own console output and reported every error as the same hint about CSP and the network, once. Each error is now logged with the original error object, and the load-failure hint appears for a failed request only.
  • The Tailwind source of tabs carries the classes its styles build at runtime: the active tab's underline and the separators of the bordered variant. An application that scans only the sources of the components it uses got them only when another source happened to carry them.
  • Hover dims, a code font and a disabled opacity follow their tokens. link, data-table, stepper, the toast region and product-card dimmed on hover by a fixed amount, so a preset that sets --opacity-wk-hover changed nothing there; code cells in a data table ignored the theme's monospace font; and branch-switcher set its own disabled opacity. A link also fades to its dim now instead of jumping.
  • The editor's placeholder sits inside the empty paragraph in Chrome and Edge 111 to 117. It floated to inline-start, which those versions do not know, so the placeholder became a box before the paragraph and the caret sat after its text. It floats left now, and to inline-start where the browser supports it, so a right-to-left editor keeps it at the start.
  • A reaction's glyph asks for its emoji form. Its class compiled to a font weight the browser dropped. Browsers that support font-variant-emoji now draw the emoji form.
  • A color picker's native readout sits 12px from its swatch, as intended; two gap classes competed, and the smaller one won.
  • An attachment rounds a file size before it chooses the unit and the decimals, so 9.96 MB reads 10 MB rather than 10.0 MB, and 1023.999 KB reads 1.0 MB rather than 1024 KB.
  • A usage meter reads a limit of 0 as over it, with its status, marker and danger intent, at 0 / 0 as at 3 / 0; before, it said nothing.
  • A table's debug warning finds raw rows around sub-component cells. A raw <thead><tr> around table.th cells passed the check while the rows lost their dividers, stripes and hover. A table nested in a cell is not counted as part of it.
  • The unknown-prop warning tells an event handler from a misspelled prop that starts with on, and knows every global HTML attribute. Any name starting with on passed as a handler, so onLable on a toggle button was never reported. Only a lowercase name that is not one edit away from a declared prop passes now, and a camel-case onClick is reported. popover, writingsuggestions, the microdata attributes, headingoffset and headingreset no longer log an unknown prop.
  • wirekit:csp-audit checks x-wk-findable and each branch of a Blade block inside an attribute. An arrow function on x-wk-findable went unchecked, and a value with @if … @endif inside it landed among the expressions the audit could not read, which nothing reported. Each branch is now parsed, a fault that every branch shares is reported once, and scanned counts attributes.
  • wirekit:doctor:props tells the parenthesis form of a slot closing tag glued to text from the others. After a parenthesis Blade closes the slot and loses only the text in parentheses; after a word it prints the directive as text. The finding now says which of the two happened.
  • wirekit:make lists the method a wire:poll or wire:init calls among the members you have to write, after a modifier with a hyphen such as .keep-alive too.
  • wirekit:editor-preset and the editor page register each Tiptap 3 extension once. Tiptap 3's StarterKit includes Link and Underline, and the snippet added both again, so Tiptap warned about the duplicates and the second Link kept openOnClick: true. Link is configured inside StarterKit.configure() now, and the collaboration recipe uses Tiptap 3's undoRedo: false and CollaborationCaret.
  • The schema file wirekit:install writes, the MCP catalog and the Boost manifest list only published components. Components still in preparation appeared there with their API. wirekit:export-json --public writes the same set.
  • The MCP tool get_component_examples answers a component documented on another component's page with examples that render it, where it returned that page's first previews.
  • The Boost manifest lists the wirekit:* commands Artisan registered, with their full names and descriptions. A name with a digit was cut short, wirekit:doctor:a11y came out as wirekit:doctor:a, and a description ended at its first quote.
  • timeline's variant is described as the free styling hook it is in wirekit:show, wirekit:export-json and .wirekit-schema.json, which offered two built-in layouts that do not exist.
  • The shipped AI guidance teaches props that exist. The Cursor rules called secondary a legacy variant value that maps to intent="neutral", while button and badge have no variant; they and AGENTS.md gave section a gap prop it does not have; and the Cursor rules allowed four CSS features newer than the supported browsers. wirekit:cursor-rules and the MCP conventions tool carry the corrected text.
  • The @wirekitTransition directive works. Every use of it ended in a parse error, and the classes it named ignored the motion tokens. It writes the transition attributes with the token utilities the components use.

Documentation

  • The README's theming example works. It wrapped the token override in @layer base, and a layered rule loses to WireKit's unlayered stylesheet, so the accent stayed at its default. It is a plain :root rule now, as on the scrollbar and tabs pages, which dropped the same wrapper, and the README's install steps match Getting Started.
  • The published config/wirekit.php, the component comments and the prop tables of brand, hero and tooltip say when an invalid prop throws, as strict validation describes it: only while the gate is strict and throwing is on, which by default means a console or test run with APP_DEBUG on. A web request logs the value and renders the fallback, and under strict=false nothing throws, whatever throw_on_invalid says. The config also says what a missing icon package does: a web request renders the icon placeholder and logs the package to install, and a console or test run throws.
  • The fonts page says what Safari shows with the matched fallback fonts: it applies size-adjust from version 17 and none of the three metric overrides, so a paragraph with a numeric line height lands within a line of the web font's height, while text with line-height: normal keeps the local face's line metrics.
  • The structured data page says that a Content Security Policy does not apply to a JSON-LD block, so a strict policy leaves your structured data alone, and that @json written over an array literal takes the text after the first comma as its flags and loses the escaping.
  • The price page names the reference units for a unit price as the German, EU, British and US rules state them, with a link to the German regulation.
  • The FAQ page no longer grounds its schema in Google's FAQ rich results, which Google has stopped showing, and Schema::webSite() no longer suggests a search action as the usual one.
  • The sandbox schema's documented shape names allowed_schemes, the check that keeps javascript: out of an href, src or action; a schema you register needs it on every such prop.

Components

Every component this release changes, each linked to its page: Accordion · Alert Dialog · Announcement Banner · App Rail · App Shell · Attachment · Avatar · Band · Blockquote · Bottom Nav · Branch Switcher · Brand · Brand Bar · Button · Calendar · Card · Carousel · Chart · Color Picker · Combobox · Container · CTA · Data Table · Date Picker · Dropdown · Editor · Empty State · Event Calendar · FAB · FAQ · Feature · Feature Grid · Footer · Grid · Heading · Hero · Highlight · Icon · Image · Indicator · Inline Edit · Input · Kbd · Lightbox · Link component · List · Main · Map · Message · Message Group · Multi-Select · Navbar · Notification Center · OTP Input · Overflow Nav · Page Header · Page Progress · Pagination · Price · Pricing Table · Product Card · Profile · Progress · Progress Radial · Prose · QR Code · Rating · Reaction · Reading · Replay Button · Reveal · Row · Scope Switcher · Section · Segmented Control · Shell Bar · Sidebar · Skeleton · Slider · Stack · Status Tiles · Step Marker · Stepper · Stream · Strength Meter · Structured Data · Table · Tabs · Testimonial · Text · Theme Controller · Ticker · Time Picker · Timeline · Toast · Toggle Button · Tool Call · Toolbar · Tooltip · Usage Meter