Skip to content
On this page In short

Changelog · latest

@codeatlasdev/ui 4.4.0

27 changes. This release has no written post; the list below is generated from the changelog.

Release 4.4.0Three sketchbook pages stacked and slightly fanned, the front one tabbed with the version number and shaded with pencil hatching in the corner.4.4.0

In short

  1. NavItem in the AppShell panel gets color (a decorative dot), l... `NavItem` in the AppShell panel gets `color` (a decorative dot), `leading` (a snippet that replaces the icon), `count` (a neutral total, announced as a bare number instead of "N unread", `1.2k` above 999) and `actions` (a snippet of `DropdownMenuItem`s behind a "…" button named "Actions for <label>") (#275).
  2. AppShellLayout takes panelWidth (px, held between 160 and 480,... `AppShellLayout` takes `panelWidth` (px, held between 160 and 480, default 200) and `resizablePanel` (#292).
  3. Combobox: multiple turns it into a searchable multi-select (va... `Combobox`: `multiple` turns it into a searchable multi-select (`value` is `string[]`, `aria-multiselectable` listbox, list stays open, removable chips, `Backspace` on an empty query removes the last chip) and `creatable` works in both modes.

Upgrade

Published under the latest dist-tag.

bun
bash
bun add @codeatlasdev/[email protected]
npm
bash
npm install @codeatlasdev/[email protected]
pnpm
bash
pnpm add @codeatlasdev/[email protected]
yarn
bash
yarn add @codeatlasdev/[email protected]

Then run your checks. Moving between majors? bunx @codeatlasdev/ui migrate --from <major> --to <major> lists what to change, and agents get the same data from the MCP tool get_migration. The migration notes per major are inUpgrading.

Changesets

### Minor Changes

- 1c6653e: `NavItem` in the AppShell panel gets `color` (a decorative dot), `leading` (a snippet that replaces the icon), `count` (a neutral total, announced as a bare number instead of "N unread", `1.2k` above 999) and `actions` (a snippet of `DropdownMenuItem`s behind a "…" button named "Actions for <label>") (#275). `NavSection.onReorder` and `NavModule.onReorder` make the rows reorderable with the `useDnd` primitive: a grip per row, mouse, touch press-and-hold and keyboard, with announcements; the shell reports `{ id, from, to }` (new `NavReorderEvent` type) and never reorders `items` itself. `PanelNavItem` now renders a row that contains the `<a class="ca-nav-item__link">` (a link cannot hold the actions button or the grip), so `.ca-nav-item` is the row, not the anchor. New messages `appShell.itemActions` and `appShell.moveItem`.
- 1c6653e: `AppShellLayout` takes `panelWidth` (px, held between 160 and 480, default 200) and `resizablePanel` (#292). The content offset and the open and close animation follow the width, so a panel for long labels no longer misaligns the page. `resizablePanel` adds a keyboard-operable drag handle on the panel's trailing edge (Arrow keys 8px, Shift 32px, Home and End for the bounds); with `persistPanel` the chosen width is stored in `ca-shell` and restored by the inline script before the first paint. New `appShell.resizePanel` message and `.ca-panel__resize` class.
- ca80cfd: `Combobox`: `multiple` turns it into a searchable multi-select (`value` is `string[]`, `aria-multiselectable` listbox, list stays open, removable chips, `Backspace` on an empty query removes the last chip) and `creatable` works in both modes. `CommandPalette`: `search(query, signal)` adds debounced (`searchDebounce`), abortable server-side results with loading, empty and failed states announced through `announce()`, plus `onSelect` and the exported `CommandResult` type.
- 49116d1: `DataTable` gains row groups, column resize, row reorder and safer interactive cells (#283). `groupBy` renders one `<tbody>` per key with a collapsible header (`aria-expanded`, `groupLabel` to customize). `meta.resizable` on a column adds a window-splitter handle (mouse, touch, keyboard arrows, Shift, Home, End, Enter) driven by `size`, `minSize` and `maxSize`; the new `DataTableColumnResize` is exported for hand-built headers. `onReorder` adds a drag grip per row on top of the new `useDnd` primitive, moves rows between groups and reports `{ id, from, to }` (`DataTableReorderEvent`); `rowId` gives rows stable ids and `pageSize` sets the page size in simple mode. A click on a link, button, input or other control inside a cell no longer fires `onRowClick` (add `data-no-row-click` to opt other elements out). The new labels (Reorder, Move, Resize) follow the active locale (en, pt-BR). Needs the drag and drop primitive from #284.
- d138bd8: New drag and drop primitive, `useDnd()`, `applyMove` and `<DndLayer>`, for sortable lists, moves between lists and drop zones (#284). It works with mouse and pen (4px threshold), touch (press and hold, so the page still scrolls) and keyboard (Space lifts, arrows move, Home/End jump, Enter drops, Escape cancels), speaks every step through `announce()` (English and Portuguese built in, `useDnd({ messages })` for others), auto-scrolls near the edges of a scrolling container and drops its animations under `prefers-reduced-motion`. The engine measures the boxes once per drag and only moves a clone, a drop line and `data-dnd-*` attributes, so the list does not re-render until `onDrop`. `<DndLayer {dnd} />` renders the keyboard instructions and the forced-colors aware styles (`.ca-dnd-overlay`, `.ca-dnd-indicator`), so apps that never drag add no bytes to `core.css` or to the i18n catalog.
- 946d69d: `Drawer` can be named without its default title: pass `aria-label`, `aria-labelledby` (your own header) or `hideTitle`, and `id`, `data-*` and other attributes now reach the `<dialog>` (#285). `AlertDialog` gains `confirmProps` and `cancelProps` (attributes for the two buttons) and `cancelVariant` (#287), plus a `children` body, a `description` that accepts a snippet and `confirmDisabled` for type-to-confirm (#278).
- e983caa: `DropdownMenu` is controllable and can open at a point. `bind:open` and `onOpenChange` read and set the open state; `anchor={{ x, y }}` opens the menu at a viewport point (a right-click menu, no `trigger` needed) and changing it while open moves the menu instead of toggling it. A menu opened from code starts on its first item and returns focus to the element that had it. On the built-in trigger Shift+F10 and the ContextMenu key open the menu like ArrowDown.
- e983caa: `DropdownMenu` gains selection items. `DropdownMenuCheckboxItem` (`role="menuitemcheckbox"`, `bind:checked`, `onCheckedChange`) and `DropdownMenuRadioGroup` + `DropdownMenuRadioItem` (`role="menuitemradio"`, `bind:value`, `onValueChange`) render `aria-checked` and a check indicator, stay open after a pick (`closeOnSelect` closes), and join arrow, Home, End and typeahead navigation (the menu now also recognizes `menuitemradio`).
- e983caa: `DropdownMenu` gains submenus. `DropdownMenuSub` takes a `trigger` snippet holding a `DropdownMenuSubTrigger` item (`aria-haspopup="menu"`, `aria-expanded`, chevron) and the submenu items as children; they nest to any depth. ArrowRight, Enter or Space opens the submenu on its first item, ArrowLeft or Escape closes only that level, hovering the trigger opens it after a short intent delay and the pointer can cross into it. Selecting an item closes every level. A popover layer that is closing no longer owns Escape or outside presses, so a closing submenu hands them to its parent.
- ff460ad: Form controls forward native attributes and expose their element, with one vocabulary (`ref`, `hideLabel`, rest props on the native element):
  
  - `Checkbox`, `TextField`, `TextArea` and `FileUpload` spread every other attribute and handler (`data-*`, `maxlength`, `inputmode`, `onclick`, `onkeydown`, `name`, `capture`...) on the native `<input>`/`<textarea>`, and take a bindable `ref` with that element. `Checkbox` `onchange` now receives the native event as a second argument, and its forwarded `onclick` makes Shift range selection possible. `TextField`/`TextArea` gain `onfocus` and `hideLabel` (label kept for screen readers with `.ca-sr-only`; `aria-label` also works without a label).
  - `Select` accepts `aria-label` and `aria-labelledby` for a compact select without a visible label. The trigger is named by that text followed by the current value (visible text stays in the name); without either, the placeholder names it as before.
  - `FileUpload` gains `ref`, an `open()` instance method for a button outside the dropzone, and `list={false}`: a plain picker whose `onchange` receives only the files just picked, with no built-in list and nothing kept in `files` (the native input is cleared, so the same file can be picked again).
- 8c24c31: Theme option `motion: { enter: "none" }` (token `--ca-enter-scale: 0`) turns off entrance and exit animations only: `Dialog`, `AlertDialog`, `Drawer`, `Sheet`, `Lightbox`, `CommandPalette`, `DocsSearch`, date pickers, `DropdownMenu`, `Popover`, `Tooltip` and `Toast` appear and leave in place, while hover and focus color transitions keep `--ca-duration-*`. New derived tokens `--ca-enter-fast`, `--ca-enter-base` and `--ca-enter-slow`; `MotionStage` demos render their final state when the theme turns entrances off, as under `prefers-reduced-motion` (#268).
- 16f89dd: `Popover` is controllable. `bind:open` and `onOpenChange` read and set the open state, and the `children` snippet receives `close`, which closes the popover and returns focus to the trigger, so a choice made inside it (a `Calendar` day, an option) can dismiss it. Existing popovers are unchanged.
- 8c24c31: New `--ca-radius-round` token (`999px × --ca-radius-scale`): "fully rounded at the current scale". `SwatchGroup` swatches, the AppShell active pips (`.ca-nav-item__pip`, `.ca-rail__pip`) and the new-item dot, and `.ca-nav-item__badge` use it, so a theme with `radius: 0` squares them with no app CSS (#267). `--ca-radius-chip` now aliases it. `--ca-radius-pill` stays a fixed 999px for functional shapes (Switch, Slider, Progress, scrollbars) and for `controlRadius: "pill"`.
- 946d69d: `Tabs` gains `variant="line"` (bare text tabs with an underline on the active one, for page headers), and `TabsList` and `TabsTrigger` forward rest attributes: `aria-label` names the tablist, `data-*`, `id` and event handlers reach the trigger button (#286). A `TabsTrigger` now points `aria-controls` at its panel only while a `TabsContent` with the same `value` is mounted, so tabs used as view switchers no longer reference missing elements.
- 2ab0d93: `ToggleGroup` gets `required`: pressing the active item no longer clears the selection (radio-like, `onchange` stays silent; `type="multiple"` keeps its last pressed item). An empty group still selects on the first press. Closes the gap with `SegmentedControl` for filters that must look like contiguous buttons.

### Patch Changes

- 05cfbfc: `AppShellLayout`: an item at `path: "/"` is active on `/` only, no longer on every page, and a module at `basePath: "/"` only gets the pages no other module claims. Module prefixes (`basePath`, `pathPrefixes`) match whole path segments and the longest one wins, whatever the module order (`/settingsfoo` no longer matches `/settings`).
- 98d2ace: Astro: the theme now survives `<ClientRouter />` (View Transitions) navigations. `caUI` registers an `astro:before-swap` handler that carries `#ca-theme`, `#ca-runtime-tokens`, the `theme-color` meta and the theme attributes onto the incoming document (#265). `codeatlas-ui init` on Astro also adds `svelte()` from `@astrojs/svelte` to `integrations`, and notes `npx astro add svelte` when the dependency is missing (#273).
- 3ae1f41: `Button` with `href` now types the anchor's own attributes: `download`, `rel`, `hreflang`, `ping`, `referrerpolicy` and `type` (the linked resource's MIME type) are accepted instead of rejected, as `...rest` was already spread on the `<a>`. The props are a union keyed on `href`: a link takes anchor attributes, a button takes button attributes (`form`, `formaction`, `name`, `value`...), and `<Button download>` without `href` is a type error. At run time a `type` passed with `href` reaches the `<a>`, and a caller `rel` is kept and merged with `noopener noreferrer` when `target="_blank"` (it used to be overwritten).
- 5c9ac81: CommandPalette: new `shortcut` prop (default `true`). With two palettes on one page, every instance opened on Mod+K; set `shortcut={false}` on all but one, or when the app binds its own shortcut.
- a61874b: Calendar, RangeCalendar, DatePicker and DateRangePicker: fast arrow-key sequences no longer lose keys. The roving focus now moves synchronously instead of after `tick()`, so the next key always starts from the day it lands on, with or without Svelte's `experimental.async`.
- e983caa: `DropdownMenuItem` forwards its remaining attributes (`data-*`, `id`, `aria-*`, `title`, ...) to the `menuitem` element. `data-testid` and `aria-keyshortcuts` used to be dropped. `role`, `tabindex` and the selection handler stay owned by the item.
- ff8686f: `DropdownMenuItem` and `TabsTrigger` icon slots now size any `svg` or `img` inside the 14px box, not only a direct child. A `SchemeAsset` logo (its variants sit in `display: contents` spans) used to collapse to 0×0 when the svg had no `width`/`height`, and an svg with a fixed `width="24"` overflowed the box; both now fill the box.
- d01c039: `LoadingBoundary` counts `minDuration` from the moment the placeholder reaches the DOM instead of from the state change that schedules it, so a busy main thread no longer shortens the time the placeholder is visible.
- 23276cf: Fix Escape going to the wrong layer. A Dialog, AlertDialog, Drawer, Sheet, Lightbox, CommandPalette or DocsSearch that is playing its exit animation no longer takes Escape or presses: they reach the layer below, so closing a confirm dialog and pressing Escape right after closes the Drawer under it (#288). A menu or popover that was open when a modal opened now ranks above that modal, so Escape closes the menu first and the dialog second instead of closing the dialog and leaving the menu open (#299). Exit animations of components that use `createPresence` (Dialog, Drawer, AlertDialog, menus, popovers) now keep the node mounted through the exit instead of removing it in the flush that closes it, and a menu hosted in a dialog is no longer left stranded after its exit.
- 05cfbfc: `Rail`: the dock magnification scales the module icon (the glyph), not the whole button, so the hovered background no longer covers the active neighbour's background and pip. The effect follows `--ca-motion-scale`: it is off with `motion: { scale: 0 }` and under `prefers-reduced-motion`.
- 05cfbfc: Native `button`, `input`, `select` and `textarea` inherit the page font (`font: inherit` in the new `ca.reset` layer, `styles/reset.css`) instead of the browser's UA font. The AppShell section header (`.ca-section__trigger` / `.ca-section__label`) and any control without its own font rule now render in `--ca-font-sans`. Component and app rules still win: the layer is the first one.
- 8c24c31: `Skeleton` and the `skeleton` prop follow the theme surface style: under `flat`, `solid` and `outlined` no gradient band is drawn, the placeholder is a flat block that pulses its opacity (`ca-skeleton-pulse`, stopped under reduced motion). Glass, frosted, neumorphic, clay, chrome and tonal keep the shimmer sweep (#280).