# @codeatlasdev/ui 2.3.0: @codeatlasdev/ui 2.3.0 Page: https://ui.codeatlas.com.br/changelog/2.3.0/. Index: https://ui.codeatlas.com.br/llms/changelog.txt. 13 changesets. 13 changes. This release has no written post; the list below is generated from the changelog. ## In short 1. The generated API reference now documents every non-component expor.... The generated API reference now documents every non-component export of every entry point (root, `kit`, `vite`, `astro`, `actions`, `seo`, `forms`, `tokens`, `table`, `charts`): `setLocale`, `registerLocale`, `caErrorPlugin`, `createErrorHandler`, `nativeFeel`, `usePageTitle`, `primeKeyboard`, the stores, the `seo` builders and the types, each with its signature and a JSDoc summary. 2. New .ca-sr-only utility (visually hidden, still in the accessibil.... New `.ca-sr-only` utility (visually hidden, still in the accessibility tree) and `.ca-sr-only--focusable` (shown again while it or a descendant has focus, for skip links). 3. Add the @codeatlasdev/ui/testing subpath: expectNoA11yViolations.... Add the `@codeatlasdev/ui/testing` subpath: `expectNoA11yViolations`, `checkA11y`, `formatViolations`, `settled` and `A11Y_EXCEPTIONS`. ## Upgrade ```bash bun add @codeatlasdev/ui@2.3.0 ``` Across majors: `bunx @codeatlasdev/ui migrate --from --to `, or the MCP tool `get_migration`. ## Changesets ### Minor Changes - 1f858d3: The generated API reference now documents every non-component export of every entry point (root, `kit`, `vite`, `astro`, `actions`, `seo`, `forms`, `tokens`, `table`, `charts`): `setLocale`, `registerLocale`, `caErrorPlugin`, `createErrorHandler`, `nativeFeel`, `usePageTitle`, `primeKeyboard`, the stores, the `seo` builders and the types, each with its signature and a JSDoc summary. `components.json` gains an `exports` array, `API.md` gains a "Non-component exports" section, the `ca-ui-mcp` server gains a `get_export` tool, and components of `kit` such as `RouteAnnouncer` now appear in the component list. The summaries were added as JSDoc, so they also show in editor tooltips. - 648a1e6: New `.ca-sr-only` utility (visually hidden, still in the accessibility tree) and `.ca-sr-only--focusable` (shown again while it or a descendant has focus, for skip links). Apps can now add a table `` or an AT-only label without custom CSS. Both live in `styles/utilities.css` (`ca.utilities` layer), so `core.css` and `global.css` carry them. Documented in `getting-started/accessibility` and `docs/css-api.md`. The kit now uses it instead of eight copies of the clip pattern (`Dialog`, `Select`, `DocsSearch`, `Lightbox`, the date pickers, and the page titles of `PageShell`, `AppForm`, `AppList` and `AppSettings`; the `Table` cards-mode header row shares the same rule). Visual output is unchanged. Migration: four internal class names are gone because they only existed to hide text. If your CSS targeted `.ca-dialog__sr-only`, `.ca-sel__sr`, `.ca-docs-search__sr` or `.ca-lightbox__sr`, target `.ca-sr-only` instead (these elements now carry that class). The page `

` hidden inside `AppShellLayout` below 900px now also carries `.ca-sr-only--shell`. Bundle budget: `styles/core.css` +173 B gzip and `styles/global.css` +206 B gzip (limits raised to the measured 9812 / 12800 B). The cost is the new public utility (the box, the focusable variant and two scoped copies that a class cannot express as one rule: the shell page title under a media query, the Table cards header under a container query); the eight component-level copies it replaces were already part of component CSS, not these entry files. - 19386b1: Add the `@codeatlasdev/ui/testing` subpath: `expectNoA11yViolations`, `checkA11y`, `formatViolations`, `settled` and `A11Y_EXCEPTIONS`. It is the library's own axe helper (animations settled, `region` disabled for isolated components), framework-agnostic (takes an `Element` or `Document`), with `axe-core` as an optional peer dependency. - a88b8a9: `EmptyState` and `GlassCard` accept `headingLevel` (`2 | 3 | 4 | 5 | 6`) to render their title as that heading without changing how it looks. `EmptyState` inside `PageShell` now defaults to `

` (it was always `

`, which made axe report `heading-order` under the page `

`); outside a `PageShell` it stays `

`. `GlassCard` renders `title` as a `

` unless `headingLevel` is set, so existing outlines are unchanged. New exported type `HeadingLevel`. - 5f7d842: New `KpiGrid`: a responsive row of `KpiCard`s that never leaves an empty cell. `Grid cols="repeat(auto-fill, minmax(160px, 1fr))"` ignores how many cards it holds, so 3 cards on a 390px phone became 2 + 1 with a hole. `KpiGrid` counts its children in CSS (`:has()` quantity queries, no JavaScript, SSR-safe) and only uses column counts that divide that number: 3 cards are 3 across or one column, 4 are 4, then 2 x 2, then one column, 6 are 6, 3 + 3, 2 + 2 + 2. It follows its own width (container query), so it also works inside a sidebar layout. Above 12 children it falls back to `auto-fill`. Props: `gap`, `class`. New classes: `ca-kpi-grid`, `ca-kpi-grid__grid`. The docs, templates, demos and agent skill now use `KpiGrid` for KPI rows instead of the `Grid` auto-fill recipe. - 4db46ab: Three patterns for result views, from the DX feedback in #48. `LiveList` shows rows that stream in (SSE, WebSocket, bulk jobs): a named `role="log"` with batched polite announcements instead of one per row, determinate or indeterminate progress, and auto-scroll that follows the newest row only while the user is at the bottom, pauses when they scroll up (with a "N new" jump button) and never moves focus. `ExternalLink` is a link to another site with `target="_blank"`, `rel="noopener noreferrer"`, a decorative icon and a localised "(opens in a new tab)" for screen readers (`externalLink.newTab` in the i18n catalog), made for table cells. `ExportMenu` is an Export button with a CSV / JSON `DropdownMenu`; the helpers `toCsv`, `toJson`, `downloadFile` and the `ExportColumn` type are exported too (RFC 4180, UTF-8 BOM in the downloaded CSV, spreadsheet formulas neutralised). New i18n keys: `externalLink`, `exportMenu`, `liveList` (en, pt-BR). - 11809ff: `MoneyInput` accepts `currency` (ISO 4217; decimals come from `Intl`, so `JPY` has none and `BHD` has three) and `locale` (BCP 47). `value` is now the currency's minor unit rather than always cents. Existing BRL usage is unchanged: with neither prop set the field still renders `R$ 1.234,56`. With `currency` set and no `locale`, the locale follows the active i18n locale (`I18nProvider` / `setLocale`). `centsToDisplay` and `parseInputToCents` take an optional `{ currency, locale }` (defaults `BRL` / `pt-BR`), `parseInputToCents` reads the locale's decimal separator, and `currencyDecimals(currency)` and the `MoneyOptions` type are exported from `@codeatlasdev/ui/forms`. ### Patch Changes - 5871641: AppShellLayout: the panel/main offset now uses the Svelte 5 `Spring` class and lands on the exact target (252px, not 251.984px). The extra CSS `margin-left` transition that lagged the spring is gone, and `prefers-reduced-motion` skips the animation and jumps straight to the target. - ef7b0fa: CommandPalette: keyboard selection no longer gets stolen by the pointer. A row appearing under a stationary pointer (list renders, dialog animates in) fired `mouseenter` and moved the highlight; hover now follows real `mousemove`. ArrowDown on an empty list no longer stores `-1`. - ee47b59: DocsSearch: keyboard selection no longer gets stolen by the pointer or reset by a lazy index. A row appearing under a stationary pointer (results render, dialog animates in) fired `mouseenter` and moved the highlight; hover now follows real `mousemove`. A lazy `index()` resolving while the dialog is open re-ran the open effect and wiped the typed query. ArrowDown on an empty list no longer stores `-1`. - 7f5d8f7: `init` now writes the full SvelteKit setup: `caErrorPlugin({ scaffold: true })` next to `caThemePlugin(theme)` in `vite.config.ts`, and `handleError = createErrorHandler()` next to the theme `handle` in `src/hooks.server.ts` (both patched into existing files with `--write`, each only when missing; your own `handleError` is never replaced). The installation guide shows one complete SvelteKit setup, the exact files `init` writes, and states that plugin order in `plugins` does not matter. - ea072c2: DropdownMenu, DataTable column toggle (and any `FloatingLayer` consumer): closing before the frame-delayed focus move ran no longer lets that move steal focus back from the trigger. `FloatingLayer`'s `onMounted` may now return a cleanup that runs on close/unmount. - ba4c458: `PageShell` actions below 900px: a lone icon-only control (`IconButton`, `ThemeToggle`) no longer stretches to the full row with its icon centred. Icon-only children (`.ca-icon-btn`, `.ca-rail-btn`) keep their natural size and sit at the end of the row; labeled buttons still share the row full-width as before.