**Minor.** MapLibre GL 6 is published as an ES module only, and 6.4.1 is the first release with a fix for a critical advisory that no 5.x release will get — so the map takes its engine as a module now, and its guide teaches a setup that actually draws. A modal whose content outgrew the window lost its top: the header and the first lines sat above the start of the scroll area, where no scroll could reach them. And a data table inside a Livewire component forgot its search text, density and selection every time the server answered, while a server-mode table had no honest way to say it was waiting.

### Added

- **[`<x-wirekit::quick-replies>`](https://docs.wirekit.app/components/quick-replies) puts a row of suggested answers under a message.** Each chip dispatches what the reader picked — `value`, `label` and the chip's `index` — and your application decides whether that sends the reply, fills the composer or calls a tool. A suggestion is a string, or an array with a `value` that differs from the label and a `disabled` for one that cannot be taken right now. The row is a named group of buttons rather than a toolbar, since that role promises an arrow-key model this has none of; every chip keeps the target floor the rest of the library uses, and the row wraps on a phone instead of running off the side.
- **The [event calendar](https://docs.wirekit.app/components/event-calendar) can tell you which day was pressed, and show that day's events.** With `selectable-days`, each day number in the month view becomes a button that dispatches `wirekit:event-calendar-day-select` with the date and the ids of its events. `day-detail` also lists the pressed day's events under the grid, and `+N more` opens that list instead of switching to the week view. Both are off by default. A theme restyles each state of the day number through the `day-number-today`, `day-number-selected`, `day-number` and `day-number-outside` keys, whether or not the days are selectable.
- **The [event calendar](https://docs.wirekit.app/components/event-calendar) can filter by category.** Give events a `category` and set `filterable`, and one toggle per category appears above the views. A hidden category leaves every view, including the month grid's `+N more` and the week grid's columns, and a polite status announces how many events remain. Events without a category are never filtered. The toggles take their look from the `category-shown` and `category-hidden` keys.
- **The [event calendar](https://docs.wirekit.app/components/event-calendar) shows who attends an event.** Give an event `attendees`, as names or with an `avatar`, and the agenda and the day list show up to three faces beside its title, then a count. Every name joins the row's accessible name, joined the way the calendar's language joins a list.
- **[`wirekit:doctor`](https://docs.wirekit.app/cli-reference/wirekit-doctor) checks that every page shell your layouts render loads WireKit.** Finding the directives somewhere in your views said nothing about whether the file your login pages render carries them. The check follows the app and auth layouts to the files that close their `<head>` and `<body>`, and warns about any that doesn't load WireKit.
- **`registerMapEngine()` hands the [map](https://docs.wirekit.app/components/map/maplibre) its engine without a global.** MapLibre GL 6 sets no `window.maplibregl`, which was the only place the map looked. A bundled app passes the module namespace instead — `registerMapEngine('maplibre', maplibregl)`, exported from `wirekit.esm.js` — and a map that mounted before its engine arrived boots the moment it is registered. A page that assigns `window.maplibregl` itself, or loads Leaflet's classic build, works as before.
- **[`<x-wirekit::activity-row>`](https://docs.wirekit.app/components/activity-row) takes a `detail` slot and a `kind-label` prop.** The `detail` slot holds block content under the line, such as the fields a change touched or a quoted comment, and starts flush with the text above it; the default slot sits inside the line and cannot hold a block. `kind-label` names the kind for a screen reader, which is how a custom kind gets a spoken name.
- **[`<x-wirekit::image-gallery>`](https://docs.wirekit.app/components/image-gallery) takes a `zoomOverlay`, and [`<x-wirekit::lightbox>`](https://docs.wirekit.app/components/lightbox) a `slideOverlay`.** `itemOverlay` covers the thumbnails only, so a label that belongs on the picture, such as an "AI-generated" marking, disappeared the moment the reader opened it. `zoomOverlay` takes the same `($item, $i)` closure and lays its markup exactly over the enlarged image, shown only while that image is; pass the same closure to both when a label has to travel with the image. The lightbox's `slideOverlay` is the same mechanism for a lightbox of its own.
- **[`<x-wirekit::message-group>`](https://docs.wirekit.app/components/message-group) prints a run of messages from one sender under one avatar and one name.** Three messages in a row used to mean three identical headers, which turns a conversation into a table. The group carries the sender; the messages inside keep their own timestamps, their own `role="article"` and their own accessible names, and the repeated name stays in the accessibility tree — hidden with the visually-hidden pattern rather than removed, because a reader who lands on the third bubble still has to be able to say who wrote it. The stack's inner corners round tighter, written in logical properties, so a right-to-left thread mirrors without a second rule.
- **The MCP server exposes resources as well as tools.** [`wirekit:mcp-serve`](https://docs.wirekit.app/cli-reference#wirekitmcp-serve) answers `resources/list` and `resources/read` for `wirekit://catalog`, `wirekit://themes`, `wirekit://recipes` and `wirekit://changelog`, so an editor can attach the catalog to a conversation once instead of calling a tool on every turn, and a client's resource picker shows what the server knows. The changelog resource serves the newest version's section rather than the whole file, which is several hundred kilobytes.
- **[`<x-wirekit::feature>`](https://docs.wirekit.app/components/feature) takes `intent`, and [`<x-wirekit::card>`](https://docs.wirekit.app/components/card) takes `surface`.** Those are the names the rest of the kit uses for these two axes — `intent` for the color role, `surface` for how heavily it is applied — and these were the last two components asking for a different word. `tone` and `variant` keep working for the whole of v2 and mean exactly what they meant; the canonical name wins when a call site sets both. `intent="primary"` is `tone="accent"` and `surface="outline"` is `variant="outlined"`, so moving a call site is a rename rather than a re-render. See [Prop naming conventions](https://docs.wirekit.app/extending/prop-naming-conventions).
- **Cards can move between the columns of a [kanban](https://docs.wirekit.app/components/kanban) board.** Add `cross-column` to a sortable board and a `column-id` to each column: a card can then be dragged onto another column, or carried there with `←` and `→` while it is lifted with the keyboard, and `Escape` puts it back where it started. The move is reported once, from the column the card lands in, as a bubbling `wirekit:sortable:moved` event carrying both columns' new order. Without `cross-column` a board behaves exactly as before, arrow keys included.
- **The [data table](https://docs.wirekit.app/components/data-table) can start sorted.** `sort-key` and `sort-dir` set the column and the direction it opens with. In server mode they mirror the order the server already applied, so `aria-sort` and the arrow describe the rows on screen; until now every column read "none" over a sorted table, and a screen reader said so. A column can also set `sortStart: 'desc'` to sort descending on its first click, which is what a column of counts is clicked for.
- **A [data table](https://docs.wirekit.app/components/data-table) cell can link to the row's own page.** Give a column `cellType: 'link'` and name the row field that holds the URL in `hrefKey`: each cell becomes a real link in the kit's link style, so middle-click, the context menu and "copy link" all work. A row without a URL shows its value as text, and a URL that would run script, such as `javascript:`, is refused the same way.
- **[`<x-wirekit::grid>`](https://docs.wirekit.app/components/grid), [`bento-grid`](https://docs.wirekit.app/components/bento-grid) and [`feature-grid`](https://docs.wirekit.app/components/feature-grid) take `scale`, as [`row`](https://docs.wirekit.app/components/row) and [`stack`](https://docs.wirekit.app/components/stack) do.** WireKit has two spacing ladders that share the rung names `xs` to `2xl` and differ from `md` up, and a grid's `gap` reached only the roomier one. `scale="gap"` points `gap` at the tighter `--gap-wk-*` ladder the components use inside themselves, so a card grid written as `gap-[var(--gap-wk-md)]` can become a `grid` without every gap growing by a third. The default is `space`, which is what `gap` has always meant.
- **[`<x-wirekit::text>`](https://docs.wirekit.app/components/text) takes `break` for an unbroken token.** A checksum, a key or a long URL has no space to wrap at, so it widened its container, and on a phone the whole page slid sideways. `break="anywhere"` breaks it only where the line would otherwise overflow, `all` breaks between any two characters, and `normal` restores the default rules.
- **A server-mode [data table](https://docs.wirekit.app/components/data-table) takes `search`, the filter the server applied.** The search field shows it on every render, so a page opened with a query in its address shows that query above the rows it filtered.

### Changed

- **`wirekit:csp-audit` no longer says a factory is unregistered when it simply did not read the file that registers it.** Both cases used one wording. *Not registered* means the page has no scope for the factory, so `x-show` never hides anything and the panel renders visible and dead; *not found in the files this run read* is a limit of the run, as on a package's views, whose registrations sit beside them. The report now counts the two separately in its heading and says on each line which one applies.
- **[`wirekit:doctor:props`](https://docs.wirekit.app/cli-reference#wirekitdoctorprops) also finds a slot closing tag that Blade does not compile as one.** A closing tag with a word directly behind it stops being a closing tag: Blade rewrites it to `@endslot` with nothing after it, the word joins the directive's name, and the slot never closes — its content lands in the default slot, the component is handed an empty string where it expected the slot, and that directive name is printed on the page as text. Nothing throws and nothing is logged, so a linter is where it can be found. The exit contract is unchanged, `--fail-on=none` included.
- **The map's missing-engine hint waits until the page has loaded.** It used to fire the moment a map mounted without an engine, so an engine loaded on purpose after the first render printed a console error on a page that was about to work. The hint now waits for the `load` event and a three-second grace period, and stays silent if an engine arrives first.
- **[`wirekit:doctor:props`](https://docs.wirekit.app/cli-reference#wirekitdoctorprops) tells you which of two prop spellings the kit calls canonical.** Several components accept `variant` or `tone` where the shared vocabulary says `intent`, and `card` accepts `variant` where it says `surface`. Both spellings resolve to the same value and both keep working, so the linter names the canonical one at the moment a template writes the other and **leaves the exit code alone** unless you pass `--fail-on-legacy-axis`, once your own tree has converted; `--fail-on=none` still overrides both. The pairs are read out of the component templates, so a component that gains its canonical spelling in a later release is covered by the same command on the day it ships.
- **`wirekit:make` and the long-form-article recipe scaffold the canonical prop name.** The generated dashboard, settings and article pages wrote `<x-wirekit::text variant="muted">`; they write `intent` now.
- **Three controls in the documentation's live prop editor take the canonical name.** [`alert`](https://docs.wirekit.app/components/alert) and [`callout`](https://docs.wirekit.app/components/callout) offer `intent`, [`card`](https://docs.wirekit.app/components/card) offers `surface` with `outline` spelled the way [`button`](https://docs.wirekit.app/components/button) spells it. One control per axis: offering both names would let you set both and then wonder which won. Every component still accepts the older name.

### Fixed

- **A form control keeps the `aria-describedby` you give it, next to its own hint or error.** [color-picker](https://docs.wirekit.app/components/color-picker), [date-picker](https://docs.wirekit.app/components/date-picker), [input](https://docs.wirekit.app/components/input), [number-input](https://docs.wirekit.app/components/number-input), [password-input](https://docs.wirekit.app/components/password-input), [radio](https://docs.wirekit.app/components/radio), [segmented-control](https://docs.wirekit.app/components/segmented-control), [select](https://docs.wirekit.app/components/select), [slider](https://docs.wirekit.app/components/slider), [textarea](https://docs.wirekit.app/components/textarea), [time-picker](https://docs.wirekit.app/components/time-picker) and [toggle](https://docs.wirekit.app/components/toggle) wrote their own `aria-describedby` beside yours when they showed a hint or an error, and a browser keeps only the first copy of an attribute; on `segmented-control` yours came first and took the control's own error out of its description. [editor](https://docs.wirekit.app/components/editor), [file-upload](https://docs.wirekit.app/components/file-upload), [inline-edit](https://docs.wirekit.app/components/inline-edit), [multi-select](https://docs.wirekit.app/components/multi-select), [rating](https://docs.wirekit.app/components/rating) and [tags-input](https://docs.wirekit.app/components/tags-input) put yours on the wrapper around the control, where it describes nothing, and [otp-input](https://docs.wirekit.app/components/otp-input) dropped it. Your ids now join the control's list after the component's own, as [checkbox](https://docs.wirekit.app/components/checkbox) and [range-slider](https://docs.wirekit.app/components/range-slider) already did, so a notice elsewhere on the page can describe the field.
- **An [avatar group](https://docs.wirekit.app/components/avatar) rendered by Alpine's `x-for` no longer pulls its first avatar over what sits before it.** The overlap applied to every child after the first, and in an `x-for` stack the first child is the `<template>` element, so the first visible avatar was treated as a second one. The rule now skips template elements.
- **The [breadcrumb](https://docs.wirekit.app/components/breadcrumb)'s structured data leaves out a step that has no page.** A trail with a navigation group in the middle emitted that step without a URL, and Google discards a whole BreadcrumbList over one such entry, so the search result showed a bare URL instead of the trail. The group stays in the visible trail; the JSON-LD skips it and counts its positions over what remains, and consecutive steps with the same URL become one entry. `Schema::breadcrumbList()` follows the same rule and now takes `null` for a step without a page.
- **An unlinked step in the middle of a [breadcrumb](https://docs.wirekit.app/components/breadcrumb) no longer looks like the current page.** It borrowed the current page's classes, so a trail with a navigation group in it showed two crumbs in the emphasis color, and personalizing `current` restyled both. It renders muted now, in an `ancestor` block of its own that `WireKit::personalize()` reaches separately.
- **[`wirekit:install`](https://docs.wirekit.app/cli-reference#wirekitinstall) wires WireKit into the Livewire Starter Kit.** The kit's `layouts/app.blade.php` hands the page to a shell such as `layouts/app/sidebar.blade.php`, and only the shell holds the `<head>` and `<body>`. The installer looked at the wrapper alone, found nowhere to insert, and reported the directives as already present, so WireKit's stylesheet and scripts reached no page. It now follows each layout to the files that close `<head>` and `<body>`, wires the auth layout's shells as well, including the alternative shells the kit keeps beside the ones in use, and names every file it changed. [Adding WireKit to the Livewire Starter Kit](https://docs.wirekit.app/getting-started/livewire-starter-kit) walks through the result.
- **[`wirekit:doctor`](https://docs.wirekit.app/cli-reference/wirekit-doctor) names the file the install actually creates.** With no layout yet, its hint said the install would create `resources/views/components/layouts/app.blade.php`. The install runs Livewire's `livewire:layout`, which writes `resources/views/layouts/app.blade.php`.
- **`wirekit:publish-fonts --prune` deletes only what it published.** A family directory that was a link was walked like any other, so the prune deleted the files at the link's target, outside `public/vendor/wirekit/fonts`, and only then stopped on the link itself; a linked category directory did the same for every family inside it, without stopping at all. Such a directory now stays where it is, the output names it, and the command exits 1. See [`wirekit:publish-fonts`](https://docs.wirekit.app/cli-reference#wirekitpublish-fonts).
- **Every [segmented control](https://docs.wirekit.app/components/segmented-control) segment keeps a 24px target, whatever size the text is themed to.** A segment's height came from its padding and the line height of its text alone, so a theme with a smaller type scale pushed the small segments below the WCAG 2.5.8 minimum without a sound. The segments now take `--size-wk-target-min` as their floor; at the default tokens nothing visible changes.
- **A [context menu](https://docs.wirekit.app/components/context-menu) opened by a right-click that had to scroll the page stays open.** Browsers announce a scroll a frame after it happens, so when the right-click itself brought the trigger into view, that scroll's own event arrived after the menu had opened, and the menu closed as if the page had moved under it. It now closes only on a scroll that actually moved its trigger. The [notification center](https://docs.wirekit.app/components/notification-center), the [filter builder](https://docs.wirekit.app/components/filter-builder) and the [navigation menu](https://docs.wirekit.app/components/navigation-menu) closed their panels on scroll the same way and follow the same rule.
- **A [chart](https://docs.wirekit.app/components/chart) whose library loads after the chart appeared draws itself.** Both the Chart.js and the ApexCharts adapter looked for their library once, when the chart mounted, and showed the missing-library panel on the spot — so a page that loaded the library on demand kept the panel even after the library had arrived. Both adapters now keep looking: a library that arrives within a few seconds of the page loading draws the chart without the panel ever appearing, and one that arrives even later replaces the panel with the chart. The panel and its console hint appear only when the library is still missing a few seconds after the page has loaded.
- **A [drawer](https://docs.wirekit.app/components/drawer) holding only text can be scrolled from the keyboard.** Its body scrolled but had no stop in the tab order, so a drawer of terms, a log or a long description gave a keyboard nothing to land on. The body is a tab stop with a visible focus ring now, and a drawer that holds a control still opens on its first one, not on the body.
- **A [modal](https://docs.wirekit.app/components/modal) taller than the viewport scrolls its body instead of losing its top.** The panel is capped to the viewport, `modal.body` scrolls between the header and the footer, and a dialog that fits is centered exactly as before. The panel used to grow with its content while its container centered it, which pushes anything taller than the window past the start of the scrollable area: on a consent screen, the notice began mid-sentence with its header out of reach. The body is keyboard-reachable now, with `tabindex="0"` and a focus ring, so a long document can be scrolled from the keyboard; initial focus still goes to the first control when the dialog has one.
- **The modal header keeps its close button in the top corner and has equal padding on every side.** The close button was vertically centered, so a header carrying an avatar and a wrapping subtitle left it at half height, and the top padding was half the side padding.
- **A [multi-select](https://docs.wirekit.app/components/multi-select) starts its text as far from its border as the fields beside it.** Its frame padded all four sides with the small vertical spacing token, so in a filter row next to a select or a combobox its placeholder and its selected values began half as far from the left edge as their text did. The sides take the same horizontal token as select and combobox now.
- **An [activity row](https://docs.wirekit.app/components/activity-row) announces its kind in the language of the page.** The label that stands in for the colored dot read the kind's key, so a screen reader on a German page heard "comment" in the middle of an otherwise German row. The built-in kinds are announced from the translation catalog now, in every language WireKit ships.
- **A radar [chart](https://docs.wirekit.app/components/chart)'s tooltip stays on the point you hover when two of its axes share a label.** The ApexCharts adapter pins a radar tooltip to the hovered point, but it found that point through the tooltip's title, so on two axes named alike it pinned the tooltip to the second one whichever you pointed at. It follows the marker under the pointer now, and falls back to the title only for a tooltip no pointer opened.
- **A [lightbox](https://docs.wirekit.app/components/lightbox) and an [image gallery](https://docs.wirekit.app/components/image-gallery) return focus to the control that opened them in Safari as well.** Closing the viewer handed focus back to whatever was focused when it opened, and in Safari a mouse click does not focus a button, so focus fell to the top of the page instead of the thumbnail or trigger the reader had used. The viewer now remembers the control that opened it, whether by a click inside the component or by an open event dispatched from a control, and returns focus there.
- **A [breadcrumb](https://docs.wirekit.app/components/breadcrumb) with a long trail wraps onto another line on a phone.** The trail never wrapped, so each item shrank to its longest word and wrapped its own label, and a deep trail still ran past the edge of the screen. It now breaks between items, so every label stays whole.
- **A [combobox](https://docs.wirekit.app/components/combobox), [multi-select](https://docs.wirekit.app/components/multi-select) and [command palette](https://docs.wirekit.app/components/command-palette) name their popup list.** A `role="listbox"` with no accessible name is announced as "listbox" and nothing else once a reader moves into it. Each popup now carries the name the component already resolved for itself, which is what [scope switcher](https://docs.wirekit.app/components/scope-switcher) has always done — four components on one ARIA pattern now give one answer instead of two.
- **A [dropdown](https://docs.wirekit.app/components/dropdown) in its quick form can name its menu.** The quick form composes the menu panel itself, and nothing written on the call site reached it, so a menu built that way was announced as "menu" and nothing else. `label` on `<x-wirekit::dropdown>` now names it. The explicit form keeps taking `label` on `<x-wirekit::dropdown.panel>`, and a `label` given to the dropdown there is reported in the development log instead of being dropped in silence. The [theme controller](https://docs.wirekit.app/components/theme-controller)'s menu takes the control's name.
- **The [reading TOC](https://docs.wirekit.app/components/reading) keeps the section you jumped to marked.** It stood the heading you picked just below its strip, then decided the active section at the top of the window, so the section before your pick was marked the moment anything recomputed. On a page that scrolls inside its own region under a header, that line lay above the region altogether. The active section is now decided at the line a jump stands a heading on and follows whichever region scrolls, a jump keeps its section until you scroll yourself, and at the end of the page the last heading in view is marked. A strip pinned to the bottom no longer leaves a gap the height of the strip above the heading it jumps to.
- **The [reading spine](https://docs.wirekit.app/components/reading) works in a page that scrolls inside its own region.** Every part of it spoke to the window. A click scrolled the window, which had nowhere to go, so nothing moved; its end-of-page rule found a document that does not scroll at its end, so the last section was marked from the first paint on; the per-section fill never moved; and back-to-top did nothing. It now finds the region the headings scroll in, as the reading TOC does, and measures, jumps and fills there. A jump keeps its section until you scroll yourself, and a page that cannot scroll at all marks its first section rather than its last.
- **A [data table](https://docs.wirekit.app/components/data-table) column with a `cellType` the table does not know shows its values as text.** It matched none of the table's cell shapes and rendered every cell empty, so a typo in a column definition emptied the whole column without a word.
- **A hovered [button](https://docs.wirekit.app/components/button) keeps one color from edge to edge.** The filled surface drew its border in the fill's own color and changed it by hand beside the fill, and the neutral, success and warning intents changed only the fill, so hovering one of them left a ring of its resting color around it. The border of every filled button is transparent now: the fill shows through it, and the button keeps its size. The border is no longer painted as a second shape over the fill either, which is where a light seam could show along the rounded corners.
- **An indeterminate [checkbox](https://docs.wirekit.app/components/checkbox) shows its third state on a page with no Alpine component around it.** The state is a DOM property the server cannot render, so the checkbox carries it in an attribute and an Alpine directive applies it, and a directive only runs where Alpine initializes the page: inside an `x-data` or a Livewire component. In a plain form the box stayed empty. The directive now starts on its own, as `x-init` does.
- **A checked [checkbox](https://docs.wirekit.app/components/checkbox) and a selected [radio](https://docs.wirekit.app/components/radio) keep their accent border under the pointer.** Both draw a hover border and a checked border on the same box, and the hover one came later in the stylesheet at the same weight, so it won: a hovered checked checkbox wore a gray ring around its accent fill, and a hovered selected radio lost the ring that marks it at all. An unchecked control answers the pointer exactly as before.
- **A [data table](https://docs.wirekit.app/components/data-table) inside a Livewire component keeps what only the browser holds across renders.** The search text, the density, the hidden columns and the selection went back to their start every time the server answered, so the search field emptied the moment its results arrived.
- **Selecting rows in a [data table](https://docs.wirekit.app/components/data-table) bound with `wire:model` no longer breaks it.** The selection reached the bound property as a JSON string rather than a list, and every request after the first selection answered 419, so the table could no longer sort, search or page.
- **A server-mode [data table](https://docs.wirekit.app/components/data-table) is busy exactly while a round trip it started is out.** From the moment the request carrying a sort or a search leaves until it comes back, with a response, an error or a cancellation, the table carries `aria-busy="true"` and its status region says "Loading results" once. The `loading` prop could not do that, because it is read when the server renders and a response is rendered after the wait is over; the documented recipe passed `:loading="true"` and marked the table busy for good. See [the loading state](https://docs.wirekit.app/components/data-table#the-loading-state). `loading` remains for a wait the table does not start, such as a queued import.
- **A [data table](https://docs.wirekit.app/components/data-table) wider than its space shows the edge it continues toward.** On a phone there is no scrollbar until a drag is underway, so a table cut off between two columns looked complete. A soft shadow now marks the edge with more table behind it, as it does on `table`.
- **An [inline edit](https://docs.wirekit.app/components/inline-edit) focuses its control when it opens in Safari.** The focus call ran before the control was revealed, so in WebKit, the engine of every browser on an iPhone, focus stayed on the page.
- **An [inline edit](https://docs.wirekit.app/components/inline-edit) reaches a WireKit component in its editor slot.** `x-ref="control"` on a slotted component, such as the editor, gave the inline edit nothing to focus or describe. It now finds the element you type into.
- **An [inline edit](https://docs.wirekit.app/components/inline-edit) shows a rich-text display value as block content, with the editor's typography.** An `Htmlable` value put paragraphs and lists inside an inline element.
- **The [editor](https://docs.wirekit.app/components/editor) works with `x-model` in both directions.** Canceling did not reach the editing surface, and every keystroke set the bound value to `undefined`.
- **An `aria-label` on the [editor](https://docs.wirekit.app/components/editor) names its textbox.** The editing surface was announced as "Rich text editor" whatever the page called it, and `required` now reaches it as well.
- **A [number input](https://docs.wirekit.app/components/number-input) bound with `wire:model` shows and steps from the bound value.** It started from `min`, so the field could show one day where the component held seven.
- **A [segmented control](https://docs.wirekit.app/components/segmented-control) scrolls its own track when its options are wider than the column.** It used to push an ancestor sideways, and on a phone the last segment was cut off at the edge of the screen with nothing to say more existed.
- **The [reading minimap](https://docs.wirekit.app/components/reading)'s heading anchors stay on screen when the minimap sits against the edge of the viewport.** The labels ran past the edge; the strip now moves inside, with a backdrop of its own, when there is no room outside.
- **The component sandbox escapes a string prop once.** A title with an ampersand showed `&amp;`, a link with a query string pointed at a broken address, and Show Code taught the escaped value.
- **The [map](https://docs.wirekit.app/components/map/maplibre)'s console hint leads with a route every bundle can take.** It recommended `registerMapEngine()`, which only `wirekit.esm.js` exports; it now leads with `window.maplibregl = maplibregl` and names `registerMapEngine()` together with the ESM build.
- **[`wirekit:doctor`](https://docs.wirekit.app/cli-reference/wirekit-doctor) no longer asks for a null-guard on a module-level observer.** An observer built once at the top of a file is never null and has no `destroy()` to race, so the finding could only be silenced by decorating it or by an opt-out that would also hide a real case.
- **A [dropdown](https://docs.wirekit.app/components/dropdown), [context menu](https://docs.wirekit.app/components/context-menu), [menubar](https://docs.wirekit.app/components/menubar), [navigation menu](https://docs.wirekit.app/components/navigation-menu), [filter builder](https://docs.wirekit.app/components/filter-builder) and [notification center](https://docs.wirekit.app/components/notification-center) no longer take focus back from a reader who has already moved into the panel.** Each of them put focus into its panel once it had finished positioning it, but the panel is on screen and usable a moment before that. Focus moved to a row in that moment was sent back to the first one, so a key pressed there acted on the wrong row. A submenu opened from the keyboard followed the same order. Focus now goes into the panel only when it is not already there.

### Documentation

- **The [MapLibre GL guide](https://docs.wirekit.app/components/map/maplibre) is rewritten for MapLibre GL 6.** Version 6 ships no `maplibre-gl.js` and sets no global, so following the old steps either kept an app on a release affected by [GHSA-jrc7-96c5-q579](https://github.com/advisories/GHSA-jrc7-96c5-q579) — critical, fixed in 6.4.1 and in no 5.x release — or produced a map that never drew. The guide names 6.4.1 as the minimum and shows the bundler setup that emits MapLibre's worker: without it the worker is requested from a file the build never wrote, and the map stays blank without an error. Each way of loading the engine comes with the Content-Security-Policy it needs.
- **The application shell blueprints and the [app shell](https://docs.wirekit.app/components/app-shell) and [shell bar](https://docs.wirekit.app/components/shell-bar) pages show the shell tag exactly as you write it.** Each preview pinned its `viewport` shell to the documentation column with an inline `height` and `overflow: hidden`, which Show Code then handed on as if it were part of the pattern, together with a comment telling you to delete it again. The documentation column now bounds the preview itself, so the code you copy is the code you keep.
- **The [feature](https://docs.wirekit.app/components/feature), [feature grid](https://docs.wirekit.app/components/feature-grid) and [hero](https://docs.wirekit.app/components/hero) examples say what is true instead of promising quality.** "Production ready", "battle-tested" and "built for speed" said neither what was tested nor against what, and an example's text is what lands in the interface built from it. The cards now name things you can check, such as one class switching every component to dark mode.
- **Search results and link previews for the documentation show a full sentence about each page.** Many pages described themselves in a few words, such as "Small status label", which named the component and said nothing about what it is for, so search engines often put a passage of their own choosing in its place. Every page now carries a description that says what the component does and when to reach for it.
- **The documentation writes the canonical prop spelling throughout.** The [prop naming conventions](https://docs.wirekit.app/extending/prop-naming-conventions) page asks you to write `intent` and `surface` in new code, while the examples across the catalog were still writing the older spelling: on the [alert](https://docs.wirekit.app/components/alert) page an `alert variant="danger"` sat one line from a `button intent="danger"`. Every example now writes the canonical name, and [variants and intents](https://docs.wirekit.app/variants-and-intents) names `intent` in its table. Both spellings produce identical markup, so nothing you render changes.
- **The README names the chat and AI components.** The catalog table on the front page had no row for them and now lists them: [message](https://docs.wirekit.app/components/message), [message-group](https://docs.wirekit.app/components/message-group), [quick-replies](https://docs.wirekit.app/components/quick-replies), [conversation](https://docs.wirekit.app/components/conversation), [assistant-message](https://docs.wirekit.app/components/assistant-message) and the rest.
- **The [lightbox](https://docs.wirekit.app/components/lightbox) page no longer links to a page that does not exist.** The row for `--color-wk-bg` in its token table ended in a dead link.
- **The [inline edit](https://docs.wirekit.app/components/inline-edit) page shows rich text through the editor slot**, with a live preview, the Livewire source, and what cancel and confirm do to the editor.
- **The [MapLibre GL guide](https://docs.wirekit.app/components/map/maplibre) says what the server must send for a self-hosted `.mjs`.** A browser runs a module only when the response carries a JavaScript MIME type, so the section gains a check against the deployed file and the console messages both engines print when it does not.
- **The [reading](https://docs.wirekit.app/components/reading) page shows the minimap's heading anchors in a preview.**