Skip to main content
WireKit

WireKit v2.53.0

Minor release

Minor. Tables can set a column aside on a phone and freeze their first column only where they need to, a dropdown trigger can show that it opens a menu, and a button beside a field can match the field's type size on a touch screen. A run of fixes makes long typed names wrap where they used to overflow, a refused copy visible, and the scroll shadows strong enough to see. And three interactions that went wrong only in WebKit — Safari, and every browser on an iPhone — now behave as they do elsewhere.

Added

  • <x-wirekit::page-header> — the header of an application screen. A title, an optional sentence under it, and an actions slot that sits beside the title while there is room and drops under it when there is not, wrapping among itself rather than running off the edge. The title is a real heading at level="1" by default, because a screen has one title and that title is the document's heading; heading-id names it for a dialog's aria-labelledby or a skip link. Reported from a starter kit whose thirteen screens had drifted into three different headers, with title cap heights of 12, 14 and 17 pixels on pages that sit beside each other. See page-header.
  • data-wk-sticky-column freezes the first column of a table you rendered yourself. Put it on any <table> — one your Markdown pipeline produced, one from another package — and the first column stays put while the rest scrolls sideways, capped by --size-wk-table-sticky-column-max and wrapping inside the cap. It is the same attribute <x-wirekit::table sticky-column> stamps; until now it was only a marker, and the behavior lived in classes that reach component markup alone. The table needs an ancestor with overflow-x: auto; the rest, including the width that makes the table wider than that container, comes with the attribute. Documented under attribute hooks.
  • An otp-input can break a long code into groups. group="4" on an eight-character code keeps it on one row wherever it fits and splits it 4+4 where it does not — the groups break against each other rather than inside, so the break always falls on a boundary the code has. Until now the row broke wherever the width ran out, measured in an adopting application as 6+2 at 1280px and 5+3 at 375px, which reads as a rendering fault to somebody copying the code out of an email. The gap between groups is the separator, so nothing extra is announced.
  • One element can keep motion that is essential to it. data-wk-motion="essential" releases the reduced-motion clamp for that element and everything inside it, under both halves — the one your reader's operating system asks for and the one your application forces with data-reduce-motion="reduce". It is for an animation that IS the information: a live trace, a queue that visibly moves work along, a shape whose stillness would read as a hang. That is the boundary WCAG 2.3.3 draws around motion essential to the functionality, and an entrance that merely looks better moving is on the other side of it. WireKit's own indicators are outside its reach and stay still: a spinner, a skeleton and an indeterminate progress bar stop rather than slow, and each announces its state through role="status". See reduced motion.
  • A table column can step aside below a breakpoint. Give hide-below a breakpoint, sm to 2xl, on the column's table.th and on each of its table.td, and below that width the whole column is hidden, so the columns the screen is for stay in view.
  • A table can freeze its first column only below a breakpoint. sticky-column-below="lg" turns the frozen column on by itself below lg; from that width up the table fills its box and wraps its cells instead of scrolling sideways past a column it does not need to hold.
  • A dropdown trigger can show that it opens a menu. Put <x-wirekit::dropdown.indicator /> inside the trigger's button, after its label: a chevron, hidden from assistive technology, that turns while the menu is open.
  • A heading takes break. The same three values text offers, so a long name with no break point wraps inside the word where nothing else fits.
  • --text-wk-touch-floor, a design token for the smallest text a field shows on a touch screen. Fields already lift their text to 16px there so iOS does not zoom on focus; that value is now one token, and a button beside a field takes it with touch-text-floor.
  • A control in a field.set can decline the group's error. :group-error="false" on a checkbox, radio or toggle leaves it out of the group's message: no aria-invalid from the group and no reference to its text, while its own error, its own hint and the set's hint are unaffected. For a rejection that only some controls in the group can resolve — three events under one caption where ticking the third satisfies nothing, which a reader on the keyboard was previously told to try.
  • A button group can become a column on a narrow screen. stack-below="md" — any breakpoint from sm to 2xl — stacks the group below that width and joins it on the block axis there, then puts it back in a row from that width up. A group holds one shape at every width otherwise, so three actions that fit a desktop ran off a phone, and the way out was to drop the group and lay the buttons out by hand.
  • A floating action button can clear a surface your page measures itself. Set --wk-fab-lift while a consent banner or an editing bar is up, and the button rises by exactly that much; the value adds to the corner clearance and to wk-fab-above-nav, so a page with a bottom nav and a banner gets both. The default is 0px, so nothing changes for a page that never sets it. WCAG 2.2 Focus Not Obscured names a cookie banner over a focused control as its example, which is what this makes possible to avoid.

Fixed

  • A focus ring has the shape of the thing it is around. An avatar-only profile control now rounds fully — with no name and no slot there is no row left to describe, so the circle's own shape wins over the radius you set, for the hover surface as much as for the ring. And a dropdown entry rounds inside its panel: the panel gained inline padding to match its existing vertical padding, and every entry — plain, checkbox, radio, submenu — takes the panel's radius minus that padding. Reported from a starter kit's screenshot review: a 2px square ring 35px across a 32px avatar, and a square ring on a menu entry running into the rounded inner edge of its panel.
  • An app shell drawer closes on an Escape pressed right after it opens. The drawer closes through its focus trap, and the trap is armed one transition after the panel arrives — measured by an adopting application at 375px: 156 to 162 ms in Blink, 164 to 173 ms in WebKit. Until then the drawer was already over the page with focus still on the toggle, so an Escape in that window reached nothing and the reader had to press it a second time. Escape now closes the drawer from the moment it opens, and hands focus back to the control that opened it.
  • A dialog that opens from the keyboard puts focus inside it, even on a busy page. Modal, drawer and alert-dialog armed their focus trap in the frame Alpine hands them, and other work on the page could release that frame while the panel was still hidden. The dialog opened, focus stayed on the trigger, and nothing brought it in; a reader on the keyboard could only get there with Tab. The trap now waits until the panel is on screen.
  • A field.set points at its own group message. The message rendered and only the checkboxes, radios and toggles inside referenced it, so a group made of anything else — a row of buttons choosing a template — showed the sentence on screen with nothing in the accessibility tree reaching it. The <fieldset> now carries the reference itself, to the error while the group is invalid and to the group hint while it is not, with an aria-describedby you wrote yourself kept and appended rather than replaced.
  • wirekit:doctor names a token override that reaches nothing. A --*-wk-* declaration inside @theme {}, or inside any @layer on a rule that targets the root, never wins: Tailwind drops a theme variable no utility references, and what survives is emitted layered, where it loses to the unlayered defaults the stylesheet ships. It looks right, does nothing in either mode, and raises no error — so until now the only symptom was a token that kept its default. A token scoped to a component is left alone whether or not it sits in a layer, because a custom property on a descendant shadows the inherited one either way.
  • The token-symmetry report gives the reason the cascade actually produces. It said a token you override in :root without a matching .dark declaration makes dark mode fall back to WireKit's default. It does not: every WireKit color default is declared at specificity zero so that your :root wins regardless of source order, which means your light value is what a dark page renders — a larger problem than the sentence described. And where the value goes through var(), the token does turn over on <html class="dark">, so the old reason was not merely imprecise but wrong in the other direction; the advice stands there because a .dark further down the tree inherits the value already resolved in light. The report now groups the tokens by which of the two applies.
  • wirekit:verify no longer reads a key as unused because its apostrophe is escaped. A key your templates render in the ordinary single-quoted form was invisible to the translation-collision scan, so the advice offered to delete a string your own page shows. Both scans now spell a key the way PHP writes it, in either quoting.
  • A wrap-label button wraps a typed name that has no space or hyphen in it. A template name with underscores kept the label as wide as the whole word and ran out of its row on a phone; it now breaks inside the word where nothing else fits.
  • Option names with no break point wrap inside the combobox and multi-select panels. A repository name with underscores made the list scroll sideways on a phone. The name now breaks inside the word, and group headings, descriptions and the empty state wrap the same way.
  • A refused copy shows on screen. When the browser refuses the write, the clipboard button shows a warning icon in place of the check, and a labeled button reads Copy failed, both for the same duration. Before, only a screen reader heard it.
  • Neighboring remove buttons in a file upload no longer share a touch area. A tap just below a file's own remove button could remove the file under it; every file row is now at least as tall as the button's 44px touch area.
  • The sidebar's collapse control draws a panel. Beside an expandable app rail, the two fold controls drew the same double chevron. The rail keeps it, and the sidebar's control now draws a panel whose arrow turns.
  • The scroll shadows are strong enough to see. The shadow a sideways-scrolling table casts at its edge, and every other scroll shadow, paints at 24% of the text color over 1.25rem instead of 12% over 0.875rem, which did not show on white.
  • A chart inside a .light or .dark subtree draws in that subtree's theme. It used to take the page's mode and token values, so a chart in a light panel on a dark page drew dark.
  • The inert end control of pagination stays legible. The disabled previous or next control faded twice, to 1.92:1 in the light theme; it keeps its muted color now without the extra fade.
  • In WebKit, clicking a filled one-time code cell lets you type over it. The cell selects its character when it takes focus, but WebKit finished the click afterwards and put a caret in its place, so the next keystroke was refused and a wrong code could not be corrected from the left. The selection now survives the click.
  • In WebKit, focus moves to the new step when the wizard control that advanced hides itself. On the last step the Next button hides, and WebKit left focus on it for a moment and then dropped it to the top of the page, so the next Tab started over. Focus now lands on the step that opened.
  • In WebKit, the event calendar's agenda sizes its time column to its times. Switching to the agenda measured the times before WebKit had laid them out, read nothing, and fell back to the fixed column width. The measurement now waits until the times are there.

Documentation

  • The documentation adopts the three new preview blocks. :::preview-matrix shows a pure combination of prop values as one table — the badge page's three surface previews are now one matrix of every surface in every intent. :::states labels the states a prop sets, on checkbox. :::preview-split shows both color modes side by side where the dark rendering is genuinely a different picture rather than the light one with the colors swapped, on code-block, whose syntax palette is its own set of tokens.
  • The browser example in checking a color pairing runs as written. It wrote its result into an element it never looked up; it now finds the element it writes to.

Components

Every component this release changes, each linked to its page: Alert Dialog · App Shell · Button · Calendar · Chart · Checkbox · Clipboard Button · Color Picker · Combobox · Command Palette · Data Table · Date Picker · Drawer · Dropdown · Editor · Event Calendar · FAB · Field · File Upload · Filter Builder · Heading · Inline Edit · Input · Lightbox · Modal · Multi-Select · Number Input · OTP Input · Page Header · Pagination · Password Input · Profile · Radio · Range Slider · Rating · Reaction · Scope Switcher · Segmented Control · Select · Sidebar · Slider · Sticky Panel · Table · Tags Input · Textarea · Theme Controller · Time Picker · Toggle · Toggle Button · Wizard