Skip to main content

WireKit v2.5.0

Minor release

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/wirekit.css 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::skip-link> ships as a drop-in WCAG 2.4.1 (Bypass Blocks) helper, and <x-wirekit::dropdown.trigger> auto-adds a fallback aria-label (the ariaLabelFallback 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 aurora theme preset — modern, color-confident, toned to the WireKit brand magenta (hue 306°). php artisan wirekit:theme aurora applies it to your app.css end-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-accent reads oklch(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-brand and --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-hue single-source pattern on Aurora. Every hue-dependent token reads oklch(L C var(--theme-hue)) and the preset declares --theme-hue: 306 once 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::skip-link> — 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 is #main-content; override via target prop. Pair with <x-wirekit::main id="main-content"> (the id is now a first-class prop on main). Auto-styled to inherit the active theme's accent color and radius.
  • id prop on <x-wirekit::main>. First-class prop so skip-link targeting reads naturally as <x-wirekit::main id="main-content">. Defaults to 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 existing wirekit:doctor:a11y command. Reads the developer's resources/css/app.css, parses every --color-wk-* override under :root / .dark, computes WCAG 2.1 contrast ratios for the canonical token pairings (accent as text on bg, accent-fg on accent, text on bg, etc.), and reports PASS / WARN / FAIL per pairing for both light and dark modes. Catches the bug class where a developer customizes --color-wk-accent without 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-contrast or set WIREKIT_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::code> 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 across --theme-hue retints), so inline code reads as part of the theme rather than as plain body text.
  • <x-wirekit::accordion> gains variant and size. variant controls 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), and separated (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::date-picker> gains a range flag. 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 as name[start] / name[end]; pass an initial range as an array ['start' => .., 'end' => ..] or a YYYY-MM-DD/YYYY-MM-DD string. The single-date default is unchanged.
  • New <x-wirekit::button.group> — 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. Supports orientation="vertical" and a label for 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::breadcrumb> items accept an icon key. 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 is aria-hidden and is never written into the JSON-LD structured data. Items without an icon are unchanged.
  • New <x-wirekit::avatar.group> — 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. Set :remaining for a trailing "+N" overflow chip, label for the group's accessible name, and size to 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::spinner> — accessible loading indicator. A lightweight CSS spinner for in-place activity that has no measurable progress. Four sizes (sm/md/lg/xl), an optional semantic intent color (defaults to currentColor so it inherits its context — drop it inside a button or badge and it matches), and a screen-reader label (default "Loading") announced via role="status". Use it when you can't show a layout preview (Skeleton) or a percentage (Progress).
  • <x-wirekit::badge> gains surface, dismissible, and trailingIcon. surface chooses how the intent color is applied — soft (default, the tinted chip), solid (filled intent background with on-color text), or outline (transparent with an intent-colored ring). dismissible adds a keyboard-operable close button that hides the badge and dispatches a wirekit:badge-dismissed event (great for removable filter chips), with the button's accessible name set via dismissLabel (default "Remove"). trailingIcon mirrors leadingIcon after the label. All three default off / to the existing look, so existing badges are unchanged.

Changed

  • dist/wirekit.css :root / .dark blocks 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 @wirekitStyles setup where this stylesheet is injected via a separate <link> tag AFTER the developer's compiled app.css. Pre-fix, developers had to either @import wirekit.css from inside app.css OR use a higher-specificity selector like html:root to win the cascade. Both workarounds are now unnecessary. Backward compatible — developers already using html:root continue to work (their higher specificity still wins).
  • <x-wirekit::dropdown.trigger> auto-injects a fallback aria-label when the inner button has no accessible name. The component inspects its interactive child (button / link / role="button"); when that element has no aria-label, no aria-labeledby, and no non-empty text content (visible OR sr-only) — the icon-only-trigger and mobile-collapsed-label cases — it sets an aria-label from the new ariaLabelFallback prop (default "Open menu"). Any explicit aria-label / aria-labeledby / text on the trigger still wins. Closes the bug class where a responsive layout hid the label below the sm breakpoint and the icon-only trigger read as just "button" to screen readers.
  • dist/wirekit.esm.js now registers every component plugin, matching the IIFE bundle. The ES-module build previously omitted stat-animate, animate, and the reading-spine / reading-minimap / reading-toc plugins, so developers registering WireKit from the ESM entry point saw <x-wirekit::stat animate> and <x-wirekit::reveal> (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).
  • 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::badge> and <x-wirekit::alert> now emit the data-replayable contract. A badge or alert with dismissible set renders data-replayable="true", so a <x-wirekit::replay-button> placed in the same DOM tree can restore the element after it has been dismissed (the button re-mounts the closest [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 @wirekitStyles setup no longer get silently overwritten by WireKit's defaults. Symptom: a developer adds :root { --color-wk-accent: ... } to their app.css, runs npm run build, and finds the value is ignored in the browser because wirekit.css loads AFTER app.css and (until this fix) its :root block had equal specificity (0,1,0) plus later source order. The :where() wrap on every top-level :root / .dark block in dist/wirekit.css brings WireKit's defaults to specificity 0, so any developer override wins. Affects every theme preset, not just Aurora.
  • 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-terminal previously 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-terminal now pins a near-black label (8.06:1); soft, material, and cupertino pin 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::range-slider> 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 a width: fit-content wrapper, a flex/grid auto item, or a table cell it shrank to the width of its two bound labels (~60px). The component now pins a 16rem min-width floor while still expanding to 100% in normal block flow, so it stays operable everywhere.
  • <x-wirekit::hero variant="dark"> and <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 a variant="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::stat animate> count-up no longer risks sticking at "0". Both start-triggers — the animateIn entrance keyframe and the standalone scroll-into-view observer — can occasionally miss on real browsers (a coalesced keyframe; an IntersectionObserver edge 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, so animate always 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 on variant="accent" surfaces blended --color-wk-accent-fg at 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 on variant="dark" and every non-colored surface.