WireKit v2.66.0
Minor. A number given to a number field, a one-time code, a tags input, a hover card or a toast region now reaches its script as a number, so text from a request can no longer run in the page, and an editor keeps its document as written under any echo format. WireKit names WCAG 2.2 AA as the standard it is built and tested to, and a floating button, a toast or the resume pill of a reading bookmark no longer hides the control that takes the keyboard focus. On a phone and on a touch surface every interactive row takes the 44px touch target, so menus, lists and sidebars stand taller there, while a desk with a mouse keeps its compact rows, and with touch-floor an icon-only button stands as tall as the labeled buttons beside it. For a shop, a swatch group lets the reader choose a color or a pattern, a review summary and a review show what customers wrote, a price can read "from" and show more decimals than its currency, an image can show another crop for another width, and a lightbox can enlarge its image. An accent can be derived from a brand color, at run time with AccentLadder:: and for a preset with wirekit:theme --accent. A modal or a drawer asks before discarding changes, and a form before a wire:navigate visit leaves it, in a dialog of its own rather than with the browser's question. A soft neutral button and a neutral badge take the shade of the surface they stand on, and an entry under the pointer moves on a panel as it does on the page, so none of them vanishes on a card footer, a selected row or a panel in a dark mode. A chart can take a categorical palette and shows the card it stands on, its data points can lead to an address, its tooltip can name a further value of each point and a treemap can show each cell's value, a stat tile can lead into its report, and a reading spine can stand beside the article as a rail. A heading, a price or any other text can shrink to fit its line, down to a floor, in CSS alone. In Safari, a component keeps its keys after a click on one of its controls, and a radio keeps its focus ring under the arrow keys. Among the rest, a long address, URL or file name stays inside the component that shows it, an event that runs past midnight or over several days shows on every day it covers, a printed page carries everything a box that scrolls holds, wraps what scrolls sideways where it can and prints a shadow as an edge rather than a gray box, text a component keeps for screen readers no longer widens the page on a phone, a calendar that holds a single date no longer shades every day, a save the server turns away keeps its fields unsaved, a scope switcher keeps the row Enter follows in view, an unsaved value reads as a warning, and a line cut with an ellipsis can show its whole text in a tooltip.
Security
- A number you hand to a component reaches its script as a number. Several components wrote a numeric prop into their Alpine data without quotes, as a number is written, and Blade's escaping leaves letters, parentheses and commas alone. A value that was text, such as
old('qty')on a number input after a failed validation, therefore ran as an expression when the page loaded. The value,min,maxandstepof a number input, thelengthof an OTP input, themax-tagsof a tags input, theoffset,delay-showanddelay-hideof a hover card and themaxanddurationof a toast region are now read as numbers first, and one that holds no number takes the prop's default, as on a slider. This holds under either echo format. - An editor keeps its document as written when your application has called
Blade::. After that callwithout Double Encoding() {{ }}leaves a character reference such as"or<as it is, and the browser decodes it. A document given as text, JSON withformat="json"or HTML, reached the editor through its configuration and through its form field, so text that spelled"could end a JSON string and add marks and nodes, and text that spelled<imgbecame an element in the document and in what the form submitted. Thevaluenow goes into the form field only, written so the browser returns exactly its bytes, and the editor reads it there; awirekitEditor()factory you build yourself without a form field still readsvaluefrom its configuration. A document given as an array was already safe, and with the standard echo nothing changes. - The link dialog of an editor refuses a script link behind a control character. It read the scheme after removing tabs, line breaks and the spaces around the address, while the browser also drops every other control character in front of an address, so a
javascript:link typed with one in front passed its check. It now reads the scheme the way the browser does.
Added
- A heading, a price or any other text can shrink to fit its line, down to a floor, with fit text.
<x-wirekit::takes the size of the element around it as its largest and settles on the largest size that fits the width it is given, in CSS alone and in every supported browser. A heading keeps one line on a phone instead of breaking after its first word, and a text that needs several lines even at the floor takes the fewest it can and fills them, without breaking a word the floor keeps whole, while a word wider than its box even at the floor breaks rather than run out of the box. Withfit-text> :wrap="false"a price or a figure stays on one line. The floor is the new--text-wk-fit-floortoken,0.75emof the text's own size, or a length of its own withfloor. A heading, text, a price, the value of a stat and the title of a hero take it asfit:fitalone for the token's floor, or a length for a floor of their own. A price fits its struck amount, the word "from" and the amount as one line. A text that Alpine writes after the page renders takes its expression throughx-wk-fit-text, so its size follows the new words. - A swatch shows a color or a pattern as a value, and a swatch group lets the reader choose one, or several with
multiple. The group is native radio buttons or checkboxes, so it posts with a form, binds withwire:modeland moves its choice with the arrow keys without a script of its own. Every option carries its name in words, an unavailable one a line across its swatch and "unavailable" in its name, and the chosen one an outline in the accent color. A color reaches the page only as a hex color,rgb(),hsl()oroklch(), and a pattern only as an image with anhttporhttpsaddress. Each swatch keeps an edge in the form fields' border color, so white stays visible on a white page. - A review summary shows the reviews of a product at a glance, and a review shows one of them with the shop's reply. The summary writes the average as a number and as stars, says how many reviews there are, and lists how the stars are distributed, each row read as a sentence such as "5 stars: 82 reviews". With
filterseach row becomes a link to the reviews with that many stars, the current one outlined, and on a touch screen every row is 44px tall. Without reviews it says so instead of showing an average of nothing. Itsnoticeslot is the place for what the shop says about how it checks its reviews, which EU law asks of a shop that shows them. A review is an article named by its title, with its stars, its author, the date in your application's locale, a "Verified purchase" badge withverified, and the shop's answer in itsreplyslot, an article of its own set off at the start edge. A list of reviews with sorting, filters and pagination is a composition of the two. - A price can read "from €19.99" with
from, for the cheapest of several variants. The word comes from the language catalog and is read with the amount, and a product card passes it on. - A price can show more decimals than its currency, with
max-precision. A price per piece such as 0.045 € reads0,045 €withmax-precision="4". It never shows fewer decimals than the currency's own, and trailing zeros drop back to them, so12.5still reads12,50 €. The previous price and the unit price beside it take the same digits. - A product card writes the unit price beside its price, and takes a
priceNoteslot right under it.unit-priceandunit-measurereach its price as on apriceof its own, and the slot carries what the price needs said beside it, the lowest price of the last 30 days or "VAT included, plus shipping", apart from the add-to-cart button and in every availability. - An image can show another crop for another width, with
sources. Each source names a file for a media condition and renders as a<source>in a<picture>, so a portrait hero on a phone and a landscape one on a desk load exactly one file, and with awidthand aheighton each source the box takes the chosen file's ratio before it arrives. - An open image in a lightbox can be enlarged, with
zoomable. Buttons to zoom in, out and back, the keys+,-and0, the wheel, a double click or a double tap, a pinch and a drag all move the same zoom, and the enlarged image may use the whole screen. While it is enlarged the arrow keys and a row of pan buttons under the zoom buttons move the view across it a tenth of the screen at a time, so a pointer that cannot drag reaches every part of it, andCtrlorCmdwith+,-or0stays the browser's own page zoom.max-zoomsets the largest scale, four times the fitted size unless you say otherwise. A step of the buttons or the keys is announced and, under reduced motion, not animated, and another image or closing the viewer puts the image back. An image gallery takeszoomableandmax-zoomand hands them to its lightbox. Withoutzoomablea lightbox renders as before. - A data point of a chart can lead to an address. Give a dataset
urls, one address per point, or a point object its ownurl, the form a treemap and a bubble chart write their points in. A click or a tap on the point follows it the way a link does: through Livewire's navigation for an address on your site, in a new tab with Ctrl, Cmd or Shift, as a jump for a fragment of the page, and never for an address whose scheme could run script. Over a linked point the pointer becomes a hand. A keyboard and a screen reader reach the same links in a list after the chart, which stands under it while one of its links has the focus. Under Chart.js and ApexCharts alike, and in a mixed chart. - A chart's tooltip names a value of each point that no series draws, with
tooltipExtras. The number of receipts of a day beside its revenue, say, where a series of its own would take an axis and an entry in the legend. Give a label and one value per point, in the order of the labels, or a list of them: each writes its values in your application's locale with its owndecimals,prefixandsuffix, and the tooltip names the value of the point it shows under the series' values. Under Chart.js and ApexCharts alike, and in a mixed chart. - A treemap shows each cell's value under its name, with
'dataLabels' => ['wkShowValue' => true]in its options. ApexCharts writes a cell's label from a formatter, and a formatter cannot be written in a chart's options in PHP, so a cell could only show its name, and a value written into the name showed twice in the tooltip. The value is written as the tooltip writes it, withvalueDecimals,valuePrefixandvalueSuffixin your application's locale, on the cells with room for it. - A chart can be drawn in a categorical palette, with
palette="categorical". Eight pastels from the new--color-wk-chart-*tokens, each a fill and an edge, keep every pair of series apart for normal vision and under protanopia, deuteranopia and tritanopia, in light and in dark mode. The edge draws a line and outlines a bar or a slice, and stands at 3:1 or more on the page; a label on a fill takes--color-wk-chart-label. Set'components' => ['chart' => ['palette' => 'categorical']]inconfig/to draw every chart in it. The default stayswirekit. php semantic, so no chart changes its colors on update. Two seven-step scales,--color-wk-chart-seq-*from faint to strong and--color-wk-chart-div-*from blue through a neutral middle to orange, color a value, for example in the ranges of a heatmap. - An accent can be derived from a brand color, with
AccentLadder::. For a color your application only learns at runtime, it returns the four accent tokens of one mode asfor() oklch()values, with their contrast held: the label reads at 4.5:1 or more on the fill and on the hover, and the text color on the page, card and subtle surfaces and on the tinted surfaces of a callout and an alert, or on surfaces you pass.Oklch::,fromCss() Oklch::toCss(),Oklch::toHex(),Oklch::andtoGamut() Oklch::convert a color to OKLCH and back, and reduce its chroma into sRGB rather than clip it. See From a brand color.inGamut() wirekit:themederives a preset's accent from a brand color, with--accent.php artisan wirekit:theme soft --accent="#e11d48"writes the preset and, after its own variables, the four accent tokensAccentLadder::would give on that preset's surfaces, light and dark. Withfor() defaultit writes the accent alone.- An icon-only button stands as tall as the labeled buttons beside it on a touch screen, with
touch-floor. A labeled button takes the 44px touch target as its height there, and an icon-only one keeps its drawn square and answers the finger through an area around it, so a row with both had two heights on a phone. With the prop the square takes the 44px as its size, 44 by 44, and drops the area. A dense toolbar leaves it off, since squares grown to 44px would run over the bar. - A sidebar row can be a button, for an action such as signing out, with
as="button". It keeps the look, the hover, the focus ring and the rail behavior of the rows beside it and takes itstypefrom the tag,buttonunless it sayssubmit, so a sign-out row can post its form. - A sidebar says why it announces its state, so its choice can be kept per person on your server.
wirekit:sidebar:toggledcarriescause, which isinitafter the column starts,togglewhen the reader folds or opens it andviewportwhen a resize moves it into or out of the app shell's drawer, andchosenCollapsed, what the reader chose, besidecollapsed, what the column shows. SavechosenCollapsedon atoggle: the other two change no choice, and in the drawer the column shows its entries whatever was chosen. - A table head takes the size and weight of a body cell with
variant="cell". A row header is often a value of its row, a product name or a budget, and stood smaller and heavier than the cells beside it. The variant changes the type alone: the scope,alignandhideBelowdo what they did. - A radio's label and its text are personalization blocks,
radio.labelandradio.text, as on a checkbox. A scope or a personalization reaches the label's display mode, gap and padding, so a radio can be a full-width row with a code or a count at its edge. - A stat leads into its report with
href. The whole tile becomes a link, so a KPI on a dashboard opens the report behind it, and in astatsgrid it stays one item of the grid without a wrapper of yours. It takes the treatment of a card that links: the pointer, a raised shadow on hover and the kit's focus ring. Its name is everything it shows, label and value first. An intent tile withhrefis a link rather than a labeled group.wire:navigateandtargetpass through, and a tile that opens a new tab getsrel="noopener noreferrer"and says so to a screen reader. - A reading spine can stand beside your article as a rail, with
layout="rail". In a grid column of its own it lists every section with its full title, a line through the entries bends with the indent, and a band on that line slides to the section the reader is in, as they scroll and when they click. A visible label names it and it sticks to the top of its column frommd. An outline taller than the window scrolls on its own there, below the label, and keeps the current entry in view as the page moves on, without moving the page. Belowmdit is hidden unless you passhide-below="none", which shows it as a disclosure above the article, closed at first. Withfill-sectionsthe band fills the line from the first entry down to the current one. Seven--reading-spine-rail-*tokens theme the line and the band. A reading shell takeslayout="rail"and builds the column for it beside your article. - A line cut with an ellipsis shows its whole text in a tooltip, with
truncate="tooltip"on text. The tooltip shows while the line is cut and only then: a name that fits shows nothing, and a window that cuts the line or lets it fit is taken into account. It shows on hover, on the keyboard focus of the link or button the text stands in, and on a long press, and a cut text in no control becomes a tab stop of its own while it is cut. A screen reader reads the whole text once, from the element. The tooltip has the colors and settings of a tooltip, and a long code breaks inside it. - Every field with a label, apart from a file upload, takes a
labelAfterslot for a mark after its label. A badge that says a setting is not decided yet, say, stands after the label and its question mark, in the same row, and outside both, so it joins neither the field's name nor the help button's. On a narrow screen the mark moves to its own line and the question mark stays beside its label. See A Mark After the Label. - A combobox says what is missing when there is nothing to choose, as a select does.
empty-text,empty-href,empty-actionandempty-navigatetake what they take on a select: while the combobox has no option, it is disabled, and the sentence below it, with a link to where the missing record is created, is its description. A server search starts every list empty, so there your component says it withnothing-to-choose. Theemptyslot replaces the sentence and the link. - A save that did not go through keeps its fields unsaved with
wirekit:not-saved. Dispatch it from the component in the response that turned the save away, over a conflict or an edit someone else holds, andx-wk-unsavedkeeps the fields it names unsaved, with the hint and the question before leaving. It names fields the waywirekit:saveddoes. - An overflow nav caps its entries with
entry-max-width. Give it a CSS length such as16rem, or a percentage of the row, and every entry in the rows stays within it, its action included, the way a tab bar caps its tabs. A longer label ends in an ellipsis. - A scroll-to-top button stands over a floating action button in the same corner with
above-fab. Centered on it and one gap above it, on a phone and on a desktop, and it follows the lift you give the floating action button for a banner along the bottom. Without it, the two shared the corner and the smaller sat on or under the larger. - A date field says so when the browser writes dates in a different order than the page.
format-hinton aninputoftype="date"ortype="datetime-local"renders the sentence the date picker has had: a worked example in your application's language, and nothing while the browser and the page agree.components.andinput. format-hint components.indate-picker. format-hint config/turn it on for every field, and a field that passes the prop keeps its own value.wirekit. php - An avatar group takes its overlap from
--wk-avatar-group-overlap. Set it on the group to stack the discs closer or looser, and an avatar the next one overlaps keeps its initials clear of it at any setting. - A box of your own that scrolls prints everything it holds, with
data-wk-print-expand. Put the attribute on an element that scrolls with a bounded height, and on paper it grows to its content as the kit's own scrolling boxes do. See attribute hooks you can put on your own markup. - A row of your own that scrolls sideways prints every item, with
data-wk-print-wrap. Put the attribute on a flex row that scrolls sideways, and on paper its items flow onto the next line, as the columns of a board do. See attribute hooks you can put on your own markup.
Changed
- On a phone and on a touch surface, every interactive row takes the 44px touch target. The items of a sidebar, a dropdown, a context menu and a menubar, the triggers and links of a navigation menu, the entries of a navbar, a bottom navigation, an overflow nav, a reading TOC and a spine rail, the rows of a tree view and the options of a combobox, a multi-select, a command palette and a scope switcher were one line of text plus their padding, 26 to 33px tall, under a coarse pointer as under a mouse. Each is now at least 44px tall with its content in the middle, and a short one as wide, so menus and sidebars stand taller on a phone, while a desk with a mouse keeps the compact rows. The items of an app rail and of a folded sidebar keep the width of their column and answer a finger over a 44px square. A row of your own takes the same floor with
wk-touch-row, see touch sizes on any pointer. The label of a checkbox, a radio, a toggle or the switch of a theme controller with a visible text takes the same floor: one line stands in the middle of the 44px, and a longer text keeps the box of a checkbox or a radio beside its first line. A control without a visible text keeps its own size and answers a finger through its hit area. - A soft neutral button and a neutral badge take the shade of the surface they stand on. Their fill was
--color-wk-bg-muted, the gray of a muted area and a step darker than a card footer, so on either one a button showed only its label, an icon button nothing, and a badge in a selected data table row vanished. The fill is now--color-wk-bg-soft: the text color mixed into--color-wk-surface, the surface the control stands on. It stays light on the page and on a card, close to the gray it had, and follows every other surface WireKit paints, at rest and under the pointer or the keyboard: a card footer, a flat card, a band with asurface, a dropdown, a modal or a popover, an entry of a menu, a striped, hovered or selected table row, so it stands apart from each in light and dark mode and in every preset. An element of your own painted with the same surface classes is followed too; one painted another way names its surface withdata-wk-surface, or withdata-wk-surface-hoverfor a gray that comes with the pointer. The arrows of a reorder on thesoftsurface follow. Set--color-wk-bg-softon a surface to tune the fill there. - On a touch surface (
data-wk-touch), the question mark beside a field's label takes the touch target, and every field label is as tall. The question mark stayed at 24px there while every other control took 44px, the one target on a till worked by finger below the touch size. Every field label, with help or without, now grows to the same height with its text in the middle, so fields side by side keep their controls on one line. Only on such a surface: outside it, a phone keeps its compact label rows. See Touch sizes on any pointer. - A modal or a drawer asks "Discard your changes?" in an alert dialog of its own, no longer with the browser's question. The question came up as the browser's confirmation box, which names the site rather than your application and takes no styling. It is now an alert dialog over the overlay, in the reader's language and your theme. Stay has the focus and keeps the overlay and everything typed into it, and Escape answers the same; the focus goes back to where the reader was. Discard closes the overlay the way the reader started to close it, with the same dismissal event as before, and the focus goes back to the control that opened it. The answers are the translation keys
wirekit::Stayandwirekit::. A copy of the modal or drawer view published before this release renders no such dialog and keeps asking with the browser's question.Discard - Before a
wire:navigatevisit,x-wk-unsaved.asks in an alert dialog the unsaved hint brings with it, no longer with the browser's question. Stay has the focus and keeps the page and everything typed into it, and so does Escape. Leave goes on to the link's page the way the link goes, without asking again. A scope without a hint of its own asks in the dialog of a hint elsewhere on the page, and a page without any hint keeps the browser's question. The answers are the translation keysconfirm wirekit::Stayandwirekit::Leave. A reload, a closed tab, a typed address and an ordinary link still get the browser's own question, which no page can replace. - An unsaved value reads as a warning. Inside
x-wk-unsaved, an unsaved field was framed in blue, which did not read as anything to act on.--color-wk-border-unsavedis now the warning text color, amber beside the red of an error and the green of a success, in both modes and in every preset. The edge is twice the border width and at least2px, the unsaved hint writes its sentence in that color behind a warning symbol, and a checkbox, a radio or a switch takes a ring around its box or track, which gives way to the focus ring. An invalid field keeps its error edge. Set--color-wk-border-unsavedto keep another color. - The question mark of a field's
helpis a filled circle with the mark cut out. It was a thin outline beside the label. Now it is a filled circle in the same color whose mark shows the page behind it, white on a light page and dark on a dark one, with the mark centered in the circle. It looks the same under every icon preset, and the button keeps its 24px target. See input. - A date picker labels itself as every other field does, and stands on one line with the field beside it. Its label was a size smaller and of normal weight, with less space below it, so beside an input or a select its field stood 2 to 3px higher. It now takes the label of the other fields, with the same type, the same space to the field and to a hint below it, and its help and its mark after the label as before.
- A reading spine reports an
expandvalue it does not know. It read every value butalwaysandalways-mdashover, a typo included, without a word. It still does, and now says so in development, naming the four values it takes. - The head and body cells of a table report an
alignvalue they do not know. They took any value and aligned it left, soalign="end"gave a left-aligned cell without a word. An unknown value is reported now, as on text, and still aligns left where nothing throws. - A data table reports a column
alignorcellTypeit does not know. An unknownalignaligned the column left and an unknowncellTypedrew it as text, without a word. Both are reported now, with the column in the message, and keep that fallback where nothing throws. - A one-time code with
justify="stretch"stays flush with both edges once its cells reach their cap. On a box so wide that every cell had grown to twice itssize, the row sat in the middle, as withcenter, so fourlgcells in a dialog of the standard width stood short of both edges. The cells now keep that size and the width they leave goes between them, and a grouped code that breaks onto two lines is flush on each. - The interactive components set the styles they move as single properties, never as a whole
styleattribute. The thumbs of a range slider, the cut of an image compare, the zoom of a lightbox, the events of an event calendar week, the plane and markers of a color picker, the marks of a reading minimap and the strength bars of a password input therefore keep moving under astyle-srcwithout'unsafe-inline', which refuses a writtenstyleattribute and allows a property, and none of them clears the other inline styles of its element any more. - The bundles carry focus-trap 8.2.3.
wirekit.js,wirekit.esm.js,wirekit-alpine.andjs wirekit-alpine.bring it for every overlay that holds the focus, as the dependency list shows. When a trap opens inside an iframe, 8.2.3 keeps the caret and the selection of a field that already has the focus, where 8.2.2 let the next key replace the field's whole value, and removing that field puts the focus back inside the trap.csp. js
Fixed
- A calendar or a date picker that holds a single date no longer shades every day as part of a range. Under Alpine 3.17.4, the release Livewire 4.4.7 brings, every day of such a calendar carried the attribute the range shading reads and painted the range tint at rest, so the whole month looked selected and a day under the pointer showed the tint instead of its hover. A day stays clear now until it lies in a range, and a range still shades the days between its start and the day under the pointer.
- Text a component keeps for screen readers no longer widens the page on a phone. A live region, a hidden label or a word that completes a visual cue sat against the page rather than inside its component, in a number input and the other form fields, a spinner, a skeleton, a stat, a button, a price, a countdown and more. Past the edge of a sideways-scrolling container it made the page wider than the screen, and on an iPhone the whole page scrolled sideways, although nothing visible spilled over; a scroll container around the component did not clip it either. It stays inside its component now. Its root is positioned for that (
position: relative), so an element you position withabsoluteinside the component resolves against it. A component you position yourself keeps your position: withabsolute,fixed,stickyorstaticon it, also behind a breakpoint prefix, its root is left to you. Text for screen readers stays inside its component shows what a breakpoint means below it, and thedata-wk-sr-hostattribute that does the same for markup of your own. - A token with no break point, such as an email address, a URL or a file name, stays inside the component that shows it. On a narrow screen such a token ran past the edge of an alert, a callout, an announcement banner, an empty state, a message, a tool call, an activity row, a breadcrumb, a card, an accordion, a blockquote, a list, a page header, a link component, the hint and the error of a field, inline code, the description of a stat, a testimonial, a feature, a timeline, a tag of a tags input, a toast, a modal and a drawer. The whole page then scrolled sideways, and a callout, a card, an accordion, a modal and a drawer cut the rest of the token off. These components now break such a token where it would otherwise run out, and nowhere else: a word that fits on a line stays whole, and a table or a row of links inside a card or a dialog keeps the layout it had. Text and heading still leave the choice to their
breakprop. - An attachment shows its whole file name, and a profile that cuts its name keeps the rest in a tooltip. A long file name ended in an ellipsis with nothing to show the rest, so two files a version apart looked the same on a phone; the name now breaks inside the card, and a row of attachments scrolls rather than squeezing its cards. A profile keeps its name on one line, as a sidebar that collapses needs, and carries the full name as its title.
- A name that a one-line component cuts keeps the rest in a tooltip, and a testimonial shows its author and role whole. A node of a tree view, the author over a message group, the current workspace of a scope switcher and its rows, the author of a typing indicator, an item of a command palette, an entry of a reading TOC and the value a select-only combobox shows ended in an ellipsis with nothing to show the rest; each now carries the whole text as its title. The author and the role of a testimonial wrap instead of being cut.
- A long name in the list of a file upload is cut in the middle, as its documentation says. The list cut the name at its end, so two versions of one file read the same in a narrow column. The start of the name now gives way with an ellipsis and its end stays, the extension and the eight characters before it, and the whole name shows as a tooltip.
- An entry under the pointer or the keyboard stands apart from the panel it sits on in a dark mode. The entries of a dropdown, a context menu, a menubar and a command palette, the options of a combobox and a multi-select and the days of a calendar turned to
--color-wk-bg-subtle, which has the color of a panel and a card in a dark mode, so the row under the pointer did not change. They turn to--color-wk-bg-hovernow: the theme's own hover shade on the page, and inside a card, a panel or another gray surface that surface with 4% of the text color. On the page nothing changes, and in a light mode the hover in a panel is a shade darker than before. A neutral badge in the row takes its fill from the hover, and a hover of your own takes the token as well, as A gray surface of your own shows. - An option under the arrow keys, a selected row and the current entry stand apart from the panel they sit on in every theme. The highlighted option of a combobox, a selected row of a data table, a selected node of a tree view, the current entry of a sidebar or an overflow nav and every stronger hover took
--color-wk-bg-muted, which has the color of a panel in theminimaltheme, so in a listbox or on a card the option the keyboard is on showed no fill. They take--color-wk-bg-activenow:bg-mutedon the page, and inside a card, a panel or a footer that surface with 8% of the text color, the page color on a muted surface. On the page nothing changes. - The current module of an app rail shows its fill, and a module under the pointer its hover, in every theme. In the default tone both were
--color-wk-bg-mutedon a column of--color-wk-bg-elevated, one color in theminimaltheme, so the current module had no pill. They are the text color mixed into the column now, as in the muted tone, and the current fill stays a step past the hover. - A product card without a picture shows its badges above the name. Placed over the corners of a picture, the sale badge and the stock fell onto the name of a card that has none. There they now form a row of their own above it.
- A link in a product card's price note or description opens where it points. The card's link covers the card, so a click anywhere opens the product, and it covered a link such as "shipping" in the price note as well. Such a link now takes its own click, and a click on the text around it still opens the product.
- A chart, a mixed chart and a sparkline show the card they stand on. The chart painted the page's background color behind it, and a card stands a step above the page in a dark mode and in any theme that lifts its cards, so every chart on a card stood in a box of the page's color. A mixed chart and a sparkline hand no style to their chart, so for them there was no way to turn it off. A chart paints no background of its own now, and the check that hands a faint series to the chart's own shade measures the series against the surface the chart stands on rather than against the page.
- An ApexCharts heatmap no longer shows a stray
0above its top row. ApexCharts lays a heatmap's rows on a scale of the series names with one blank entry above the top row, and the y-axis formatter read that entry as the number 0. It stood beside a legend at the top, or left of the chart. A blank entry now stays blank. - An ApexCharts radar keeps its category labels inside the chart in a narrow box. ApexCharts leaves too little room for a long label at a side corner, and on a phone that label ran past the chart and was cut. After each draw the chart measures its labels and shrinks the radius by what is missing. A radius you set with
plotOptions.stays as you set it.radar. size - The entry the keyboard is on in a menubar's menu shows a ring. An entry and a submenu marked focus with a background alone, which had the color of the menu's panel in a dark mode, so a keyboard user could not see where they were. They draw the ring the entries of a dropdown and a context menu draw, and a menu the pointer opened paints no entry.
- On a touch screen, the step controls of a resizable handle hide once a tap lands beside the handle. A tap on content that is not clickable left the handle its focus in WebKit on iOS, and Chrome kept the handle's hover after the tap, so the two controls stayed over the text of the panels beside it. A press outside the handle now takes its focus, and a hover shows the controls only where the pointer can hover.
- A reader can scroll past an image compare on a touch screen. A vertical swipe that began on the image moved the divider to the finger and left the page where it was, so a wide comparison held a phone's page in place. A horizontal comparison now leaves vertical swipes to the page, and its divider follows a finger that moves along the image or taps it; in WebKit a tap moved nothing before. A vertical comparison keeps vertical swipes for its divider, and a mouse drags as before.
- The arrow keys keep working in a lightbox after a click inside it. WebKit, the engine of Safari and of every browser on iOS, does not focus a button it clicks, and no browser focuses a clicked image, so after a click on Next in WebKit, or on the image in any browser, focus left the viewer and the arrow keys did nothing until Tab brought it back. The viewer now keeps the focus itself after such a click, with no stop in the tab order and no ring around the screen.
- A width in the
classof an input, a select, a textarea or a combobox sets the field's width. Each field filled its row with a width of its own on the element yourclasslands on, soclass="w-24"was decided by the order of the two utilities in the stylesheet, and the field stayed full width. Your width replaces the field's own now, as it does on a number input, and a width behind a breakpoint prefix leaves the full width below that breakpoint. - A font size or weight in the
classof a table head replaces its own. Beside the head's size and weight, the order of the utilities in the stylesheet decided, and the head's own often won, so a row header given the size and weight of a cell stayed smaller and heavier than the cells beside it. A size or weight behind a variant prefix still applies beside them. - A setting switched off in the environment with
offornois off.WIREKIT_,STRICT_ VALIDATION WIREKIT_,THROW_ ON_ INVALID WIREKIT_,ANNOUNCE_ ERROR WIREKIT_,DEDUPE_ IDS WIREKIT_andDOCTOR_ SCAN_ LOGS WIREKIT_arrive as text when they carry a word Laravel'sLEGACY_ TRANSLATION_ KEYS env()does not translate, and they were read as on: Under strict validation,WIREKIT_made an invalid value throw, in a web request too. Each is read as a switch now.THROW_ ON_ INVALID=off on,yesand1mean true andoff,noand0mean false, in upper or lower case, astrueandfalsedo, and for the two validation settings a word that means neither leaves the setting unset, as if the line were not there.announce-error="false"on a form control andannounce-errors="false"on a form read the same way. - A page that updates through Livewire no longer collects work from a sticky page header or an unsaved-changes scope with every update. Each update clones the elements it compares, and the kit's
x-wk-stuckandx-wk-unsavedstarted on every clone as well: the first hung a scroll and a resize listener on the page there, the second an entry that every later request checked again, and neither was ever taken off. On a page that polls they grew without end. They now leave the clones alone, as Alpine's own directives do. wirekit:show, the component manifest, the API map and the MCP server list only slots you can pass. A form control that takes its label from an enclosing field was listed with a required slot for the value the field hands it, so an assistant reading the catalog asked for markup no control takes, and a variable named in a code comment of a component counted as a required slot as well.wirekit:showalso listed the properties of a class-based component as slots it requires, which the manifest never did.- An aspect ratio keeps its ratio when you also pass a
style, on every supported Laravel release. Its own declaration did not end with a semicolon, and some releases, Laravel 12.44 and 13.4 among them, join your style to it with a space alone. The two then read as one invalid declaration, and the box lost its ratio and your declaration together. - A mixed chart formats its tooltip values with
valueDecimals,valuePrefixandvalueSuffix. It did not hand the three to the chart, so its ApexCharts tooltip wrote every value as it came, and they stood on its wrapper as attributes of adiv. - A Chart.js chart keeps a
borderColoryou give a dataset without abackgroundColor. That is the usual way to color a line, and the chart replaced the color with its palette's when it was built; the palette's color then stayed through every change of mode. A dataset that names either color now keeps both its colors as you gave them, as a change of mode already did. - An amount in a currency is rounded half away from zero. A price of
0.125in euros read0,12 €, the formatter's default of rounding half to even, while the same price read0.13 EURwithout the intl extension. Both read0,13 €now, the way a till and an invoice round. An amount with no more decimals than its currency reads as before. - Every other number is rounded half away from zero as well. With the intl extension a review summary whose average is
4.25read4.2, and a read-only rating of4.25announced4.2, while both read4.3without the extension. They read4.3in either case now, the way an amount rounds and the way the browser rounds a chart's tooltip. - A sortable table on a page whose language groups digits with a period sorts a whole number such as
12.345by the value its reader sees. On a German, Spanish, Italian, Dutch, Danish, Turkish, Greek, Indonesian or Brazilian Portuguese page, a number from1.000to999.999without decimals was read in the machine spelling, as just over twelve, so a column sorted2.000,12.345,999,1.234.567. A cell's text is now read in the application's locale first and in the machine spelling when the locale could not have written it, so1.5and0.125still sort as decimals. Adata-wk-sort-valueis read in the machine spelling first, as before, and a raw value with three decimals on such a page belongs there. wirekit:show chart, the component manifest and the MCP server describe the chart as drawn with Chart.js or ApexCharts. They called it a Chart.js wrapper, which it has not been alone for a long time.wirekit:verify,wirekit:doctor:props,wirekit:doctor:a11yandwirekit:csp-auditread Blade's escapes in your views as the text Blade prints. Blade prints@@php,@@include(…)and every other directive written with two@, and@{{ … }}, as they stand. The commands took an escaped@@phpfor the start of a block that ran to the next real@endphpand read the markup in between as PHP: they skipped its elements and took the//of a URL for a comment.wirekit:doctor:propsalso skipped the elements inside the parentheses of an escaped directive and took an escaped@@props([…])for the props of your component, andwirekit:csp-auditcounted an expression holding an escaped@@if(as one it could not check.- A field set no longer warns about the legend of a set nested in it. In development a
field.setlogs a warning when its default slot holds a<legend>, which is no caption there. It counted the legend of a set nested in the slot as well, though that one is the caption of its own group, so a form that nests sets logged the warning on every render. Only a legend outside every nested set counts now, and none inside an HTML comment. - On a phone and on a touch surface, the buttons a component draws in its own style take the 44px touch target, as fields and buttons do. A tab, a segment of a segmented control, a quick reply, a reaction, Today, the view switch and the agenda rows of an event calendar, the buttons and chips of a filter builder, a copy button with its text, the density switch, sort heads and Columns of a data table, the filters and rows of a notification center, the interval switch of a pricing table, Back and Next of a tour and a profile with a name that opens a menu stayed between 18 and 43px tall under a coarse pointer. Each is now at least 44px tall, and a desk with a mouse keeps the compact sizes. The steppers of a number input stand as tall as their field, a slider answers a finger over a 44px band with its track where it was, and the swatch of a color picker grows to 44px.
- On a phone and on a touch surface, the small icon buttons inside components answer a finger over 44px. The dismiss of an alert, an announcement banner and a toast, the close of the drawer in an app shell, the menu toggle of a sidebar and a navbar, the toggle that folds an app rail, the arrows of a calendar, a carousel and a lightbox, the play button of a carousel, a copy button without text and a profile shown as its avatar alone keep their drawn size and take a 44px area around it. The arrows of an event calendar and the control that folds a sidebar stand beside other controls, so they grow to 44px instead. The clear button of a combobox takes the touch target as the buttons inside a field do, and the field keeps its text clear of it.
- On a phone and on a touch surface, a small control beside other controls no longer takes the taps meant for its neighbor. Around Confirm and Cancel of an inline edit, the arrows that move a row of a reorder list or a table, the action beside an entry of an overflow nav and the box of each option in a multi-select list, a 44px area reached into the control beside it: a tap on the edge of Confirm canceled the edit, a tap on the edge of the up arrow moved the row down, and a tap on the end of an entry's label ran the action beside it. Each of them now grows to 44px instead, an option of a multi-select list as a row, and a desk with a mouse keeps the compact sizes.
wk-touch-rowdoes the same for a small button of your own that sits beside other controls, and on awk-buttonit also drops the area an icon-only button takes. - The trigger of a collapsible takes the 44px height every button takes under a coarse pointer and on a touch surface. It stayed as tall as its line of text, a short row on a till worked by finger, while the accordion's header took the touch target. The floor spans the whole row, and the trigger carries
wk-collapsible-triggerfor it. - Opening the mobile menu of a navbar with its button moves the focus to the menu's first entry. The menu stands before the button in the page and is laid out below it, so the next Tab left the bar and the menu could only be reached backwards, from its last entry.
- A pagination under Livewire keeps the keyboard focus when it turns the page. The page number you chose comes back as the current page, and Previous on the first page and Next on the last come back disabled, so the focus they held fell to the page and the next Tab started at its top. It now lands on the current page, without undoing the scroll to the top of the list, and a control that is still there after the turn keeps it.
- Closing a dialog over a modal or a drawer puts the focus straight back where it was. When an alert dialog opened over a modal or a drawer closed, the focus stopped on the first control of the modal or drawer, usually its close button, before it reached the field or button the reader had been on, and a screen reader announced both. The focus now returns in one step.
- An accordion, a branch switcher, a carousel, a navigation menu and a slider keep their keys in Safari after a click on one of their controls. WebKit does not focus a button or a range it clicks, so the focus fell to the page: the arrows did nothing after a click on an accordion header, an arrow of a branch switcher or the trigger of a navigation menu, or after a click or a drag on a slider, and the Next button of a carousel took no Enter, on a Mac and on an iPad with a keyboard. The clicked control now takes the focus, and it draws no focus ring until a key is pressed.
- A radio keeps its focus ring on the option the arrow keys reach in Safari. WebKit does not count the focus an arrow key moves in a radio group as keyboard focus, so the ring went away at the first arrow key on a Mac and in every browser on iOS, on the circle and on a card, while the focus moved on. WireKit's scripts now note when an arrow key brings the focus to a radio, and the ring stays with it, also through a
wire:model.liveround trip. Without@wirekitScriptson the page, Safari draws the ring after Tab only, as before. The new swatch group rings the same way. - The arrow keys of a calendar go on from the day a click chose. After a click on a day, the arrows moved on from the day the keyboard had held before, the date the calendar opened with or today, and a reader who tabbed away and back returned to that day. The clicked day is now the one the keys start from and the one Tab returns to, also with several months side by side and with the
optimisticprop. - A modal or an alert dialog without a trigger, a drawer, a command palette and a tour take no room where you write them. Their element was an empty box, so in a row of buttons, such as the actions of a page header, each of them added a gap, and the last button ended that many gaps before the end of the row. Their element draws no box now. A modal or an alert dialog with a trigger keeps its box, so the trigger keeps its place, and
class="contents"still hands it to the container. - A modal or a drawer no longer asks before it closes because your server showed more fields. The question compared the dialog's fields as a whole, so a step your server showed after the reader's first touch, such as empty fields for a new customer after a search that found nobody, read as the reader's changes, and a click beside the dialog asked "Discard your changes?" about nothing. Each field is now compared on its own: a field that appears counts once the reader changes it, and a field your server removes counts no more.
- An alert dialog or a drawer composed without its title and without a
labelreports itself, as a modal does. Itsaria-labelledbypointed at an id that only the title or the header binds, so a screen reader announced a bare "alert dialog" or "dialog" while the markup looked complete. It now goes through strict validation: a log line in a request, an exception in a test or a console run withAPP_DEBUGon. - Escape closes a navigation menu flyout that a pointer opened, wherever the focus is. The flyout listened for Escape on its bar and on its panel only, and a pointer leaves the focus where it was, often on the page itself, so a flyout opened by hovering stayed open until the pointer moved away. With the focus in the bar or the flyout, Escape still hands it back to the trigger.
- Play starts a carousel rotating while the focus is inside it. A carousel pauses while the pointer or the focus rests in it, and its play/pause button is inside: pressed with the keyboard, Play set the carousel playing and nothing moved until the focus left, and after a click it did the same once the button took the focus. Play now lifts that pause; the next pointer or focus that enters pauses again, and Pause stays pause.
- Home and End on the focused slides of a carousel go to the first and the last slide, as its keyboard table says. The arrows were the browser's and still are, one slide a press, but Home and End are keys a horizontal scroller does not take: with the slides focused, End stayed on the first slide in Chrome and Safari alike, and in Safari End on the last slide went back to the first. A field or a control inside a slide keeps both keys for itself.
- A focus that comes up from above rests below a head pinned to the top of a component's own scroller, rather than under it. The head of a table with
sticky-header, the head of the days in the week view of an event calendar, the heading of a map's list, the labels of a notification center's groups and a date separator withvariant="sticky"covered the element the browser brought to the top edge on the way up, a field in a table row wholly. Each now tells its scroller how much of the top it covers, as the page's pinned bars do, so the scroller keeps that much padding. - A control the keyboard reaches at the top or bottom edge of a conversation keeps its whole focus ring. The conversation leaves the ring room at its edge through a scroll padding, which not every browser reads when it scrolls a focused element into view: WebKit on Linux left a reaction just under the top edge where it was, with most of its ring cut off. Once the browser has scrolled, the conversation now moves by what the ring still lacks, and leaves a control the browser kept out of view where it is.
- A floating action button, the scroll-to-top button, the resume pill of a reading bookmark and a toast region no longer hide the control that takes the keyboard focus. Fixed to an edge of the viewport, each could cover a small control entirely: one already in view under it kept its place when it took the focus, and one the page scrolled in came to rest under it. They now mark the band they cover, as the sticky bars do, so the page keeps a focused control clear of it (WCAG 2.4.11); a toast region does so while it shows a toast. At the very top or bottom of a page, where nothing is left to scroll, a toast stack moves to the other edge while a control under it has the focus, and comes back once the focus moves on. For the other three, leave room at the end of a page as their pages describe.
- A drawer, the lightbox and an app shell that fills the screen keep their controls clear of a notch and a home indicator. On a page that sets
viewport-fit=cover, a drawer's close and footer buttons, the lightbox's close, arrows and zoom controls, and the shell's navigation toggle and drawer sat under a phone's camera island, notch or home indicator, where they are hard to tap. Each now stands in by the screen's inset on the edges it touches, as the bottom nav and the floating action button already did. Withoutviewport-fit=covernothing moves. If you padded these for the insets yourself, remove that padding. - A result that
x-wk-scroll-into-viewbrings into view comes out from under a fixed header. The directive measured whether the region stood in full view against the window, so in an application whose main area scrolls under a header, a region whose top stood between the top of the window and the top of that area counted as in view and stayed cut off. It now measures against the part of the window the region can be seen in. See Bringing a result into view. - A scope switcher opens with the current scope in view, and keeps the row Enter follows in view while the reader types. In a list taller than its panel, the panel opened at the top while the current scope stood further down. After the reader scrolled the list and typed, the first match stood above the part that shows, and Enter followed it. Opening now scrolls the list to the current scope, and typing scrolls the row Enter follows into view, as the arrow keys do.
- The search of a scope switcher finds a row through a wrong or swapped letter in a query typed with the row's punctuation.
wroker-01.example.andcom worker-01.exmaple.found nothing, while the same words typed with spaces between them found the row: a row's words are split at dots, hyphens, underscores and slashes, and the query was not. A query is split at the same characters now, and each part has to find the row.com - A page turn of pagination jumps where the reader asked for reduced motion, on a page that scrolls smoothly. The scroll after the turn, and the scroll that keeps an option in view in a combobox, a multi-select, a command palette, a scope switcher and a selecting sidebar, followed your page's
scroll-behavior, so withscroll-behavior: smoothon an<html>that carries nowk-class they glided under reduced motion too. They still follow it, and jump when the reader asks for less motion. The hover preview of a reading minimap in its rendered mode now jumps to the place under the pointer, where it could glide behind it. - The notifications of a notification center are lists, one per group, named by the group's label. A screen reader heard a label such as "Today" as a line of text, without learning which notifications it stood over or how many there were. Each group is now a list and each notification one of its items, so the label and the count are announced with the list, and an ungrouped panel is one list without a name. The panel looks as it did.
- The visibility toggle of a password input reports its state once. Its name already says "Show password" or "Hide password", and it also carried a pressed state, so a revealed field was announced as "Hide password, pressed", which leaves open whether the password is shown or hidden. The pressed state is gone and the name carries it, as on the carousel's play and pause button.
- The error of an inline edit reaches a screen reader after a save that fails validation. Its message stood in a paragraph without a live region, and only the give-up message after
save-timeoutwent to a live region, so the error was shown and linked to the field but never announced. The paragraph is a polite live region now, as on every other form control, andannounce-errorswitches it as before. - A button with
loadingsays a long wait to a screen reader. Its spinner was the only sign of the wait, and assistive technology does not see it: the defaultdisabledtook the focus away, andaria-busyannounces nothing on its own, although the button page said it did. A wait that lasts past a second now says "Loading", in the application's language, through a status region of the page, which empties when the request ends and stays reachable while a dialog is open. A request that answers sooner says nothing.data-wk-busy-textgives the wait a word of your own, andx-wk-announce-busywith adata-wk-busy-spinnergives the same to a control of yours. - A phone field inside a form follows the form's
announce-errors. It read only the global setting, so in a form that turns the per-field announcements off for an error summary of its own, the phone field's error was announced twice. It takes the form's policy now, as every other form control does. - A comma ends a tags input tag however it reaches the field. Only a comma pressed as a key ended a tag: one that arrived without a key press, from a paste or an input method, stayed in the text, and a pasted list became a single tag with the commas inside it. A pasted list now becomes one tag per piece, the text after the last comma stays in the field, a piece already in the list is skipped, and a screen reader hears the added tags in one sentence.
- The "See all" link of a notification center is named with the panel's title. Alone in the panel's footer it said "See all" and nothing about where it goes, which is what a screen reader's list of links showed. Its name is now its own text followed by the title, "See all Notifications", and a
see-all-labelandtitleof your own are combined the same way. The visible text is unchanged. - The bulk-action bar of a data table names its button "Clear selection". It read "Clear", while a combobox names the same function "Clear selection", and "Clear" alone did not say what it clears. The button now uses the
wirekit::Clear selectiontranslation, and thewirekit::Clearkey, which nothing else used, is gone: if you published the language files and changed that key, changewirekit::Clear selectioninstead. - A chat marker's separator carries one name. It named its separator with its text even when you gave one with
aria-labelledby, so the element held two. The separator now takes its name from its text only while you give none, as a divider does. - The default reading spine names its navigation. The spine is an
<aside>named "Page contents", and the<nav>inside it had no name, so a screen reader listed it by its role alone beside the other navigation on the page. The<nav>now carries the aside's name: anaria-labelledbyoraria-labelyou pass, or "Page contents" in the reader's language. The rail layout named its<nav>already. - Two headings with the same
idare two entries of a reading spine or a reading TOC. The outline listed its entries by the heading'sid, so two headings that shared one rendered as a single link, every later entry stood one place off its section, and a click on the second could only ever reach the first. Each entry now has a key of its own and jumps to its own heading. Theida heading already carries stays as it is, since a link from elsewhere may point at it, and anidthe outline generates for a heading without one never repeats anidalready on the page. - An app rail that starts expanded shows its brand name at once instead of fading it in. The rail is drawn collapsed until its script knows the width of the window, and the name revealed itself with its own fade on that switch, so a rail with
expanded, a stored choice or a cookie faded it in on every page load, below its contrast while it did. The fade now runs only when the reader collapses or expands the rail. wirekit:themegives the Soft, Material, Cupertino and Retro Terminal presets their hover. It wrote their accent and its label but left the hover at the stock value, so in dark mode the white label of a primary button sat on a near-white hover under the pointer at 1.26:1, and Retro Terminal's near-black label on a near-black one in light mode at 1.29:1. It now writes the hover each preset's page names.- In the dark mode of the Cupertino preset, a field inside a card, a dialog or a drawer has an edge that clears 3:1. The field's fill is the page's own color there, so its border is its only edge, and on an elevated surface the border drew 2.94:1, under the 3:1 WCAG 1.4.11 asks of a control's boundary. It is now Apple's systemGray,
#8E8E93: 5.21:1 on the page and 4.27:1 on an elevated surface. If you pasted the preset's dark block, update--color-wk-border-strongthere. - In a Windows contrast theme, the current page of a pagination, the track of a slider and a range slider, and the edge of a tooltip stay visible. Each was drawn with a background or a shadow alone, and forced colors repaint every background in the page's own color and drop every shadow: the current page looked like the pages around it, a slider's handle had no track to stand on, a range slider showed two handles with nothing between them, and a tooltip's text had no edge. The current page and a range slider's chosen range now take the reader's selection color, the tracks and the tooltip an edge in the text color, and so does every other panel drawn on the tooltip's surface: the tooltip of a text cut with
truncate="tooltip", the value above a slider's handle, the bubble of an event calendar and the label of a reading minimap. A square the mode drew between a range slider's handles is gone as well. Nothing changes outside the mode. - A reading spine keeps its ticks under forced colors. Its ticks were fills and nothing else, and Windows High Contrast repaints every fill in the page's own color, so the collapsed spine showed an empty rail and the current section looked like every other. The ticks now take the reader's own palette: the text color for a section and the selection color for the current one, the per-section fill of
fillSectionsincluded. - A reading minimap that faded while idle comes back to full opacity while the focus is inside it. With
heading-anchorsthe minimap has controls of its own, and only pointer movement, scrolling or hover lifted the fade, so a reader who tabbed onto an anchor saw it and its focus ring at the idle opacity. Focus inside now lifts the fade the way hover does. - A tooltip, a popover and a dropdown inside a stack stand at their button. A stack stretches its items, so each of the three was as wide as the column while its button kept its own width, and the panel was placed against the full width: a tooltip or a popover stood in the middle of the column, and a dropdown with
placement="bottom-end"at its far edge. The same happened in any flex or grid container that stretches its items. The panel is placed against the button now, and the component keeps its width, so a trigger that spans the column still does. - A command palette trigger in a narrow header stays inside its frame, and a tap on its magnifier opens the palette. A field in a frame narrower than its content reached past the frame's edge, as a 44px search trigger on a phone showed, and the magnifier, most of such a frame, opened nothing when tapped. A framed field now shrinks with its frame, and a click anywhere in the trigger's frame opens the palette, once.
- A table draws its row lines under a frozen first column as it draws them beside it, in WebKit too (Safari, and every browser on iOS). Each frozen cell is painted after the one above it, and WebKit places each cell's background on whole device pixels on its own, so where a line fell on a fraction of a pixel the cell below covered part of it, and under the frozen column the line could show at half its strength or not at all. The cell below now draws the line again on its own top edge. The last row has no line of its own, and neither has a record whose detail row is open, so those two lie below the row above them instead.
- A data table with a frozen column draws its row lines under the frozen cells as it draws them beside them. The frozen cells covered the lines, so where a row sat on a fraction of a pixel a line was missing under them in Chrome and Edge and half as strong in Safari. They now leave a line and a half at their top and bottom edges to the table, and the table's own line shows through.
- A selected row of a data table wears its selected fill in every frozen cell, also under the pointer. The selection cell stayed in the page's background whatever the row's state, and the first frozen data cell showed the hover over a selected row, which the row itself does not. The selection column's heading takes the heading's background like the other headings, and no edge line stands after the selection cell any more.
- A date picker with
format-hintis no taller than one without it while the hint has nothing to say. The empty sentence still took the gap of a line below the field, so a picker with the hint stood a little taller than the one beside it. It now takes no room until it holds text. - An overflow nav keeps to its
linesrows when the links of a row add up to a fraction of a pixel more than the row holds. It allowed each row half a pixel of slack and read the row's width rounded to a whole pixel, so it could leave a link in a row the browser had already wrapped, and on a phone the links then took three rows where two were allowed. It now reads the row's width to the fraction of a pixel and compares without slack, so such a link moves into the menu. - A long label in an overflow nav ends in an ellipsis inside the row. An entry was as wide as its label, so a label wider than the row ran past its edge, and a width set on the link cut it off without an ellipsis, with the markup after the label hidden behind the edge. An entry is now never wider than the row: its label truncates, and the icon, the markup after the label and the action stay in view. In the menu a long label still wraps. A personalized
itemorlinkblock keeps the fix. - A stepper and an app rail keep a word that fits its line whole. A step label and a rail's name below its icon hyphenate a word too long for them, and Safari, with every browser on iPhone and iPad, also hyphenated a word that would have fit on the next line: "Where it appears" read "Where it ap-" and "pears", and "Alle Projekte" read "Alle Pro-" and "jekte". Both now hyphenate only words of twelve letters or more, in Safari and in every browser with
hyphenate-limit-chars, and a shorter word that does not fit still breaks inside its space. - The marker list of a map shows a long name or description in full. Each line was cut at the end of its first line, and the list is where a reader finds a marker's name: choosing a row moves the map and opens nothing. A name that just fit lost its end once a reader widened the spacing between letters and words (WCAG 1.4.12). The name and the description wrap now.
- A map without its sidebar shows its list under the map while one of the list's buttons has the keyboard focus. With
list="false"the list stayed out of sight for everyone but a screen reader, while its buttons, the keyboard's only way to the places, were still stops on Tab: the focus went to buttons nobody could see. The list now appears below the canvas while the focus is in it, the way a skip link does, and the canvas keeps its size. - An event calendar shows an event on every day it covers. An event whose
endfell on a later day than itsstartappeared on its first day only: the week view drew a night shift from 22:00 to 02:00 as a half-hour block and nothing after midnight, the month grid showed a Monday-to-Friday vacation on the Monday, and the agenda left out an event that began before its range. Every view now puts the event on each day it covers, the week view runs it to the foot of its first day and from the top of the next, a later day shows no start time, and its accessible name gives the whole span. For an all-day event, anenddate is the last day it covers; if you pass the day after the last, as iCalendar does, pass the last day instead. - An event calendar keeps the day markers of its month view inside it when a Safari window narrows. A week's row kept the height it had at the wider width, so the markers of the last week, which wrap in a narrower column, were cut at the bottom of the month view. A week is now a row of seven equal cells that grows with its markers.
- A day marker on an event calendar shows its whole name in a narrow calendar. Its label was cut at one line in the month, week and agenda views, and the tooltip that held the full name opened only under a pointer, because the label is no control. A holiday read "Company h…" to anyone using a keyboard, in a calendar a reader's larger default font makes narrow as well. The label wraps now.
- The tip of a cut title on an event calendar stays when you move the pointer onto it. The calendar draws its own tip, and it closed the moment the pointer left the event, so you could not move onto it to read it or rest the pointer there, as WCAG 1.4.13 asks. It now waits as long as a tooltip does before it closes, takes the pointer, and closes once the pointer has left both. Escape still closes it at once.
- A clearable search field shows one clear button. In a field of
type="search"the browser draws a clear button of its own once the field holds text, in Safari always and in Chrome on hover and focus, and it stood beside the X ofclearable. A clearable field now hides the browser's button; withoutclearablethe browser's stays, as the field's only one. - An icon keeps its size in a flex row that is short of space. It shrank along with the items beside it, down to nothing when the row was narrow enough, as the heading icon of a narrow toolbar did on a phone. It now keeps its size, and a
shrinkclass of your own still lets one give way. - An icon-only button keeps its square in a crowded row. In a flex row too narrow for its items, the square shrank to the width of its glyph, 18px for
sm, and its hit area narrowed with it. It keeps its width now; ashrinkclass of yours still lets it give way. - The initials in an avatar group stay clear of the next avatar. Every avatar overlapped the one before it by
0.6remat every size, and its initials stood in the middle of the whole disc, so the next disc covered their last letter: in a small group "AB" read as "AE". An avatar the next one overlaps now sets its initials in the part of its disc that stays visible, and groups inxsandsmoverlap a fifth of the disc. A photo keeps the whole disc. The attendee stack of an event calendar is drawn the same way and keeps its initials clear as well. - An
xsavatar keeps its initials inside its disc. They took the text size ofsmon a disc a third smaller, and two wide letters ran past the edge of the circle. They now take the2xssize, as does the "+N" chip of anxsavatar group. - On a touch screen, the step controls of a resizable handle no longer lie over the panels beside it. They were always shown there, because a touch screen has no hover, and each reached over the panel next to the handle and onto its text. They now show once the handle is tapped, and a tap elsewhere hides them again; a tap on a control still moves the handle without a drag.
- The foot bar of an open, collapsible sidebar keeps its height in an app shell that sets no
--wk-shell-foot-h. It shrank to the height of what it held, 33px instead of 56px, so the room a shell bar keeps for the focus rings of its controls stood below the column. The shell cut it there, and with it the bottom edge of the ring the bar's scrolling strip shows when it has the focus. A shell that sets the property still gets a bar that fills its taller band. - A
styleyou pass to a reading spine together withedge-offsetapplies. The spine wrote the offset as astyleattribute of its own in front of yours, so the element carried two, and the browser keeps only the first: your declarations were dropped without an error. Both now stand in onestyleattribute, the offset first and yours after it, so a declaration of yours that sets the same property still wins. - A reading spine indents its entries from the shallowest heading on the page, as it numbers them. The indent counted from the smallest of its
levels, thenumberedlabels from the shallowest heading the page has. On a page with onlyh3headings under the defaultlevels, every entry was therefore numbered as a top entry and indented as one below it. Both now start at the shallowest heading the page has, so an entry at the top stands without an indent. - A reading spine and a reading TOC list a section that Livewire adds after the page loaded. Both collected the headings in their
targetonce, when they started, so a section a component loaded or rebuilt later never reached the outline, and the active section was measured against the headings that were there before. They now follow thetargetas it changes, a removed or renamed section included, and the section the reader is in keeps its mark. See Headings that change after the page loads. - A progress bar and the partial star of a rating show nothing rather than everything where a
style-srcpolicy drops their inline style. A determinate bar took itsvalueas an inline width and spanned its whole track without it, so 40% read as done, and the partial star of a rating of 4.5 stood whole. Both now fall back to empty, as the bars of a review summary do. See Style attributes needstyle-src-attr. - The partial star of a rating with
readonlyfills from the start side in a right-to-left page. Its filled part stood on the left, away from the whole stars, so 4.5 read as four stars and a half apart from them. It now stands against them in either direction. - A box that scrolls prints everything it holds. A printer cannot scroll, so a scroll area, a table with a sticky header, a conversation, a sticky panel, an editor with a maximum height, the week of an event calendar and the list beside a map printed the part their box showed and nothing past it, and an app shell with
viewportprinted one screen of its main column. On paper each now grows to its content, over as many sheets as it needs, and a scroll area that fades at its edge prints its first and last lines unfaded. - A sortable table or a data table prints its column names on every sheet. A table repeats its head at the top of each printed sheet, and Chromium leaves a button out of those copies, so a head whose cells sort showed its names on the first sheet only. On paper the sort buttons now step aside, and each name and its indicator stand in the cell where they show on screen.
- What scrolls sideways comes onto the sheet. A sheet cannot scroll, so the columns of a kanban board and a row of attachments printed the ones that fit and cut off the rest, a code block printed the start of a long line, and a table or a data table whose column headers held it wider than the sheet lost its last columns. On paper the columns of a board and a row of attachments now flow onto the next line, every
<pre>breaks its lines, and a table wraps its column headers between words, so it fits the sheet as far as its words allow, a table with a sticky first column too. Words stay whole, so a table or a status matrix whose words alone are wider than the sheet prints the columns that fit, and a landscape sheet holds more of them. - A shadow prints as an edge, not as a gray box. Printed from Chrome and opened in the PDF view of macOS, or printed from a Mac, a blurred shadow came out as a gray box: around an elevated card, behind a soft badge, around fields and buttons. On paper
--shadow-wk-sm,--shadow-wk-mdand--shadow-wk-lgnow print as a hairline in the border color in every theme, so a card keeps its edge and a chosen segment its outline, and a badge keeps its ring without the shadow below it. See shadows. - What a component hides stays hidden where a
style-srcpolicy drops inline styles. An action bar with:visible="false"hid itself with an inlinedisplay: noneand stood on the page without it; it now takes thehiddenclass. The filter block of liquid glass took a 300 by 150 box at the top of the page and is now sized by its own attributes, and the other billing intervals of a pricing tier and the copied label of a clipboard button no longer show before the scripts start.
Documentation
- WireKit names WCAG 2.2 AA as the standard it is built and tested to. The criteria 2.2 adds hold across the kit: a control that takes the focus stays clear of anything pinned or floating at an edge (2.4.11), every drag has a way with a single pointer (2.5.7), a target is 24 by 24 or stands far enough from the next one (2.5.8), and nothing asks for a cognitive test to sign in (3.3.8). The footer and app shell pages cover consistent help (3.2.6), which an application meets in its own layout.
- The theming page documents all eight methods of the PHP contrast check.
WcagContrast::, the channels of a color without its alpha, andparse To Linear Rgb() WcagContrast::, the luminance WCAG computes a ratio from, were public and named nowhere.relative Luminance() - The integration guide asks for
style-src-attr 'unsafe-inline'in a Content Security Policy. It called a policy withstyle-src 'self'one that works, and under it a grid stacks into one column, a map has no height and the levels of a tree view stand at one edge, because such a policy drops every style attribute in the markup. Both example policies now allow them, and a table names what each component loses under a policy that does not. - The one-time code page warns before a code is checked at the last digit. Its examples checked the code the moment the last box was filled, and a correct code that leads to another page changes the page without a button press. Both examples now say so in the field's
hint, and the section explains why, after WCAG 3.2.2. - The toast page says how a reader who needs longer than the default gets it. Bind the region's
durationto a setting of your application, or use0to keep each toast until it is dismissed, as WCAG 2.2.1 asks. - The avatar page says its status dot and ring tell the states apart by color alone. Where the state matters to the reader, show it as text as well, as WCAG 1.4.1 asks.
- The status dot page says how far apart dots with a tooltip stand. A dot with a tooltip is a target smaller than 24 by 24 pixels, so it meets WCAG 2.5.8 through its spacing: two of them need 24 pixels between their centers. The rows of dots in its examples use
gap="lg"now; before, a 6-pixel dot stood 23 pixels from the next one. - The app shell page points at the skip link. The header and the sidebar come back on every page, and a skip link before the shell lets a keyboard reader past them, as WCAG 2.4.1 asks.
- The footer and app shell pages say where help belongs. Contact details, a link to a help center or a chat button that the pages of an application repeat stand in the same place on each of them, in the footer or the shell every page shares, as WCAG 3.2.6 asks.
- The stat and stats pages describe the markup the tiles render. Both said a stat carries
<dt>/<dd>semantics and is announced as a definition list, and neither component renders one: a stat is adivwith ansr-onlyword for the direction of its change, a labeled group in the KPI-tile form, and one link withhref. The advice to keep a stat out of prose stays, with the reason it has: the tile stands in a paragraph as a box. - The pages of the reading components say how the active section is found. The reading spine, the reading TOC and the minimap were described as tracking it with an
Intersection, while each measures at most once a frame as the page scrolls: the spine against a lineObserver offsetbelow the top of what scrolls, the TOC just below its own strip, the minimap at the middle of what is visible. The on-page TOC recipe no longer says that a spine without:offsetmeasures from the top of the window: its line sits6rembelow it unless you set one, so a sticky bar needs an:offsetonly when it is taller. - The reading spine's documentation promises only what it does. The expanded width is
16rem, the token link of the on-page TOC recipe reaches the token reference,focusis documented as the same ashover, a CSS selector asboundaryis checked at runtime by the progress bar alone, and anedge-offsetthat follows the scrollbar inset goes through--reading-spine-edge-offset, since the prop takes a plain length. - The editor page says what a document given as an array has to be. Since 2.63.0 an array
valueis encoded as JSON when the page renders, and one withNAN,INFor text that is not valid UTF-8 makes the render fail with aJsonException, rather than show an empty editor whose next save would overwrite your document. A string is written as it is. - The navigation in the stack and header examples has a name. A
navwithout one is listed by its role alone, the same entry as every other unnamed navigation on the page. The stack example also holds links now, where it held buttons that led nowhere. - The alert dialog page says what happens without a Cancel. It said the component refuses to render without both action slots. It renders: Escape closes it, while a pointer or a finger has nothing to tap but the action itself, unless
dismissibleis on. - The CLI reference no longer says a CSP build cannot run an inline
x-dataobject. It can: Alpine's CSP build evaluatesx-data="{ open: false }", and only a method or a getter inside the object fails to parse, whichwirekit:csp-auditreports like any other expression the grammar refuses. The check against yourAlpine.data()registrations is for anx-datathat calls a factory. - The dependencies page names the directives the core bundle carries. It said
dist/carries one directive for the zero-JS form primitives. The bundle registerswirekit. core. js x-wk-indeterminate,x-wk-findable,x-wk-focus-firstandx-wk-ref, and the page now names all of them. - The ApexCharts theming page names the series colors in the order a chart takes them. It listed success, warning, danger and info as the first to fourth series. The second series takes the danger color, followed by success, warning and info.
- The password input example that sets a new password says so, and the email field of the CTA sections says what it holds. Without
autocomplete="new-password"the field fell back tocurrent-password, so a password manager offered the saved password instead of suggesting a new one, and the newsletter field gave autofill no purpose to fill (WCAG 1.3.5). - Every form control in the code examples has a name of its own. The examples a page gives for copying showed a select, a time picker, a number input, a slider, a rating, a color picker and more without a label, so a copy announced its placeholder, a generic word or no name at all. Each example now carries a label, or an
aria-labelwhere the layout has no room for one; a color picker, which takes no label, takes anaria-label. - The marketing landing page recipe builds its footer links from components. Its four link columns were lists without spacing, and their links stood about 20 pixels apart, closer than WCAG 2.5.8 allows for targets this small. The columns now use a list with
spacing="sm"and links, in the preview and in the source to paste, and so does the legal row. - The table in the animations example of rows that just changed names its columns. It showed three rows of an item and a number with no header, so nothing said what the numbers were.
- The cart in the sticky panel example shows a cut product name in full. Its long names were cut with an ellipsis and nothing showed them whole, though the cart is the one place to read them, and wider spacing between letters and words cut names that fit before (WCAG 1.4.12). They are cut through
truncate="tooltip"now, which shows a cut name in a tooltip and gives it a tab stop. - The table in the context menu example keeps its columns in line on a phone. Its card was too narrow for the longest email address, so that row ran past the card and its role stood out of line with the others. The card is wider now, and every role starts at the same place.
- The reading bookmark section says to leave room for the resume pill at the end of a page. The pill is fixed to the bottom of the viewport, so the last lines of an article that ends there sat under it; a bottom padding as tall as the pill lets them scroll clear, and the section's example now has one. It also says which element announces the offer: a polite live region beside the pill, not the pill itself.
- The table page says that a table needs a width in a parent that sizes to its content. Its frame is sized by the space it is given, so in a flex column with
align-items: centeror aninline-blockit collapses to nothing without an error;width: 100%on the table or on the card around it keeps it. - The three-panel example on the resizable page keeps its words whole on a phone. The last panel's 15%
minSizeis about 55px there, and its words ran past the panel's edge and were cut. The panels now start at 30/40/30 and let a word that is still wider than its panel break inside it, and the tip below the example says why.
Components
Every component this release changes, each linked to its page: Accordion · Action Bar · Activity Row · Alert · Alert Dialog · Announcement Banner · App Rail · App Shell · Aspect Ratio · Assistant Message · Attachment · Avatar · Badge · Blockquote · Bottom Nav · Branch Switcher · Brand · Brand Bar · Breadcrumb · Button · Calendar · Callout · Card · Carousel · Chart · Chart Mixed · Chat Marker · Checkbox · Clipboard Button · Code · Code Block · Collapsible · Color Picker · Combobox · Command Palette · Context Menu · Conversation · Countdown · Data Table · Date Picker · Date Separator · Drawer · Dropdown · Editor · Empty State · Event Calendar · FAB · Feature · Feature Grid · Field · File Upload · Filter Builder · Fit Text · Grid · Heading · Hero · Hover Card · Icon · Image · Image Compare · Image Gallery · Inline Edit · Input · Kanban · Label · Lightbox · Link component · Liquid Glass · List · Main · Map · Menubar · Message · Message Group · Message Typing · Mockup · Modal · Multi-Select · Navbar · Navigation Menu · Notification Center · Number Input · OTP Input · Overflow Nav · Page Header · Page Progress · Pagination · Password Input · Phone · Popover · Price · Pricing Table · Product Card · Profile · Progress · Prose · Quick Replies · Radio · Range Slider · Rating · Reaction · Reading · Reorder · Resizable · Review · Review Summary · Row · Scope Switcher · Scroll Area · Scroll to Top · Segmented Control · Select · Shell Bar · Sidebar · Skeleton · Skip Link · Slider · Sparkline · Spinner · Stack · Stage Card · Stat · Status Matrix · Status Tiles · Stepper · Sticky Panel · Stream · Strength Meter · Swap · Swatch · Swatch Group · Table · Tabs · Tags Input · Testimonial · Text · Textarea · Theme Controller · Time Picker · Timeline · Toast · Toggle · Toggle Button · Tool Call · Tooltip · Tour · Tree View · Unsaved Hint · Wizard