WireKit v2.5.0
Minor release. A new Aurora theme preset — color-confident, toned to the WireKit brand magenta, with dashboard-tuned radii, soft layered shadows (visible in both light and dark mode), and a single --theme-hue variable for one-line retinting. Its scope is bounded to the accent surface only — semantic intents stay on the WireKit default palette. Paired with a foundational cascade fix in dist/ that lets every developer-side :root {} token override win without specificity tricks (matters across every preset, not just Aurora). Two new accessibility surfaces: <x-wirekit:: ships as a drop-in WCAG 2.4.1 (Bypass Blocks) helper, and <x-wirekit:: auto-adds a fallback aria-label (the aria prop, default "Open menu") when its rendered button has no accessible name (icon-only triggers, responsive layouts that hide the visible label below sm). The wirekit:doctor:a11y linter gains an opt-in --theme-contrast stage that computes WCAG ratios for the active theme's token pairings.
Added
- New
auroratheme preset — modern, color-confident, toned to the WireKit brand magenta (hue 306°).php artisan wirekit:theme auroraapplies it to yourapp.cssend-to-end, or copy-paste the block from the theming guide — both produce identical output. Modestly rounded, soft layered shadows, cool-tinted surfaces, and a body text color softened off pure black with a whisper of the brand hue (--color-wk-text: oklch(0.24 0.02 var(--theme-hue)), WCAG AAA at 16.3:1) so it reads as part of the theme rather than stark monochrome on the tinted surfaces. The interactive--color-wk-accentreadsoklch(0.55 0.22 var(--theme-hue))— WCAG-AA-clean at 5.28:1 (light) / 6.29:1 (dark) against accent-fg — while two extra tokens,--color-wk-accent-brandand--color-wk-accent-brand-fg, preserve the brand-exact L=0.605 magenta for decorative-only surfaces (logo backplate, hero glyphs) where body text never sits on top. --theme-huesingle-source pattern on Aurora. Every hue-dependent token readsoklch(L C var(--theme-hue))and the preset declares--theme-hue: 306once at the top of the block. Developers can retint the entire Aurora palette to any hue in ONE LINE —:root { --theme-hue: 264; }produces an indigo Aurora;:root { --theme-hue: 24; }a warm-orange one — without touching the 14 hue-touching tokens individually. The L values are chosen to stay WCAG-AA-clean across the full hue rotation, so the retint is AA-safe at any hue.<x-wirekit::— drop-in WCAG 2.4.1 (Bypass Blocks) component. Renders a visually-hidden anchor that becomes a focused pill when keyboard-tabbed, jumping to a target landmark. Default target isskip-link> #main-content; override viatargetprop. Pair with<x-wirekit::(themain id="main-content"> idis now a first-class prop onmain). Auto-styled to inherit the active theme's accent color and radius.idprop on<x-wirekit::. First-class prop so skip-link targeting reads naturally asmain> <x-wirekit::. Defaults tomain id="main-content"> null(no id emitted). When set, the main element also becomes programmatically focusable (tabindex="-1") so JS-routing edge cases and direct fragment navigation move keyboard focus into the landmark.wirekit:doctor:a11y --theme-contrast— WCAG-contrast audit of the active theme. New optional stage on the existingwirekit:doctor:a11ycommand. Reads the developer'sresources/, parses everycss/ app. css --color-wk-*override under:root/.dark, computes WCAG 2.1 contrast ratios for the canonical token pairings (accentas text onbg,accent-fgonaccent,textonbg, etc.), and reports PASS / WARN / FAIL per pairing for both light and dark modes. Catches the bug class where a developer customizes--color-wk-accentwithout verifying the new value still clears 4.5:1 against--color-wk-accent-fg. Opt-in stage (the existing static blade-scan continues to run first); enable with--theme-contrastor setWIREKIT_.DOCTOR_ THEME_ CONTRAST=1 - Inline
<code>is now themeable via two new tokens —--color-wk-code(text) and--color-wk-code-bg(background). Both default to the previous values (body text on the muted surface), so every existing theme is unchanged.<x-wirekit::reads them automatically. The Aurora preset sets them to the brand magenta on a light magenta-tint highlight (WCAG AA at 5.74:1 light / 7.56:1 dark, hue-stable acrosscode> --theme-hueretints), so inline code reads as part of the theme rather than as plain body text. <x-wirekit::gainsaccordion> variantandsize.variantcontrols the container chrome:bordered(default — the existing self-contained card),flush(no outline, just row dividers — for an FAQ that sits inline in page content), andseparated(each item becomes its own standalone card with a gap between them).size="lg"gives the trigger roomier padding and a larger title for marketing and touch-first layouts. Both default to the previous look, so existing accordions are unchanged.<x-wirekit::gains adate-picker> rangeflag. Renders a linked start + end pair of native date inputs — the end can't be set before the start and vice versa (a small reactive link, no calendar dependency). They submit asname[start]/name[end]; pass an initial range as an array['start' => .., 'end' => ..]or aYYYY-MM-DD/string. The single-date default is unchanged.YYYY-MM-DD - New
<x-wirekit::— joined / segmented button bar. Wrap buttons to join them into a single control: inner corners are squared, the shared border seam collapses to one line, and the active button rises above the seam. Supportsbutton. group> orientation="vertical"and alabelfor the group's accessible name. The same wrapper also joins an input with a trailing button (attached search field, newsletter signup). RTL-safe (logical properties). <x-wirekit::items accept anbreadcrumb> iconkey. Add'icon' => 'home'to any breadcrumb item to render a decorative glyph before its label (a leading home icon, per-category icons, etc.). The label stays the accessible link text — the icon isaria-hiddenand is never written into the JSON-LD structured data. Items without aniconare unchanged.- New
<x-wirekit::— stacked / overlapping avatars. Wrap a set of avatars to render them as an overlapping stack, each ringed in the surface color so the discs stay distinct. Setavatar. group> :remainingfor a trailing "+N" overflow chip,labelfor the group's accessible name, andsizeto match the avatars inside. Plus a documented "avatar with name & detail" composition pattern on the avatar page (Row + Stack + Text — no new component needed). - New
<x-wirekit::— accessible loading indicator. A lightweight CSS spinner for in-place activity that has no measurable progress. Four sizes (spinner> sm/md/lg/xl), an optional semanticintentcolor (defaults tocurrentColorso it inherits its context — drop it inside a button or badge and it matches), and a screen-readerlabel(default"Loading") announced viarole="status". Use it when you can't show a layout preview (Skeleton) or a percentage (Progress). <x-wirekit::gainsbadge> surface,dismissible, andtrailingIcon.surfacechooses how the intent color is applied —soft(default, the tinted chip),solid(filled intent background with on-color text), oroutline(transparent with an intent-colored ring).dismissibleadds a keyboard-operable close button that hides the badge and dispatches awirekit:badge-dismissedevent (great for removable filter chips), with the button's accessible name set viadismissLabel(default"Remove").trailingIconmirrorsleadingIconafter the label. All three default off / to the existing look, so existing badges are unchanged.
Changed
dist/wirekit. css :root/.darkblocks now wrap in:where(...)for specificity 0. Foundational cascade fix: any developer-side:root { --color-wk-accent: ... }override (specificity 0,1,0) now wins over WireKit's defaults regardless of source order — including the very common@wirekitStylessetup where this stylesheet is injected via a separate<link>tag AFTER the developer's compiledapp.css. Pre-fix, developers had to either@importwirekit.cssfrom insideapp.cssOR use a higher-specificity selector likehtml:rootto win the cascade. Both workarounds are now unnecessary. Backward compatible — developers already usinghtml:rootcontinue to work (their higher specificity still wins).<x-wirekit::auto-injects a fallbackdropdown. trigger> aria-labelwhen the inner button has no accessible name. The component inspects its interactive child (button / link /role="button"); when that element has noaria-label, noaria-labeledby, and no non-empty text content (visible OR sr-only) — the icon-only-trigger and mobile-collapsed-label cases — it sets anaria-labelfrom the newariaprop (defaultLabel Fallback "Open menu"). Any explicitaria-label/aria-labeledby/ text on the trigger still wins. Closes the bug class where a responsive layout hid the label below thesmbreakpoint and the icon-only trigger read as just "button" to screen readers.dist/now registers every component plugin, matching the IIFE bundle. The ES-module build previously omittedwirekit. esm. js stat-animate,animate, and thereading-spine/reading-minimap/reading-tocplugins, so developers registering WireKit from the ESM entry point saw<x-wirekit::andstat animate> <x-wirekit::(plus the reading primitives) render their static value with no animation behavior. All 29 plugins are now registered (bundle: ~31 KB gzip / 109 KB raw, up from ~25 KB / 82 KB).reveal> - Badge border + depth are now theme-tokens; the Aurora preset renders softer, flat tinted badges. Two new tokens control the badge outline:
--border-wk-badge-width(default: the global--border-wk-width) and--shadow-wk-badge(default: the inset-ring + faint drop that lifts the chip). The Aurora preset sets both to a flatter look — no outline, no inset ring — so badges read as soft tinted chips rather than bordered pills. Every other theme is unchanged, and you can restore the bordered look under Aurora (or harden it anywhere) by setting the two tokens back to their defaults in your:root. - Dismissible
<x-wirekit::andbadge> <x-wirekit::now emit thealert> data-replayablecontract. A badge or alert withdismissibleset rendersdata-replayable="true", so a<x-wirekit::placed in the same DOM tree can restore the element after it has been dismissed (the button re-mounts the closestreplay-button> [data-replay-target]from a saved snapshot). The attribute is inert on its own — it does nothing unless a replay button is wired up — and the dismissible behavior itself is unchanged.
Fixed
- Theme overrides loaded via the
@wirekitStylessetup no longer get silently overwritten by WireKit's defaults. Symptom: a developer adds:root { --color-wk-accent: ... }to theirapp.css, runsnpm run build, and finds the value is ignored in the browser becausewirekit.cssloads AFTERapp.cssand (until this fix) its:rootblock had equal specificity (0,1,0) plus later source order. The:where()wrap on every top-level:root/.darkblock indist/brings WireKit's defaults to specificity 0, so any developer override wins. Affects every theme preset, not just Aurora.wirekit. css - Built-in theme presets that recolor the accent now pin a co-tuned
--color-wk-accent-fg, so primary-button labels clear WCAG AA.php artisan wirekit:theme retro-terminalpreviously left the accent foreground at the default near-white, which paired with the light-green accent at only 2.13:1 — well below the 4.5:1 AA threshold for the button label.retro-terminalnow pins a near-black label (8.06:1);soft,material, andcupertinopin a near-white label (5.03–7.73:1). The accent / accent-foreground pair is written together so it stays aligned in both light and dark mode. <x-wirekit::no longer collapses to an undraggable sliver in shrink-to-fit containers. The track is a full-width block with no intrinsic width of its own, so inside arange-slider> width: fit-contentwrapper, a flex/grid auto item, or a table cell it shrank to the width of its two bound labels (~60px). The component now pins a16remmin-width floor while still expanding to 100% in normal block flow, so it stays operable everywhere.<x-wirekit::andhero variant="dark"> <x-wirekit::cta variant="dark">now establish a dark token context for their content — no more white-on-white nested surfaces. Previously the dark variant only painted a dark band with light text; a token-surfaced child (e.g.<x-wirekit::,code-block> card,input,table) still read light-mode--color-wk-*values, rendering a near-white panel on the dark band — invisible. The dark variant now carries a dark token context so every nested token-driven surface renders dark. As a consequence avariant="dark"band now also stays genuinely dark in dark mode (it previously flipped to near-white). Light-mode appearance is unchanged;variant="accent"is unaffected (it is a colored surface, not a dark one).<x-wirekit::count-up no longer risks sticking at "0". Both start-triggers — thestat animate> animateInentrance keyframe and the standalone scroll-into-view observer — can occasionally miss on real browsers (a coalesced keyframe; anIntersectionedge case on some touch browsers), which would leave the live counter frozen at "0" over the correct server-rendered value. A safety-net timer now starts the count-up if neither trigger fires, soObserver animatealways resolves to its target.- Muted text on
<x-wirekit::/hero variant="accent"> <x-wirekit::cta variant="accent">now clears WCAG AA on every preset. The previous muted-text formula onvariant="accent"surfaces blended--color-wk-accent-fgat 72% opacity over the accent, which clears AA on dark-accent themes but dropped to ~3.4:1 on medium-lightness accents (Aurora, Soft, Material, and Cupertino — their regular accent-fg-on-accent contrast is only ~4.8–5.3:1, leaving no AA-safe muting headroom). Muted text on a colored accent surface now renders at the full--color-wk-accent-fg, so it inherits the surface's regular text contrast (≥ 4.5:1 on every preset, both light and dark). Real muting is unchanged onvariant="dark"and every non-colored surface.