Changelog
Release history by design-system version — new and changed components with doc links and commit refs — plus how updates reach you.
Last updated
Release history for the whole system, versioned by the design-system (registry) version;
npm package versions are listed per entry. This page is generated from the repo's root
CHANGELOG.md — the two can never drift (CI enforces it).
[0.26.6] — October 30, 2026
📦 npm
@vegastack/design→0.9.5(was0.9.4).- The design-system registry (
@vegastack/ui) bumps 0.26.5 → 0.26.6.
🐛 Fixed
- Settings page tabs keep the list page rhythm (
page-header): a header withtabsplaced directly in aSettingsLayoutContentsits 12px above the settings below, as it already does in anAppShellPage.6728ed2
[0.26.5] — October 30, 2026
🛠 CLI & tooling
- Registry items carry their own version: the release in which their content last changed. A release now restamps only the items that changed, so
check-updatesreports only real updates.4edd4ef
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.26.4 → 0.26.5.
[0.26.4] — October 30, 2026
📦 npm
@vegastack/design→0.9.4(was0.9.3).- The design-system registry (
@vegastack/ui) bumps 0.26.3 → 0.26.4.
🐛 Fixed
- + Filter with every field already a facet (
add-filter-menu): with nothing typed and no field left to add, the menu offers just "Advanced filter" instead of "No field matches" above it.50e16e6 - Page header tabs keep the list page rhythm (
page-header): thetabsrow sits 24px under the title and, in anAppShellPage, 12px above the page below — the same title → tabs → toolbar spacing as aListToolbar's tabs row — instead of 8px under the title and 24px above the page below. No prop change.50e16e6
[0.26.3] — October 30, 2026
📦 npm
@vegastack/design→0.9.3(was0.9.2).- The design-system registry (
@vegastack/ui) bumps 0.26.2 → 0.26.3.
📚 Docs
- The
vegastack-consumeskill updates a component with the full verify → overwrite → verify flow and lockfile-pinnedpnpm exec shadcn; the internal Runbook page documents the release flow.928adf8
[0.26.2] — October 30, 2026
🛠 CLI & tooling
pnpm genruns every generator; the component contract writes the fields its source determines (exports, dependencies, data attributes, wave rosters).b06d1ed
📦 npm
@vegastack/design→0.9.2(was0.9.1).- The design-system registry (
@vegastack/ui) bumps 0.26.1 → 0.26.2.
🐛 Fixed
- Filter chips: hover and open fill the whole chip (
filter-row): an unsetListFacet,PeopleFacet,DateRangeFilterorunsetFilterChipdrops its dashed line while hovered or open, so the fill reaches the chip's edge like a chip with a value instead of sitting inside the dashes.DateRangeFilterValuetakes an optionallabelthe chip reads in place of a custom range's dates ("Due: Last 2 days"), for relative ranges a host keeps as one token.0a51593 - Collapsed sidebar: every icon clickable, neutral counts (
sidebar, INT-15): in icon mode aSidebarGroupLabel(faded and pulled up over the item above it) no longer takes the click meant for that item, so every rail icon opens and shows its tooltip; the rail's count pill and dot use the app header's count ink (bg-primary/text-primary-foreground) in both themes instead of the bluesidebar-primaryaccent.45a8890
[0.26.1] — October 30, 2026
🧩 New components
- New:
CitedList,CitedListItem,Citation(itemcited-list) — an answer as numbered points, each a sentence followed by its sources as small muted chips after a real space;Citationis a link by default and takesrender(a router link, a play-from-here button) and an optionalicon. ActivityFeed takesactions: extra header controls placed before the filter menu. Additive — nothing changes until a consumer opts in.91ff3d3
🔧 Changed components
- Nested modals keep the backdrop (
dialog,alert-dialog; OVL-23): aDialogorAlertDialogopened from inside another dialog or a sheet now dims and blurs the page like any other modal — the overlay sets Base UI'sforceRender, which otherwise skips a nested backdrop. Popovers and menus are unchanged.91ff3d3
📦 npm
@vegastack/design→0.9.1(was0.9.0).- The design-system registry (
@vegastack/ui) bumps 0.26.0 → 0.26.1.
⚠️ Breaking
- List toolbar, round 3 — no compatibility aliases.
ListToolbarPopoveris one layout for every toolbar menu: on a phone the bottom sheet's rows sit on the title's 16px edge, 8px under it (no empty band); asize="lg"(576px) for a condition builder;ListToolbarDrillHeader(new) is the one ‹ back row for every drill-in (Display › Sort / Columns, + Filter › a field / Advanced filter, a date's Custom range).ListToolbar: the search grows to 320px;search.collapsibleis removed (a table's search is the same full field as every list); the tab row keeps 12px to the toolbar row.FilterRow:+ FilterandClearsit right after the last chip inside the scrolling line, and opening a chip's picker scrolls that chip into view;aiNote(+FilterRowAiNote, theaiAdded/undo/unmatched/dismisslabels) is removed —useAiFilterApplied()toasts what an AI run added, with Undo.DateRangeFilteris the one date filter: presets add Overdue, Tomorrow, This/Last/Next week, Next 7 days and No date (weekStartsOn), shown as the menus' 28px rows with a tick; Custom range drills into the calendar (one month on a phone); a value'sfrom/toareDate | null(open-ended ranges).AddFilterMenu:onAdvanced→advanced(the condition builder drilled into in the same, wider popover — never a dialog);ai.onApplied(result, text)fires after a run that added filters.SortBuilder/GroupByPicker: fields and directions are 28px menu rows (ghost selects), "Add sort" / "Group by" are 14px rows.ViewTabsuse the same pill tabs as every page's scope tabs.RouteTabs: no pending bar under a clicked tab (it flashed).DataList: "N columns hidden" is screen-reader only.Inbox:InboxFiltersloses the Unread toggle (unreadOnly,onUnreadOnlyChange,unreadCount,unreadLabel);InboxItemActionvariant: "primary"(the row's main action) now drawssecondary, the restoutline— a row action is never the filled button (alsoApprovalCard's Approve; the rule is in Button's docs).useAiFilterAppliedtakeslabels.note(the AI disclaimer line).FileViewer's backdrop dims and blurs like every modal (Dialog, AlertDialog, Sheet, Drawer); popovers and menus keep none. docs ·4827db3
[0.26.0] — October 30, 2026
🧩 New components
- New:
FloatingCellEditor,DropdownMenuInput/DropdownMenuSwitchItem/DropdownMenuAtPoint(itemdropdown-menu-form),OptionListEditor.FloatingCellEditoropens a grid cell's editor over the cell, with the page locked behind it and pinch-zoom still passing;DataGridtakesediting="floating"to use it for itscustomeditors. The dropdown-menu form parts hold a small form in a menu: an input that keeps its keys, a switch row with a hint, and a menu that opens at a point.OptionListEditoredits a choice field's options as rows (hue, name, remove, add).FieldTypeIcon,FIELD_TYPE_ICONSandFIELD_TYPE_LABELSinfield-cellgive every field type one icon and one name. Additive — nothing changes until a consumer opts in.55d70fb
🔧 Changed components
- Grid cell editors that sit on the cell (
field-cell,data-grid): withFieldCellEditor variant="cell", long text edits in place on the cell's own surface instead of a second popover that missed the cell; a person is the chosen people as removable chips over the people list, which leaves them out; a date and a date-time's date take the inline picker (FieldInputtakesvariant="cell"for it).DataGrid's fill handle now sits under frozen cells, so it no longer draws over a frozen column when the grid scrolls sideways.55d70fb - Grid cells without app-side hacks (
field-cell,data-grid,app-shell; additive — nothing changes until a consumer opts in):FieldCellDisplayandFieldCellEditortakevariant="cell"— the display is plain (no digit grouping except currency, links without scheme, dates clipped on one line, every person named) and the editor is borderless, with a growing one-paragraph text box and a number typed as written (decimal mark of the reader, no steppers, arrows step by 1).DataGridtakesfill(viewport height to the bottom of its scroller, ≥240px),endSpacer,columnDividers(frozen columns too),editingTint, andheaderAlign(grid-wide or per column) so a numeric column's header can lead while its values stay end-aligned. NewAppShellBleedlets a grid reach the page's edges without a gutter wrapper.55d70fb
📦 npm
@vegastack/design→0.9.0(was0.8.53).- The design-system registry (
@vegastack/ui) bumps 0.25.105 → 0.26.0.
[0.25.105] — October 29, 2026
📦 npm
@vegastack/design→0.8.53(was0.8.52).- The design-system registry (
@vegastack/ui) bumps 0.25.104 → 0.25.105.
🐛 Fixed
- Display's Group by spacing: a
flushSortableListwith no rows draws its footer as the list's first row — no top margin, padding, border or gutters — so an emptyGroupByPicker's "+ Group by" sits on the menu's inset under its heading like the rows around it.DisplayMenu's Group by heading keeps a 4px step from the row above it.451329b
[0.25.104] — October 29, 2026
🔧 Changed components
- ListToolbar search suggestion:
search.suggestionoffers one action under the toolbar's search once the query reads as a phrase (minWords, default 3) — a page's plain-language filter. Nothing runs while typing; Enter still searches, ↓ then Enter (or a click) picks the row and handsonSelectthe query, and Escape hides it until the query changes.opensFilteropens the filter row onceonSelectsettles. The field becomes acomboboxpointing at aSuggestionMenulistbox.040c968
📦 npm
@vegastack/design→0.8.52(was0.8.51).- The design-system registry (
@vegastack/ui) bumps 0.25.103 → 0.25.104.
🐛 Fixed
- Segmented controls, popup search rows and empty lists: a joined outline
ToggleGroup(spacing={0}) keeps a pressed item's whole outline at every position — each item draws its four borders and overlaps its neighbour by a pixel, and the pressed one sits on top (Display's Row height, two or three options).PanelSearch's hairline is a divider in the defaultbordercolour in every state; its field is caret-only. ASortableListwithhandle="auto"and fewer than two movable rows keeps no handle column, so a one-keySortBuilderor one-levelGroupByPickerstarts at the menu's inset (no big gap left of Group by);SortBuilder's header takes the menu rhythm. ADataListwith no rows never says "N columns hidden" or how its rows are sorted.040c968
⚠️ Breaking
- List toolbar, round 2 — no compatibility aliases.
ListToolbar: the search sits at the start (left) and the tools at the end;tabsrender in their own row above the toolbar (they fold by themselves); theactionprop andListToolbarActionare removed — a page's main button lives at the right end of the app's top bar on every page type;layoutis one icon button that shows, and switches to, the other layout (three layouts step in order; Display's Layout section still picks any); the collapse order is search 240→144, Layout → Display, Sort → Display, search → icon, edit and state → ⋯.ListToolbarPopover,SortMenu,DisplayMenuandAddFilterMenutake theDropdownMenu's rhythm: a 4px inset, 28px rows at a 6px inset, 24px labels, edge-to-edge hairlines; Display's Reset is a menu row.FilterRow: facets and chips are one line that scrolls sideways and never wraps, with+ Filterand thenClearat the right end; every control on it is the toolbar's tool height (32px, 28px in a compact toolbar).AddFilterMenu: one "Filter by…" input (the people dropdown's filled field) for field names and plain language; withai, "✦ Filter with AI: “…”" shows first only for a phrase (two or more words, or a word no field matches).AiFilterInput(+AiFilterInputProps) andAddFilterAi.placeholderare removed. docs ·040c968
[0.25.103] — October 29, 2026
🗑 Removed / renamed
- List toolbar release 2 —
FilterBar,ViewToggleandDensityToggleare removed, with no compatibility aliases: a list's toolbar isListToolbarwith aFilterRow(its Filter tool), the Layout tool (ListToolbar.layout) and aDisplayMenu(row height). Thefilter-barandview-toggleregistry items are gone. The facet pieces move intofilter-row, renamed:FilterBarFacet→ListFacet(FilterBarFacetProps→ListFacetProps,FilterBarFacetOwnProps→ListFacetOwnProps),FilterBarPeopleFacet→PeopleFacet(FilterBarPeopleFacetProps→PeopleFacetProps),FilterBarFilter→ListFilter;FilterChip,FilterChipProps,FilterFacetReporter,FilterFacetReport,DateRangeFilter(+DateRangeFilterProps,DateRangeFilterValue,DateRangeFilterPreset) anddateRangeForPresetkeep their names — import them all from@/components/ui/filter-row. A facet'sdata-slotis nowlist-facet*(wasfilter-bar-facet*).ListView→DataListViewandDensity→DataListDensity(fromdata-list). DataList no longer mounts a view or density switch:onViewChange/onDensityChangefill aListToolbartoolbar's Layout tool and Display row height; any other toolbar renders as it is. Blockslist-page-01andboard-01composeListToolbar+FilterRow+ListFacet. docs ·8a67a7e
📦 npm
@vegastack/design→0.8.51(was0.8.50).- The design-system registry (
@vegastack/ui) bumps 0.25.102 → 0.25.103.
[0.25.102] — October 29, 2026
📦 npm
@vegastack/design→0.8.50(was0.8.49).- The design-system registry (
@vegastack/ui) bumps 0.25.101 → 0.25.102.
🐛 Fixed
- List toolbar fixes:
DisplayMenu's Columns/Fields drill-in scrolls inside the menu (a 24-field table no longer runs off the screen).ViewTabsfolded on a phone merges its+and the view's ⋯ into one ⋯, andListToolbarnow counts those buttons and keeps a folded tab dropdown at a readable width, so the view name no longer collapses to "All re…".SortMenufields takefixedDirection("asc","desc"or"none") for a field the list sorts only one way: the direction shows beside the name and the direction group hides while it is chosen.126da08
[0.25.101] — October 29, 2026
📦 npm
@vegastack/design→0.8.49(was0.8.48).- The design-system registry (
@vegastack/ui) bumps 0.25.100 → 0.25.101.
🐛 Fixed
- InboxFilters: the Unread toggle no longer sits on top of the last tab. The tabs were an unshrinkable pill list inside a
min-w-0box, so with four tabs (For you · Updates · Snoozed · Archived) in the 384px Inbox sheet the list ran under the toggle and hid most of Archived. The row now wraps: the tabs keep their natural width and Unread drops under them, end-aligned, when both do not fit one line. The tabs useTabsList overflow="menu", so they fold into one dropdown only when they alone do not fit.5cef805
[0.25.100] — October 29, 2026
🧩 New components
- List toolbar (release 1 of the list and table toolbar redesign): one header row for every list and table. ListToolbar (new) — tabs left; search, a tight cluster of 32px borderless icon tools (Filter · Sort · Layout · Display · ⋯, 4px apart, tooltips, counts and dots, names that carry state) and the page's main button (
action, aListToolbarAction: filled, labelled on a wide toolbar, an icon+on a narrow one) right; the filter row under it. It measures its own width and collapses in a fixed order — search 240 → 144, tabs fold to a dropdown, Layout then Sort move into Display, search becomes an icon, undo/redo move into ⋯ — and never wraps; every toolbar popover is a bottom sheet on a phone (ListToolbarPopover);/focuses search andFopens the filter row. AlsoListToolbarButton,ListToolbarMenu(⋯, renders nothing when empty) anduseListToolbar. FilterRow (new):+ Filter, pinned facets, chips, Clear in a fixed order, wrapping from@3xland one scrolling line below it, with an AI note line (count · Undo · words it could not match); it reports its count to the Filter tool. AddFilterMenu + AiFilterInput (new): one input that narrows the fields or, when the page passesai.onSubmit, sends a plain-language request (no AI code in the DS; no ✦ withoutai); fields drill into their pickers in the same popover. SortMenu (new): the one-key sort as two radio groups, directions worded by field type, Reset away from the default;sortSummary(). DisplayMenu (new): Layout and Sort once folded in, row height, group by, a Columns/Fields drill-in with eye toggles and reorder, Show switches, Reset — one surface, no fills. useOverflowFold (new hook). Changed: TabsTabsList overflow="menu"folds into one dropdown when it does not fit (LAY-14 widened); RouteTabsoverflow="menu"; ViewTabs folds throughuseOverflowFoldand takesdirtyper view (a dot); SortBuilder rows carry no fill or hover wash, the grip shows only from two keys, "Add sort" sits on the field column and is replaced by "Up to N sorts" atmax, a header with Clear (header,labels); GroupByPicker the same row grid; SortableListhandle="auto"; FilterChipaiandunset, and unset facets are dashed; SearchInputcollapsibleandonExpandedChange; DataList fills aListToolbartoolbar's Layout and Display row height, drops the column header when empty,noResults.onEditFilters; ActionBar docks full width with icon-only actions on a phone; PanelSearchicon; InboxFilters use the small pill tabs. Deprecated (removed in release 2):FilterBar,ViewToggle,DensityToggle. docs ·4825a99
📦 npm
@vegastack/design→0.8.48(was0.8.47).- The design-system registry (
@vegastack/ui) bumps 0.25.99 → 0.25.100.
[0.25.99] — October 29, 2026
📦 npm
@vegastack/design→0.8.47(was0.8.46).- The design-system registry (
@vegastack/ui) bumps 0.25.98 → 0.25.99.
🐛 Fixed
- RecentStrip's ‹ › scroll buttons and the DocumentLayoutJumpCue pill no longer jump down when pressed. Both positioned their Button with a y-translate (
-translate-y-1/2,-translate-y-full), and Button's press nudge (active:translate-y-px) sets the sametranslateproperty, so a press replaced the positioning and dropped the control by half (or all) of its height. The strip's buttons now centre withinset-y-0 my-auto(the idiom DatePicker, SearchableSelect and Carousel already use) and the jump cue's band aligns the pill withitems-end; no other registry Button positions itself with a y-translate.0561c7e
[0.25.98] — October 29, 2026
📦 npm
@vegastack/design→0.8.46(was0.8.45).- The design-system registry (
@vegastack/ui) bumps 0.25.97 → 0.25.98.
🐛 Fixed
- StatGroup and RecentStrip cells are transparent with a border in both themes (they were
bg-card: filled in dark, invisible on a white page in light; StatGroup now draws its dividers with borders instead of abg-bordergrid). A RecentStrip card's hover isbg-mutedand its icon tile turnsbg-backgroundon hover, so the tile no longer disappears into the hover fill (same pairing in Inbox item icons); the strip's scroll buttons keep a visible border and foreground chevron in dark.007d163
[0.25.97] — October 28, 2026
🧩 New components
- TextEdit highlights text: the bubble menu's Highlight, Mod Shift H or typing
==text==. VegaStack Markdown stores it as==text==, with an optional colour right after it (==text=={green}: yellow, green, blue, purple, pink or red — every colour reads, renders and round-trips; the editor makes yellow). MarkdownView renders it as<mark data-color>on the colour'stag-*-subtlefill (the prose recipe's newmarkrule, kept in print), andtypedFirstLineand the newhighlightTextread it as plain text.text-edit-grammaraddsreadHighlight,highlightMarkdown,HIGHLIGHT_COLORSandHIGHLIGHT_SUFFIX_SQL. A literal==run that could open or close a highlight is now written\=\=. docs ·ba95612
📦 npm
@vegastack/design→0.8.45(was0.8.44).- The design-system registry (
@vegastack/ui) bumps 0.25.96 → 0.25.97.
[0.25.96] — October 28, 2026
🧩 New components
- RecordPicker browses folders: pass a
browseloader (one folder's contents,nullfor the top) and mark folder rowsfolder: true, and the empty search box walks the app's folders — a folder opens instead of being picked, a breadcrumb (or Backspace in the empty box) climbs back, and typing still searches everything. Withmultiple,selectAlladds a "Select all" for the rows on screen;kindFilter={false}hides the kind filter. RecordPickerDialog takes a countingconfirmLabel("Add 3"), acancelLabel, and an asynconConfirmthat keeps the dialog open and busy until it settles (falsekeeps it open); its confirm is off while nothing is picked. docs ·5725c61
🔧 Changed components
- AgentHoverCard shows the agent's
lastAction(its latest progress line, under the state), and its body ships on its own as AgentCard. PersonHoverCard takes a host's card throughPersonHoverCardProvider(keyed by the new optionalPerson.id), so an agent's name in comments and activity opens the same agent card. docs ·0530a15
📦 npm
@vegastack/design→0.8.44(was0.8.43).- The design-system registry (
@vegastack/ui) bumps 0.25.95 → 0.25.96.
[0.25.95] — October 28, 2026
📦 npm
@vegastack/design→0.8.43(was0.8.42).- The design-system registry (
@vegastack/ui) bumps 0.25.94 → 0.25.95.
🐛 Fixed
- DataGrid keeps a long primary value readable beside the row's Open button. The button and an editable value sat side by side as inline boxes, so a value long enough to fill the column overflowed the cell by the button's width, and the cell's ellipsis then replaced the whole value — its ref or leading content included — with a lone "…". The button and the value now share one flex row (
data-grid-cell-lead), the value takes the width that is left, and a long value ends in its own ellipsis. docs ·025f625 - A DataList
numericcolumn (a date, a count) keeps its whole width on a narrow list'slinemeta line, like an end-aligned ormonoone: the text values beside it truncate first, so "1w ago" never clips to "w ag". docs ·78b59e3
[0.25.94] — October 28, 2026
📦 npm
@vegastack/design→0.8.42(was0.8.41).- The design-system registry (
@vegastack/ui) bumps 0.25.93 → 0.25.94.
🐛 Fixed
- A long text whose first line ends in a hard break (Shift+Enter) no longer shows a trailing backslash at rest in a grid cell or a FieldProperty row:
typedFirstLinedrops the break's marker (\or two trailing spaces) when it takes the first line. docs ·61dc419
[0.25.93] — October 28, 2026
📦 npm
@vegastack/design→0.8.41(was0.8.40).- The design-system registry (
@vegastack/ui) bumps 0.25.92 → 0.25.93.
🐛 Fixed
- FieldProperty values sit where a property's value starts. A read-only number, currency, percent or calculated value no longer slides to the far end of the row:
FieldCellDisplaytakes a newalignprop ("start" | "end"; by default a number keeps the grid column's end alignment), and FieldProperty passesalign="start". An open editor now fills the value column instead of shrinking to the value, and its box hangs over the line as the resting value's does, so the row keeps its height entering and leaving edit and the text stays put. A long text shows its first line as typed — the editor's Markdown escapes (\#,1\.) undone and mentions by name — at rest, in a grid cell and in a property row alike; only the editor shows the source. The newtypedFirstLineandmentionLabelshelpers intext-edit-grammardo it. A date-time editor keeps its date and time on one line where they fit, and the time no longer clips "PM". docs ·3b7eea8 - MarkdownView's find tint is a plain hoisted style instead of Tailwind
::highlight()variants, so a consuming app's Tailwind build no longer warns "'highlight' is not recognized as a valid pseudo-element" (Turbopack's "1 Issue" badge). The matches paint exactly as before. docs ·3b7eea8 - An embedded DataGrid inside a TextEdit page (a live table block) keeps its pinned column pinned while it scrolls sideways. The editor's column-resize rule set
position: relativeon everytd/thbeneath it, which beat the frozen cells'sticky. It now reaches only the editor's own table cells, as does itstable-fixed. The prose recipe's table rules (borders, padding, header ground) also leave alone anything under[data-prose="off"], which DataGrid's root now carries, so a grid in a page's prose draws exactly as it does on a table page: its pinned header stays opaque and its cells keep their own padding. The read view's pinned column already stayed put. docs ·b75815b
[0.25.92] — October 27, 2026
🧩 New components
- FieldProperty (in
field-cell): one field as an editable property row inside a PropertyList, for a row peek, a row page, or a customer's or project's custom fields. The name sits on the left. The value sits on the right, drawn exactly as the grid draws it (FieldCellDisplay), with no box at rest and a quiet wash on hover. A click, Enter or a typed character opens the grid cell's own editor (FieldCellEditor) in place: text, numbers, URLs, emails and phones inline, long text growing in place, choices on the in-cell option list, dates on the calendar, and a link on the record picker. A checkbox toggles where it is. A value that is itself a link (URL, email, phone, record chips) stays followable, and an Edit pencil beside it opens the editor. Files show and never edit here. Read-only types,readOnlyanddisablednever edit and show no hover.focusMode="managed"witheditingandonEditingChangelets a host's roving focus own reachability and open the editor.onSavereturns a promise: the value shows at once, a spinner follows after 300ms, and a rejection rolls back with a Retry toast, as EditableCell does.conflictshows "Someone changed this field." under the value. An empty value reads "Empty" (emptyText). On a phone the value goes full width under its label. FieldCellDisplay takes alabelthat names a checkbox. docs ·1fc6977
🔧 Changed components
- TextEdit takes
inset="none"so it can sit flush with a ghost Input or Textarea title above it. Inside a Dialog, Sheet, Drawer, Popover or Card whose padding is narrower than the block grip needs, TextEdit adds start padding (about 10px in a Dialog) so the ⋮⋮ grip sits before the text. That pushed a New task dialog's "Add a description…" right of its title. Withinset="none"the text, placeholder and caret start at the editor's own edge, and the block grip is not shown. The block menu still opens from ⌘/ or ⇧F10, and on touch from a long-press (implied, whatevertouchBlockMenusays); ⌘⇧↑ and ⌘⇧↓ still move blocks, and tables keep their grips. The default,inset="auto", is unchanged. docs ·507578d
🛠 CLI & tooling
vegastack-design check-updatesand the registry verification behindaddretry transient registry failures before reporting the registry unreachable. A network error ("fetch failed"), a response cut off mid-read ("terminated"), a 5xx or a 429 gets up to three tries, with jittered exponential backoff, a 15-second timeout per try andRetry-Afterhonoured (seconds or a date, up to 5 seconds). Item fetches still run at most eight at a time. A 404, a 401/403 or an integrity mismatch is still an answer and fails at once. This stops--fail-on-updatefrom failing CI on a passing network flake. docs ·103f084
📦 npm
@vegastack/design→0.8.40(was0.8.39).- The design-system registry (
@vegastack/ui) bumps 0.25.91 → 0.25.92.
🐛 Fixed
- ViewTabs stays as tabs while they fit. It used to switch to its narrow
Selectbelow a fixed@3xlcontainer width, so in a FilterBarleadingslot a table page's two short tabs ("Grid", "Board") collapsed to "Grid ▾", with the+floating far right, once the facet row opened. Now it measures its tab row against the room it has: it collapses only when the tabs don't fit, and goes back to tabs as soon as they do. The collapsed select keeps its+and ⋯ beside it. The root is no longer a size container. DataGrid shows a quiet trailing edge shade while more columns sit past the end of a sideways-scrolling viewport (anembeddedblock on a phone, for one), and the viewport usesoverscroll-x-contain, so a sideways pan stops at the grid instead of moving the page. docs ·c140a1d
[0.25.91] — October 27, 2026
🔧 Changed components
- CommentThread, CommentItem: Resolve moves from the thread header to the first comment's action row — a ✓ hover action before add-reaction, shown on hover or focus and always on a touch screen — through
CommentItem's newonResolve. The header now shows only when it has something to say: the quote, "Original text was removed", or "Resolved by …" with Reopen; a page-level thread (no quote) no longer opens with an empty band above the author line. docs ·4f98ad7 - FieldCellDisplay, FieldCellEditor, FieldInput, DataGrid: a choice value is drawn as a
Badgefilled in its option's hue — no outlined pill — in a cell, a phone card, the editor's list and a form trigger, through the new exportedFieldChoiceBadge(hue+ Badge's props), still two then "+N" with every name on hover and keyboard focus. A choice cell's editor no longer puts a field with a pill and a chevron inside the cell: the cell keeps its badges where they were, and the option list opens under it at once with the search field first, each option a badge with a check on the chosen ones and "Add “…”" whenonCreateOptionis given. A single choice commits on the pick; a multiple choice now toggles with the list open (it used to commit and close on the first pick) and commits on Tab, on Enter with no option highlighted, or when the list closes; Escape cancels.DataGridno longer shows the fill handle on a cell being edited. docs ·9d6ec9a - VersionCompare: the host can choose the side a phone shows —
defaultPhoneView("list"by default) or controlledphoneViewwithonPhoneViewChange, which reports"preview"on a pick and"list"from the back button. A "View changes" link to one checkpoint passesdefaultPhoneView="preview"withselectedIdandshowChanges, so a phone opens on that version's changes instead of the list. Nothing changes when none is passed. docs ·5497cf6
📦 npm
@vegastack/design→0.8.39(was0.8.38).- The design-system registry (
@vegastack/ui) bumps 0.25.90 → 0.25.91.
🐛 Fixed
-
Row states, empty values, phone board lanes and inline chips (Regent polish audit).
- DataList and DataGrid use one set of row states. Hover is a faint
muted/30 wash with no start edge. A ticked row keeps the strongermuted/50 wash. The open row (activeRowId) gets theaccentwash and the 2px start edge. The edge used to follow the mouse, because DataList's pointer-driven cursor drew it; now only the keyboard cursor and keyboard focus draw it. - DataGrid pinned (frozen) cells now take a ticked row's wash. They checked
data-selected="true", but rows setdata-selected="". The phone card view has the same fix. - DataGrid shows an empty primary value as a muted "Untitled" in the primary cell and in a card's title. The new
untitledLabelprop overrides the word. Cards leave out fields that have no value, label included. - Board on a phone with lane tabs: each lane header drops the title and count that its tab already shows, and keeps only its actions.
- InlineChip (mentions, file and record chips) never wraps. Its icon and label stay on one line, and a chip wider than its line ends in an ellipsis.
- DataList and DataGrid can show a sort set somewhere else, such as a toolbar. Pass
sortwithoutonSortChangeand the headers are no longer sort buttons. The sorted column still shows a read-only ↑/↓ (with its position number in a multi-sort) and keepsaria-sort.SortableHeadalso draws the read-only glyph when it gets adirectionand noonSort. - Board card and lane menus and SortableList item menus use ⋯, the same as table rows. The app-shell header's actions menu keeps ⋮.
- SortableList takes
handle="fine": the drag grip ⠿ shows only on a mouse or pen. On a touch screen there is no grip, and a long-press still lifts the row or tile. The handle stays in the tab order and comes back into view while it has keyboard focus. The default,handle="always", is unchanged.1d94395
- DataList and DataGrid use one set of row states. Hover is a faint
-
FieldCellEditor: a choice cell's list puts focus in its search field as soon as the list is there, so the first keys typed after opening the editor search and choose instead of landing nowhere. docs ·
0d6a462 -
FilterBuilder, GroupByPicker: "Add group" no longer means two things.
FilterBuilder's nested-condition button reads "Add condition group" (newlabels.addGroup), andGroupByPicker's add button reads "Group by" while no level is set and "Add level" once one is (newlabelsprop,GroupByPickerLabels:groupBy,addLevel). docs ·0d6a462 -
Enter that confirms an input-method (IME) candidate no longer submits or commits early. The Enter that ends an IME composition can arrive with
isComposingalready false andkeyCode229, and Enter-to-submit only checkedisComposing. It now checks both, in TextareasubmitOnEnter, ChipInput, PickerPanel search, TaskInline, EditableCell,useInlineEdit, SearchInputonValueCommitted, DocumentLayout find, Transcript search and speaker rename, the AgentPanel reply and instructions and the DelegatePopover ⌘Enter send, TextEdit (⌘Enter submit, block exits, the bookmark field, find and replace), the editor suggestion menus (suggestionMenuKeyDown, used by TextEdit and TemplateEditor), BlockView's ⌘Enter, and the search-palette block's ⌘Enter. DataGrid, FieldCell and InlineChip already checked both.4f4da52
⚠️ Breaking
- ViewTabs: the
+menu offers just Grid view and Board view, each with its icon, instead of "Shared" and "Personal" groups of both.onCreateis now(kind) => void— it takes the kind alone; the host's own create step names the new view and decides who sees it (an "Only visible to me" switch).canCreateSharedand thecreateShared/createPersonallabels are removed. MigrateonCreate={(kind, scope) => …}toonCreate={(kind) => …}and dropcanCreateShared. The tab row's shared and personal grouping ("Mine") is unchanged. docs ·9d6ec9a
[0.25.90] — October 27, 2026
🧩 New components
- RecordPicker and RecordPickerDialog: pick one or several records of the kinds the app passes in — a search over the app's loader (debounced, abortable, paged), a kind filter, recents while the box is empty, results grouped by kind, rows refused with a reason;
valueholds records so earlier picks survive any search, Enter never picks a row from a superseded search, and the dialog starts fromvalueeach time it opens. DestinationPicker: choose where something goes — spaces at the top of a folders-onlyFolderTree, folders loaded as they open, recent places, a folder search, and destinations refused with a reason that is shown and announced. FieldTypePicker: a searchable, grouped list of field types with their icons; a type withnextopens a second step (renderStep(type, { back, setTitle })) under a back header. FolderTree picker mode: a node'sdisabledReasonkeeps the row visible and openable butaria-disabledwith the reason as its description. SearchableSelectrenderTrigger: your own trigger element (aRecordChip) over the same picker. SelectContent and ComboboxContent takeloading,errorandonRetry(API-38). ItemGroupvariant="flush"for rows in a card (API-37). Inputcredential={false}keeps password managers off a name field (FRM-17). TabsTrigger, Checkbox, RadioGroupItem and Switch reach a 44px hit area on a coarse pointer (A11Y-24 widened). PersonAvatar: an agent with a photo keeps a purple bot badge. docs ·f88c403 - RecentStrip: one row of the records a person opened last — up to
max(6) compact cards (icon tile · title · one meta line) in a sideways strip with edge fades, ◂ ▸ buttons on a mouse and one Tab stop with ←/→/Home/End; a single line of chips under 28rem of its width (a phone).collapsible(withopen/defaultOpen/onOpenChange),loading(same-shape skeleton),emptyState(otherwise an empty strip renders nothing),linkRenderfor the router's link andheadingRenderfor the heading level. SidebarMenuFlyout: a sidebar row whose entries open in a menu beside it on hover or press — Spaces in the collapsed icon rail;railOnlykeeps the expanded sidebar's inline group, andbadge/badgeLabelwork as onSidebarMenuButton. AppShellHeadermobileActions: the phone bar's own tools (Search, the Inbox with its count) beforeactions, below 768px only, with a 40px sidebar trigger beside them; AppShellHeaderMobileAction is one such 40px button with a count pill (count,countMax99,countLabel) or adot. AppShellFoldplace="sidebar" | "menu"folds the drawer's footer entries into the profile menu on a phone. AppShellSidebarrailSeparatorsrules a hairline between groups in the collapsed rail. All additive: without the new props the shell renders as before. docs ·ec6227e - DocumentLayout aside:
DocumentLayout aside="discussion" | "outline" | nullgives a page one non-modal rail after the text, andDocumentLayoutAsideis the rail — a heading with itscount, a × (onClose), andtabs({ value, label, count?, content }: Open · Resolved · Page, Review while a review is open), the panel scrolling inside a rail as tall as the window. Anyaside, evennull, keeps the end track from the L tier, so opening the rail never moves or narrows the text (inmeasure="wide"the text and an open aside share wide'smin(100%, 1200px)block, the text keeping its start edge). Below the L tier the discussion is a bottomSheet, the whole screen below 640px;kind="outline"pins the contents from the XL tier, where the contents rail's hover ticks step aside. An open discussion takes the margin's place. Withoutasidenothing changes. Type rename: the nativeasideprops type ofDocumentLayoutContents/DocumentLayoutMarginis nowDocumentLayoutColumnProps(DocumentLayoutAsidePropsis the new part's props).useDocumentLayoutTieralso accepts a ref to a part inside the layout. CommentMarginquote={false}hides each card's quote beside its highlight (default unchanged). VersionListgroupBy="day"groups the rows under a heading per day. VersionCompare (new): a document's history at full size — the selected version or its changes (DiffView) beside the day-grouped list on resizable panels, withactions(Restore this version, Copy) pinned; on a phone the list first and the preview pushed in. DialogDialogContent size="fullscreen": the viewport less a 16px inset (none on a phone), header and footer pinned, body scrolling (OVL-16 widened). docs ·4c4d25c - FieldTypeLinkStep (new): the "Links to" step of
FieldTypePicker, rendered from itsrenderStepfor the link type.kinds(FieldLinkKind[]—{ id, label, icon?, group?, description?, keywords?, disabledReason?, table? }) list in their groups (groupOrder), each with one line on what a link to it does; a kind markedtable(Rows) asks which table (tables,FieldLinkTable[];tablesLoading) in a third view and retitles the picker's step header throughcontrols.setTitle(tableTitle, "Which table's rows?"). The choice comes back throughonSelectas{ kind, table? }(FieldLinkChoice);valuechecks the current one. Backspace in an empty search goes up one view. DataGrid: withphoneLayout="cards", asummaryRowshows above the cards as one read-only line — itslabel, then "Column · Function result" for each summarised column, wrapping on a narrow screen (no change for grids without one). docs ·264ec0e
🔧 Changed components
- Board, DataList and SectionRow: a lane, section or hidden-lane count above 99 shows "99+" (the cap the notification bell and the Inbox already use), while its spoken label keeps the number the host passed (
countLabel,sectionCountLabel);displayCount(n)indata-table-partsformats it. DataList nests rows one level deep withgetRowParentId(a sub-task under its task, in its parent's section, stepped in), folded throughcollapsedParents/onCollapsedParentsChange, with the toggle left to the host through the cell context'snesting. docs ·be76d4a - AppShellProgress: a 2px indeterminate bar along the top of
AppShellContent, shown at once while a page loads.motion-reveal(design-tokens utility): a loading placeholder that waits--reveal-delay(150ms) before it fades in — a fast load never flashes a skeleton, and withAppShellProgressa slow one is never a blank page. docs ·774e258 - FilterBar:
leadingputs content before the search on the tools' row — a table'sViewTabs— taking the room the tools leave and scrolling sideways when squeezed; on a phone-width bar it keeps the first row with the tools and a search field moves below them.searchMode="icon"(default"field") shows the search as a Search button that expands into the field when pressed; the field stays while it holds a query, and an empty one folds back when focus leaves it or on Escape, with focus back on the button. On a phone-width window (below thetabletlayout tier) the View menu opens as a bottomSheetinstead of a popover. docs ·c482b50 - Textarea takes
variant("default" | "ghost") andsize("default" | "lg"), as Input does, andsubmitOnEnter(decision API-40).variant="ghost"is borderless and transparent, starts at one row and grows with its text;size="lg"is the heading size —<Textarea variant="ghost" size="lg" submitOnEnter />is a big title that wraps. WithsubmitOnEnter, Enter submits the form (requestSubmit) and Shift+Enter adds a line; IME composition is left alone. Existing textareas are unchanged. docs ·58a6452
📦 npm
@vegastack/design→0.8.38(was0.8.37).@vegastack/design-tokens→0.8.38(was0.8.35).- The design-system registry (
@vegastack/ui) bumps 0.25.89 → 0.25.90.
🐛 Fixed
- Calendar makes today easy to see (decision A11Y-25). Today used to be
bg-muted, the same grey as hover and the range middle, so it was almost invisible in the neutral theme, dark mode most of all. An unselected today now has no fill: a thinborder-foreground/40outline and medium weight. A selected today keeps the normal filled selection, and range start, end and middle look as before. Today's day button also carriesaria-current="date". Every date picker built on Calendar picks this up. docs ·33b06d4 - RecordAsideSection, SectionToggle, AgentPanel and ShareDialog no longer paint an expanded section's header grey. A ghost Button paints
aria-expanded:bg-mutedto say a menu or popover is open, but on a section that expands in place, expanded is the resting state, so every open section (the task page's "Subtasks 3" header, for example) looked pressed. The fix lives in a new lib,@vegastack/disclosure-trigger. Put itsdisclosureTriggerClasseson any ghost Button that toggles a section in place. It clears the fill while the pointer is elsewhere, and hover still shows. docs ·14fb0d4 - MakeTaskPopover fits the window: it is never taller than the room the window leaves (
--available-height), the lines scroll between the heading and the actions, and Create and Cancel stay in view however many lines there are (up to 50). Its title inputs stop attitleMaxLengthcharacters (new, default 500), so an over-long title can't be typed. docs ·ed5ecda - RecordPeek: the "2 of 3" position stays on one line and never shrinks, so it no longer wraps beside the arrows on a phone.
e0b3bd2
[0.25.89] — October 27, 2026
🧩 New components
- AgentPanel and DelegatePopover (new): agents as collaborators on a record.
AgentPanelis the agent's rail card (variant="card") or a peek's collapsible section (variant="section", open unless done or stopped, its header a one-line summary): the agent's bot, name and "Agent" label, "Acting for Priya · Read & write", a ⋯ menu (View agent · Change agent · Stop · Remove, each only when its handler is passed), and astateofnone,queued,working,question,approval,failed,blocked,doneorstoppedwith that state's line and actions (Stop; one-tap answers and Reply…; Approve/Decline; Retry/Remove; Reconnect; Mark task done; Delegate again), the live progress line with elapsed time, the queued hint, and a muted "Agent since disconnected" that never turns a finished card back into Needs you. Below: Instructions with ✎ edit in place, Latest (up to three messages and changes, Show all), a Reply box (Cmd/Ctrl+Enter, a disabled reason) and History (runs with View and Undo, Undo disabled with its reason). State changes are announced politely. The same file addsAgentSlot(the one-line Delegate property, its "Delegate to an agent…" ghost and its muted can't-delegate reason),AgentChip(the pill for a peek or phone),AgentStateBadge(icon and word),AgentStateRing(amber while it needs you, red when it failed) andAgentHoverCard(acting for, access, state, View agent).DelegatePopoveris the one Delegate popover for tasks and rows: search the viewer's agents (arrow keys and Enter; an agent that can't take it is disabled with its reason), optional instructions, a "When finished" choice for tasks, "Connect an agent ›" and Delegate — with a bulktitle, the no-agents empty state and loading rows. Every string comes in throughlabels, with English defaults. docs ·bd32fd1 - BlockHeader (new): the one header row of a live block inside a page — a table view, a task list.
BlockHeaderlays outicon·title· its controls (children:BlockHeaderButtons, small quiet buttons to use as therenderof the host's View ▾ and Filter triggers) · a ⋯ menu (menuitems,menuLabel) ·open(aBlockHeaderOpen: ↗ Open, a real link viahrefor a router link asrender, with alabel). Before the block has a source,pickertakes the title's place ("Pick a table…") so a block inserts at once and is configured where it sits. On a narrow block the controls wrap under the title and Open folds to its icon. docs ·bd32fd1 - CommandSearch (new): the search palette, results first.
CommandSearch(aCommandDialog+ non-filteringCommand) withpendingandpreview;CommandSearchInputruns a thin progress bar under the input whilepending;CommandSearchScopeskeeps the scope chips on one line — what does not fit (or pastmaxVisible) goes into aMore ▾menu, with "Include archived" (includeArchived,onIncludeArchivedChange);CommandSearchResultscaps the results atmin(60dvh, 520px), dims and marks them busy whilepending, and showspreviewbeside them from 1280px;CommandSearchSectionsrenders Recent · Create · Places in that order. Pair withuseAsyncSearch, which drops out-of-order answers. Thecommand-search-01block now builds on it (its palette is renamedSearchPalette, and itssearchcall gets{ includeArchived }). docs ·bd32fd1 - TextEdit ergonomics and BlockToolbar (new).
BlockToolbar(aria-label,reveal: "always" | "hover",placement: "corner" | "inline"),BlockToolbarButton(label+Buttonprops, 28px, tooltip) andBlockToolbarMenu(label, items) are the one place a block's controls sit — top-right, four at most, the rest in ⋯; the code block (TextEdit andCodeBlock), the image toolbar (Replace, Caption, Alt text, Open; Download and Copy link now in ⋯) and the diagram header use it, all at 28px.TextEditgainsbubbleActions((context: TextEditBubbleContext) => ReactNode),toolbar("bubble" | "dock"— a 44px bottom bar on a coarse pointer) andtouchBlockMenu("handle" | "longPress"— no grip on touch, a long press opens the block menu sheet); the bubble also offers Make task(s) for selected checklist lines withonMakeTask, and stays inside the text column (wrapping onto a second row when narrower).onImageUpload/onFileUploadgetonProgress(TextEditUploadOptions): a bar on the image placeholder, a ring on the file chip. ⌘↵ or ↓ on the last line leaves a code block or a diagram's source for the line below, with a footer hint; a new Diagram starts with one comment and draws "Nothing to draw yet" (MermaidBlockemptyMessage). The block grip keeps its 16×20 glyph with a 24×28 hit area, and the find bar floats over the text instead of pushing it down. Breaking:codeBlockActionsClassNameis removed (useBlockToolbar). docs ·bd32fd1 - FileViewer carries a file's discussion:
rail={{ details, comments, activity }}adds a column beside the stage with a tab per slot given (a bottom sheet below 768px), opened by a header Details toggle —railOpen/defaultRailOpen/onRailOpenChange,railTab/defaultRailTab/onRailTabChange,railLabels.header={{ share, watch, menu }}puts Share and Watch before Download and the ⋯ menu after it. Every slot is a node or a function of the open file.mode="page"renders the same viewer in place as a file's page (no dialog or Close, the page's theme, the name as itsh1).onOpenChangeis now optional (needed only for the overlay). Existing overlay usage is unchanged. docs ·bd32fd1 - FormulaInput (new): a calculated field's formula on one line.
fields(FormulaInputField[]—{ key, label, type? }) are offered when you type{and inserted as chips;*and/read as × and ÷ (operators); the value is text with{{key}}tokens. Under the input,previewshows the first row's result ("First row = 1,240",previewLabel) anderrortakes its place, marks the input invalid and is announced politely;invalidFieldsmarks a chip invalid. Built on TemplateEditor, which gainstriggers("@" | "{{" | "{", default["@", "{{"]) andpickerLabel(default "Insert placeholder"). docs ·bd32fd1 - HelpMenu (new): the sidebar-footer Help item — one menu with What's new, Docs, Keyboard shortcuts and Contact support, in that order, and the build version at its foot (
version). Each entry inlinksis a link (href, rendered throughlinkRender,externalfor a new tab) or an action (onSelect); omit one to hide it.whatsNewUnreadputs the unread dot on What's new and on the trigger and adds "(new updates)" to both names. In the collapsed rail it is the help icon with a tooltip, the dot on the icon. docs ·bd32fd1 - SuggestionMenu and TextEdit: roomy slash and
@menus, and phone sheets.SuggestionMenugainssize("default" | "lg"—lgis 320px wide, up tomin(60vh, 440px)tall, with the description as a second line under the label and a 28px icon box) andpresentation("popover" | "auto"—autoopens a bottomSheetwith its own search field on a coarse pointer or a window under 640px, driven byquery,onQueryChange,onCloseandsearchPlaceholder);useSuggestionMenuSheet(presentation)says when it does, and an option'sdescriptionShown: "active"shows its description on the highlighted row only.TextEditgainsmenuSizeandmenuPresentationfor its slash and mention menus: onlgeach built-in block says what it is ("Big section heading") and the full slash menu is grouped Basic · the host's items · Media · Advanced, withTextEditSlashAction.groupnaming a host item's heading ("Insert" by default).TextEditMentions.sections(MentionSection[]—"people" | "agents" | "records") fixes the@menu's order: People, Agents (with the agent avatar), then each record kind, the nearest match first; a person's email shows only on the highlighted row unless two people share the name (MentionOption.descriptionShownoverrides it). Every new prop is opt-in: without them the menus are unchanged. docs ·bd32fd1 - RouteTabs (new): tabs whose panels are pages — a named
nav(label) of links drawn as the pill tabs, one URL per tab, the current one fromcurrentHref(or a tab'salsopaths) witharia-current="page", counts, andoverflow="scroll"keeping the current tab in view. A clicked tab is current at once with a pending bar untilcurrentHrefmoves (or drive it withpendingHref/onPendingHrefChange); links render throughlinkRender. RouteTabsPanel dims and isaria-busywhile a page loads (pending, or from a RouteTabsProvider shared with the tabs). docs ·bd32fd1 - ShortcutOverlay takes page-scoped
groups={[{ id, label, active, items }]}as an alternative toshortcuts: groups withactive: true(this page) come first, groups withoutactive(General, Go to) follow, andactive: falsegroups (other pages) appear only with the new All shortcuts switch (allShortcutsLabel), which resets at every opening. A filter query searches every group. The line under the title is nowdescription(default unchanged).shortcutsis optional; existing callers are unchanged. docs ·bd32fd1 - StatGroup in
stat: a hairline-divided row of inline stats (items={[{ label, value, href }]}), one row from 24rem of its own width and a 2×2 grid below. A cell with anhrefis a link throughlinkRender;value: undefinedis loading (a skeleton,aria-busy),nulla failed read ("—" anderrorLabel);hideWhenAllZerorenders nothing when every value is 0. docs ·bd32fd1 - TaskInline (new) and Bookmark cards: one box per task, Make task in place, and a link shown as a card.
TaskInline(title,done,struck,status,assignee,due,overdue,checkbox,onToggle,onOpen,href, label props) draws a task in running text — one round box, the title that opens it (a peek), the assignee's avatar, a due chip and ↗ on hover — andMakeTaskPopover(open,onOpenChange,anchor,lines,onCreate,assignee,due,spaceslots, labels) makes tasks from checklist lines in a popover anchored to the line instead of a modal.TextEditandMarkdownViewgainonTaskOpen: a linked checklist line draws as aTaskInline(its own box, round, is the task's; the chip's task glyph — the "second box" — is gone), withmentionStatus(andInlineChipStatethrough the provider) answeringassignee,dueandoverdue; Alt+Enter on the line opens it.onMakeTasknow also receives{ anchor }(the first line's element), and an unlinked checklist line under the pointer shows → Task.TextEditgainsbookmarks: Bookmark in the slash menu and Link · Bookmark in the paste popover, a card with the page's title, blurb, icon and site from theInlineChipProvider'slinkPreview(which gains optionalsiteName,descriptionandimage), stored as[url](url "bookmark")on its own line;MarkdownViewdraws it asMarkdownBookmark(also usable directly withprevieworfetchPreview). New grammar helpersBOOKMARK_TITLE,bookmarkMarkdownandbookmarkOfParagraph. Every new behaviour is opt-in except that a bookmark line already in a document renders as a card. docs ·bd32fd1
🔧 Changed components
- DataGrid, Board, BoardCard and DataList: table blocks in pages and calmer lanes and rows — every change is opt-in except where noted. DataGrid
embeddedframes the grid as one block in a page (thetoolbar— aBlockHeader— on top,footerunder the rows): its height comes from its rows, at mostmaxRows(25 by default when embedded; the first rows in display order), it scrolls sideways in the fixed layout with no column cap, its column picker is off unlesscolumnPickerasks, andvirtualize/maxHeightdo not apply.footeralso takes aDataGridBlockFooterobject —{ onNewRow, newRowLabel, shown, total, countLabel }— for "+ New row" and "25 of 117".readOnlyturns off every write path (cell editors, paste, clear, cut, fill, undo keys,headerTrailing, + New row) while rows still open, sort and select. BoardemptyLane="compact"draws an empty lane as its header over a 48px drop target with "+";emptyLane="collapse"moves empty lanes into the Hidden columns list (choose one to bring it back; drop a card on it there; a lane a move empties stays).noValueLane="last"moves the lane markednoValue(a newBoardColumnfield) to the end and lets it collapse on any board.loading(withloadingLanes, 4) shows lane shells only — one skeleton per load. Always on: a lane loading its first cards shows no count (never "0" over skeletons), and the edge fades carry a soft scroll shadow. BoardCardChipemptykeeps a no-value chip off the card at rest (shown on card hover and focus, while its menu is open, and on touch). DataListdensity="compact"is now one line per row: merged values join the primary value's line, a plain primary value clamps to one line and a thumbnail steps down to 24px. A columnrender's context carriesdensity,useDataListDensity()reads it deeper down, andDataListSecondarywraps a custom cell's second line so compact leaves it out. docs ·bd32fd1 - Toggle (and every ToggleGroup item): a pressed toggle is now filled with
bg-primary/10instead of upstream'sbg-muted— which was also its hover fill, so pressed read as hover — deepening tobg-primary/15on hover, with the outline variant's pressed border atborder-primary/30(decision API-39). Unpressed toggles are unchanged. docs ·bd32fd1
📦 npm
@vegastack/design→0.8.37(was0.8.36).- The design-system registry (
@vegastack/ui) bumps 0.25.88 → 0.25.89.
📚 Docs
- Shorter roster summaries for RecordPeek, RecordHeader and useRecordPeekRow, and the
llms.txtindex tripwire raised to 128 KiB (MK, 27-10-2026); no package change.9267e6c
[0.25.88] — October 27, 2026
🧩 New components
- RecordPeek: a record opened beside its list — a non-modal side panel, resizable by its start edge (480px to 90% of the window, remembered with
storageKey), whose header walks the list (↑/↓ and J/K, "n of N" fromposition, disabled at the ends), opens the full page (⌘↵ / Ctrl+↵ throughfullLink,fullHreforonOpenFull), holds the record'sactionsand closes with Esc, returning focus to the row (returnFocus). Full-screen below the tablet tier. useRecordPeekRow:data-peek-activeon the open row of a hand-rolled list, scrolled into view as the peek walks (DataList,DataGridandBoardtakeactiveRowId). RecordHeader: one head for every record on its page and in a peek — eyebrow (where it lives) and key chip, icon and title renamed in place, status, meta line, chips, presence and actions — withRecordHeaderSkeleton. RecordAsideSection:collapsible(the title folds the section; an empty one is its header alone),open/defaultOpen/onOpenChange,noticeandheadingLevel; its count is read as a separate word. docs ·caccebb
📦 npm
@vegastack/design→0.8.36(was0.8.35).- The design-system registry (
@vegastack/ui) bumps 0.25.87 → 0.25.88.
[0.25.87] — October 27, 2026
🧩 New components
- MemberList (new): one list for every "people in X" — a team, a space, a share list, an invite list.
MemberListAddis the add row (apickerslot · an optional role fromroles· Add, stacking below 28rem);MemberListrows are 36px — avatar · name over a muteddescriptionsub line ("2 people", "Creator") · the role as aPermissionMenuchip (or plain text withoutroles, or read-only withoutcanManage) · a ⋯ menu with Remove (onRemove) and the host'smenuItems.lockedrows show the role with a lock andlockedReasonand no ⋯, while the ⋯ column keeps its width so the chips line up; pastmaxRows(8) the list scrolls inside itself;loading,emptyand every string (labels) are props. docs ·6b445b6 - SettingsLayout (new): the two-pane Settings frame.
SettingsLayout nav={…}puts aSettingsNavin a 220px column that sticks below the page's top edge and the page in one column of up to 960px;SettingsLayoutContent kind="form"caps a page at 720px andkind="list"fills the column, both on the same left edge. Below 42rem of its own width it shows one pane at a time:view="nav"makes the nav the screen (44px rows with a chevron),view="content"shows the page with a "‹ Settings" link tobackHref(backLabel).SettingsNav sections={[{ label, items: [{ href, label, badge }] }]}marks the current page fromactiveHref(a nested path marks its section) orisActive, witharia-current="page"; links render throughlinkRender(your router'sLink). docs ·6b445b6
🔧 Changed components
- Layout tiers: one width vocabulary for every surface —
phonebelow 40rem,tablet40–64rem,laptop64–90rem,widefrom 90rem — as Tailwind container sizes and breakpoints (@max-tablet/app-shell-content:,max-tablet:) inutilities.cssand asLAYOUT_TIERS,layoutTierOf,layoutTierQuery,RAIL_COLLAPSED_QUERYandpageGutterOfinpage-layout. AppShell: the page gutter steps to 48px from 1920px;AppShellSection measure="default"holds a list at a default page's content width, centred, inside afullframe (width follows the content, not the route);AppShellHeaderActionsmoves a page's top-bar actions into one ⋮ menu by the bar's own width and drops labels to icons below 40rem; an open sidebar folds to its rail when the window narrows into the tablet span and reopens on the way out (collapseRail). DocumentLayout: the text stays centred when the comment margin shows (the start track mirrors the margin while there is room) its tiers start at the layout tiers (L from 64rem), andmeasure="wide"stops at 75rem (1200px). Sidebar:SidebarStateScriptstarts an icon rail collapsed on a tablet by default (LAY-13 amended;collapsedWhen={null}opts out), and a collapsed icon rail keeps a menu count in sight at the icon's corner — a pill up to two characters, a dot beyond (A11Y-17 widened). Sheet: below the tablet tier a side sheet drops its side border and pads the safe areas (OVL-22). docs ·c7a1a33 - Comments, Activity Feed: quieter record history.
CommentThreadtakesdensity="compact": no reply row at rest — Reply is a hover action on the first comment (on focus too, always on a touch screen;CommentItem's newreplyAction) that opens the row with the caret in it, and the row folds away once its reply posts or is left empty. A trailing lone "@" (a mention started and left) is dropped as a comment, reply or edit posts, in every density.ActivityFeed's heading count now says what it counts —countLabel, "2 comments" by default.ActivityEventprops takechange(ActivityChange—{ field, verb?, from?, to }), so anActivityEventGrouprun that changes one field reads as one chain ("moved it Todo → In progress → In review");summarizeAtfolds a longer run of different changes into "made 3 changes" (changesLabel, or the host'ssummarizeRun), a disclosure listing them.groupActivityRuns(items, { actorKey, date, isBreak?, breakBefore?, runWindowMs? })splits a chronological list into those runs — one actor, within 10 minutes of the run's first event by default, broken by comments and the host's own boundaries. docs ·e052ff2 - SettingsRow
controlSize(sm160px ·md256px ·lg320px ·auto): every row's control shares one measure and one right edge once the row is side by side; the slot's first child fills it and a trailing status icon keeps its size. Left unset, a row holding a field control (input, input group, textarea, select, combobox, date or people picker) takesmd, and a switch, button or read-only value keeps its own width — so a width class on a control inside a row (w-64,@sm/settings-row:w-72) is no longer needed and is overridden; delete it. New SettingsMatrix (columns,rows: [{ label, description, values, onChange(column, checked), disabled }],label): settings × channels with the column names once in a sticky header and each switch named by its row and column; below 28rem the switches stack under their label with the column name beside each. docs ·6b445b6 - ShareDialog is built on
MemberList: the invite row is aMemberListAddand People with access are 36pxMemberListrows (avatar · name over its reason · level chip), with Remove access moved from the level menu into each row's ⋯ menu (More for {name}; newlabels.removeAccessandlabels.moreFor). The "Who can see this" summary (audience) is now the first thing on the Share tab, above the invite row. The props are unchanged. docs ·6b445b6
🛠 CLI & tooling
vegastack-design doctornow warns where a project works around a DS part instead of using it, each asfile:line: a Tailwind!modifier in theclassNameof a part imported from theuialias; aw-*,min-w-*ormax-w-*class onPopoverContent,DropdownMenuContent,ContextMenuContent,MenubarContent,SelectContent,ComboboxContentorHoverCardContent; a hand-writtenheaderelement insideSheetContent,DrawerContent,DialogContentorAlertDialogContentin the same file; and anAppShellPagein an App Router page or layout whosesizediffers from the route map (definePageWidthsrows plus anywidths: { … }object feeding it). They are warnings by default;--strictmakes them failures. Newvegastack-design owned-fileslists the registry-owned files installed in a project (index targets resolved throughcomponents.jsonaliases, plus header-carrying files) as plain lines or--json, so lint ignores, knip and drift checks can read one generated list. docs ·bd4f239
📦 npm
@vegastack/design-tokens→0.8.35(was0.8.17).@vegastack/design→0.8.35(was0.8.34).- The design-system registry (
@vegastack/ui) bumps 0.25.86 → 0.25.87.
🐛 Fixed
- AutoSaveIndicator no longer shows "Saved" beside a field's own error:
invalidreplaces the tick (and the spinner) with a visible "Not saved" in the warning ink, in both variants (labels.invalid). NewuseInvalidRevert({ invalid, onRevert, delay = 1000 })returnsonBlur/onFocusfor the field: leaving it while invalid puts the last saved value back a second later throughonRevert, which shows the host's message; returning to the field or fixing the value cancels it.AutoSaveInputreads a valuevalidaterejects as "Not saved" rather than "Save failed", andonInvalidRevert(savedValue)(withrevertDelay) turns on the revert. docs ·6b445b6
⚠️ Breaking
- Inbox: one row per record, tabs, and J / K triage.
InboxItemgainsactors(stacked 20px avatars, three then "+n", names read out),events(InboxItemEvent[]—{ id?, actor?, text, time? }— listed under the row behind a disclosure),status("pending" | "answered") withansweredLabel(the line that replaces Allow / Decline once answered),value(the row's identity for triage) andmenuLabel;countnow reads after the actors, on its own line undermeta, throughcountLabel("9 updates") and becomes the events disclosure, instead of a pill after the title.InboxFiltersis now tabs —tabs?: InboxTab[], For you · Updates · Archived by default (INBOX_TABS) — plus a separate Unread toggle (unreadOnly,onUnreadOnlyChange,unreadCount,unreadLabel);value/onValueChangetake the tab's value.InboxgainsactiveId,onActiveChangeandonOpen: J / K (and ↓ / ↑) walk the rows with focus, the active row gets a start bar andaria-currentand scrolls into view, and Enter or a plain click callsonOpenwithout navigating (a modified click still openshref). Breaking:InboxFilters'extraprop and its built-in All / Unread chips are gone (passtabs, and the Unread toggle's props), and theInboxFilterandInboxFilterOptiontypes are removed (usestringandInboxTab). Thenotifications-01block follows. docs ·e052ff2
[0.25.86] — October 27, 2026
🧩 New components
- QrCode (new): a QR code for a
value, drawn as one SVG path (shape-rendering="crispEdges") — dark modules on a light, rounded tile in both themes so it always scans, with the standard 4-module quiet zone inside the drawing (quietZoneto change it).errorCorrectiontakesL,M(default),QorH; size it with asize-*class (defaultsize-40). The root isrole="img"named by the requiredlabel. Server-safe: the matrix is encoded during render byuqr(zero dependencies, MIT), a new sanctioned encoder dependency imported only byqr-code.tsx. An empty or unencodable value draws the empty tile (data-state="invalid") instead of throwing. docs ·913f987
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.85 → 0.25.86.
[0.25.85] — October 27, 2026
🔧 Changed components
- FilterBar: grid slots for a spreadsheet-like list with saved views.
filterBuildermakes the Filters toggle open aFilterBuilderin a popover (the appliedfilterschips stay in the chip row whenever there are any);sort(withsortLabel, default "Sort", andsortCount) adds a Sort button that opens aSortBuilder;group,fields,densityandviewActionsshare one View menu (viewMenuLabel, default "View") with labelled sections in the order Group, Fields, Row height, then the view's own actions (groupLabel,fieldsLabel,densityLabel). The three controls sit after the Filters toggle and go icon-only below@3xl, keeping their words as accessible names. A saved view's "View changed · Save · Reset" goes intrailing, in the chip row. docs ·c0169da - DataGrid, DataList, Board and the shared table parts: one way to open a record from a list, and a grid that fills its box. DataGrid's fixed layout is never narrower than its viewport: a filler track takes what the columns leave, so the header band, row borders, washes, summary row and footer reach the end, and the "+" column stays beside the last real column.
onRowOpen(rowId, { event, index })adds a 24×24 Open button at the start of each row's primary cell (shown on hover and keyboard focus, always on touch), with Space on a cell and Enter on the row header opening it (F2 still edits).activeRowIdmarks and scrolls to the open row (data-peek-active), andactionsRefexposesfocusCell(rowId, key)andgetRowOrder(). The active cell is a 2px insetinfooutline for pointer and keyboard alike, and the rest of a range takes a blue tint that never reads as hover. Empty cells are blank: the placeholder shows only while editing. A header with aheaderMenuis one button that opens the menu (sort lives there), columns take anicon, and anumericcolumn aligns its values and header to the end. The empty or error state is held to the viewport,loadingRowssizes the skeleton,selectionActionsdocks the same selection bar asDataList, andphoneLayout="cards"shows cards on a container under 40rem (cardFields, default 3). In cells mode, dragging a range never selects text. DataList addsonRowOpenfor the whole row: a link row stays a real link for ⌘ and middle clicks, while a plain click opens the row in place. It also addsactiveRowId,actionsRef.getRowOrder(), andhiddenSections/onShowSection/onHideSectionwith a section'shideablefor its board. Rows follow one density scale (comfortable 40px, compact 32px, the grid'smediumandshort). Board addsactiveItemIdand a built-in "Hidden columns" list (hiddenColumns,onShowColumn,onHideColumn, a column'shideable).onCardOpenopens every card in place, so a link card's plain click (or Enter) opens it while modified and middle clicks still follow the link.onCardActivatekeeps its link-free meaning and now receives the event. Row and card ⋯ menus mount on demand. Shared parts: a squeezed list clamps atruncateelement to one line instead of wrapping it over its neighbours. New exports:SelectionActionBar,SelectionBarSpacer,RowOpenDetails,PEEK_ACTIVE_ROW_CLASS,LIST_ROW_HEIGHT_PXandLIST_ROW_HEIGHT_CLASS.EmptyRowtakesviewportWidth, andEditableCell/EditableCellResttakeemptyText. docs ·fb85832
📦 npm
@vegastack/design→0.8.34(was0.8.33).- The design-system registry (
@vegastack/ui) bumps 0.25.84 → 0.25.85.
[0.25.84] — October 26, 2026
📦 npm
@vegastack/design→0.8.33(was0.8.32).- The design-system registry (
@vegastack/ui) bumps 0.25.83 → 0.25.84.
🐛 Fixed
- FieldCellEditor: Enter, F2 or a click on a long-text cell that holds a value opens the editor with the caret at the end of the text, nothing selected, so writing goes on from it; typing on the cell still replaces the value with the typed key. TextEdit: with a query, the
@menu's kind groups sit by their best match on the name — the whole name, then its start, then a word's start, then anywhere — and only then inorder, so "@Arc" lists the customer named "Arcadia Architects" ahead of tasks that only mention it; each group keeps its results in the order the search returns them. docs ·3805287
[0.25.83] — October 26, 2026
📦 npm
@vegastack/design→0.8.32(was0.8.31).- The design-system registry (
@vegastack/ui) bumps 0.25.82 → 0.25.83.
🐛 Fixed
- DataGrid: a horizontal scroll tick at 150 columns now stays under 8 ms of main-thread time. A cell whose column has a
customeditor rests asEditableCellRest— the same button, name, description, box and activation asEditableCell's display, in one element with no state or effects — and becomes the fullEditableCellonce it edits or the host reports a save status (and stays one while mounted, so a save in flight keeps its optimistic value). A header's menu mounts its machinery once the header is pointed at, focused or opened. The row window overscans two rows. The observer that keeps cell content out of the tab order looks only at what each change added instead of searching the whole table. docs ·9862b44
[0.25.82] — October 26, 2026
📦 npm
@vegastack/design→0.8.31(was0.8.30).- The design-system registry (
@vegastack/ui) bumps 0.25.81 → 0.25.82.
🐛 Fixed
- DataGrid: a scroll tick at 150 columns costs about a quarter to a third less main-thread time. A body cell is no longer
position: relativeunless it holds the remote-change cue or the fill handle (each positioned cell was a paint layer of its own, so paint and layerize were most of a tick), andEditableCell's display is positioned only while it edits. The row window overscans three rows and the column window one column. The frozen edge'sdata-scrolled-xis set once a frame from the offset the column virtualiser already read, so no grid scroll handler forces a layout. Header and summary cells are memoised behind stable actions, so a column-window move renders the entering column alone, and a remote change's cue (and its timer) mounts only on the cell it marks. A managedEditableCell's text leaf that measures on contact keeps its element in a ref, so a newly mounted cell renders once instead of twice. docs ·5c5d4e0
[0.25.81] — October 26, 2026
🔧 Changed components
- field-cell, version-list: a long-text field takes
@mentions.FieldConfiggainsmentions,mentionHrefandmentionImage, passed to the long-textTextEditin bothFieldInput(a row panel) andFieldCellEditor's popover (a grid cell), where choosing in the mention menu keeps the popover open.VersionListgainsheading(default "Version history"), so a host can call the list "History". docs ·e472b76
📦 npm
@vegastack/design→0.8.30(was0.8.29).- The design-system registry (
@vegastack/ui) bumps 0.25.80 → 0.25.81.
[0.25.80] — October 26, 2026
📦 npm
@vegastack/design→0.8.29(was0.8.28).- The design-system registry (
@vegastack/ui) bumps 0.25.79 → 0.25.80.
🐛 Fixed
- data-grid, editable-cell, field-cell, inline-chip, filter-bar-managed, text-edit: the grid's final acceptance fixes.
GroupByPickerandSortBuilderno longer crash when every field is already used (a compiled memo readfree[0].keywhile rendering); Add stays disabled. Typing on a long-text cell opens its editor holding every typed key in place of the value (a#or-is the character, not Markdown), and typing on a date cell opens a field that takes a typed date ("12/3", "Mar 12", "today") seeded with the key. Links and chips inside grid cells leave the tab order, so Tab leaves the grid from any cell: Alt+Enter (or Enter on a read-only cell) moves into its first chip, arrows step, Escape returns; a key or click on a chip inside an editable value is the chip's (it follows the link), never an edit.TextEditHandle.focustakes an optional"start"or"end", and focuses the read view while the editor loads so early keys are kept. docs ·8de8175
[0.25.79] — October 26, 2026
📦 npm
@vegastack/design→0.8.28(was0.8.27).- The design-system registry (
@vegastack/ui) bumps 0.25.78 → 0.25.79.
🐛 Fixed
- DataGrid: scrolling a wide grid costs much less per frame, and a fast fling through clipped values no longer stops the grid with "Maximum update depth exceeded". A cell the grid mounts no longer measures its own overflow:
TruncatedText,IconTextand a managedEditableCellunder a grid or list host measure when the pointer reaches them (the full text still opens on hover), and every otheruseOverflowshares oneResizeObserverthat reads sizes after layout instead of forcing a layout on mount (measureOverflowis the one-shot read).useMediaQuerykeeps onematchMedialist and listener per query, whichTruncatedTextnow uses for(hover: none). InsideDataGrid, anEditableCellspeaks through the grid's one live region (AnnouncerScope, joined withuseAnnouncer({ scoped: true })) instead of arole="status"region per cell, a row's selection checkbox no longer re-renders when the column window moves, andEditableCellruns only the mode half of the edit machine (useInlineEditMode). docs ·9fdae8d
[0.25.78] — October 26, 2026
📦 npm
@vegastack/design→0.8.27(was0.8.26).- The design-system registry (
@vegastack/ui) bumps 0.25.77 → 0.25.78.
🐛 Fixed
- field-cell: a link cell's chips no longer break inside a name. A chip's first word may wrap in running text, and inside a host's prose (
overflow-wrap: break-word, an editor's embedded table) a narrow column split its letters ("Merid / ian"). Each chip inFieldCellDisplaynow stays on one line, cut with an ellipsis and its full name on hover; a host that lets the cell grow (className="flex-wrap") wraps whole chips onto new lines. docs ·4e13ad8
[0.25.77] — October 25, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.76 → 0.25.77.
🐛 Fixed
- DataGrid: Tab back into the grid returns to the cell that was active, even after its row or column was windowed out by scrolling (it scrolls back in), instead of landing on whichever cell stood in as the tab stop. The grid is its own stacking context, so its sticky summary row no longer covers an
ActionBarand its buttons can be pressed. FieldCellEditor: opened from the keyboard, a choice or link picker focuses a usable control (never the picker's hidden form input, where keys went nowhere), a choice, person, link or date picker opens its list or calendar with the editor, and long text's editor takes the caret when opened with Enter too. DataList: a press on a clickable grid card's text opens its row; only the card's own controls (its checkbox, a menu, aninteractivecolumn) keep a press to themselves.67a3c27
[0.25.76] — October 25, 2026
📦 npm
@vegastack/design→0.8.26(was0.8.25).- The design-system registry (
@vegastack/ui) bumps 0.25.75 → 0.25.76.
🐛 Fixed
- text-edit-grammar:
escapeMentionLabel(and somentionMarkdownand every mentionTextEditwrites) now escapes a backtick in a mention's label too. A bare backtick in one label could pair with one in another label, or in the text, into a code span, which Markdown reads before links, so the mentions it crossed rendered as code.parseMentionLinkreads the escaped label back unchanged. Image alt text keeps its own escaping. docs ·9c91680
[0.25.75] — October 25, 2026
📦 npm
@vegastack/design→0.8.25(was0.8.24).- The design-system registry (
@vegastack/ui) bumps 0.25.74 → 0.25.75.
🐛 Fixed
- EditableCell, DataGrid, FieldCellEditor: a
customeditor now receives theseed(the key that opened it by type-to-edit, which was lost) andcommit(next, move?), wheremove(down,up,right,left— the newEditableCellMovetype) steps the grid on throughonNavigate.FieldCellEditorfollows the spreadsheet keys — Enter commits and moves down, Shift+Enter up, Tab right, Shift+Tab left (after a date-time's own controls), Escape cancels, and IME composition never commits — throughonCommit(value, move), and starts fromseed: it replaces a text, URL, email, phone or number value and opens a choice, person or link picker searching for it (the newdefaultSearchonSearchableSelect,PeoplePickerandFieldInput). ViewTabs: a cut view name shows in full in a tooltip on its tab and the narrow select's trigger, and the select's list wraps it. docs ·41328dd
[0.25.74] — October 24, 2026
📦 npm
@vegastack/design→0.8.24(was0.8.23).- The design-system registry (
@vegastack/ui) bumps 0.25.73 → 0.25.74.
⚠️ Breaking
- MarkdownView, TextEdit, DocumentLayout: live blocks of any kind show their stored snapshot generically.
liveBlockKindsnow takes the host's kinds asLiveBlockKindobjects ({ name, rowKind, label, empty? }, defaultDEFAULT_LIVE_BLOCK_KINDS, the live task list) instead of bare names, inMarkdownView,TextEdit,markdownToDocandtextEditExtensions;MarkdownTaskListbecomesMarkdownLiveBlock(the kind's heading, its rows' chips, a box only on rows that have one) andparseTaskListRowsbecomesparseLiveBlockRows(snapshot, rowKind), whose rows carrychecked: nullwhen they have no box. A live block's rows may be plain list items (- [@Row](mention://row/<id>)). NewDocumentLayoutBlockTableparts (Head,Body,Row,Column,Cell): a quiet read-only table for a block of a table's records, its name column first, keyboard-reachable while it scrolls sideways. docs ·0305052
[0.25.73] — October 24, 2026
🔧 Changed components
- DataGrid: an
editablecolumn with arenderkeeps it as the cell's display while not editing (aFieldCellDisplay's tags, chips or avatars), instead of the value as plain text; and acustomeditor'srendernow receives therowwithvalue,commitandcancel(the newDataGridEditor<T>type). docs ·143cb17
📦 npm
@vegastack/design→0.8.23(was0.8.22).- The design-system registry (
@vegastack/ui) bumps 0.25.72 → 0.25.73.
[0.25.72] — October 24, 2026
🧩 New components
- FieldCell (new, Regent R5): one display and one editor per field type —
FieldCellDisplay(text, long text, number, currency, percent, date, date-time, single and multiple choice as coloured tags with "+N", checkbox that toggles in place, person, link asInlineChips in their four states, file as kind tiles, URL / email / phone as links out, calculated with its missing inputs, auto-number, created/updated;fillingandrecalculatingstates),FieldCellEditor(forEditableCell'scustomslot; commits a typed value, Enter / Escape, pickers commit on choosing) andFieldInput(the same editors as form controls:NumberFieldfor numbers so a comma-decimal locale reads "1,5" as 1.5,DatePickerand a time field,SearchableSelectwithonCreateOption,PeoplePicker, an async record search, checked URL / email / phone fields, theTextEditeditor for long text). Locale and time zone come from the reader. docs ·17550e2
📦 npm
@vegastack/design→0.8.22(was0.8.21).- The design-system registry (
@vegastack/ui) bumps 0.25.71 → 0.25.72.
[0.25.71] — October 24, 2026
🧩 New components
- Views and filters (Regent R4). ViewTabs (new): a table's saved views as a line tab row — shared views, a divider, then the reader's own under "Mine"; a lock on a locked view;
onCreate(kind, scope)adds a + menu for a grid or board view, shared or personal;getViewActions(view)fills the open view's ⋯ menu; below@3xlof its own container the row is oneSelectwith the same groups. filter-bar-managed:SortBuilder(up to three{ key, dir }keys onSortableList, read-only chips) andGroupByPicker(up to two levels); value editorsDateValueEditor(exact day, Today, This week, Last N days, and{ from, to }for a range),PersonValueEditor(onPeoplePicker, with Me asFILTER_ME, several for a list operator),CheckboxValueEditorandLinkValueEditor(async record search through the field'ssearch, stores{ id, label });FilterFieldtakespeopleandsearch; aneditorskey<type>:rangeor<type>:listwins for that type alone;describeFilterand the chips read the new values. docs ·737a604
📦 npm
@vegastack/design→0.8.21(was0.8.20).- The design-system registry (
@vegastack/ui) bumps 0.25.70 → 0.25.71.
[0.25.70] — October 24, 2026
🧩 New components
- DataGrid cells mode (Regent R3):
selectionMode="cells"adds spreadsheet ranges kept by row id and column key — Shift+arrows, Shift+click and drag extend; Shift+Space, Ctrl+Space and ⌘/Ctrl+A take a row, a column and everything; copy, cut and paste through the clipboard events as TSV and HTML; Delete clears; a paste, clear or cut is oneonCellsCommit(changes)batch answered per cell (CellChange,CellCommitResult) and announced ("Pasted 120 cells · 4 skipped"); rows past the last loaded row go toonPasteOverflow; the fill handle and ⌘/Ctrl+D callonFill({ source, target, series }); undo and redo keys reachonUndo/onRedo;remoteChanges(keyed bygridCellKey) pulses a cell withmotion-flash, shows its author for 1.5s and announces it only on the focused cell. Columns gainparse(cell, row)andformat(row). useGridSelection (new hook): the range model. grid-clipboard (new lib):parseTsv,serializeTsv,parseHtmlTable,serializeHtmlTable,parseClipboard(Google Sheets' compact-table typed values, Excel'sx:num) andexpandPaste, tested against the recorded G3 clipboards. docs ·f7f9449
📦 npm
@vegastack/design→0.8.20(was0.8.19).- The design-system registry (
@vegastack/ui) bumps 0.25.69 → 0.25.70.
[0.25.69] — October 24, 2026
🧩 New components
- DataGrid grid core (Regent R2): the grid now hosts a 50,000-row, 150-column, server-sorted grid. DataGrid:
sortMode="manual"rendersdatain the host's order and only signals sorts; a FIXED layout (any columnwidth,frozenorresizable, oronColumnResize/virtualizeColumns) sizes columns from a<colgroup>, keeps every cell to one line and scrolls sideways; a leading run offrozencolumns sticks to the inline start with a scroll-edge shadow;onColumnResizeadds the header resize handle (drag, double-click to fit, Alt+arrows);onColumnReordermakes the scrolling headers draggable;virtualizeColumnswindows the columns;rowHeight(short/medium/tall) fixes row heights; clientgroupcolumns and the new hostsections(each with its ownloadMore) are flattened into the one row virtualiser, so grouping andvirtualizecombine;summaryRowadds a sticky footer of per-column functions (summaryOptions) with host-computed values;cellMenuand columnheaderMenuopen from a right click or Shift+F10;headerTrailing(a render function handedDataGridFocusProps) adds a control after the last column on the header row's roving path;pagedModeturns row windowing off and shows aCursorPager;ariaRowCount,ariaColCountandariaRowIndexOffsetkeep the ARIA totals true. Focus is kept by row id and column key and roves over the header, group, body and summary rows (one tab stop); primary cells arerowheaders; PageUp/PageDown, ⌘/Ctrl+arrows, type-to-edit and Enter/Tab commit-and-move are new, and an IME composition never moves or commits. The grid renders its own<table>in one scroll viewport (data-slot="grid-viewport", formerlydata-grid-scroll). CursorPager (new): Previous / Next for keyset paging, with no page numbers or total. data-table-parts:ColumnResizeHandle,columnResizeKeyWidth,GridSummaryCell(withleadingandonOpenChange);useRowSelectionmemoises its all/some scan;SortHeaderButtonandSectionToggletaketabIndex(andSectionTogglearef);SelectAllHeadandSelectionCelltakecontrolTabIndex/controlRef. EditableCell:editSeedstarts a host-opened text or number edit from the typed key, andonNavigatereceives which key moved ("tab" | "enter"); useInlineEdit takes the matchingseed. docs ·1273a5a
📦 npm
@vegastack/design→0.8.19(was0.8.18).- The design-system registry (
@vegastack/ui) bumps 0.25.68 → 0.25.69.
[0.25.68] — October 23, 2026
🔧 Changed components
- Open references (Regent R1): the design system no longer lists an app's record kinds. text-edit-grammar: a mention kind is any
[a-z][a-z0-9_]{1,31}(MENTION_KIND_PATTERN,isMentionKind);MentionKindis"user" | "agent"(BUILT_IN_MENTION_KINDS) or any host kind, andMENTION_LINK_SOURCE/MENTION_LINK_SQLare built from the pattern, both with groups 1 label, 2 kind, 3 id (MENTION_KINDSis gone). Live blocks are:::<name>{…}…:::for the names a host reads:readLiveBlock(source, { names })andliveBlockMarkdown({ kind, … })replacereadTasksDirective/tasksDirectiveMarkdown, and:::tasksreads and writes byte for byte as before; text-edit-markdown'smarkdownToDocandnormalizeMarkdowntakeliveBlockKinds. TextEdit:mentions.kindsisMentionKindConfig[]({ id, noun, plural, icon?, order }: the group heading, its icon, its order); aMentionOption.group("Recent") shows under its own heading, first;liveBlockKindsandrenderLiveBlock(kind, props)read and draw any live block, stored as thetaskQuerynode with a newkindattribute (nullfortasks, so a task list's Markdown, HTML and shared state are unchanged;renderTaskQuerystill drawstasks). MarkdownView: the sameliveBlockKindsandrenderLiveBlock; a mention of any well-formed kind renders as a chip. InlineChip:InlineChipResolver.kindsnames each host kind's icon and noun; an unnamed kind shows a link glyph and "Item";doneshows the check on any kind (INLINE_CHIP_KINDSandINLINE_CHIP_KIND_LABELare gone). date-time:formatCalendarDate(day, { locale, format }),parseCalendarDayandtoCalendarDay— a stored day never shifts with the reader's zone; EditableCell's date editor uses them. SearchableSelect:onCreateOption(query)adds an "Add “query”" row while no option matches exactly, and selects the option it resolves with (createLabel). docs ·453a08c
📦 npm
@vegastack/design→0.8.18(was0.8.17).- The design-system registry (
@vegastack/ui) bumps 0.25.67 → 0.25.68.
[0.25.67] — October 23, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.66 → 0.25.67.
🐛 Fixed
- TextEdit: a table column's width is its own (Notion) — the first resize, and every column added (160px), stores each column's width, so widening or adding a column no longer squeezes the others and a wide table scrolls inside its box. A document ending in a table, audio, video or image keeps room below it: clicking there, ↓ from the table's last row, or ↓ / → on the selected block adds an empty paragraph to type in. A
variant="composer"comment box no longer shows a scrollbar after a mention (the chip's hit area overflowed its last line). (Regent operator, 23-10-2026.)70a2787
[0.25.66] — October 21, 2026
🧩 New components
- DocumentLayout sections, pages and live blocks:
DocumentLayoutSection(named byDocumentLayoutSectionTitle, withcount),DocumentLayoutSectionHeader,DocumentLayoutSectionButton(more·add),DocumentLayoutSectionSkeleton;DocumentLayoutPageList,DocumentLayoutPageItemandDocumentLayoutPageLink(icon, name, excerpt, meta);DocumentLayoutBlockwith its header, title, rows (DocumentLayoutBlockRow,…RowMain,…RowRef,…RowMeta), note, skeleton, footer andDocumentLayoutBlockAction.DocumentLayoutDecisionBarkeeps 24px between itself and the text it leads. TableOfContents: a scroller that overflows by only 2px no longer marks its last heading at its top. DataList / DataGrid: a column'snumericsets its body values in tabular figures.db39dee
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.65 → 0.25.66.
📚 Docs
- DocumentLayout's
marginguidance: show the comment margin only while it holds threads or a draft, decided with the text, so a page without threads is centred (Regent operator, 21-10-2026).d896e40
[0.25.65] — October 21, 2026
🧩 New components
- EntryList (new): a short, compact list of plain entries in a sheet, dialog or popover —
EntryList,Entry(row·stack),EntryMeta,EntryActions. Sheet:SheetSection,SheetSectionHeader,SheetSectionTitle,SheetSectionLabel,SheetSectionDescription. Popover:PopoverContent size(default·md·lg·fit),PopoverBody(scrolls from 70% of the window),PopoverAnchor(an invisible trigger to point an opened-from-elsewhere popover), andPopoverDescriptionkeeps line breaks and inks astrong. Skeleton:SkeletonText(text·rows). Attachment:AttachmentSkeletons, andAttachmentDescriptionsets numbers in tabular figures. VersionList:headingHidden. Comments:CommentThreadSkeleton. DocumentLayout:DocumentLayoutDecisionBarwith its text and actions. Badge: a badge rendered as a button or link shows the pointer.8044584
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.64 → 0.25.65.
[0.25.64] — October 21, 2026
🔧 Changed components
- InlineChip:
InlineChipResolver.state(kind, id)gives every chip without state of its own its record's state — a done task's check, a canceled or duplicate task struck through with its status — so oneInlineChipProvidershows a record alike in an editor, a rendered body and a comment. A surface's ownmentionStatusstill wins.TextEditandMarkdownViewpass no state when theirs answers none.aeb74b1 - DocumentLayout: page tools without app styling —
DocumentLayoutFindBar(a docked find field with status, Previous, Next, Close and optional match highlights),DocumentLayoutJumpCue,DocumentLayoutFloatingButton,DocumentLayoutStarters/DocumentLayoutStarter,DocumentLayoutNote, andfocusableonDocumentLayout. AppShell:AppShellHeaderSlot(a fixed 32px place in the top bar) andAppShellHeaderShow(narrow·wide, by the bar's own width). AutoSaveIndicator:compact="narrow"shows the icon alone in a narrow bar. CommentComposer:hint, a quiet line under the box.c959fa9
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.63 → 0.25.64.
[0.25.63] — October 21, 2026
🧩 New components
- TextEdit turns a lone pasted link into what it names, as Notion and Google Docs do. The URL lands as text, then becomes a mention of the app's own record (the new
mentions.fromUrl), a YouTube or Loom embed (where it can stand in for an empty paragraph), or a link. With InlineChip's newlinkPreviewresolver, a link's text is the page's title when it answers within 1.5 seconds (link and title land as one step), and the link card shows the page's icon and title. A small paste popover offers the other choice (Mention · Link, Embed · Link) until the next keystroke, click or Escape; Tab moves into it. Each step is its own undo step, so ⌘Z right after a paste leaves the URL as text, in shared documents too. Pasting over a selection, in code or with ⌘⇧V never converts. The "Embedded" toast and its Undo are gone: the popover and ⌘Z replace them. Escape with a chip's card open (a click on a person shows it) closes only the card, and the caret stays beside the chip, so typing goes on there instead of being dropped. InlineChip's card closes when a key is typed anywhere, so a person's card never stays over a comment box (or the text) being typed in.a6f4201
🔧 Changed components
- ShareDialog: the publish hint ("Anyone with the link can view, without signing in", "Off — only people with access can open it") wraps instead of being cut off on a narrow sheet; checked at 320 and 375px, published or not, on both tabs. Empty: a long unbroken word in
EmptyTitleorEmptyDescription(a quoted search) breaks inside the empty state instead of running past it (LAY-17). MermaidBlock: the error's Details toggle is a full 24px target.bfce471
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.62 → 0.25.63.
[0.25.62] — October 21, 2026
🔧 Changed components
- Type ramp: the copy tier (
text-xs,text-sm,text-base) declares its letter-spacing as 0, so copy-size text inside heading type — a character count under a title, a badge or a meta line in a heading — no longer inherits the heading's negative tracking. Outside heading type nothing changes (body text was already 0); across every preview fixture, no other element's tracking moves.454506c - @vegastack/design: released beside
@vegastack/design-tokens0.8.17 (the copy tier's zero tracking), so the preset's tokens range names that release.4e6a569 - DocumentLayout: meta lines under the title.
DocumentLayoutMeta(a dotted line whose wrapped lines never start with a·;variant="status"reserves one line fromsmand two on a phone and fits its chips — full words, then short forms longest first, then wrapping), withDocumentLayoutMetaItem(icon,yields),DocumentLayoutMetaGroup(unit·flow),DocumentLayoutMetaField,DocumentLayoutMetaBadge(label·short),DocumentLayoutMetaWords,DocumentLayoutMetaButton,DocumentLayoutMetaLinkandDocumentLayoutMetaSkeleton.DocumentLayoutContentsButtonis the floating "On this page" trigger below the M tier, andDocumentLayoutTitletakeseditable(the text cursor over the row). An Add icon text button sits flush with the title.fd223e2 - TextEdit: stepping through Find matches no longer pops the selection toolbar over the find bar; a regular expression that doesn't compile says "Invalid pattern" (the field marked invalid) instead of "No results"; a pasted Notion callout whose icon is a lone emoji keeps no stray emoji line, and the emoji picks the tone (💡 a tip, ⚠ a warning, ❗ important, 🚫 caution, any other a note); inserting an embed, image, video or audio from its panel puts the caret on a line after it, so typing never replaces the block. ShareDialog: at 320px its rows wrap inside the sheet instead of running past its edge (the panels' grid track may shrink below its widest line).
a4dfb45 - InlineChip: a person is the one coloured chip (the tag blue); every record — page, file, task, meeting, customer, project, product, family and agent — wears the same neutral page chip and is told apart by its icon alone. Done, closed and restricted states are unchanged. A new
familykind (a product family, thelayersicon) joinsINLINE_CHIP_KINDSand the Markdown grammar'sMENTION_KINDS. TextEdit's@menu opens on placeholder rows in its own row geometry instead of "Searching…", at the height its answer usually takes, and holds that height while a search is in flight, so rows never shift under the caret; the opening query is asked at once, later keystrokes after 120ms, and answers are kept per query so a query asked before shows instantly. Its row icons are neutral, as the chips are, and it offers "Families". SuggestionMenu shows placeholder rows whileloadingwith nothing to show yet.9911375 - ShareDialog: Reset link asks first ("Reset the public link?"), as Stop publishing does, since the current link stops working at once.
publicLink.expiresAt(the instant the link lapses) shows under Expires as "Until Oct 22 · 2:30 PM" in the viewer's zone, so a link opened again later says when it ends, not only the preset that was chosen.30a4535 - Sidebar:
SidebarStateScripttakescollapsedWhen, a media query. With nosidebar_statecookie yet, a window it matches starts the panel collapsed before the first paint, and the cookie records it. A width-dependent default no longer has to collapse the panel after hydration, which slid it shut and shifted the page. A cookie (a person's own choice) always wins.30391d7
📦 npm
@vegastack/design-tokens→0.8.17(was0.8.12).@vegastack/design→0.8.17(was0.8.16).- The design-system registry (
@vegastack/ui) bumps 0.25.61 → 0.25.62.
🐛 Fixed
- TextEdit: a heading's
#link stays on its own heading. While it shows it follows the heading wherever the page moves it (content arriving above the editor, a resize), instead of staying at the coordinates it was drawn at — where it could sit beside a meta line or a paragraph that had moved into the heading's place. It hides while the page scrolls and returns a beat after, and a collaborator's edit drops a hovered position until the pointer moves again.0289f34 - DocumentLayout: a
DocumentLayoutMetaline prints (a public page's "Approved version · date" belongs on paper; hide a line with your own print rules where it doesn't), andDocumentLayoutMetaItemtakesnowrap: a fact built around aDocumentLayoutMetaLinkkeeps its words on one line ("From meeting" and its date never wrap beside a long meeting name) while the link truncates. Plain facts still wrap.ad344d8 - TextEdit: a key acts at the caret the reader sees. ProseMirror read a caret the browser had just moved (a click, an arrow key) only on
selectionchange, which the browser queues behind input already waiting, so Enter, Backspace or a shortcut pressed right after a click acted at the selection from before. In a shared page opened a moment ago, that was the document's start: Enter split the first line and what followed was typed into it, and it was saved that way. Every key now reads a pending caret first. NewTextEditHandle.clearHistory()for a shared document: the host calls it when it replaced the text under the reader (a version restored), so ⌘Z doesn't undo the reader's earlier changes onto the new text and splice the old text into it letter by letter.b5a4f3b
[0.25.61] — October 21, 2026
🔧 Changed components
- DocumentLayout: a fluid text column on five tiers by the layout's own width — S < 40rem (full width), M 40–60rem (max(36rem, 75%)), L 60–90rem (clamp(40rem, 75%, 52rem)), XL 90–120rem (60rem), 2XL ≥ 120rem (64rem) — replacing the 47ch measure; Wide is the whole width less the gutters and the margin. The contents rail moves to the gutter before the text (
TableOfContents side="start") and stacks above the text and the comment margin, so its open panel takes the pointer over both. A comment margin (20rem, 24rem from XL) keeps the text's width and shifts the centred block to the start; the text gives way only when text, margin and gutters can't all fit.DocumentLayoutTieradds2xl. NewDocumentLayoutTitleGroup,DocumentLayoutIcon(revealfor an Add icon control) andDocumentLayoutTitle(30/36, one step above the text's#). TableOfContents: the rail's panel sits in a frame that bridges the gap to the rail, with 300ms of grace before it closes, and marks the reached heading with a bar. CommentComposer:size="sm"pads the card 12/8 for a comment margin or popover, where the composer is the card.82def75
📦 npm
@vegastack/design→0.8.16(was0.8.15).- The design-system registry (
@vegastack/ui) bumps 0.25.60 → 0.25.61.
[0.25.60] — October 21, 2026
🔧 Changed components
- TextEdit: choosing a type in the block menu's Turn into closes the menu, and a menu whose block left the page closes with it, so the block grip never disappears for the rest of the page. ⌘/ (Ctrl+/) opens the block menu for the caret's block, as ⇧F10 does. ⌘K and the image, video, audio and file panels refuse text that isn't a link, inline: they take a web address, a bare domain,
mailto:/tel:(links only) or a path, never saving "https://not a url". Replacing an image, by upload, import or link, keeps its alt text and caption. Choosing an insert panel's Link tab puts the caret in its field. FileViewer: a small image opens at its own size, never upscaled. MermaidBlock: a diagram that can't be drawn says "Couldn't draw this diagram — check line 2." with the parser's words behind Details.a32b576 - TextEdit pads a table's columns only up to 200 characters, so one very long cell no longer pads every row and a table's Markdown grows only with its content (ordinary tables are written exactly as before); writing a code span's edge spaces never changes another character the document holds; repeated headings never share an id. MarkdownView turns heading ids on when only
headingLinkHrefis passed, and reads Markdown with the same bounded rules as the editor, so long runs of unmatched*,_,~, unclosed links or unclosed toggles render in linear time; an empty checklist item (- [x]) is a checked task, as in the editor; a code block's final newline is kept when shown, copied and anchored; HTML read mode renders the editor's videos, toggles, mentions and callouts; a comment highlight over a mention or link never hides it from assistive technology or puts it inside a button; find keeps its matches when highlights reshape the text, folds the query exactly as the text (ΟΣ,İ) and covers a whole character a match ends inside; and the highlight layout is reported again when a highlight moves without the view resizing.1d83aca
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.59 → 0.25.60.
[0.25.59] — October 21, 2026
🧩 New components
- EditableCell: the
texteditor takesmaxLength, the field'smaxlength. Typing stops at the limit; a paste keeps the characters that fit, and when the limit cut it short the count says "Only the first N characters fit" and the cell announces it; from 90% of the limit a quietn/maxcount shows under the field (its description), as wide as the field and laid over what follows, so nothing moves. What is typed or pasted in place can no longer run past what the store accepts and then be lost to a failed save.1dee53c
🔧 Changed components
- TextEdit: what you act on stays the thing you meant while others edit, and nothing you type, paste or upload is lost. The block menu, a block or table-row drag, the file picker and an image's Replace hold their block through a collaborator's edits — acting on it where it is now, never on the block that took its place (a table changed mid-drag moves nothing, with a toast). Replacing an image by Upload keeps the original until the upload lands, and a failed upload leaves it. Pasted images import each in its own place among the pasted blocks, every pasted
data:image uploads (past the import limit too), and a file name that is not valid percent-encoding no longer stops an import. Typing, Enter, Backspace, a paste or a drop while the editor loads all reach it;flush()waits for files handed over before it loaded; the handle'sfocus()follows editability. A read-only document can open Find, folds toggles for its reader without changing the document, offers Comment on a selection withonCreateAnnotation, shows heading links withheadingLinkHref, and renders an HTML body holding only a live task list. Find's Replace field shows what Replace all will insert, and Escape in the text closes Find. An upload whose placeholder is deleted stops at once, and an upload'ssignal.reasonsays why it aborted —TEXT_EDIT_UPLOAD_ABORT.removed(the placeholder was deleted) or.unmounted(the editor went away with it still in the text), so a host can keep the file as an attachment. ImageViewerScope: turned off, its images are plain pictures again.3b2ae3d
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.58 → 0.25.59.
[0.25.58] — October 21, 2026
🔧 Changed components
- MultiStepForm: a pinned (
sticky) actions row keeps a gap above its buttons in its own surface colour, so content scrolling under it stops short of the buttons instead of running into them, and while it is stuck over content a hairline marks its top edge. Resting at the end of the step, the row sits exactly where it did.81fc154 - Comments: a comment, reply or edit holds still while it sends (read-only, attaching paused), so text typed meanwhile is never lost, and the caret comes back if sending fails. Send and Cmd/Ctrl+Enter wait until files uploading into the text have landed, so a comment never posts without them. Escape keeps an unsent comment or reply; only editing a posted comment reverts. ShareDialog shows the Share tab whenever public links are unavailable, instead of an empty dialog. SortableList drops a touch drag exactly where the finger lifts. DataList grid cards tell their renderers when they are selected, never wrap an
interactivefirst column in their link, and the board keeps every row in its one lane whengetRowSectioncomes withoutsections. MermaidBlock never prints an older diagram's drawing in place of the current one, and a diagram not yet drawn prints its source instead of a placeholder. BlockView prints the source when Code is chosen without a split preview, and the expanded view fits its canvas inside the screen in right-to-left layouts. InlineChip passes keys toonKeyDownon linked chips too.53e2f3e
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.57 → 0.25.58.
[0.25.57] — October 20, 2026
🔧 Changed components
- DataList: a reorder seam is the row's only cue — while
data-drop-edgeshows, the row drops its hover, keyboard-cursor and selection washes, and the 2px line paints above the next row's cells, so a place between rows never reads as the "into this row" wash.b390bb3
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.56 → 0.25.57.
[0.25.56] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.55 → 0.25.56.
🐛 Fixed
- TextEdit, MarkdownView: an image stores its size (
;parseImageAltandimageMarkdownread and write the height), read from the file on upload and from the image on import, so the read view and the editor reserve its box before it loads; an image on a line of its own is afigureinMarkdownView, laid out asTextEdit's image block (16px apart at the document scale in both), a diagram's block view sits 16 from its neighbours in the read view as in the editor and its server fallback no longer takes thepremargins, and the content box keeps its blocks' margins (flow-root) — the swap from the read view into the editor moves nothing. A failed upload stays in the text as "Couldn't upload" with Retry and Remove (newonFailedUploadsChangecounts them) instead of vanishing. The link card truncates a long link in the middle (the full URL on hover and in its accessible name) and never overflows its card.05b58bc
[0.25.55] — October 20, 2026
🔧 Changed components
- DataList, SortableList: a
DataListlist row that takes drops (canDropOnRow) takes them in its middle half only — its top and bottom quarters never nest, so a drag aimed between two rows can't land inside one (a grid card still takes a drop anywhere);SortableListcolumns="cards"lays a grid onDataList's dense card grid, column for column and 12px apart, and a grid's gap no longer shrinks when a tile holds a small (xs/sm) control.03d02e5
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.54 → 0.25.55.
[0.25.54] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.53 → 0.25.54.
🐛 Fixed
- StatSkeleton: each bar sits in the line box of the text it stands in for (the label's 16px, the value's 20px or 32px), so a loading stat is exactly as tall as a loaded one — dashboard tiles no longer grow 8px when their counts arrive.
19da478
[0.25.53] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.52 → 0.25.53.
🐛 Fixed
- TextEdit, MarkdownView: an image's caption is its Markdown title (
) — the toolbar's Caption edits it,TextEditshows it under the image andMarkdownViewrenders afigurewith afigcaption(no tooltip); Notion paste maps its to-do lists to checklists and its callouts (figure.callout,aside, and Markdown's<aside>) to callouts; the editor hands Tiptap stableeditorProps, so inserting a callout or a code block no longer logs React's "flushSync was called from inside a lifecycle method" (and the view is no longer re-propped on every render).ac4dd66
[0.25.52] — October 20, 2026
🔧 Changed components
- DocumentLayout: with a comment margin, the text column moves as near the middle of the canvas as the room allows (fully centred from about 72rem, where the last release waited for 74rem), never squeezing its measure — at 1440 with a sidebar it now sits within 18px of the middle instead of a margin's width to the left.
8868fc1
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.51 → 0.25.52.
[0.25.51] — October 20, 2026
🔧 Changed components
- DocumentLayout: with room for a comment margin on both sides (74rem), the text column sits in the middle of the canvas and the margin hangs to its right, instead of the pair being centred with the text off to the left. ShareDialog: the Publish tab lays the link's settings out as one grid (Expires and the host's
publicLinkOptionsrows, label beside control), and while published its own footer says what stays private and holds Stop publishing, in the same box as the Share tab's footer. TextEdit: behind a modal Dialog or Sheet (the editor inert) the bubble toolbar and every floating part hide, so nothing floats over the backdrop; an editor inside a modal keeps its own floating parts live.3df6c23
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.50 → 0.25.51.
[0.25.50] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.49 → 0.25.50.
🐛 Fixed
- TextEdit and MarkdownView: section marks are a flat partition. A marked
#–###heading's bar now stops at the next#–###heading of any level, instead of running on through its sub-headings; a####and deeper stays a block of its section.sectionHeading(level)(exported frommarkdown-view) is the shared rule.87d445b
[0.25.49] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.48 → 0.25.49.
🐛 Fixed
- TextEdit, CodeBlock, EmojiPicker: the image panel's Link imports another site's image through
onExternalImage(it no longer inserts a hotlink the page can't show), and an image that can't load keeps a visible frame; the image toolbar is Replace, Alt text, Open, Download and Copy link (no ⋯ menu; Delete stays in the block menu and on Backspace), and the newimageDownloadHrefgives Download (toolbar and viewer) a link that saves the file; link and media panels open under the caret instead of over the text above; the caret in a link shows a link card (destination, Edit, Remove); the slash menu shows group headings (Text, Lists, Blocks, Media, Insert) and Divider sits with the blocks; the bubble toolbar puts Comment last; a toggle's chevron no longer looks pressed while open, and the toggle's own ⋯ menu is gone ("Open by default" is in the block menu); ⌘A in a code block selects its code first; code block controls are not printed; Enter in the emoji search picks the first result.9e9294e
[0.25.48] — October 20, 2026
🔧 Changed components
- DataList, ActionBar, PageHeader:
DataListonReorderRow/canReorderRowdrag list rows to a place between others — a drop on a row's top or bottom edge lands before or after it under a 2px drop-indicator seam (data-drop-edge), while a folder row's middle still takesonDropInto(useDragIntogainsreorder,onReorderandcanReorder);ActionBarendcloses the set aftersecondaryActions, so the selection bar's Clear no longer sits between the primary action and the secondary ones;PageHeader's actions wrap, end-aligned, instead of running off a phone screen.59132c2
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.47 → 0.25.48.
[0.25.47] — October 20, 2026
🧩 New components
- TableOfContents: the rail is a slim column of dashes (a button with
aria-expanded) that opens a floating panel of the headings on hover, keyboard focus or a tap —side="end"anchors it to a page's inline-end edge — and Escape closes it with focus back on the rail.open/onOpenChangeopen the sheet from a menu item, and an item'sblockIdlets the scroll spy find a shared document's headings. MarkdownView puts the#heading link in the gutter before the heading (after it on touch, shown on a tap) and copies with a toast, and drawssectionMarks(withonSectionMark) asTextEditdoes, inside a phone's gutter too. TextEdit's#heading link moves to the same gutter place, the block grip stepping out beside it. CommentComposercompactrests as one quiet "Add a comment…" line that opens the card on demand.f27cf47
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.46 → 0.25.47.
⚠️ Breaking
- DocumentLayout: the contents take no column.
DocumentLayoutContentsfloats at the layout's inline-end edge, in a 2.5rem gutter the grid keeps from the M tier whether or not a page has headings, so a contents list that arrives never moves the text.DocumentLayoutContentsRail,DocumentLayoutContentsListand thecontentsprop (onDocumentLayoutandDocumentLayoutSkeleton) are gone: put aTableOfContents variant="rail" side="end"inDocumentLayoutContents, and offer "On this page" from a menu below M.f27cf47
[0.25.46] — October 20, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.45 → 0.25.46.
🐛 Fixed
- InlineChip, MermaidBlock, SortableList: a task chip shows the task glyph and only a finished task its check (
done, set byTextEditandMarkdownViewfrommentionStatus's newdone); a chip's icon and its label's first word never part on a wrap; a mermaid diagram scales down to fit its column (never above its drawn size) and scrolls sideways only below 0.6 of that size;SortableList'sminTileWidthraises a tile grid's 8.5rem floor so wider cards sit one a row on a phone.bf6a775
[0.25.45] — October 20, 2026
🧩 New components
- DataList: on a tight
mergedLayout="line", a figure (an end-aligned or mono column: a size, a count, an id) keeps its whole width and the text values beside it truncate first — "Produ… · 1w ago · K Manoj… · 25 MB", never "25 M…".9c5f045
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.44 → 0.25.45.
[0.25.44] — October 19, 2026
🧩 New components
- DataList: a
mergedLayout="line"meta line stays ONE line in a squeezed phone row. The squeeze released every descendant of the fill cell, so a folder row's space, time, person and size each wrapped inside its own box ("Produc" / "t", "25" / "MB"). The line is now kept out of the release and contains its inline size, so it truncates and never widens the table.f47cb07
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.43 → 0.25.44.
🐛 Fixed
- TextEdit: node views follow the editor's editability. Checklist boxes, live task lists (
renderTaskQuery'seditableandsetQuery), callouts, toggles and code blocks redraw whensetEditableruns, so an editor that mounts read-only and turns editable (a collaborative page whose room becomes ready) no longer keeps disabled checkboxes and a read-only task list.ca55b16
[0.25.43] — October 19, 2026
🧩 New components
-
TextEdit, InlineChip, MarkdownView: linked task lines, Make task, closed-target chips, product mentions and section marks.
- Linked task lines:
linkedTaskId(node)returns the task id of a checklist line whose first inline is a task mention (- [x] [@Order glass](mention://task/t1)).onTaskItemToggle(taskId, checked)reports only this person's own ticks. It never reports a collaborator's tick, avaluechange orsetTaskItemChecked. - Make task: with
onMakeTask, the block menu of an unlinked checklist line offers Make task, or Make N tasks for the lines the selection reaches.onMakeTaskgives each line akey(itsblockId);handleRef.linkTaskItems([{ key, id, label }])turns each still-unchanged line into a leading task chip. Text after a matching plain-text title stays after the chip; otherwise the whole line stays, mentions included.handleRef.setTaskItemChecked(taskId, checked)ticks or unticks every line linked to a task that changed elsewhere. - Closed targets:
mentionStatus(kind, id)onTextEditandMarkdownViewstrikes a chip's label through and adds a small status tag ("Canceled"). The props behind it areInlineChip's newstruckandstatus. - Products: a new
productmention kind with the package icon on a magenta tint. The@menu has a Products group after Projects. - Section marks:
sectionMarks({ key, tone, label }, keyed by a heading'sblockId) draws a gutter dot and awarningorinfobar along each marked section. They are decorations only: nothing is written to the document and no text moves. Clicking the dot callsonSectionMark(key). renderTaskQuerysetQuery: in an editable editor the live task list getssetQuery(query), which replaces its filter as one local edit; read views get none.c1f2fd2
- Linked task lines:
-
ShareDialog:
publicLinkOptions— the host's own settings for a published link (what it shows, say), rendered in the Publish tab under the link and its expiry for a viewer who can publish. Additive; nothing changes without it.9612b82 -
TextEdit and MarkdownView: live task lists.
taskQueryblock: an atom{ blockId, query, snapshot }, selectable and draggable, and inBLOCK_ID_TYPES. Its Markdown is the:::tasks{id="…" …}container directive: the task mention rows (- [ ] [@Title](mention://task/<id>) — <status>), then:::. It round-trips byte for byte.idis itsblockId, the one block id Markdown carries. The other attributes are itsquery, unknown keys included. Lines that do not make a whole block stay text and a list.renderTaskQuery: a new TextEdit prop. It draws the list with the host's own component, in the editor and in the read view. Without it the stored snapshot shows as a quiet card.- MarkdownView: renders
:::tasksas that card (MarkdownTaskList: "Task list", task chips with their status labels, or "No matching tasks"). It is server-safe and takes the same optionalrenderTaskQuery. text-edit-grammar: addsreadTasksDirective,tasksDirectiveMarkdown,parseDirectiveAttrsandparseTaskListRows. docs ·5d1f4f3
🛠 CLI & tooling
- Test lanes:
geometry.cssreadshover:as plain:hover(@custom-variant hover). Headless Chromium in the Linux runner image reports(hover: hover)as false, so everyhover:style vanished in CI while desktop hosts applied it (ghost-chip-alignfailed in CI only).3b00157
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.42 → 0.25.43.
🐛 Fixed
-
MermaidBlock and TextEdit find:
- MermaidBlock prints from dark mode: each diagram is laid out in its own off-screen box with real layout, so a print stylesheet that hides the rest of the page no longer draws its labels at
translate(NaN, NaN)(a blank diagram on paper), and a drawing that couldn't be measured is never cached. A diagram first drawn while printing takes its colours from the design tokens, not the print-time CSS. - No
flushSync: the dark drawing's light copy is swapped in by CSS for print; React no longer warns "flushSync was called from inside a lifecycle method". - Wide diagrams: the diagram scrolls sideways in its own box with the table scroll cue (the overflowing edge fades) and is a named, focusable region ("Diagram, scrolls sideways") while it scrolls.
- Find bar: the find field gives way first, so the bar stays on one row in a 27rem text column.
1813d14
- MermaidBlock prints from dark mode: each diagram is laid out in its own off-screen box with real layout, so a print stylesheet that hides the rest of the page no longer draws its labels at
-
DataList
mergedLayout="line": every value sits on the line's centre. A person (a flex row of avatar and name) no longer drops to a second line under its dot, and a chip or a time with its 24px tooltip target lines up with the plain text and the dots. A"—"wrapped in a plain element (<span className="tabular-nums">—</span>) now drops with its dot, like a bare one.7e846d1
[0.25.42] — October 14, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.41 → 0.25.42.
🐛 Fixed
-
TextEdit:
- Find: ⌘F with words selected now searches for them and typing replaces the term. It used to append to it, and a whole selected block was taken as the term.
- Heading links: the
#copy link follows its heading when the text above it changes, instead of staying where the heading was. - MermaidBlock: printing from dark mode prints the light diagram (drawn while the page is idle), not dark shapes on white paper.
slashActions:onSelectalso getsinsertContent(json)to put a block at the caret.- Block views: a new Diagram (or any block view) holding its starter opens in Code, as documented, instead of Preview.
docs ·
09a3866
[0.25.41] — October 14, 2026
🔧 Changed components
- ChipInput: a refused entry (a duplicate, or one past
max) and an invalid entry now also show as one short line under the field, cleared by the next keystroke, so a sighted user sees why the typed text stayed put. The polite announcement is unchanged. The field now renders inside adata-slot="chip-input-root"column that takesclassName, so layout classes (flex-1,col-span-*, a min width) keep placing it;refand thedata-invalid/data-disabledstate stay on the box (data-slot="chip-input"). docs ·9798291
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.40 → 0.25.41.
[0.25.40] — October 14, 2026
🔧 Changed components
AgentEditBannershows the full "Undo this agent's changes" from a 24rem banner (was 28rem), so a banner in a desktop page column reads the long label; a phone-width banner keeps "Undo changes".0c31e06
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.39 → 0.25.40.
[0.25.39] — October 14, 2026
🧩 New components
-
MermaidBlock (new): a
mermaidfence drawn as a diagram in the browser —mermaid12.1.0 loaded lazily the first time a diagram nears the viewport, every render pinned to strict security, SVG labels and the dagre layout inside one queue, token colours for light and dark (the other scheme pre-drawn on idle, so theme switches and print are instant), an LRU of 50 SVGs, the measured height reserved on the next visit, and the last diagram that worked kept, dimmed, with arole="status"line when the source stops parsing. ExportsMermaidBlock,renderMermaid(source, { theme }),useMermaidSvg(source, { debounce })andmermaidBlockView. TextEdit and MarkdownView:blockViewsmaps a fence's language to a renderer —blockViews={{ mermaid: mermaidBlockView }}makes those fences Diagram blocks with Preview / Code (split beside a live preview on a block at least 720px wide), Expand (full screen, pan and pinch) and a ⋯ menu (Copy source, plus the view's own actions); the mode is per viewer and never saved; ⌘/Ctrl+Enter and Escape return to the preview; the slash menu and Turn into offer Diagram whileblockViews.mermaidis set. MarkdownView shows the source in the same frame on the server and before hydration. New shared internal:block-view. docs ·0f856e3 -
TextEdit editor tools (B.2):
- Block menu: a click on the ⋮⋮ handle, or ⇧F10 / the context-menu key, opens Turn into ▸ (Callout ▸ by tone, Toggle), Duplicate (⌘D), Copy link, Copy as Markdown, Move up / down, Open by default and Delete. On touch it is a bottom sheet, the handle follows the caret, and a drag needs a 350ms hold. Drags auto-scroll at the window's edge, and in a shared document a drop re-resolves the block by its Yjs position.
- Find and replace:
findReplaceadds a docked bar (⌘F, ⌘G, RE2 regex, Replace all as one undo step) andhandleRef.openFind(). - Emoji:
emojiadds a:menu. - Heading links:
headingLinkHrefadds a#copy link on the hovered or caret heading.scrollToHeadingnow takes<slug>~<blockId>, opens toggles and returns whether it found the heading. - Word count:
onStatsChange/getStats(), counted when idle. - Toggles:
toggleStatepersists each viewer's toggles. Find results, links, comments and the caret open the toggles around them for the moment. - Host slash items:
slashActionsadds host/items after the built-in ones, andinsertMentioninserts a mention chip. - Outline: items carry
blockId, and a shared document no longer rebuilds heading decorations on every change.
-
TextEdit: YouTube and Loom embeds, opt-in with
embeds={EMBED_PROVIDERS}(without it an embed shows as a link and a pasted link stays one) — a newembedblock ({ provider, url }, with ablockId) whose Markdown is the URL as written, alone on its line; a paragraph holding only a link to its own URL on an allowlisted host parses back to an embed, any other lone URL stays a link, and existing documents serialize unchanged. It draws a facade (16:9 card, provider glyph, "YouTube video" / "Loom video", Play) that requests nothing from the provider until Play, then a lazy, sandboxed frame whosesrcis rebuilt from the video id (youtube-nocookie.com,t=as?start=) with its own referrer policy; printed, it is the URL. A lone link pasted into an empty paragraph embeds, with an "Embedded" toast and Undo back to a link; the slash menu's Video link tab embeds YouTube and Loom links. NewonExternalImage(url, { signal }): images pasted or dropped from another site leave the paste, show an upload placeholder and land as your stored copy — or as a link with "Couldn't import that image. A link was added instead." — and pasteddata:images upload throughonImageUpload(20 a paste, 3 at a time). MarkdownView renders the same facade (MarkdownEmbed) with the sameembedsprop. New lib embed-providers:matchEmbed,EMBED_PROVIDERS,EmbedProviders,EMBED_FRAME_ORIGINS(theframe-srcallowlist) andEMBED_FRAME_ATTRIBUTES. Corpus casesextensions/embed-*,editor-json/embed-*. docs ·37b51ca
📦 npm
@vegastack/design→0.8.15(was0.8.14).- The design-system registry (
@vegastack/ui) bumps 0.25.38 → 0.25.39.
[0.25.38] — October 14, 2026
🧩 New components
-
AgentEditBanneris new, andVersionListwrites an agent author as everywhere else.AgentEditBanner(shadcn add @vegastack/agent-edit-banner) is the strip under a document's header after an agent changed it: the agent's purple bot, "Edited by Claude (via Manoj) · 3 changes · 2m ago", View changes, "Undo this agent's changes" and a ✕.status="undoing"spins Undo and disables both buttons;refusedadds "Couldn't undo only Claude's changes because people edited the page since." (or yourrefusedReason) and swaps Undo for Open versions;kind="created"reads "Created by Claude (via Manoj) · 5m ago". The callbacks passed decide which buttons show, so a read-only viewer sees the words and the ✕.moreAgentsends the line with "+1 more agent", a popover listing the rest, each with its own Undo. Under a 42rem container the buttons take their own row. It is a polite status region, the buttons are named "Undo Claude (via Manoj)'s changes" and "View Claude (via Manoj)'s changes", and every string can be replaced throughlabels.VersionList: an author withkind: "agent"reads "Claude via Manoj" with the purple "Agent" label after the bot, as in comments and activity. A row'snamecan be any title the host writes ("Before Claude (via Manoj)'s edits"), withsummaryas its second line ("Claude edited 3 blocks").9d1edc8
📦 npm
@vegastack/design→0.8.14(was0.8.13).- The design-system registry (
@vegastack/ui) bumps 0.25.37 → 0.25.38.
📚 Docs
- The shipped
vegastack-design-systemskill's component roster listsAgentEditBanner.9d1edc8
[0.25.37] — October 14, 2026
🧩 New components
- MarkdownView: comment on a selection in read mode —
onCreateAnnotation(anchor)shows a floating Comment button over a selection inside the view (TextEdit's read-only selection bubble;commentLabelrenames it) and hands over{ quote, prefix, suffix, start, end }computed over the same textannotationsresolve against, so the anchor passed straight back highlights exactly the selected words. Mentions and images count whole; whitespace, atoms or code alone show nothing. It flips and slides into view, hides while the selection is scrolled out, opens below the selection on a coarse pointer, closes on Escape, and Tab reaches it from a keyboard selection; the view stays server-safe. TextEdit, MarkdownView and SuggestionMenu:floatingSafeArea({ top, bottom }px) keeps every floating part — selection bubble, link panel, slash and mention menus — clear of a sticky header or a bottom bar; TextEdit's selection bubble opens below the selection on a coarse pointer. docs ·2eba8c1
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.36 → 0.25.37.
[0.25.36] — October 14, 2026
🔧 Changed components
- VersionList:
authormay benull— a version the system kept with no person behind it (a page's text from before live editing) names no one: no avatar or placeholder person, the time alone. docs ·de023eb
📦 npm
@vegastack/design→0.8.13(was0.8.12).- The design-system registry (
@vegastack/ui) bumps 0.25.35 → 0.25.36.
🐛 Fixed
- text-edit-markdown / TextEdit: a link (or any other span) followed by an image on the same line —
See [docs](/d) — no longer fails to parse ("Empty text nodes are not allowed"); the image is lifted to its own block as anywhere else. Corpus caseregressions/link-then-image. docs ·61d4d55 - VersionList: a system-kept row's time (no author before it) starts its line capitalized — "Today, 11:59 AM".
docs ·
dfd4214
[0.25.35] — October 14, 2026
🧩 New components
- DocumentLayout (new): a page read as a document —
DocumentLayoutHeader,DocumentLayoutBodyat a 47ch reading measure (about 67 characters a line),DocumentLayoutFoot, aDocumentLayoutContentsrail or list and aDocumentLayoutMarginfor comments — on one grid with four tiers by its own width (36 · 52 · 64rem),measure="wide"up to 60rem,DocumentLayoutSkeletonon the same grid and line pitch, anduseDocumentLayoutTierreading the tier from the CSS; it prints as one column on a named page with paper margins. MarkdownView gains a read mode that needs no editor:annotations(comment highlights resolved from their quote, withactiveAnnotationId,onAnnotationClick,annotationCountsandonAnnotationsLayout),onOutlineChange,headingAnchors(slug and block id) withheadingLinkHrefcopy links, per-readertoggleState, andfindQuery(CSS Custom Highlight API, a match bar,onFindClose); a table wider than the text fades at its scrolling edge and is a named, focusable region while it scrolls (the editor's tables fade too). BreadcrumbTrail:compact/compact="auto"—‹and the parent's name, a menu of the whole path. ShareDialog:publishExposure,onPublishRequestand a "Who can see this"audiencesection. Button: a 44×44 hit area on a coarse pointer at every size, nothing moving (A11Y-24). Sheet: a bottom sheet clears the home indicator (LAY-10). Tokens: printing from dark mode prints the light palette. docs ·8ab9e76
🔧 Changed components
- TextEdit
diff={{ doc, baseline }}: a version diff, formatted and read-only — added text and blocks underlined on the success tint, removed ones struck through (ins/del, announced "Added:"/"Removed:"; changed images outlined), underCompared with {baseline}.text-edit-schemaaddsTextEditDiff(theychangemark and block attribute a Yjs snapshot diff produces, never in a live document — whose schema now refuses it),textEditDiffSchema()(textEditExtensions({ diff: true }), code blocks take the change mark) andychangeType(). A diff view never firesonValueChange/onCommitand ignoresvalue. docs ·cb419b5
📦 npm
@vegastack/design-tokens→0.8.12(was0.8.3).@vegastack/design→0.8.12(was0.8.11).- The design-system registry (
@vegastack/ui) bumps 0.25.34 → 0.25.35.
⚠️ Breaking
- MarkdownView / TextEdit:
headingScaleis replaced bytypeScale—"compact" | "document", no alias — andproseDocumentHeadingsClassNameis gone.typeScale="document"is now the whole document type scale (TYP-20, MK 2026-10-14), not just bigger headings: 14/24 text (16/26 on a coarse pointer or a narrow window, so reading and editing match and iOS never zooms), 12px between paragraphs,#24 ·##20 ·###16 (18 over 16px text), inline code in the body's ink, and print rules;@vegastack/designexports it asproseDocumentandproseDocumentClassName. RenameheadingScale="document"totypeScale="document"; headings are smaller than the old document scale (24/20/16, was 30/24/20). TextEdit:onOutlineChangeno longer mounts the editor — the read view reports the outline from the Markdown until the editor is in. docs ·8ab9e76 - VersionList kinds are now
auto · safety · restore · import · agent_checkpoint · named · approved, each with its badge (VERSION_KIND_LABELS: "Before a large deletion", "Before a restore", "Before an import", "Before an agent's edits", "Approved"); a row's ownlabelreplaces it ("Before Claude's edits") and a named row shows none, except Approved.conflict("Unsaved copy") is removed, andrestorenow means the text just before a restore (it was "Restored"). docs ·cb419b5
[0.25.34] — October 14, 2026
📦 npm
@vegastack/design→0.8.11(was0.8.10).- The design-system registry (
@vegastack/ui) bumps 0.25.33 → 0.25.34.
🐛 Fixed
- date-time / RelativeTime / ApprovalCard: a future time rounds to the nearest unit instead of cutting down, so a request that expires 7 days from now reads "Expires in 7 days" the moment after it is made (it read "in 6 days");
formatRelative"in 6d" becomes "in 7d" the same way. Past times still count completed units ("6 days ago"). docs ·6ca36dd
[0.25.33] — October 14, 2026
📦 npm
@vegastack/design→0.8.10(was0.8.9).- The design-system registry (
@vegastack/ui) bumps 0.25.32 → 0.25.33.
🐛 Fixed
- TextEdit: in
collaboration, two people typing at the same spot no longer get one side's characters reversed (12345678landing as87654321). After a collaborator's change the caret now stays exactly where Yjs puts it whenever its text still exists, instead of the binding re-placing it by its old offset in a paragraph whose text changed. docs ·85d4bea
[0.25.32] — October 14, 2026
📦 npm
@vegastack/design→0.8.9(was0.8.8).- The design-system registry (
@vegastack/ui) bumps 0.25.31 → 0.25.32.
🐛 Fixed
- text-edit-markdown / TextEdit: inline code inside a link, bold, italic or strikethrough (
[`code`](/x),**`code`**) no longer throws on parse — the code mark now combines with other marks. Link titles are kept for inline and reference-style links ([text][ref]+[ref]: url "Title"), written back as inline links with their title; corpus cases for both. docs ·4b0d50d
[0.25.31] — October 14, 2026
🧩 New components
- TextEdit:
collaboration={{ document, provider, user, peer }}edits a shared Yjs document live — collaborators' carets and selections in their tag hue with their name (an agent in purple), the identity taken only frompeer(clientId)(the server's word), never from what a client writes into awareness, ⌘Z undoing only this viewer's changes, nothing serialized per keystroke, and the editor mounted at once (read-only viewers watch it move;readCopypaints first). A toggle's chevron is each viewer's own ("Open by default" in its ⋯ menu sets it for everyone), a page, task or file mention is stored without its title (pasted and imported ones too) and labelled per reader by the newmentionLabel, a comment made here carriesanchor.yYjs positions (TextEditAnchor, kept byonAnnotationsLayout) that keep the highlight on its words and orphan it once they are deleted, uploads in flight survive collaborators' edits, an open slash or@menu survives them too, and block ids are given only to blocks made here (a document's own are the server's). The slash and@menus take spaces and digits (/heading 2,/head2,@Asha Rao);handleRef.pendingUploads()andonPendingUploadsChangereport uploads still landing. AutoSaveIndicator addsconnectingandoffline; InlineChip and mentions add theagentkind ("Your agents").docToMarkdown(doc, { labels })fills label-less mentions. New dependenciesyjs,y-protocols,@tiptap/y-tiptapand@tiptap/extension-collaboration(-caret), in the editor's lazy chunk. docs ·e5a1833
📦 npm
@vegastack/design→0.8.8(was0.8.7).- The design-system registry (
@vegastack/ui) bumps 0.25.30 → 0.25.31.
⚠️ Breaking
- TextEdit: Escape in a
documenteditor now keeps the text, commits and blurs (it reverted unlessautosavewas set) — reverting a document erased text people had typed;boxedandcomposerfields still revert, andescapeBehaviorstill chooses either way. AreadOnlyeditor is now a document (role="document") rather than a read-only text box: noaria-readonlyoraria-multiline, so assistive tech announces content, not a field. docs ·e5a1833
[0.25.30] — October 14, 2026
🧩 New components
- TextEdit: its document lives in three server-safe registry libs, so a server reads and writes exactly what the editor does.
text-edit-grammarholds VegaStack Markdown's constructs as plain strings (parseMentionLink,mentionMarkdown,MENTION_LINK_SQL,headingIds,parseImageAlt,imageMarkdown,parseCodeFenceInfo— moved here fromMarkdownViewandCodeBlock);text-edit-schemaevery node, mark and attribute with its Markdown rules (textEditExtensions(),textEditSchema()), with a stableblockIdon every heading, paragraph, list item, toggle, callout, table and code block that Markdown never carries;text-edit-markdownthe conversion (markdownToDoc,docToMarkdown,normalizeMarkdown,docToText) and the editor'sVegaStackMarkdownextension. EachTextEditparses with its ownmarked, and a megabyte of Markdown parses in well under a second (it took over twenty). A round-trip corpus checks every construct in Node and in Chromium against the same output. Tiptap moves to 3.31.4. docs ·46e19d5
📦 npm
@vegastack/design→0.8.7(was0.8.6).- The design-system registry (
@vegastack/ui) bumps 0.25.29 → 0.25.30.
⚠️ Breaking
- TextEdit Markdown output changes once for existing documents (derived copies are rewritten on their next save): text is escaped only where Markdown would read it as syntax (
snake_case,R&Danda * bstay as typed;1.at the start of a paragraph is1\.); a hard break is\and a newline, or<br>in a heading or a table cell; a table cell's leading and trailing<br>are dropped; a link with a space in its address is written<…>. Parsing no longer loses text: an indented code fence is a code block,Hi. thereis a sentence, a less-indented ordered item stays,&in a link is&, and raw HTML is text in the browser as it is on a server.MarkdownViewreads<br>as a line break everywhere, not only in a table cell. docs ·46e19d5
[0.25.29] — October 14, 2026
🧩 New components
-
Agents get one identity across people surfaces, and
ApprovalCardis new.Person.kindadds"agent"(an AI assistant working for a member), withvianaming the member.PersonAvatardraws an agent as theBoticon on the purple tag hue. The newPersonNamewrites the name as "Claude Code via Manoj" with the purple "Agent" label (PersonAgentLabel), and a screen reader hears "Claude Code via Manoj, agent".personDisplayName("Claude Code (via Manoj)") andpersonAccessibleNamegive the same name as plain text.PersonCard,PersonHoverCard,AvatarStack,Comments,ActivityEventandPeoplePickerrows all use it.CommentDataaddstone: "question"(amber bar, "Needs your reply") andchoices. Choices render as buttons under the body when the item, thread or list getsonChoose.ActivityKindaddsdelegated,undelegated,agent-queued,agent-working,agent-needs-you,agent-done,agent-failedandagent-step, each drawn in its status tone.PeoplePickerOptionaddsdisabledReason, which lists the option disabled with the reason as its second line. The list props addheading("Your agents").ApprovalCard(shadcn add @vegastack/approval-card) shows a risky action an agent asked to take: who asks, the action, the records it touches ("and N more"), the reason, a reassurance, "Requested 2m ago · Expires in 5 days", and one Approve / Decline for the whole request. It covers the pending, approving, approved, declined, expired, cancelled and already-answered states, plus a "Waiting for Manoj's approval" view for anyone who can't decide.- Deprecated:
CommentData.agentandActivityEvent'sagentprop still work for one release; give the author or actorkind: "agent"instead.b82b754
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.28 → 0.25.29.
[0.25.28] — October 14, 2026
🧩 New components
- Field:
FieldCounter— a character counter forInputandTextareaat the end of the label row (FieldHeader), so it takes no room under the control.<FieldCounter value={bio} maxLength={100} threshold={0.8} />shows92/100(12px, muted, tabular) only fromthresholdofmaxLength(default 0.8), in the destructive ink at the limit. Inside aFieldit describes the control (aria-describedby) while it shows, and a polite status speaks only at its thresholds ("8 characters left", "Character limit reached"), never per keystroke. docs ·e142894
🔧 Changed components
- DataList and DataGrid: fill and fit columns. The first column takes the width the others leave, wraps anywhere and (DataList) clamps a plain value at two lines with the full value in its tooltip; every other column is sized to its content on one line, and headers never break inside a word. One long unbroken value no longer squeezes every cell and header ("Prod" / "ucts"): the squeeze releases only the first column, then DataList merges another column, and only when nothing is left to merge does it strain (
data-strained) the rest.columnCellClass,headerCellClassandSortableHeadtake afillrole,isNowrapColumndefaults non-first columns to one line (nowrap: falselets one wrap between words), andrevealColumnstakesextraOverflow. BreadcrumbTrail's current step truncates with its full name in a tooltip on hover and focus; PageHeader's title breaks an unbroken token anywhere, clamped or not. docs ·e142894
📦 npm
@vegastack/design→0.8.6(was0.8.5).- The design-system registry (
@vegastack/ui) bumps 0.25.27 → 0.25.28.
[0.25.27] — October 13, 2026
🔧 Changed components
- TemplateEditor and TextEdit: the
@/{{placeholder picker and the slash and mention menus are now one sharedSuggestionMenu(@vegastack/suggestion-menu, installed with either editor). Rows look exactly likeComboboxItem's — theaccenthighlight on hover and arrows (visible in dark mode), the same padding, radius, gap, icon sizing and muted trailing text — with group headings, astatusfor empty, loading and error, a flip above the caret, and the textbox'saria-activedescendanton the active row. Escape in TemplateEditor's picker no longer reaches a host (a dialog stays open). TemplateEditor addssingleLine(Enter inserts nothing, pasted line breaks become spaces) andreplacements([{ from: "*", to: "×" }], applied as you type) for formulas. docs ·f5f16fe
📦 npm
@vegastack/design→0.8.5(was0.8.4).- The design-system registry (
@vegastack/ui) bumps 0.25.26 → 0.25.27.
[0.25.26] — October 13, 2026
🔧 Changed components
- Searchable Select, Select: an opt-in
wrapshows a long value whole.SearchableSelect wrap, andSelectTrigger wrapwithSelectContent wrap: the trigger's fixed height becomes its minimum and the value runs onto as many lines as it needs, and long options wrap in the list instead of being cut off (API-35). Off by default; both writedata-wrap. docs ·77051bc
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.25 → 0.25.26.
🐛 Fixed
- Dialog, Alert Dialog, Sheet, Drawer, Popover: a long name stays inside the overlay.
DialogContentandAlertDialogContentlay out on oneminmax(0, 1fr)column (grid-cols-1), so a no-wrap child such as a select showing a long value no longer widens the popup past its cap and pushes the field and footer out (LAY-17). All five surfaces setwrap-break-word, so a long unbroken word breaks inside them. While a dialog or sheet shows its corner close ×, its header keeps clear of it, so a long title wraps before the × instead of running under it; the alert dialog header (with or without media) and a sheet header with an action wrap a long title instead of overflowing.DialogTitleisleading-snuginstead ofleading-none, so a wrapped title's lines no longer collide (TYP-19). docs ·77051bc
[0.25.25] — October 12, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.24 → 0.25.25.
🐛 Fixed
- Multi-step form: a step body now stays mounted (hidden) after its first visit, so a move no longer replaces the node, resets its state or re-runs its effects. The entrance animation plays only on a move, never on first paint.
docs ·
15325c6
[0.25.24] — October 12, 2026
🔧 Changed components
- Attachment:
AttachmentGroup layout="cards"(soFileCardandUploadCard) now wear the same surface asMediaCard— card background, border, radius,p-2 pe-3padding and the subtle hover — instead of a bare transparent row;MediaCardexportsRECORD_CARD_SURFACE_CLASS, the one surface both share. Other layouts are unchanged. docs ·eff079d
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.23 → 0.25.24.
[0.25.23] — October 12, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.22 → 0.25.23.
📚 Docs
[0.25.22] — October 12, 2026
🧩 New components
- FileCard and UploadCard: new — one file as a card (thumbnail or type icon, name, description lines, a ⋯ actions menu) that opens the file viewer, or with
href(+download,target) makes the whole card a link to a download or a page;UploadCardis the same row for a file uploading, with progress, Retry and Cancel. Install with@vegastack/file-card. docs ·5e37f27
🔧 Changed components
- Attachment:
AttachmentGrouptakeslayout="cards"— the list row in a responsive grid (three columns on a wide container, two on a tablet, one on a phone) with a 48px thumbnail, name, description lines and trailing actions. Existing layouts are unchanged. docs ·5e37f27
📦 npm
@vegastack/design→0.8.4(was0.8.3).- The design-system registry (
@vegastack/ui) bumps 0.25.21 → 0.25.22.
[0.25.21] — October 12, 2026
🔧 Changed components
- FilterBarFacet, SearchableSelect: a
FilterBarFacetwith 7 or fewer options no longer shows a search box (server-searchedremotelists always do). The newsearchableprop overrides it on both components;SearchableSelectstill defaults to showing search, so nothing changes unless you opt in. docs ·2f97a7a
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.20 → 0.25.21.
[0.25.20] — October 12, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.19 → 0.25.20.
🐛 Fixed
- Stepper, Toast, Toggle: the current step now carries the same 1px transparent border as the clickable steps, so the node and label no longer shift by a pixel when the step changes. A bottom
Toasterstack also rises by--toaster-offset-bottom(default0px) — set it on:rootwhile a sticky footer is mounted so toasts stop covering it. A pressedToggleandToggleGroupItemname theirforegroundink instead of inheriting it, so a selected chip stays at AA contrast on themutedfill. docs ·5fb69c2
[0.25.19] — October 11, 2026
🧩 New components
- ValueChange: new inline before → after diff of one value — the old value struck through in a
<del>(destructive ink), a muted arrow, the new value in an<ins>(success ink, no underline). Onlyafterrenders an addition, onlybeforea removal; screen readers hear "changed from A to B", "added B" or "removed A". Server-safe. docs ·0a40ba0
🔧 Changed components
- Stepper: a navigable step is now one target — node and label together — so the circle is as clickable as the text and the hover fill covers both. Clearer states: the current step alone is the solid primary circle with a medium-weight label (its bold width reserved, so nothing shifts), a completed step is a check on a muted primary tint with its connector in the same tone, upcoming stays an outline. An overflowing horizontal rail scrolls the current step into view. The non-interactive step box is
data-slot="stepper-item"; thestepper-node-railwrapper is gone. No prop changes;MultiStepFormpicks this up as is. docs ·0a40ba0
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.18 → 0.25.19.
[0.25.18] — October 11, 2026
🧩 New components
- SortableList:
align="start"keeps the handle, the × and the ⋯ on a row's first line instead of its middle, for tall rows such as a card whose title line sits over an expandable body. docs ·5523f81
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.17 → 0.25.18.
[0.25.17] — October 11, 2026
🔧 Changed components
- LoadMore: keyset lists load on scroll — no Load more button anywhere. A sentinel at the list's end fetches the next cursor page as it nears its scroll container's edge, skeleton rows hold the new rows' place while it loads, and one page is requested at a time.
onLoadMore/hasMore/loading/errorkeep their meaning;skeletonandrootMarginare new andlabelis deprecated. A failed page still shows its error with Try again.DataListandDataGridappend skeleton rows to the table body, board lanes show skeleton cards,SearchableSelectpages inside its option list with skeleton options,VersionListpages on scroll,RecordListMoreloads the next batch on scroll instead of a Show more button,CommentListloads older comments on scroll instead of Load earlier,ActivityFeedshows skeleton rows instead of a busy button, anduseAsyncSearch'sloadMore.loadingis now true only for a next page. docs ·02a8c1b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.16 → 0.25.17.
[0.25.16] — October 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.15 → 0.25.16.
🐛 Fixed
- ShareDialog: Space access's mode is a quiet pill like
RecordChip's ghost form — no border, a tint and a ▾ on hover.generalAccess.levelis optional: leave it out when members open the item with their own space role, and no level shows. While everyone in a named space can open the item, the footer reads "Members of Sales and people with access can open it" (newcopyLinkHintSpacelabel) instead of "Only people with access can open it". docs ·474c6aa
[0.25.15] — October 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.14 → 0.25.15.
🐛 Fixed
- ShareDialog: the invite picker's search field reads "Search people or teams", since it lists both. A new
inviteSearchPlaceholderlabel sets it. docs ·4397871
[0.25.14] — October 11, 2026
🧩 New components
-
Add
PeoplePicker, the one picker for people and teams; it replacesPeopleInput.PeoplePicker(shadcn add @vegastack/people-picker) chooses one person or, withmultiple, several people and teams. Rows are the avatar, the name (the viewer's reads "(you)" and leads), then the email, adescriptionor "Team · N people". The trigger matchesButtonandSelectheights atsm,defaultandlg, so it sits flush in an "Add people… | Can edit | Add" row. Footeractions("Assign to me", "Unassign"),leadingOptions("Unassigned"),exclude,viewerIdand a fixedoptionslist are built in; loading shows skeleton rows, never a spinner.- Only active people are listed: an option with
active: falseis hidden unlessincludeInactive, which badges it "Inactive". PeoplePickerContentis the popover body for a trigger the host owns (a record pill);PeoplePickerMenuis the same list inside aDropdownMenusubmenu ("Assign ›").FilterBarPeopleFacet(infilter-bar) is a person filter chip over the same body.- ⚠️ Breaking:
people-inputis removed — usePeoplePicker multiple.ShareDialog'ssearchis now aPeoplePickerSearchloader that resolves{ items, nextCursor? }(it wasPromise<Option[]>), and its invitees arePeoplePickerOptions. The invite field is a picker trigger that reads the chosen names, not a chips field.2f9bce4
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.13 → 0.25.14.
[0.25.13] — October 11, 2026
🧩 New components
- DataList:
keyboardNavigationgives a page's main list a row cursor. J or ↓ and K or ↑ move it, Enter opens the cursor row, and Escape clears it. The cursor wears the keyboard-focus cue (the hover wash and the 2px start edge) and follows the mouse.cursorIdandonCursorChangegive a host with its own keys the same cue. A row link that gets focus back, for example when a drawer opened from it closes, no longer paints a rounded grey box behind the first cell. SortableList: aheaderrow above the list, full width, for an add field whose new value goes first, andflushrows with no hover wash or side padding, for rows that hold their own bordered input. docs ·c01155a
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.12 → 0.25.13.
[0.25.12] — October 11, 2026
🔧 Changed components
- Re-publish
@vegastack/designso its@vegastack/design-tokensrange follows the token release that adds thegradient-sparkutilities.1005935 - Add the
gradient-sparkutility (a subtle purple to blue gradient from the theme-aware tag hues) andstop-spark-from/stop-spark-tofor SVG gradient stops, so a stroked icon such as What's new can carry the same gradient in light and dark.c0140d4
📦 npm
@vegastack/design→0.8.3(was0.8.2).@vegastack/design-tokens→0.8.3(was0.8.0).- The design-system registry (
@vegastack/ui) bumps 0.25.11 → 0.25.12.
[0.25.11] — October 10, 2026
🧩 New components
-
Add
ShareDialogas an installable component, withcanPublishandhints.ShareDialog— the Share dialog from theshare-01block — is now its own component. Add it withshadcn add @vegastack/share-dialog(it installs tocomponents/ui/share-dialog.tsx) and keep it current through the integrity flow instead of maintaining a copy. Every prop and type the block's dialog exported is unchanged.canPublishgates the Publish tab's controls (the switch, Reset, Expires and Stop publishing) separately fromcanManage, and followscanManagewhen unset.hintsplaces an optional help node, such as anInfoHint, right after the People with access heading, the Space access heading and the Publish label.- The
share-01block now imports the dialog from@/components/ui/share-dialogand lists it as a registry dependency; its demo and page are unchanged.461515f
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.10 → 0.25.11.
[0.25.10] — October 10, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.9 → 0.25.10.
🐛 Fixed
- NumberField: the − and + steppers draw one divider each against the input, inside the field's single border. Before, each stepper painted its own full box, doubling the outer edge and the dividers in light and dark. The divider no longer nudges a pixel on press. Tabs: a click on a panel's empty space no longer tints the whole panel once a key is pressed (⌘ alone was enough in Chromium, turning a drawer body grey). A panel reached with the keyboard still shows the focus tint. SortableList: a new
footerrow, such as an "Add a value" field and its Add button, lines up with the rows' content, between the handle gutter and the × gutter. docs ·d4abc18 - BoardCard and Board: every band starts at the card's edge. That covers the eyebrow, the status icon (not its hit area), the context, the chips and the footnote. A wrapped title hangs under its own first line beside the status, and stops at two lines with an ellipsis. Bands keep fixed heights: the eyebrow and title line are 20px, and the chips and footnote are 24px with an even 8px gap. A card without chips or without an assignee keeps the same rhythm. On a
Board, only the first band leaves room for the ⋯ menu (data-board-card-menu). The rows under it run to the card's end padding, so a footnote avatar sits flush and in line with the ⋯, which moves onto the end padding. The loading card mirrors these bands. docs ·1681ebe - TruncatedText: a single line is centred inside its 24px target floor instead of sitting at the top. The current breadcrumb crumb now shares a vertical centre with the links and chevrons without an app-side override.
docs ·
71082c6
[0.25.9] — October 10, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.8 → 0.25.9.
🐛 Fixed
- Board and ActionListSkeleton — a loading lane's skeleton cards follow the card's bands (the eyebrow, the title, a chip, then the footnote with the assignee at its end) instead of a chip row with the avatar.
ActionListSkeleton's title and meta bars no longer touch: each sits in its text line's height, so a loading row keeps the loaded row's height with a gap between the bars. docs ·96ce883 - EditableCell, useInlineEdit, TextEdit and MarkdownView — clicking a value to edit it puts the caret where you clicked (a keyboard open puts it at the end) instead of selecting the whole value;
useInlineEdit'sstarttakes the opening pointer event for this. ATextEditwithcitationshows its read view whenever it is not being edited: a preloaded editor waits for a click instead of swapping in on mount, and the read view comes back once focus leaves the editor and its menus and panels (after the blur commit, never while an upload is in flight), so[[n]]markers read as citations again.MarkdownViewalso recognises a marker the editor's Markdown escaped (\[\[n\]\]). docs ·d32e54e - Share dialog (
share-01) — switching between Share and Publish no longer leaves the other tab's controls painted for a beat (the Publish switch over the Invite button, or Invite and Copy link over the Publish row). The hidden panel and the hidden footer hide withvisibility, which upstream'stransition-allon Button, Switch and Select animated; descendants of a hidden box now carry no transition, so they hide in the same frame. docs ·dcee40f
[0.25.8] — October 10, 2026
🔧 Changed components
- BoardCard drops its leading gutter: the completion tick or
statuscontrol now sits inline before the title (a wrapped title wraps under itself), and the eyebrow, context, chips, alert and footnote start at the card's own edge instead of being indented to the title. docs ·88ed5f6
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.7 → 0.25.8.
[0.25.7] — October 9, 2026
🧩 New components
- BoardCard gains quieter Linear-style slots —
eyebrow,aside,chips,alert,footnoteandtitleWeight(normal by default beside astatuscontrol) — and exportsBoardCardChip, a thin bordered pill with no fill that renders as a menu trigger throughrender. Board takes a columnicon,addPlacement="header"(the add button as an icon beside the lane's ⋯) andtrailingcontent after the last lane; cards sit on a slightly raised surface. DataList passes them through: a section'siconandactions,addPlacementandboardTrailing. docs ·28f1696
🔧 Changed components
- Drawer floats inset by default (OVL-21):
--spacing(2)off every viewport edge, every corner rounded and a full border, in all four directions, as shadcn's inset styles ship it. PassflushonDrawerfor the previous edge-to-edge sheet. Width overrides are unchanged (w-*/max-w-*onDrawerContent), and a different inset is[--drawer-inset:--spacing(4)]. docs ·679d19b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.6 → 0.25.7.
[0.25.6] — October 9, 2026
🔧 Changed components
- StatusIcon keeps Linear's shapes but now colours each status with the system's standard semantic tokens instead of Linear's palette:
todo→foreground,progress→warning-text,review→info-text,done→success-text(was purple);backlog,canceledandduplicatestaymuted-foreground,triagestaystag-orange-text. docs ·c751e87
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.5 → 0.25.6.
[0.25.5] — October 9, 2026
🔧 Changed components
- StatusIcon — Linear's workflow status set, drawn as 16-unit glyphs instead of lucide icons:
backlog(dashed ring),todo(ring),progress(half pie, yellow),review(three-quarter pie, green),done(filled check, purple),canceled(filled cross, grey),duplicate(ring with diagonal strokes, grey) andtriage(filled left-right arrow, orange).blockedandcancelledkeep working as deprecated aliases; the defaulttodolabel reads "Todo". docs ·478de35
📦 npm
@vegastack/design→0.8.2(was0.8.1).- The design-system registry (
@vegastack/ui) bumps 0.25.4 → 0.25.5.
[0.25.4] — October 9, 2026
🔧 Changed components
EmptyMedia variant="icon"is a larger, bordered muted plate: 48px (rounded-xl) with a 24pxtext-muted-foregroundicon, and 40px / 20px (rounded-lg) in the compactsize="sm"Empty.f1f73fdActivityJumpToLatestand the Transcript's "Back to current line" float only while the reader scrolls away from the latest (or current) line and fade once they settle; hovering or focusing holds them. Text Edit's upload placeholder spins the systemLoaderglyph instead of a CSS ring.dd0e424
🗑 Removed / renamed
EmptyIllustrationis removed: empty states useEmptywithEmptyMedia variant="icon"and a lucide icon only. Replace<EmptyIllustration name="tasks" />with<EmptyMedia variant="icon"><ListTodo /></EmptyMedia>(or pass the icon toEmpty'siconprop).f1f73fd
📦 npm
@vegastack/design→0.8.1(was0.8.0).- The design-system registry (
@vegastack/ui) bumps 0.25.3 → 0.25.4.
[0.25.3] — October 9, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.2 → 0.25.3.
🐛 Fixed
SkeletonRows(theDataListandDataGridloading rows) sizes each placeholder bar from its column'sminWidthbudget and follows the column's alignment, with an icon-sized square for a narrow ⋯ column, so a loading table's columns settle where the loaded table's do instead of jumping when the rows arrive.7b3e423
[0.25.2] — October 9, 2026
🧩 New components
email-kitgainsEmailMarkdown: long-form Markdown (meeting notes) as email-safe blocks in the in-app notes' reading styles — no injected HTML, safe links only, dark-mode hooks.9b22921
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.1 → 0.25.2.
🐛 Fixed
InlineEditTrigger(cell layout) is block-level, so it centres on a table row instead of sitting on the text baseline; the inline date and select editors drop the dark-mode resting border.9b22921
[0.25.1] — October 9, 2026
🧩 New components
- Whole-cell inline editing:
InlineEditTrigger(ineditable-cell) makes a list cell's whole value — icon and text — the button that opens an app-owned status, priority or assignee editor, with no border at rest and the properties-panel tint on hover, focus and while open.EditableCell'sdateandselecteditors use the same tint (no hover border, the select trigger is ghost) and fill acellvariant.BoardCardgainsrenderFieldto make its assignee, due and priority fields editable the same way.IconGlyphwithout a hue now takes the sidebar nav icon ink instead of full foreground.4a2b49b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.25.0 → 0.25.1.
[0.25.0] — October 8, 2026
🧩 New components
- Motion and interaction primitives:
motion-staggerutility (staggered reveal for content replacing a skeleton),useViewTransition/startViewTransition(View Transitions with reduced-motion and unsupported fallbacks),useOptimisticAction(apply now, Undo toast, commit after the window),LevelMeter(live recording input level),EmptyIllustration(shared monochrome empty-state set),EditableCelldateeditor, andDataListdensitywith the newDensityToggle.5760011
🔧 Changed components
- Status alerts (
info,success,warning,destructive) now tint their border (30%) and background (5%, 10% in dark) instead of keeping the neutral card; an outline button inside one wears the variant colour at rest. Padding is 12×10px, and with an action and no title a single-line description sits centred on the 32px button. The Alert docs gain a Status surfaces matrix.59300fb
📦 npm
@vegastack/design→0.8.0(was0.7.111).@vegastack/design-tokens→0.8.0(was0.7.80).- The design-system registry (
@vegastack/ui) bumps 0.24.5 → 0.25.0.
[0.24.5] — October 8, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.24.4 → 0.24.5.
🐛 Fixed
-
Mention chips keep their avatar inside the chip, at every text size, and never spellcheck.
- The photo no longer drops out of the chip in rendered text and the editor: a prose root's picture rule (block, margin, border) reached the avatar's
<img>; the chip now pins it to the circle. - Initials scale with the text (
55%of the chip's size, not a fixed 12px), so "MA" fits its circle in a comment, a paragraph or a heading. The avatar is1.1em, aninline-blockcentred on the label, so it no longer drops by half its height when it shows initials. - A chip is
spellcheck="false", and inTextEditits node view is alsocontenteditable="false": no red underline under a name. - A long name still breaks inside a narrow column (the prose root's
wrap-anywhere), so a chip never overflows it. - New
InlineChipPerson.inactive: the name is followed by the person surfaces' small muted outline "Inactive" badge, and the chip carriesdata-inactive. The label text is unchanged, so comment anchors keep matching.93f7d5d
- The photo no longer drops out of the chip in rendered text and the editor: a prose root's picture rule (block, margin, border) reached the avatar's
[0.24.4] — October 8, 2026
🛠 CLI & tooling
-
Searchable pickers get a footer for their static actions, and the editor, space picker and pill calendar get review fixes (Regent new-task review, OVL-20).
CommandActions(new): the last child ofCommandList, a sticky footer behind a hairline for a picker's static actions ("Assign to me", "Unassign", "Clear"). ItsCommandItems are never filtered and never counted as results, so "No results" still shows with the actions under it; the arrow keys reach them after the last result; the list's scroll padding keeps the active row clear of the footer.PanelListandPanelActions(new, inpanel-search): the scrolling list under aPanelSearchrow and the matching sticky footer for menus. A row action with bothsubmenuanditemsnow rendersitemsas that footer below the submenu content (they used to sit above the search).SpacePickerrows are one line (tile and name; a disabled reason muted at the end);SpacePickerItem.secondaryis deprecated and no longer shown.placement="title"is an outlined, rounded chip, with no "› New task" after it;SpaceChip readOnly size="title"is the same outlined shape without the ▾, and a read-only chip now passes its other props (a tooltip trigger's) to its span.TextEdit: the bubble menu no longer opens over a selection with no text (select all in an empty editor), and it follows its selection when a dialog or panel around the editor scrolls, hiding while the selection is out of view. In a narrow frame (a Dialog's 16px padding) the content takes just enough start padding that the block grip sits clear of the text and the caret.RecordChipMenu: aCalendarinside it is transparent, so its square box no longer paints over the popup's rounded border.164172b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.24.3 → 0.24.4.
🐛 Fixed
-
IconPickerkeeps focus in its own search when its trigger sits in anInputGroupAddon, and its footer and tabs line up.- Clicks inside the popup no longer bubble (through the portal) to an
InputGroupAddonaround the trigger, which used to move focus to the field's input while you typed a search. Opening by pointer or keyboard now focuses the search field; touch keeps the default. - The footer is one row that never wraps: "No colour" and the ten hues on the left, Remove as a compact trash icon button (
aria-label"Remove icon", tooltip "Remove") on the right. - The checked swatch shows the selected cell's 1px
primaryborder, rounded, instead of a ring. - Both tabs share one width sized to the grid (
w-79, compactw-62.5, with 6px cell gaps), and both browse eight categories: emoji flags leave the category bar and sections but stay searchable (newPickerPanelsearchOnlyprop).EmojiPickeruses the same grid width.00ff4bd
- Clicks inside the popup no longer bubble (through the portal) to an
-
InputGroupAddonfocuses its input only when the click started inside the addon itself, so choosing an item in a menu (or any portalled popup) opened from the addon no longer pulls focus into the field (INT-14).48956d1 -
ActivityEvent,VersionListandCommentThread's "Resolved by" header show a person'sbadgeafter their name, like every other place a person is named. An actor, version author or resolver withbadge: "Inactive"(someone deactivated, or whose invite is pending) now reads "Jamie RaoInactivechanged status…" with the small muted outline badge fromPersonBadge, instead of dropping it.activity-feedandversion-listnow depend onsearchable-select(commentsalready did).ac500f1
[0.24.3] — October 7, 2026
🛠 CLI & tooling
-
IconPickerpicks colour from inline swatches in a sticky footer instead of a popover beside search.- In icon mode the footer row starts with a "No colour" swatch (clears the hue), then the hues, as one "Icon colour" radiogroup; Remove sits on the right when
onRemoveis passed. The emoji tab shows only Remove, and the footer is omitted when empty. - Line tabs sit over a full-width hairline, search runs full width, and section headers stay pinned while the grid scrolls.
PickerPanel'ssearchActionprop is deprecated.5b0d8db
- In icon mode the footer row starts with a "No colour" swatch (clears the hue), then the hues, as one "Icon colour" radiogroup; Remove sits on the right when
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.24.2 → 0.24.3.
[0.24.2] — October 7, 2026
🔧 Changed components
-
Add
InfoHint, a footer slot onShortcutOverlay, and a linkable email footer note.InfoHintis a muted icon-only info button that opens one sentence in a popover, with an optionalhreflink that opens in a new tab.labelnames the trigger ("About spaces"). Add it withshadcn add @vegastack/info-hint.ShortcutOverlaytakes an optionalfooter, rendered below the shortcut list.- The email kit's
EmailLayoutfooter takesnoteHref, which links "VegaStack" in the default "Sent with VegaStack" note, andnotenow accepts inline content.95d592a
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.24.1 → 0.24.2.
[0.24.1] — October 7, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.24.0 → 0.24.1.
🐛 Fixed
- Keep the full floating Jump to latest button inside its scrolling viewport, including bottom padding.
6959fb9
[0.24.0] — October 6, 2026
🔧 Changed components
-
Jump to latest follows an explicit content boundary and keeps held pointer clicks stable.
The host can capture a batch at activation and acknowledge it after that same target is reached.
e8d0004
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.128 → 0.24.0.
🐛 Fixed
- Keep Jump to latest pointing in the selected feed direction when its boundary is already visible.
11defd8
[0.23.128] — October 6, 2026
🛠 CLI & tooling
-
Verify generated picker helper mirrors during version-package releases.
- Allow only provenance-header changes for the four registered picker helper mirrors; runtime edits remain rejected.
2753a93
- Allow only provenance-header changes for the four registered picker helper mirrors; runtime edits remain rejected.
📦 npm
@vegastack/design→0.7.111(was0.7.110).- The design-system registry (
@vegastack/ui) bumps 0.23.127 → 0.23.128.
⚠️ Breaking
-
Choose consistent icon and emoji identities with shared search, recents and a nested hue palette.
- Add IconPicker, IconGlyph and a 160-icon server-safe catalogue.
- Share panel geometry and visual-row navigation with EmojiPicker.
- Replace filled swatch checks with persistent cell borders and balanced colour grids.
- Remove SpaceAvatar; compose generic glyphs with existing space controls and app-owned presentation adapters.
ff3d9fb
[0.23.127] — October 6, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.126 → 0.23.127.
🐛 Fixed
- Activity cursor loading preserves the current reading position in both orders, including when older pages regroup visible changes or the reader scrolls during the request.
812ebf4
[0.23.126] — October 6, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.125 → 0.23.126.
🐛 Fixed
- Activity icons align with their first text line, and pre-grouped changes render as one visible sentence with a single actor and timestamp. Cursor pages can load automatically at the history edge while preserving the reader's position, with an explicit retry after a failed read.
c402dbc
[0.23.125] — October 6, 2026
🔧 Changed components
- Inbox rows say more at a glance: an unread row carries a small dot in its start gutter (beside the tint and the sr-only "Unread:"), and
InboxItemtakes abadge— a small glyph on the avatar's corner naming the kind of event (assigned, a mention, a comment), decorative so the title carries the meaning.SettingsSectionkeeps its actions (an autosave status, a button) on the title row beside a long description instead of wrapping them onto a row of their own. A personInlineChip's initials no longer break onto two lines in a narrow, break-anywhere column (a mention in a comment on a phone).101ba5e
📦 npm
@vegastack/design→0.7.110(was0.7.109).- The design-system registry (
@vegastack/ui) bumps 0.23.124 → 0.23.125.
[0.23.124] — October 5, 2026
🧩 New components
-
Add the
email-kitblock: server-rendered HTML email that installs intocomponents/email/(shadcn add @vegastack/email-kit).- Three templates, every string a prop:
NotificationEmail(one event — heading, quotes, a record card, one button),DigestEmail(grouped rows and "Open inbox") andActionEmail(an invite or reset — button, raw-link fallback, expiry note). EmailLayoutand its parts —EmailHeading,EmailText,EmailLink,EmailButton,EmailCard,EmailQuote,EmailItems,EmailDivider— on React Email's tables: a text wordmark, a 600px fluid card, the system font stack, a 46px bulletproof button that goes full width on a phone, and a footer with the reason, preference links and "Sent with VegaStack".- Light colours inline; dark mode from
prefers-color-schemeand, in its own style block, the Outlook.com[data-ogsc]/[data-ogsb]hooks; a 600px ghost table for classic Outlook. The colours are the design tokens converted to hex bytooling/generate-email-tokens.mjs, AA in both modes and never pure white or black;design:verifyfails ifemail-tokens.tsdrifts. renderEmail(element)returns{ html, text }; each template renders to about 10 KB.- The docs page shows every template light and dark and carries the "Writing notifications" rules for in-app, push and email copy.
docs ·
66439bc
- Three templates, every string a prop:
📦 npm
@vegastack/design→0.7.109(was0.7.108).- The design-system registry (
@vegastack/ui) bumps 0.23.123 → 0.23.124.
[0.23.123] — October 4, 2026
🧩 New components
-
Add
ActivityFeed, a record's history in Linear style: comment threads and change events in one list.ActivityFeedis the section: an "Activity" heading with a count, an All / Comments / Activity filter and an Oldest / Newest first toggle, andpendingto dim the list while a new filter loads.ActivityFeedListandActivityFeedItemhold the rows: events 4px apart, a thread card or a divider 12px.ActivityEventis one event — a 20px icon (anActivityKindIcon, aStatusIconorPriorityIcon, or the actor's avatar), the actor's name opening their hover card ("System" without an actor), the sentence withActivityValues, and the relative time.ActivityKindIconcovers twenty common kinds.ActivityEventGroupfolds a run of events to the first with "N more changes"; "Show less" folds them back.ActivityUnreadDivideris the "New" line before the first unread item and callsonVisibleonce it is half on screen;ActivityJumpToLatestfloats "Jump to latest" while the feed's end is off screen;ActivityFeedSkeletonis the loading shape.useActivityFeedKeyboard: J / K move between items, X toggles the focused one, Esc clears; keys typed in a field, a menu or a dialog are ignored.PersonHoverCardtakestrigger="name": the person's name, inline, is the trigger.- Comments: the author's name opens their person hover card, and
agent: trueon a comment shows a violet bar, a bot avatar when there is no image, and an "Agent" label.adedbe1
📦 npm
@vegastack/design→0.7.108(was0.7.107).- The design-system registry (
@vegastack/ui) bumps 0.23.122 → 0.23.123.
[0.23.122] — October 4, 2026
🔧 Changed components
- Comments match Linear's spacing: rows sit 16px in from the card's sides and 12px from top and bottom, avatars are 20px (a reply's body indents 28px, and the hairline between replies starts at the text), and the reply row is a 44px row — a 20px avatar, "Leave a reply…", attach and Send at its end. The composer card and the skeleton take the same 16px sides.
71219d1
📦 npm
@vegastack/design→0.7.107(was0.7.106).- The design-system registry (
@vegastack/ui) bumps 0.23.121 → 0.23.122.
[0.23.121] — October 4, 2026
🔧 Changed components
-
Comments take the Linear-style thread: one card per thread, indented replies, a flat reply row and a bordered composer with a toolbar row.
CommentThreadis onerounded-lgcard with no padding of its own; its comments are rows 12px in from its sides. The first comment's body runs the full width, and each reply sits over a hairline with its body indented to the name.hiddenReplies+onShowRepliesadd a full-width "Show N more replies" row between the first comment and the replies (with a spinner while a promise settles), andonHideRepliesadds "Show less". The reply row sits flat at the card's foot: theviewer's avatar, a one-line box with no border of its own, and attach and Send.highlightedIdtints a comment in the thread.CommentComposeris a bordered card: the text on top (two lines at rest), the draft's files, and a toolbar row with attach and the round ↑ Send at its end. Files dragged over any comment box withonAttachFilesshow a dashed "Drop files to attach" overlay. Send's tooltip names the shortcut.CommentItem: the hover actions (add reaction, ⋯) sit at the end of a header row. Edit puts ✕ Cancel and ✓ Save in that header, with the body becoming a compact box. The ⋯ menu adds Copy text (onCopyText) and, on a touch screen inside a thread, Reply, which focuses the reply row. "(edited)" replaces "· edited". A deleted comment keeps its author and time over "This comment was deleted.". Reaction pills get an add button after them. A standalone comment card isrounded-lg, andvariant="reply"indents the body.CommentListputs the composer under the heading while newest first and names the day between comments posted on different days (dayDividers, on by default). New parts:CommentListHeader,CommentDayDivideranduseCommentDayDividers, for hosts that lay out their own threads.CommentListSkeletonnow draws two thread cards and the composer.36baad3
📦 npm
@vegastack/design→0.7.106(was0.7.105).- The design-system registry (
@vegastack/ui) bumps 0.23.120 → 0.23.121.
[0.23.120] — October 4, 2026
🧩 New components
-
Add
UploadPanel,UploadItemandUploadGroup, theupload-progresslib, and 25 file kinds infile-kind.UploadPanelis the uploads card pinned bottom-end: a header with the batch, time left and bytes, collapse and close; file rows with a thumbnail or file-type icon, a middle-truncated name that keeps its extension, a destination link (onOpen) and a state icon (clock, percentage ring with × on hover, spinner while finishing, check, alert with Retry, Choose file when interrupted); folder rows with aggregate progress that expand. Closing while uploads move asks first. A clean finish shrinks it into a done card that dismisses itself after 8 s, paused on hover or focus; failures stay. Below 768px (or withcompact) it is a bar that opens the list in a bottom sheet. It publishes--upload-panel-insetwhile on screen.- The
upload-progresslib:createRateEstimator(EWMA of acknowledged bytes, alpha 0.15 over 1 s ticks, ignores the first 3 s, reports after 5 s and 2%, moves only on a 15% change),formatTimeLeft("About 2 minutes left") andformatBytesProgress("2.1 MB of 3.4 MB"). file-kindadds thedata,json,config,script,cad,photometric,design,ebook,email,calendar,contact,key,encryptedandfontkinds, each with an icon, tint and label. CSV and TSV are nowdata, JSON isjsonand YAML isconfig;rtfis a document by extension and by MIME type, and a.tslabelledvideo/mp2tis code.FileViewerstill reads all of these as text.- The Toast stack's bottom positions rise by
--upload-panel-inset, so a toast never covers the upload panel.cf43d17
📦 npm
@vegastack/design→0.7.105(was0.7.104).- The design-system registry (
@vegastack/ui) bumps 0.23.119 → 0.23.120.
[0.23.119] — October 4, 2026
🧩 New components
AppShellSectiontakesalign: "start" | "center"(defaultstart).centerputs ameasure="prose"section in the middle of the page, so a settings tab under a full-width header reads like a settings page;alignis ignored atfull.c48726d
📦 npm
@vegastack/design→0.7.104(was0.7.103).- The design-system registry (
@vegastack/ui) bumps 0.23.118 → 0.23.119.
[0.23.118] — October 4, 2026
📦 npm
@vegastack/design→0.7.103(was0.7.102).- The design-system registry (
@vegastack/ui) bumps 0.23.117 → 0.23.118.
🐛 Fixed
- Align share-01's Share and Publish tabs with the dialog's content edge. The underline and the label of the first tab now start where the title and the rows below start, in the Dialog (they sat 4px and 10px in) and in the phone sheet (they sat 12px and 6px outside the 16px content inset). The tabs drop the line list's padding and the trigger's inline padding, sit 16px apart, and take the sheet's 16px inset.
28b06eb
[0.23.117] — October 3, 2026
📦 npm
@vegastack/design→0.7.102(was0.7.101).- The design-system registry (
@vegastack/ui) bumps 0.23.116 → 0.23.117.
🐛 Fixed
- Keep the share-01 dialog the same height when switching between its Share and Publish tabs. Both panels stay mounted in one grid cell, with the inactive one invisible and inert, and the Share tab's footer keeps its space on the Publish tab. The dialog and the phone sheet are as tall as the taller panel, whether the people list is long or empty.
f05e02b
[0.23.116] — October 3, 2026
🧩 New components
-
Give toasts a second action, the emoji picker a footer, and the space and share parts a way to name a space the viewer cannot see.
- Toast:
data.actionstakes one or two{ label, onClick }buttons after the text, with the type icon kept, each closing its toast once taken unlessdata.keepOpen. Thetoastmanager'sdatais typed asToastData(also exported, withToastActionItem), so a second action needs no hand-rendereddata.renderbody. - EmojiPicker: a
footerslot renders a row at the bottom of the panel, such as "Remove icon". - New
SpaceHinttype,spaceHintLabel()andSpaceHintIconin space-avatar name a hidden space by kind — "Priya's My space" or "Private space" — never by name.AccessChipgains{ kind: "hidden", hint },SpaceChipgainshint, and share-01'sgeneralAccess.spaceHintturns the Space access row into a plain statement with no mode or level. - share-01 shows a one-line "Published to the web" notice on the Share tab while published, with Manage opening the Publish tab.
- Removed unneeded API:
AccessChiplabels/AccessChipLabels(the hidden kind replaces the relabelling),SpacePickerlabels/SpacePickerLabels, and share-01's unusedshare,publicSection,publicHeading,publicOff,stopSharing,doneandunknownSpacelabels. SpacePicker's empty trigger now reads "Space: none chosen", and the toast Action example no longer closes its own toast by hand.84a7db2
- Toast:
-
Add the spaces and sharing parts Regent needs, restructure the Share block around an explicit Publish switch, and fix the audit's review findings.
- New
ResponsiveDialog(a Dialog from 768px up, a bottom sheet with a handle and a safe-area footer below),SpaceChipandSpacePicker(My space first, a check on the current space, disabled rows that say why,placement="title"for[General ▾] › New task),AccessChip(one focusable chip for a record's reach that opens Share) andChoiceCard/ChoiceCardGroup. share-01has Share and Publish tabs at the newmddialog size. Copy never publishes: the Share tab's footer is "Copy link", and only the "Publish to the web" switch callsonCreatePublicLink; "Stop publishing" confirms. Built-in rows are locked with their reason, a personal space reads "My space",defaultTabopens on Publish, andonInvitemay resolvefalseto keep the chips.Dialoggainssize="md"(512px).PermissionMenugainsvariant(ghost,chip,outline),lockedandlockedReason, and two-line descriptions.SidebarGroupActiongainsshowOnHover.SpaceAvatargains the 16px2xssize for sidebar rows,showLock, a person-with-lock tile for a personal space and theSpaceIcon(Layers) export.ColorPickergainsHUE_COLORS,variant="inline"and a None swatch throughonClear.AppShellSectionwithmeasure="prose"holds a start-aligned 720px column.AvatarStackgainstotalandfooter, overlaps by 4px and draws initials from letters only. The comment composer puts attach beside Send on the right.- The settings guidance and
settings-01move to autosave, with confirmation for consequential changes and explicit Save only for drafts that must apply together. DataList's own empty state is the only empty state in a list, and its empty row keeps an inset. - Board keyboard moves out of a
droppable: falselane work again; date components render a clock-free absolute date without a request reference and re-render only when their label can change;refresh={false}freezes at mount; pickers mark the provider zone's today and presets take atimeZone;MultiStepFormnever resolves a step index of -1; unsupported pastes show a toast; stale DataGrid focus requests expire; grid cards with their own controls are labelled groups; and stale focus-border and toggle-group docs are corrected.1e016bc
- New
📦 npm
@vegastack/design→0.7.101(was0.7.100).- The design-system registry (
@vegastack/ui) bumps 0.23.115 → 0.23.116.
[0.23.115] — October 3, 2026
📦 npm
@vegastack/design→0.7.100(was0.7.99).- The design-system registry (
@vegastack/ui) bumps 0.23.114 → 0.23.115.
🐛 Fixed
-
Preserve permissions, editor content and interaction state across asynchronous work, and make design verification report the behavior it actually checks.
- Guard board drops, serialize pending moves, cancel lazy playback, retain wizard locks and support keyboard activation of grid cards and virtualized cells.
- Normalize image URLs, bound thumbnail inflation, preserve unsupported paste selections and honor React callback-ref cleanup.
- Render relative timestamps from a shared request reference and live clock; hide unavailable comment composers without hiding replies.
- Preserve no-ring focus while raising the existing text-entry border tint to the first standard opacity that passes the unchanged contrast threshold.
- Protect cleanup and idempotency checks, verify negative-test identity, load credential-only dotenv values and correct generated installation/API/example guidance and docs demos.
7152602
-
Keep calendar dates and due labels live across viewer midnight, with stable request-time hydration.
- DateTime and DueLabel use the existing shared clock; explicit DateTime options.now stays controlled and formatting/tooltip choices remain intact.
- Use the bundled full Chromium headless channel for component verification after repeated legacy headless renderer crashes. CI retains native failures and reports only process/signal metadata, never memory contents.
7152602
-
Keep date-picker Today highlighting and application date groups on the shared viewer clock.
- DateRangeFilter forwards its explicit calendar-day reference to the custom Calendar.
- Export useDateTimeNow from the relative-time registry item for groups, badges and picker presets, preserving serialized SSR references and one shared live timer.
7152602
[0.23.114] — October 2, 2026
🔧 Changed components
boardanddata-list: newcanMoveItemprop locks single items — a view-only record in an editable view. OnBoard,canMoveItem(item)returningfalsemeans the card cannot be picked up by pointer, touch or Space, keeps the default cursor (nocursor-grab), carriesdata-move-lockedand drops the "Draggable card" role description; it still focuses, opens, keeps its ⋯ actions, and other cards move around it. OnDataList,canMoveItem(row)makes that row (and grid card) no drag source withdata-move-locked, leaves it out of a dragged selection, keeps it openable and a drop target, and passes through to the board view. The docs gain a "Locked cards" board example and a locked row in the drag-into example.992e9f3
📦 npm
@vegastack/design→0.7.99(was0.7.98).- The design-system registry (
@vegastack/ui) bumps 0.23.113 → 0.23.114.
[0.23.113] — October 2, 2026
🔧 Changed components
InboxFiltersgainsextra: app-defined chips after All and Unread, each{ value, label, count? }(newInboxFilterOptiontype;InboxFilterwidens to accept their values), so an inbox shows All · Unread 3 · Requests 2 without a second control. A chip's count is now separated by a space in its accessible name ("Unread 3").notifications-01adds a Requests chip for rows that ask for a decision.cce47edshare-01: newgeneralLevelReadOnlyprop onShareDialogshows General access's level as text while a manager can still switch between "Everyone in ‹space›" and "Only people invited".defaultInviteesis now reactive — when it changes (compared by id) the invite chips follow it, so a "Share with Priya" prompt can open the mounted dialog with Priya chosen; chips the viewer edits stay until the prop changes again. The dialog body's section gap moves from the off-scalegap-5togap-4.b19f577
📦 npm
@vegastack/design→0.7.98(was0.7.97).- The design-system registry (
@vegastack/ui) bumps 0.23.112 → 0.23.113.
[0.23.112] — October 1, 2026
🧩 New components
- Sharing parts.
PersonAvatargainskind: "team"(a rounded-square tile with the team icon on its hue), andAvatarStackstacks and lists teams beside people. NewSpaceAvatar(a space's tile — icon or initial on its hue, a corner lock when private, the lock itself for a personal space) withSpaceOption, the space row for pickers and menus. NewPeopleInput: add people or teams as removable chips, searched as you type through the host'ssearch(query), with Backspace removing the last chip. NewPermissionMenu: an access level with one-line descriptions, a check on the current one, an optional destructive Remove access, and a read-only mode for built-in rows. New blockshare-01: a data-agnostic Share dialog (a bottom Sheet on phones) with an invite mode that gives the whole batch one level, people with access and why, general access in the item's space, and a view-only public link with copy, reset, expiry and Stop sharing.2df5784
📦 npm
@vegastack/design→0.7.97(was0.7.96).- The design-system registry (
@vegastack/ui) bumps 0.23.111 → 0.23.112.
[0.23.111] — October 1, 2026
📦 npm
@vegastack/design→0.7.96(was0.7.95).- The design-system registry (
@vegastack/ui) bumps 0.23.110 → 0.23.111.
🐛 Fixed
- Composer controls never touch the border: the paperclip and Send are 24px and sit 4px in from the border on every side (still 32px tall at rest, pinned to the last line as it grows). A record's new-comment box (
CommentComposerwithoutreplyingTo) reserves two lines at rest; reply boxes stay one line.1dbc57e
[0.23.110] — October 1, 2026
🔧 Changed components
- Comments match the shared web/mobile spec. A 24px avatar 8px from the name, 4px from the name row to the body (or files), 6px from the body to the files, 12px between comments; thread cards have a 1px border, a light fill and 4px padding (
CommentThreadtoo). NewCommentMedia: a comment's (or a draft's) images as a wrapping row of rounded previews up to 200px tall that open theFileViewer. The composer follows the mobile layout: paperclip on the left, Send on the right, both centred on the line and 6px in, on a light fill (TextEdit's composer gainsleading). Reaction pills always have a light border.InlineChipPersontakeshue(initials on the member's colour, as comment avatars) andcardImage; a person chip always leads with their avatar.0df640e
📦 npm
@vegastack/design→0.7.95(was0.7.94).- The design-system registry (
@vegastack/ui) bumps 0.23.109 → 0.23.110.
[0.23.109] — October 1, 2026
📦 npm
@vegastack/design→0.7.94(was0.7.93).- The design-system registry (
@vegastack/ui) bumps 0.23.108 → 0.23.109.
🐛 Fixed
- TextEdit composer is 32px at rest (the editor surface's 24px minimum no longer adds 4px) and an empty
footertakes no room; a click on an image selects it.04a041e
[0.23.108] — October 1, 2026
📦 npm
@vegastack/design→0.7.93(was0.7.92).- The design-system registry (
@vegastack/ui) bumps 0.23.107 → 0.23.108.
🐛 Fixed
- TextEdit type-checks against Tiptap 3.31.4: the paragraph parser no longer calls
Paragraph.config.parseMarkdown(whosethistype changed), it builds the paragraph itself.c367765
[0.23.107] — October 1, 2026
📦 npm
@vegastack/design→0.7.92(was0.7.91).- The design-system registry (
@vegastack/ui) bumps 0.23.106 → 0.23.107.
🐛 Fixed
- TextEdit read view and editor are pixel-identical. A callout lays out in one row with its icon at the same x in both (the read icon no longer spans two rows; the editor's tone button takes the icon's 16px column), and a read-only code block's language label sits where the editor's picker does. The swap browser test now compares every block of a rich document (headings, lists, tasks, quote, callout, code, table, toggle, file chip, rule) — position, height and first-character x — between the read view and the editor.
cc31e7f
[0.23.106] — October 1, 2026
📦 npm
@vegastack/design→0.7.91(was0.7.90).- The design-system registry (
@vegastack/ui) bumps 0.23.105 → 0.23.106.
🐛 Fixed
- TextEdit swap. A
variant="composer"box mounts its editor at once (it is an input — no read view to click through first), and every editableTextEditfetches the editor when the page goes idle, so the first click swaps it in immediately. A new browser test holds the read view and the editor to the same first-line position, height and caret start (within half a pixel) for documents and composers, empty and with text.a7fdd5a
[0.23.105] — October 1, 2026
🔧 Changed components
- TextEdit polish. Inline code keeps a neutral
mutedchip with orange (tag-orange-text) text, Slack/Notion-style, in every markdown surface. The callout tone menu shows each icon in its tone colour; the callout's tone button is centred on the first text line, keeps its tone colour while pressed or open, and takes a light tone-tinted hover wash. Toggles use Notion's filled triangle (▶, turning to ▼ in 150ms) in a muted icon button with a hover wash, here and inMarkdownView.3a257c9
📦 npm
@vegastack/design→0.7.90(was0.7.89).- The design-system registry (
@vegastack/ui) bumps 0.23.104 → 0.23.105.
[0.23.104] — October 1, 2026
🔧 Changed components
- TextEdit: images are blocks and a composer variant. An image is a block of its own (still
in markdown; an image inside a paragraph is lifted into its own block on load, andMarkdownViewshows it the same way). Dragging an image moves it between blocks with the block drop line (2px, info blue); pasted, dropped or inserted images, videos and audio land after the current block. A selected image has a 2px info-blue ring at its radius and handles in the same colour, with no ring on hover. Newvariant="composer"withactionsandfooter: a box that looks exactly likeInputat rest, grows line by line to about ten lines then scrolls, actions pinned bottom-right, file cards under the text.CommentComposer, replies and in-place edits use it; the old composer styles are gone.c9140ce
📦 npm
@vegastack/design→0.7.89(was0.7.88).- The design-system registry (
@vegastack/ui) bumps 0.23.103 → 0.23.104.
[0.23.103] — October 1, 2026
📦 npm
@vegastack/design→0.7.88(was0.7.87).- The design-system registry (
@vegastack/ui) bumps 0.23.102 → 0.23.103.
🐛 Fixed
- TextEdit fixes from a live check. Slash and mention menu items use
DropdownMenuItem's look (accent highlight, radius, padding, shortcut text), and the active item is markeddata-selected="true"so apps that map the variant to="true"show it. Callouts render their text as theAlert's description, in the tone colour, with the tone icon in colour. Code blocks scroll unwrapped lines sideways in the editor too (Tiptap's content element no longer forcespre-wrap). A closed toggle hides only its body, not its title. Images: a thin outline at the image's own radius when selected, Notion-style side handles only, no extra spacing, and a blurred hover toolbar (Open, Download, Copy link, ⋯ Replace / Delete). A file chip opens in theFileVieweron a double-click or from the link panel's Open.e69fa24
[0.23.102] — October 1, 2026
🧩 New components
- New
InlineChip: the one inline reference — a person, page, file, task, meeting, customer or project — forTextEdit,MarkdownViewandComments. It is an inline box on the text's baseline (no more label riding above the line), sized by the surrounding text with a 1em icon, tinted per kind from the tag hues, and wraps cleanly. A person chip shows its photo and previews avatar, name and email on hover or focus; other chips open on click (onOpen, e.g. a file inFileViewer, elsehref), ⌘/Ctrl-click opens a new tab, andpreviewadds a small hover card (InlineChipPreview).InlineChipProviderresolveshref,person,previewandonOpenfor every chip below it.MentionChipand file links now render through it, and mentions gain themeeting,customerandprojectkinds.610784a
📦 npm
@vegastack/design→0.7.87(was0.7.86).- The design-system registry (
@vegastack/ui) bumps 0.23.101 → 0.23.102.
[0.23.101] — October 1, 2026
🔧 Changed components
- TextEdit blocks and media. Code blocks always show the language picker and get icon-only wrap and copy buttons (a check confirms the copy); wrap is saved after the language in the fence info string (
ts wrap) andCodeBlocktakeswrap; unwrapped lines scroll inside the block with a thin scrollbar. Editor tables use a fixed layout, so typing wraps inside the cell. Toggles get a clickable chevron that opens and closes them (saved as<details open>), with hints in an empty title or body;MarkdownViewdraws the same chevron. New video and audio blocks (<video src>/<audio src>, native players, also inMarkdownView); a dropped video or audio file lands as one. The slash menu's Image, Video, Audio and File open an insert panel with Upload and Link tabs.35824b2
📦 npm
@vegastack/design→0.7.86(was0.7.85).- The design-system registry (
@vegastack/ui) bumps 0.23.100 → 0.23.101.
🐛 Fixed
- TextEdit menus and callouts. The slash menu has one active item that the pointer and ↑/↓ share (pointer cursor on items), scrolls it into view inside the list only, and lists commands in one canonical order wherever a subset is passed (comments and full editors match). The selection, link and image menus open just above the caret or selection without page scroll jumps. Callouts render as
Alerts in the editor and inMarkdownViewand gain GitHub's[!IMPORTANT]and[!CAUTION]; the tone icon opens a menu. Inline code in prose is tinted orange (tag-orange-textontag-orange-subtle).b2d18f3
[0.23.100] — October 1, 2026
🔧 Changed components
- Comment boxes. One line of text is exactly the controls' height, so a comment or reply box stays the
Inputheight (32px) whether focused or not — no extra space when the editor loads — with theInputtype size (16px on a phone, 14px from md); the attach and send buttons stay on the last line as the text grows. The paperclip is muted, so Send carries the emphasis.47bbd1c
📦 npm
@vegastack/design→0.7.85(was0.7.84).- The design-system registry (
@vegastack/ui) bumps 0.23.99 → 0.23.100.
[0.23.99] — October 1, 2026
🔧 Changed components
TextEditimages resize with Tiptap'sResizableNodeView(corner and side handles, aspect kept, snap guides at ¼ ½ ¾ and full width, touch included), keep their width in Markdown asand in HTML aswidth, and carry a ⋯ menu — Open, Download, Copy link, Remove image. Open, a double-click, and a click on an image in a read-only document or a posted comment show it in theFileViewer, paging through the document's images;ImageViewerScopedoes the same around anyMarkdownView, which now renders the stored width.ec194c9
📦 npm
@vegastack/design→0.7.84(was0.7.83).- The design-system registry (
@vegastack/ui) bumps 0.23.98 → 0.23.99.
[0.23.98] — October 1, 2026
🔧 Changed components
- Comments. The comment and reply boxes take the
Inputradius (rounded-lg) instead of a near-pillrounded-xl.CommentListshows no "No comments yet" line by default — the composer alone;emptyTextstill adds one. Delete in a comment's ⋯ menu callsonDeleteat once, without a confirmation dialog (hosts offer Undo). A box'sfiles(the draft's attached files) now render inside the box at its bottom, under the text, as the same cards a posted comment shows — not above the box; withonAttachFiles, pasted or dropped files join them instead of going into the text.CommentComposerandCommentThread'scomposertakehasFiles: a draft with attached files can be sent without text. Reaction pills are lighter: a soft hairline border with no fill, and a light primary tint when reacted, in both themes.c8965a5
📦 npm
@vegastack/design→0.7.83(was0.7.82).- The design-system registry (
@vegastack/ui) bumps 0.23.97 → 0.23.98.
[0.23.97] — October 1, 2026
🔧 Changed components
- Comment boxes and code blocks. A code block's language label and Copy button get their own room above the code (
codeBlockControlsPadClassName) instead of covering the first line, inCodeBlockand inTextEdit.TextEdittakesslashHint(default on); comment boxes turn it off, so "Add a comment…" and "Write a reply…" stay while focused instead of "Type / for commands". A press on aTextEditbox's padding before its editor has loaded now focuses the read view, so text typed while the editor loads is kept and replayed with the caret at its end.ba00a25
📦 npm
@vegastack/design→0.7.82(was0.7.81).- The design-system registry (
@vegastack/ui) bumps 0.23.96 → 0.23.97.
[0.23.96] — October 1, 2026
🔧 Changed components
- File chips, inline images and comment files.
MarkdownViewandTextEdittakefileContentType(href)so a file chip's icon follows the file's content type when the host knows it, not only the name's extension.TextEdittakesinlineImageTypes(default JPEG, PNG, WebP, GIF and AVIF): a pasted, dropped or picked file of another type — an SVG, a HEIC photo — goes toonFileUploadand lands as a file chip instead of an inline image.CommentComposer(andCommentThread'scomposer) takeonAttachFiles, which receives the attach button's files instead of inserting them into the text (pasted and dropped images stay inline), andfiles, shown over the box for the draft's attached files and their upload progress;mentionImagereaches the comment boxes and bodies, andCommentItem,CommentThreadandCommentListpassfileContentTypeto each body;CommentListtakesrenderAttachments.b15a39b
📦 npm
@vegastack/design→0.7.81(was0.7.80).- The design-system registry (
@vegastack/ui) bumps 0.23.95 → 0.23.96.
[0.23.95] — October 1, 2026
🔧 Changed components
- Comments and editor polish.
CommentComposerand the reply box are one line and 32px tall at rest, with a paperclip attach button (shown whenonImageUpload/onFileUploadis set; picked files go through the editor's own upload flow) and Send inline on the bottom line as the text grows;replyingTo/onCancelReplyshow a "Replying to" line (× or Escape cancels and keeps the text) and the "Write a reply…" placeholder.CommentListwith no comments shows a muted "No comments yet" line with the composer under it (no Empty illustration or "Add a comment" step). Replies are flush rows inside their comment's card (noms-9indent), avatars are 28px, long names truncate, and composer buttons get 40px touch targets. Mention chips are inline-flex on the info tint (bg-info/10,text-info-text, hoverbg-info/15; restricted stays muted) with a centred icon, and a person chip shows a 16px avatar via the newmentionImageprop onMarkdownViewandTextEdit.TextEditHandlegainsuploadFilesandpickFiles. The block drag handle is a 16px grip inset from the frame edge, and the drop line (the shareddropIndicatorClasses, SortableList included) is info blue and inset. Paste parses Markdown from the plain-text flavour when the HTML only wraps code (VS Code, a lonepre), turns a single fenced block into a code block, and Mod-Shift-V pastes plain text.CodeBlockand the editor's code block are Notion-style: no border or header bar,bg-muted/60, 13px mono, the language (a searchable picker while editing) and Copy on hover, lowlight syntax highlighting with alias normalisation, Tab/Shift-Tab indent and any-language fence input rules.utilities.cssadds token-mapped.hljs-*colours for both themes.AutoSaveIndicator's saved check is muted ink instead of green, and errors show in warning ink.ffdee11
📦 npm
@vegastack/design-tokens→0.7.80(was0.7.79).@vegastack/design→0.7.80(was0.7.79).- The design-system registry (
@vegastack/ui) bumps 0.23.94 → 0.23.95.
[0.23.94] — October 1, 2026
📦 npm
- Ships beside
@vegastack/design-tokens0.7.79 (the INT-13 comment relabel inbase.css), so its@vegastack/design-tokensrange moves with it. No code change.4db7d90 - The design-system registry (
@vegastack/ui) bumps 0.23.93 → 0.23.94.
📚 Docs
base.css: the no-iOS-focus-zoom rule now names its own register row, INT-13 (MK 2026-09-26), instead of INT-9 (the grab-cursor decision), and the file's header list includes it. Comment only; no CSS change.c01c929
[0.23.93] — September 30, 2026
🔧 Changed components
base.cssstops the page bouncing under a mouse or trackpad (INT-12): onpointer: fine,html, bodygetoverscroll-behavior-y: none, so the page no longer rubber-bands at its top and bottom edges and an inner scroller at its end no longer chains into the page. Vertical only, so a trackpad two-finger swipe still goes back/forward; touch devices keep pull-to-refresh and the native bounce.8825076
📦 npm
@vegastack/design-tokens→0.7.78(was0.7.56).@vegastack/design→0.7.78(was0.7.77).- The design-system registry (
@vegastack/ui) bumps 0.23.92 → 0.23.93.
[0.23.92] — September 30, 2026
🔧 Changed components
DataListpassesselectionSecondaryActionsandselectionMoreLabelto its selection bar'sActionBar(assecondaryActionsandmoreLabel).BreadcrumbTrailtakesrenderCurrentto render the current step's content, and a step'sitemPropsspread onto itsBreadcrumbItem(data-*attributes,onDragOver/onDrop).4663fa5
📦 npm
@vegastack/design→0.7.77(was0.7.76).- The design-system registry (
@vegastack/ui) bumps 0.23.91 → 0.23.92.
[0.23.91] — September 30, 2026
🔧 Changed components
AutoSaveIndicatortakesvariant="icon": the status as one 16px icon for a field's trailing slot — a muted spinner while saving, a success check once saved that fades out when the status returns to idle, and a destructive warning triangle on error (warning ink on conflict). Each state pops in (fade plus a 0.9 → 1 scale), the box is always reserved, and the wording stays as screen-reader text in the live region. In a textareaInputGroup, an inline addon now sits top-aligned with the first line instead of mid-height (FRM-16), so a status icon lands in the top-right corner.f45d422
📦 npm
@vegastack/design→0.7.76(was0.7.75).- The design-system registry (
@vegastack/ui) bumps 0.23.90 → 0.23.91.
[0.23.90] — September 30, 2026
🧩 New components
MarkdownViewtakescitation: a[[n]]marker (n = 1–999) renders as a small superscriptn— a muted pill button named by the source'slabelthat opens itscontenton hover, keyboard focus or a long press and callsonSelecton a click or tap. A marker without a source, every marker without the prop, and markers in code or link text stay literal.TextEditpassescitationto its read view, where pressing a marker never starts editing.Transcripttakesreveal={{ id, key }}: the row scrolls to centre, takes the accent wash for a moment, and following pauses.b688b9e
📦 npm
@vegastack/design→0.7.75(was0.7.74).- The design-system registry (
@vegastack/ui) bumps 0.23.89 → 0.23.90.
[0.23.89] — September 30, 2026
🔧 Changed components
- Design review fixes. The
TableOfContentsrail opens as a solid, raised panel over the page (z-20, a shadow).DataList'smergedLayout="line"is one truncating meta line: avatars in it are hidden, and a value that renders nothing,""or"—"is dropped with its dot. Keyboard focus in aDataListrow now washes the whole row and paints a 2px start bar on its first cell (a background layer, FOC-13).ActionBarstays inside the viewport and takessecondaryActions, which fold into a ⋯ menu (moreLabel) belowsm.VideoPlayerkeeps its controls, and the play button, shown on a coarse pointer.FileTypeIcontakestinted, and the newFileKindTile(withFILE_KIND_LABEL) is a no-preview card's picture.MarkdownViewandTextEdittakeheadingScale="document". Menus size to their longest item, so items never wrap (OVL-19). The newBreadcrumbTrailfolds its middle steps into a "…" menu before it shortens the current step.FileViewerreads Excel workbooks (sheet tabs, the first 500 rows) and Word documents itself, with SheetJS and mammoth imported only when such a file opens, up to 20 MB.6886ed1
📦 npm
@vegastack/design→0.7.74(was0.7.73).- The design-system registry (
@vegastack/ui) bumps 0.23.88 → 0.23.89.
[0.23.88] — September 30, 2026
🔧 Changed components
AlertActionnow lays its children out in a wrapping flex row with agap-2gap, so two buttons (a "Resend invite" beside a "Revoke invite", say) go straight inside it with noButtonGroupor wrapperdiv, and wrap onto a second line when the alert is narrow. The Alert docs gain a Two actions example.552698a
📦 npm
@vegastack/design→0.7.73(was0.7.72).- The design-system registry (
@vegastack/ui) bumps 0.23.87 → 0.23.88.
[0.23.87] — September 30, 2026
📦 npm
@vegastack/design→0.7.72(was0.7.71).- The design-system registry (
@vegastack/ui) bumps 0.23.86 → 0.23.87.
🐛 Fixed
AudioWaveformfits its bar count to its width (2px a bar, 1px apart), so manypeaksor a largebarsin a narrow card never thin to nothing. A comment reopened for editing from the ⋯ menu starts from the host's retained draft (editDefaultValue) instead of the stored body;CommentListandCommentThreadpass it through the same way.f98f37c
[0.23.86] — September 30, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.85 → 0.23.86.
🐛 Fixed
- RecordChip — the development-only "
valueis ignored withperson" warning now fires from a module-level helper instead of reassigning a module variable during render, so the component passes React'sreact-hooks/globalslint rule in consuming apps. Behaviour is unchanged: one warning per page load, none in production. docs ·d63eb17
[0.23.85] — September 30, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.84 → 0.23.85.
🐛 Fixed
- SearchableSelect — a person option can no longer render two avatars and two emails. With the person contract (
itemToStringLabelfor the name,itemToSecondaryLabelfor the email,itemToAvatarfor thePerson), the component draws each option as onePersonOptionand the trigger as one avatar plus the name;renderItemandrenderValueare ignored there, with a one-time developmentconsole.warnwhen they are passed.renderItemis now optional and defaults toitemToStringLabel.FilterBarFacetfollows the same rule, andRecordChipwarns in development whenvalueis passed withperson(the person already wins). docs ·6d5ec6f
[0.23.84] — September 30, 2026
📦 npm
@vegastack/design→0.7.71(was0.7.70).- The design-system registry (
@vegastack/ui) bumps 0.23.83 → 0.23.84.
🐛 Fixed
- A sidebar item's
showOnHoveraction no longer overlaps its count.SidebarMenuActionnow writes thedata-show-on-hoverattribute the badge swap selects on — Base UI lower-cases state keys, so it had only ever writtendata-showonhoverand the API-33 swap never engaged. At rest the count shows; on hover, keyboard focus or while the action's menu is open the action takes the count's place, with no layout shift or motion. Touch keeps both side by side: belowmd, and now also on a coarse pointer at any width, the action stays visible and the count sits just before it.ceb5c59
[0.23.83] — September 30, 2026
📦 npm
- The audio player loads less.
AudioPlayerProvider,useGlobalPlayer,useGlobalPlayerTimeandGlobalAudioPlayernow live inaudio-player-global(still re-exported fromaudio-player): import them from there in an app shell and the player UI loads only once a recording opens, so routes that never play one ship no player code.AudioPlayertakesdeferControls: a light play button (and the still waveform withpeaks) until the first play, then the full controls with focus on their play button, for lists of recordings.fae3c2e @vegastack/design→0.7.70(was0.7.69).- The design-system registry (
@vegastack/ui) bumps 0.23.82 → 0.23.83.
[0.23.82] — September 30, 2026
📦 npm
@vegastack/design→0.7.69(was0.7.68).- The design-system registry (
@vegastack/ui) bumps 0.23.81 → 0.23.82.
🐛 Fixed
- Dates no longer differ between the server and the browser.
RelativeTime'slocaledefaults toDEFAULT_LOCALE(en-US, now exported from thedate-timelib) instead of the runtime's, so a server under a non-USLANGrenders the same text as the browser and the page hydrates cleanly. This covers every DS part that shows aRelativeTime(inbox, comments, version list, media card, item, data list, truncated text).DatePicker's display and a comment's "Edited" time use the same default.aed72d5
[0.23.81] — September 30, 2026
🧩 New components
- Library redesign gaps.
AppShellHeaderis a width container (@container/app-shell-header), so header tools compact by the header's own width.FileViewertakesactions(item): your controls in the top bar before Download, kept at icon size belowsm. One media plays at a time: starting anAudioPlayer,VideoPlayer, the global player or aFileViewerstage pauses any other that is playing, withexclusive={false}to opt out. While open,GlobalAudioPlayersets--dock-inset-bottomon theAppShellroot;ActionBarfloats above it, and a selectingDataListends with a spacer so its last row and Load more stay reachable.useDragIntotakesonDropFilesandonFilesOver: its targets take files dragged in from the desktop (folders walked, withrelativePath), and an outeruseFileDropsurface leaves that drop alone.DataListpasses them through asonDropFilesOnRowandonFilesOverRow, andFolderTreeasonDropFilesandonFilesOver. The denselggrid drops to 1 column below a 20rem container, and thelggrid skeleton is theMediaCard lgshape (16:9 and two lines).RecordLayouttakesstack: below 64rem the rail flows under the main column, with one scroller. Comments:CommentItemtakeseditDefaultValue, andCommentListandCommentThreadtakeeditingId,onEditingIdChangeandeditDefaultValue(commentId), so an unsaved edit survives a remount.375e445
📦 npm
@vegastack/design→0.7.68(was0.7.67).- The design-system registry (
@vegastack/ui) bumps 0.23.80 → 0.23.81.
🐛 Fixed
TextEditkeeps what you typed when it is hidden and shown again (React's<Activity>), and image uploads still insert afterwards. It no longer offers block commands (headings, lists, quote, callout, toggle, code block, table, divider) or "Turn into" while the cursor is in a table cell.MarkdownViewrenders<br>,<br/>and<br />inside table cells as line breaks; other raw HTML still shows as text. AFolderTreefolder withhasChildren: falseis a leaf: a spacer in place of its disclosure,data-leafon the row, and → does nothing on it.375e445
[0.23.80] — September 30, 2026
🧩 New components
- Library grid and sidebar tree.
FolderTreetakesdragScope: its folders and section headings accept rows and cards dragged from aDataListin the same scope, and its own rows drop onto that list's folder rows and ontoBreadcrumbDropTargets; every drag now carries the host's own ids.canDropIntoadds the host's rule after the tree's own (and is the only rule for ids the tree does not hold),FolderTreeSection.hrefmakes a section heading a link (active whenactiveIdis the section's id) with its open/close on a chevron beside it, and the labels gaintoggleSectionanditemCount.DataListtakesgridDensity="dense": 2 columns, then 3, 4 and 5 as the container widens atgridSize="lg".MediaCardtakesimageBadge, a chip over the image's bottom end ("▶ 1:24") that never takes the click.audio-playerexportsAudioWaveform, the player's waveform as still,aria-hiddenbars for cards.bdf75c0
📦 npm
@vegastack/design→0.7.67(was0.7.66).- The design-system registry (
@vegastack/ui) bumps 0.23.79 → 0.23.80.
[0.23.79] — September 30, 2026
🔧 Changed components
- Comments keep drafts.
CommentComposertakesdefaultValue, the editor's starting text (Send is enabled while it is non-empty; the box starts empty after a post).CommentThread'scomposerpassesdefaultValue(the reply box opens unfolded on it),onValueChange(every change, and""after a reply posts) andposting(busy, OR'd with the reply's own state: Send and Cmd/Ctrl+Enter do nothing, e.g. while an upload runs).CommentItem,CommentListandCommentThreadtakeonEditValueChange(commentId, value), called with the edit box's text on every change and withnullwhen the edit is saved or cancelled, so a host can guard unsaved edits.c478509
📦 npm
@vegastack/design→0.7.66(was0.7.65).- The design-system registry (
@vegastack/ui) bumps 0.23.78 → 0.23.79.
[0.23.78] — September 30, 2026
📦 npm
@vegastack/design→0.7.65(was0.7.64).- The design-system registry (
@vegastack/ui) bumps 0.23.77 → 0.23.78.
🐛 Fixed
TextEditno longer erases saved text on Escape in a document that saves as you type. A newescapeBehaviorprop —"revert"(restore the document as focus found it, blur,onRevert) or"blur"(keep the text, commit, blur) — defaults to"blur"wheneverautosaveis set and to"revert"otherwise.handleRefgainsmarkSaved(), which advances the baseline Escape reverts to (and a commit compares against) after a host's own acknowledged save.d43ea00
[0.23.77] — September 30, 2026
📦 npm
@vegastack/design→0.7.64(was0.7.63).- The design-system registry (
@vegastack/ui) bumps 0.23.76 → 0.23.77.
🐛 Fixed
CommentListtakesmentionHrefand passes it to every comment, so mention chips in top-level comments link as they already did inCommentThread. A top or bottomSheetContentnow stops at85dvh(API-21), so a long body — a comment thread on a phone — scrolls insideSheetBodywith the footer in view instead of running past the screen.8e6abe4
[0.23.76] — September 30, 2026
🔧 Changed components
FileViewerpreviews every file type an app stores: text and code files show their first 1 MB, a CSV or TSV is a table (a built-inparseCsv; the header row and at most 500 rows, "Showing first 500 rows"), Markdown and MDX render throughMarkdownView(loaded lazily, JSX never run), and Office and other files are a card with the file's own picture (thumb), size, facts and a primary Download. A newloadPreview(item, { signal })lets the app hand back parsedtext,table,markdown,htmlorcardcontent (FileViewerPreview) so parsers stay app dependencies. Items gainrefreshSrc(an expired video or audio URL) andpeaks(a stored waveform); video letterboxes instead of cropping;data-kindnow names the stage on screen, and the plain file card iscard(wasother).VideoPlayerno longer shows a Quality menu unlessqualityOptionsare passed, never autoplays, documentsposter, shows "Can’t play this video here" with a Download button (downloadHref,loadErrorLabel,downloadLabel), and renews an expired signed URL withonSourceExpired, resuming where it stopped.AudioPlayertakes precomputedpeaksand decodes a file for its waveform only up tomaxDecodeBytes(20 MB), showing the plain seek slider above it; a renewal that returns the same URL now reloads it in both players.fileKindOftreatstext/plainas generic (the extension decides) and maps.mdx,.toml,.iniand.jsonc. New libmedia-probe:probeVideo(duration, size and a JPEG poster),probeAudio(duration and up to 200 peaks, skipped over 50 MB) andextractPptxThumbnail(an Office file's embedded preview picture), browser-only and dependency-free.3b005c7
📦 npm
@vegastack/design→0.7.63(was0.7.62).- The design-system registry (
@vegastack/ui) bumps 0.23.75 → 0.23.76.
[0.23.75] — September 30, 2026
🧩 New components
- Outline, keyboard comments, list selection and drag into folders. New
TableOfContents: an "On this page" outline with a scroll spy (the last heading abovemin(180px, 28%), the last one at the bottom),aria-current="location", alistor arailof ticks that opens on hover or focus, sticky placement, one tab stop with arrow keys, and a leftSheetthroughtrigger;useActiveHeadingis exported andpage-editor-01uses it.TextEditcomment highlights are tab stops in the read view (Enter or Space opens the thread) and open with Alt+Enter while editing;annotationCountsadds a count pill after each highlight (TextEditAnnotation.count,annotationCountLabel,annotationLabel).DataListadds shift-click ranges, ⌘/Ctrl+A, Esc and Space on a row link, grid-card checkboxes, aselectionActionsbar, and dragging rows onto folder rows and breadcrumbs (onDropInto,canDropInto,canDropOnRow,dragScope);useDragIntogainsscope,getDragIdsandgetDragPreviewLabel, andActionBar's actions scroll on a narrow screen. Newbreadcrumb-cascade:BreadcrumbDropTarget(a crumb that takes dropped rows) andBreadcrumbSiblings(a menu of a segment's siblings).268d6bd
📦 npm
@vegastack/design→0.7.62(was0.7.61).- The design-system registry (
@vegastack/ui) bumps 0.23.74 → 0.23.75.
[0.23.74] — September 30, 2026
🧩 New components
- Comments beside the text and version history.
CommentThread(incomments): a quoted thread with replies, a one-line reply box, resolve/reopen, orphaned and collapsed states;CommentItemtakesattachments, and the composers passmentions,mentionHrefand uploads to their editor. NewCommentMarginlays thread cards beside their highlights (active card aligned, the rest stacked) withCommentPopoverfor narrow screens;VersionListis a version history listbox (Current, Restored, Unsaved copy, Named only, Load more);DiffViewshows word-level changes between two texts, line-only above 200 KB, with the sanctioneddiffengine in its own lazy chunk. New block:page-editor-01.78e2a2c
📦 npm
@vegastack/design→0.7.61(was0.7.60).- The design-system registry (
@vegastack/ui) bumps 0.23.73 → 0.23.74.
[0.23.73] — September 29, 2026
🧩 New components
- New
FolderTree: a Notion-style navigation tree for a file library — sections of folders, pages and files; folders that load their children on first open (loadChildren, or a controlledchildrenOf) with loading, error (Retry), empty and "Show all" rows pastmaxChildren; one tab stop with tree arrow keys,*and typeahead; a ⋯ menu per row throughrenderRowActionsandFolderTreeRowAction; pointer drag into a folder or onto a section heading (onMove, which may be async); andmode="picker"— folders only, oneselected— for Move dialogs.use-drag-reordergainsuseDragInto, the drag-into-a-target engine the tree runs on. A newfile-kindlib gives every file surface oneformatBytes,fileKindOfandFileTypeIcon.AttachmentGrouptakeslayout="list"for dense full-width file rows;FileViewerplaysvideo/*andaudio/*items in the system players and pauses them when paging away;useFileDropandDropzonetakedirectories— each accepted file'srelativePaththroughonEntriesAccepted,maxFilesrefusing a whole oversized folder, andopenDirectory()(on the hook, and onDropzone's newactionsRef) for a folder picker. New block:library-01.655d595
🔧 Changed components
TextEditgains what a page editor needs, every construct round-tripping exactly through Markdown:@mentions (mentions,mentionHref) stored as[@Label](mention://kind/id); image and file uploads by paste, drop, the image panel and the slash menu (onImageUpload,onFileUpload,onUploadError,fileLinkPrefix) that commit even after blur; callouts (> [!NOTE],[!TIP],[!WARNING]) and toggles (details); an outline (onOutlineChange) and an imperativehandleRef(scrollToHeading,flush,focus,getAnchorForSelection,pulseAnnotation); and comment highlights (annotations,activeAnnotationId,onAnnotationClick,onAnnotationHover,onCreateAnnotation,onAnnotationsLayout) drawn as decorations, read-only too. ⌘K inside the editor no longer reaches a page's own palette.MarkdownViewrenders mention and file chips, callouts, toggles andheadingIds. New libstext-anchorandtext-anchor-docstore and map comment anchors.9799b19
📦 npm
@vegastack/design→0.7.60(was0.7.59).- The design-system registry (
@vegastack/ui) bumps 0.23.72 → 0.23.73.
[0.23.72] — September 29, 2026
🔧 Changed components
- Dates & times —
formatDurationtakesstyle: "long"for sentences and emails ("1 hour 15 minutes", "7 days"); newformatLongDatefor page headers ("Thursday, September 24", day-first withlocale: "en-IN", the year only outside the current year) andhourOfDayfor the hour on the viewer's clock (greetings, working hours). docs ·82ade2c - New
TemplateEditor: a plain-paragraph editor for text with spec placeholders. Typing@or{{opens a searchable list oftokens; the picked one becomes an inline chip that deletes as one unit and is stored as{{id}}. The value is plain text (paragraphs joined by a blank line) and round-trips exactly; unknown ids andinvalidTokenIdsrender in the destructive style. Sized and bordered likeTextarea, with its focus border.0e3bd65
🛠 CLI & tooling
vegastack-design check-updatesalso checks registry files outside the components dir —@lib/…targets underaliases.lib(date-time,page-layout,tile-overlay,drag-item,emoji-data,geo-data) and@hooks/…underaliases.hooks— so a locally edited lib file is drift and fails--fail-on-updatelike an edited component.--dirstill scans only that directory.82ade2c
📦 npm
@vegastack/design→0.7.59(was0.7.58).- The design-system registry (
@vegastack/ui) bumps 0.23.71 → 0.23.72.
[0.23.71] — September 28, 2026
🔧 Changed components
- DataList and DataGrid sort headers follow the standard indicator pattern: the sorted column's ↑/↓ always shows at foreground strength; other sortable columns keep their ⇅ transparent (space reserved, so labels never shift) and fade it in muted on header hover (fine pointers only) or keyboard focus, so touch shows only the active arrow; unsortable columns show nothing. Each sort button is now named
Sort by <column>alongside the header'saria-sort.343da2b
📦 npm
@vegastack/design→0.7.58(was0.7.57).- The design-system registry (
@vegastack/ui) bumps 0.23.70 → 0.23.71.
[0.23.70] — September 28, 2026
🧩 New components
- Field
FieldHeaderandFieldAction: a label row with a trailing field-level action (such as a ghostsize="sm""Add more" button) right-aligned on the label's baseline, wrapping on narrow widths, with the description staying under the label.ab64e86
📦 npm
@vegastack/design→0.7.57(was0.7.56).- The design-system registry (
@vegastack/ui) bumps 0.23.69 → 0.23.70.
[0.23.69] — September 28, 2026
🧩 New components
- Attributes and SKU generator support.
ComboboxChips showClearadds a clear-all × to the multiple-selection chips field (clearLabel, "Clear all"); both clear controls are muted until hovered and are a keyboard stop while the list is closed (API-34).FilterBuilderalways renders the Minimum–Maximum pair for a "between" (range) operator, even wheneditorsregisters a single-value editor for the field's type.MultiStepFormActionshas one footer everywhere: a ‹ icon button (Back, hidden on the first step) then Cancel at the start; the status, the optional outline save (newMultiStepFormSave) and a › icon button (Next) at the end; on the last step the finish takes the primary slot with its text label, and an outline save that repeats it steps aside — never two saves. ‹ and › carry "Back"/"Next" as their accessible name and tooltip;nextLabelnow defaults to "Next".RecordListgainsRecordListGroup(a heading over one list, for grouped conflicts) andRecordDiff(the compact "what differs" table that marks differing rows), and its titles take the foreground ink inside a statusAlert. New documented patterns: the review list dialog (pill tabs with counts over a scrolling numbered list), the conflict message (single and grouped), and checkbox chips for picking allowed values (ToggleGroup multiple wrap).23e7e5b
🔧 Changed components
@vegastack/designnow depends on@vegastack/design-tokenswith the--scrim/--scrim-foregroundpair, so a consumer installing the preset gets thebg-scrim/text-scrim-foregroundutilities thetile-overlayrecipe paints.dd91b95- New theme-invariant
--scrim/--scrim-foregroundtoken pair (black and white in both themes, withbg-scrim/text-scrim-foregroundutilities) for the wash a photo wears under a control. Thetile-overlayrecipe —Attachmenttile corner actions,SortableList's grid handle and actions, andAvatarPicker's photo overlay — now paintsbg-scrim/40 text-scrim-foreground(/60on hover and open) instead of rawbg-black/40 text-white, so it follows the tokens-only rule and a theme can retune every tile overlay in one place. The rendered result is unchanged.f667c77
📦 npm
@vegastack/design→0.7.56(was0.7.55).@vegastack/design-tokens→0.7.56(was0.7.49).- The design-system registry (
@vegastack/ui) bumps 0.23.68 → 0.23.69.
[0.23.68] — September 28, 2026
🧩 New components
- One page layout.
AppShellPagehas three widths —prose(720px of content, centred;narrowis its deprecated old name for one release),default(1280px, centred) andfull(edge to edge) — and one gutter,--page-gutter(16px, 24px from 640px, 32px from 1024px), whichAppShellHeaderpads with too andRecordLayoutRailsticks below, so the header, page and rail edges line up. The newpage-layoutlib'sdefinePageWidthsdeclares every route's width once for the page, its loading skeleton (widthOfPath) and a route test, andvegastack-design doctorfails on anAppShellPagethat sets its ownmax-w-*,mx-*or padding class and warns onsize="narrow". New docs page: Foundations › Page layout.3032c9d
📦 npm
@vegastack/design→0.7.55(was0.7.54).- The design-system registry (
@vegastack/ui) bumps 0.23.67 → 0.23.68.
[0.23.67] — September 28, 2026
🔧 Changed components
- Image and file tiles overhaul.
AttachmentGroup layout="tiles"shows at mostcolumnssquare tiles a row (four by default), three and then two as its container narrows;layout="scroll"withcolumnssizes each tile so that many fit the row. On a vertical tile,AttachmentActionsis an overlay slot —side="end"top-right (up to two icon actions),side="start"top-left (a drag handle) — inset from the corner, shown on hover or focus within the tile and always on touch, withAttachmentActionwhite on a blurred dark scrim.Attachmenttakesfile,preview(default on) andonOpen: a tile with a file is oneOpen {name}button that opens theFileViewer(lazily loaded), paging through its group or an enclosing newAttachmentPreview.AttachmentTitlecarries the full name as itstitle.SortableList's grid uses the same overlay slots and scrim for its ⠿ handle and ×/⋯ actions, andcolumnsis now a responsive cap instead of a fixed count. Every reorder drop indicator — SortableList rows and tiles, and TextEdit's block and table drag line — is drawn in the standardbordercolour from onedrag-itemtoken (dropIndicatorClasses). A newtile-overlaylib holds the shared scrim, overlay-slot and tile-grid recipe;AvatarPicker's photo overlay reads its scrim from it.866d5b1
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.66 → 0.23.67.
[0.23.66] — September 28, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.65 → 0.23.66.
🐛 Fixed
RecordLayoutRailkeeps its gap when the page scrolls: it sticks--record-rail-gap(default--spacing(8),AppShellPage's top gutter) below the top of the scroll container instead of touching the header, and its own scroll height leaves that gap above and below (--record-rail-offsetnow defaults to--spacing(14), theAppShellHeader).useRecordLayoutWide()also returnsrailandsheetmount flags — bothtrueuntil the layout is measured — so a server-rendered record page paints its rail on first load instead of popping it in after hydration.8321134
[0.23.65] — September 28, 2026
🔧 Changed components
TextEditandMarkdownViewtables are Notion's simple table: a rounded outer border, a rule between every cell, and a header row on a muted ground at medium weight — the sameprose.tablestyles in the read view and the editor. The floating table toolbar is gone; hovering a table shows a row grip and a column grip (click for a menu — insert, move, duplicate, clear, delete; drag to reorder), a corner grip where every block's handle sits (select the table, clear, delete; drag to move it; header row/column toggles in HTML), and "+" bars that add a row or column at the end. Column borders drag to resize.Shift+F10opens the menu from the keyboard, Tab in the last cell adds a row, and markdown round-trips unchanged. The stray block handle over a table is gone.fc926dc
📦 npm
@vegastack/design→0.7.54(was0.7.53).- The design-system registry (
@vegastack/ui) bumps 0.23.64 → 0.23.65.
🐛 Fixed
- Inline-edit fields paint no fill:
EditableCellvalues and the ghostInput(a dialog's title) no longer tint on hover — at rest, on hover and while editing they are transparent, and the text cursor is the affordance. Keyboard focus on a restingEditableCellkeeps the system focus tint. Text alignment and hit areas are unchanged; picker pills and menus keep their button hover.fc926dc
[0.23.64] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.63 → 0.23.64.
🐛 Fixed
- FileViewer: a PDF fits to a reader's page width (920px max) centred on the dark stage instead of the full screen; the previous/next buttons no longer drop by half their height when pressed; opening focuses the viewer itself, so the Download button no longer looks hovered.
007aec0 SortableList(and everyuseDragReorderitem): dragging a row by its handle no longer drags a picture of half the page. The browser sizes its drag image from the row plus every descendant's box, and aCheckboxorSwitchwithout anamekeeps its hidden input fixed at the viewport's top-left — so a row with a "Required" checkbox dragged everything between the page corner and the row. The drag image is now a copy of the row alone, held under the pointer where it was grabbed. Keyboard move mode, touch long-press, drop indicators and announcements are unchanged.d7e8139
[0.23.63] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.62 → 0.23.63.
🐛 Fixed
- FileViewer builds its file-type icon as an element, so consumers'
react-hooks/static-componentslint passes.d1a525f
[0.23.62] — September 27, 2026
🧩 New components
- New
UploadDialog(UploadDialog,UploadDialogFileRow) — the "Add files" modal: a drop zone with the staged files under it (thumbnail or file icon, name, size, ×), then Next to a per-file details view the host renders (renderDetails,detailsValid) and Add. WithoutrenderDetailsit adds straight from the staged list.validaterefuses a file with a message andmaxFilescaps the staged count. It stages files only —onSubmithands them to the host's upload queue and the dialog closes.AttachmentGroupgainslayout="tiles": compact square tiles for forms, two per row on a phone and three fromsmup.758ee45
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.61 → 0.23.62.
[0.23.61] — September 27, 2026
🧩 New components
- New
FileViewer— a full-screen dark overlay for stored files, controlled byitems,index(nullclosed),onIndexChangeandonOpenChange. Images open fit to the screen with the thumb's blur fading into the full-sizesrcSetvariant, zoom by double-click/double-tap, pinch, ⌘/Ctrl+scroll or +/−/0, and pan by drag; neighbours preload. PDFs render withpdfjs-dist(^6.3.289, now a sanctioned renderer, installed by the item and loaded lazily only when a PDF is shown, worker served same-origin) over range requests, as windowed canvas pages at the device pixel ratio with fit-width, zoom and "Page 3 of 20". Anything else shows its type icon, name, size and Download. ←/→, side buttons (fine pointers) or a swipe page; Esc or a swipe down closes and focus returns to the opener; paging announces "Image 3 of 12".ab850e4
🔧 Changed components
PropertyListtakescolumns={2}: rows flow into two columns once the list's own container is@xl(36rem) wide and stay one column in a narrower pane, with both thestackedandinlinevariants.EditableCellgains anumbereditor ({ type: "number", unit, min, max, step, placeholder }): the string value displays as1,234 mm(locale-formatted, unit muted), edits with a decimal keyboard and the unit trailing the caret in the same box, and a draft that is not a number, is outsidemin/maxor offstepstays open witharia-invalidand an announcement instead of saving; an empty draft saves"".Attachmenttakessize="lg": a 10rem vertical tile, and a larger media box when horizontal. AselectEditableCell inside aPropertyValuelines its option text up with plain values.ab850e4
📦 npm
@vegastack/design→0.7.53(was0.7.52).- The design-system registry (
@vegastack/ui) bumps 0.23.60 → 0.23.61.
[0.23.60] — September 27, 2026
📦 npm
@vegastack/design→0.7.52(was0.7.51).- The design-system registry (
@vegastack/ui) bumps 0.23.59 → 0.23.60.
🐛 Fixed
- TextEdit's block drag handle stays inside its container. The ⋮⋮ grip is now 10px wide and sits 3px before the text (was 18px, 4px out), so it fits in the 16px padding of a Dialog, Sheet, Popover or Card instead of touching the edge, and it is clamped just inside a
boxededitor's border or the container when the padding is narrower. The text keeps its column: nothing indents while the handle is hidden.84ae2d9
[0.23.59] — September 27, 2026
🔧 Changed components
- A ghost
Input(a title typed straight onto the page) keeps its hover tint only until it is focused: typing into a field under the pointer shows no grey, just the caret. AnEditableCellalready paints no fill while editing, in a property list or not.3d7aa6b - Heavy UI stays out of a page's first bundle.
TextEditrenders its saved value as light read HTML (server-rendered, same typography, still editable to the touch) and loads the Tiptap editor on hover, focus or tap, swapping it in place with the caret where you clicked;preloadTextEdit()loads it ahead of time.MarkdownViewnow parses withmarkedinstead ofreact-markdown(about a quarter of the size), addsformat="html"for rich-text HTML andallowedImageOrigins={["*"]}.DatePickerand theFilterBardate filter load the calendar engine only when the picker is about to open.0ccadf1
📦 npm
@vegastack/design→0.7.51(was0.7.50).- The design-system registry (
@vegastack/ui) bumps 0.23.58 → 0.23.59.
[0.23.58] — September 27, 2026
📦 npm
@vegastack/design→0.7.50(was0.7.49).- The design-system registry (
@vegastack/ui) bumps 0.23.57 → 0.23.58.
🐛 Fixed
- Pills sit one step above any surface, one empty pill, and a sidebar hover action.
SplitChip(behindRecordChip) drops itsbg-backgroundfill for the outline Button's surface-aware one — none in light, a translucentinput/30in dark — so a pill in a Dialog, Sheet, Popover or Card no longer reads darker than its surface. An emptyRecordChiplooks the same in both variants:variant="ghost"now draws the dashed rounded-full border around its muted placeholder too (inside the same 24px box), so every unset property pill — status, priority, assignee, due date, customer, project — shares one empty state.SidebarMenuAction showOnHoverswaps with the row's badge in the same spot (API-33): the count shows at rest and the action takes its place on hover, focus or while its menu is open, with no layout shift or motion; belowmdthe action stays visible and the count moves beside it.5d12da5
[0.23.57] — September 27, 2026
🔧 Changed components
- Form flow: Stepper labels keep one padding and weight in every state (no shift, no press motion) and a crowded horizontal rail scrolls; MultiStepForm gets self-clearing
validatechecks,mobileNav="stepper"and an action row withstart/status/secondaryslots (Cancel is a ghost button before Back); newFieldGrid/FieldGridItem/FieldChoices(responsive 1–3 column fields, inline radios and checkboxes);useAutoSave+AutoSaveIndicatorfor forms that save as you go;AttachmentGroup layout="grid"tiles at listing size.4cfe999
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.56 → 0.23.57.
[0.23.56] — September 27, 2026
🔧 Changed components
- Editable values in a
PropertyValueline up exactly with read-only ones: an inlineEditableCellthere keeps the plain values' left edge, baseline and row height at rest, on hover and while editing, with a compact tint (6px × 2px, hung outside by negative margins) instead of an offset 10px × 6px box. A ghostRecordChip(the property pickers) now draws the same compact 24px box, so every editable property value reads the same.baf9d99
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.55 → 0.23.56.
[0.23.55] — September 27, 2026
🔧 Changed components
SortableListlist rows are as dense as aDataList's — compact padding, 2px between rows (ItemGroup's 10px gap no longer applies), a subtle wash on hover — and the row's ⋯ menu trigger now shows on row hover or focus, while its menu is open, and always on touch, like the ×.1dba41b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.54 → 0.23.55.
[0.23.54] — September 27, 2026
🧩 New components
- New
RecordList(RecordList,RecordListItem,RecordListMore) — the records a change affects, for confirmation dialogs: numbered rows (an<ol>, so the numbers continue when "Show more" appends the next batch), a muted record-type icon, the name with a small ↗Open {name} in a new tablink right after it (shown on row hover or focus, always on touch), an optional badge and description line.88c9150
🔧 Changed components
SortableList: touch long-press reordering (the Board's model — a 250ms hold that moves under 8px lifts the row or tile; the page doesn't scroll under it and the long-press menu is suppressed) for lists and grids, beside the handle's pointer drag and keyboard move mode. The Move up / down / to top / to bottom menu items are gone. NewonRemoveputs a small × (Remove {label}) in each unlocked row's corner, shown on hover or focus and always on touch;getItemActionsstill gives a ⋯ menu for more actions, and a row with no actions has no menu. Grid:columns(2–6) fixes the tiles per row, andtile="bare"drops the tile frame so anAttachmentinside is the one frame. A locked row's reason is now the row's description.88c9150
📦 npm
@vegastack/design→0.7.49(was0.7.48).@vegastack/design-tokens→0.7.49(was0.7.46).- The design-system registry (
@vegastack/ui) bumps 0.23.53 → 0.23.54.
🐛 Fixed
- Text fields show focus as a subtle darker border that eases in (150ms), with no grey fill and no darker rectangle inside input groups.
base.cssagain leaves text inputs and textareas out of the background tint and drops the field-group tint that stacked a square inner rectangle over the group.Input,Textarea,InputGroup(and soNumberField,ChipInput,Combobox,Command,SearchInput,PasswordInput,AutoSaveInputand the popup search rows),ComboboxChips,PanelSearch, theQuestionnaireanswer field and theInputOTPactive slot takeborder-ring/40. An invalid field keeps its destructive border while focused. Buttons, rows, tabs, menu items and chips keep the tint.TextEditgainsvariant="document" | "boxed"andchildren.document(the default) stays caret-only.boxeddraws a bordered box whose border darkens on focus, and the comment composer now uses it. The ghostInputhas no focus fill.design-lint'sno-focus-borderallows the text-entry border only in those components, and the geometry sweep checks the new contract.a0e3482
[0.23.53] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.52 → 0.23.53.
🐛 Fixed
AudioPlayerno longer grows while it loads. The "Loading audio…" status row below the controls is gone: whileloadingis set, a lazysrcresolves, or the media buffers after play, the play button's glyph becomes a spinner of the same size in the same button (aria-busy, same accessible name, still pauses).loadingLabelis still announced once to screen readers.MediaPlayerControlsgains theloadingprop that draws it.c2bcdc8Board: an empty lane's "+ Add" action is now as wide as its label, centred under "Nothing here" like anyEmptyaction, instead of stretching the full lane so its hover background ran far past the text. After the last card it stays a full-width row.1e89391
[0.23.52] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.51 → 0.23.52.
🐛 Fixed
MediaCard'sbadgenow sits beside the title, whereDataList's list view puts a pill, instead of in the meta line — a long title truncates before it and the badge never shrinks; the meta line is justmetaandtimestamp. The record title has one weight in the grid and the list:DataList's first column is nowfont-medium, the grid card's title weight, through a sharedRECORD_TITLE_CLASSexported frommedia-card; merged values under it stay regular. Docs show the badge variants (with, without, long title) and the grid and list side by side.747476d
[0.23.51] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.50 → 0.23.51.
🐛 Fixed
- Photos show instantly.
PersonAvatarnow renders the photo as a plain<img>in the server HTML, layered over the initials, instead of Base UI'sAvatarImage(which mounts its<img>only after a JS loader reports it loaded) — so the browser fetches it from the markup and a cached photo is there on first paint, with no flash of initials; the initials come back if the photo fails.Imageno longer hides a server-rendered image until hydration: it paints as soon as it decodes, and only a client-mounted image that has not loaded yet fades in; a load or error that fired before hydration is read from the element. Newpriorityprop onImage(loading="eager"+fetchPriority="high") for above-the-fold images.8d620f5
[0.23.50] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.49 → 0.23.50.
🐛 Fixed
Boardlanes no longer scroll sideways: the lane body wasoverflow-y-auto, which turnsoverflow-xtoautotoo, so a few px of card overflow made the cards jiggle sideways and swallowed horizontal trackpad, Shift+wheel and touch pans. The lane body is nowoverflow-x-hidden overflow-y-auto overscroll-y-contain, and the board scroller istouch-pan-x touch-pan-y, so a sideways pan anywhere on the board scrolls the board. Each lane now has a subtle top-to-bottommutedfade (drop-over still swaps to the solidaccentwash), and "+ Add" moved from the lane's foot into the scrolling content: full width and centred right after the last card (afterLoadMore), or as the empty state's action under "Nothing here".a214a5b
[0.23.49] — September 27, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.48 → 0.23.49.
🐛 Fixed
AvatarPicker's dialog no longer leaves extra space under its footer: the hidden file input sat in the dialog's grid as a zero-height row and added a 16px gap, so it now lives, not displayed, inside the drop circle, whose wrapper isflexso the dialog no longer grows 4px when a photo replaces the initials. A pending call's spinner now sits in the button that started it (Update or Remove, vialoading), not over the photo; the other button and the circle are disabled until it settles.5e94a40
[0.23.48] — September 27, 2026
🔧 Changed components
AvatarPickeris now just the avatar circle that opens a "Profile photo" dialog. In the dialog a 128px circle is the file button and a drop target (with a "JPEG, PNG or WebP · up to 10 MB" hint); a chosen file is only staged as a preview, Update awaitsonUpload(file)and Remove clears a staged file or awaitsonRemove(). While a call is pending the dialog shows a spinner and cannot be dismissed; it closes when the call resolves and shows the error's message when it rejects. API:onUpload(file): Promise<void>replacesonSelect;onRemovereturns a promise;busyanderrorare gone (the component owns pending and error state); newtitle(default "Profile photo"),updateLabel("Update");sizeisxs · sm · md · lg · xl(32–80px) withsm(40px) the new default.48ec31e
📦 npm
@vegastack/design→0.7.48(was0.7.47).- The design-system registry (
@vegastack/ui) bumps 0.23.47 → 0.23.48.
[0.23.47] — September 27, 2026
🔧 Changed components
AvatarPicker's hover, focus and busy scrim: the pencil and the spinner are white again (the ghost Button's icon ink no longer overrides them), and the scrim no longer blurs, so the photo or initials stay recognisable under it. With no photo, the initials fade out while the scrim shows (hover, keyboard focus, busy), so only the icon sits on the person's colour; a touch device keeps the initials beside its corner badge.9af2c55
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.46 → 0.23.47.
[0.23.46] — September 27, 2026
🔧 Changed components
AvatarPickeris an avatar-edit control: the circle is the button (named "Upload photo" or "Change photo"), with a dark scrim and pencil fading in on hover and keyboard focus, and a small pencil badge on the edge on devices without hover. The separate Upload / Change photo button is gone. Whilebusythe scrim holds a spinner over an instant local preview of the chosen file;person.imageshows again whenbusyends.sizeis nowsm(40px) ·md(48px, the new default) ·lg(64px) ·xl(80px), with initials scaled to match. "Remove" is a small ghost button beside the circle.3c3b354
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.45 → 0.23.46.
[0.23.45] — September 27, 2026
🔧 Changed components
- Blur placeholders reach record grids and lists.
MediaCardtakesimagePlaceholder(a data URL blurred over the image area until it loads),imageSrcSetandimageSizes, passed to its thumbnail. ADataListcolumn'sthumbnailmay return{ src, placeholder?, srcSet?, sizes? }(theDataListThumbnailtype) as well as a URL string, forwarded to the row'sThumbnailand to the grid'sMediaCard; string thumbnails work as before.AvatarPickernames a refused HEIC/HEIF photo (“<name>is a HEIC photo. Export it as JPEG and try again.”) and announces every refusal and everyerrorexactly once through one polite live region.772f4b7
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.44 → 0.23.45.
[0.23.44] — September 27, 2026
🔧 Changed components
- People carry their own colour and photo everywhere.
AvatarFallbacktakes ahue(AvatarHue, the ten tag hues: initials in the hue's ink on its subtle fill; API-32). A new sharedperson-avataritem ownsPersonAvatarand thePersonshape (now withhue) —person-hover-cardstill re-exports them — and it exports the one initials rule,personInitials(name, email?): first letters of the first and last words, two letters of a single word, else the email's first two characters.RecordChip,BoardCardandInboxItemdrawPersonAvatarinstead of their own initials, and takehueonperson,assigneeandavatar({ name, src?, hue? });PropertyPersonand comment authors take it too.SearchableSelect(andFilterBarFacet) gainitemToAvatar, leading each person option — and a single selection on the trigger — with the person's avatar.ImageandThumbnailtake aplaceholderdata URL, a blurred cover until the image loads (Imagestill runs a caller'sonLoad/onError), andThumbnailpassessrcSet/sizes, retrying when either changes. NewAvatarPicker: a largePersonAvatarwith a busy spinner, Upload / Change photo through the file picker, Remove, and a line for a refused file.app-shell-01's user menu drawsPersonAvatar. The shippedvegastack-design-systemskill says a person is alwaysPersonAvatar.74415be personInitials(name, email?)now lives inperson-avatar— import it from@/components/ui/person-avatar. The separatepersonlib item is gone, soperson-avatarinstalls nothing into yourlib/alias.74415be
📦 npm
@vegastack/design→0.7.47(was0.7.46).- The design-system registry (
@vegastack/ui) bumps 0.23.43 → 0.23.44.
[0.23.43] — September 26, 2026
🔧 Changed components
FilterBar's search field is ~320px (w-80) on a desktop- or tablet-width bar again, with the Filters toggle and the view at the far end; it takes the full width only on a phone-width bar (below@lg), where it sits on its own row.98a6981
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.42 → 0.23.43.
[0.23.42] — September 26, 2026
🔧 Changed components
- One list toolbar for every list page.
FilterBar's filter row is hidden by default and never opens by itself when a filter becomes set — the Filters toggle shows it and carries the applied count meanwhile;autoOpenFiltersopts back in, andfiltersOpenStorageKeyremembers the toggle per page for the browser session. Row 1 is the search taking the free space, then the Filters toggle, then theviewswitch at the far end. On a tablet-width bar (below@3xl) the Filters toggle and the view go icon-only, with the count as a badge; below@lgthe search takes its own row and the icon-only toggle and view share the next one, start and end.PageHeaderdrops theviewslot (andpage-header-view) added in 0.7.42: the layout switch belongs only in theFilterBar'sview, and the header'stabshold the scope.list-page-01moves its Grid | List switch into the toolbar and uses the bar's "Clear". Docs: FilterBar gains a Breakpoints example, and the PageHeader "View tabs", DataList "View toggle" and list-page-01 pages show the layout.74adb14
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.41 → 0.23.42.
[0.23.41] — September 26, 2026
🔧 Changed components
RecordChipgainsvariant="ghost"for property values: no border and no fill, the value in plain body text (the same size, weight and colour as a plainPropertyValue), the standard tint on hover and keyboard focus, and a ▾ that fades in then and stays while the menu is open. A status or priority icon keeps its colour, and any other leading icon is dropped. A newpersonprop shows a 20px avatar (initials fallback) and an "Inactive"-style badge. ADueLabelvalue keeps its overdue colour, and an empty chip shows the muted placeholder.PropertyValuehangs a ghost chip's padding and 28px height outside the cell, so its text starts where plain values do and every row is the same height.SplitChip's name segment drops the button's transparent border, so its hover background sits the same 2px from the pill's border as the ↗ segment's on every side and around the separator.PropertyPerson's avatar is 20px, matching the ghost chip. Date labels losetabular-nums(BoardCard's due badge,MediaCard's timestamp), so dates use Geist's proportional figures everywhere, and tabular figures stay only on live timers. Docs: the RecordChip and PropertyList pages and the issue-detail-01 block show both variants and every state.fe030ca
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.40 → 0.23.41.
[0.23.40] — September 26, 2026
🔧 Changed components
- Comments: each posted comment sits in its own card (the composer's
bg-muted/30surface,border-border,rounded-xl), and so do the deleted placeholder and edit mode; the border never changes on hover or focus. The add-reaction and ⋯ buttons are identical ghosticon-smbuttons at the card's top-right, both shown on hover or focus-within (kept while their popup is open, always on touch). The ⋯ menu is sized to its content with muted Link, Pencil and destructive Trash2 icons.ReactionAddgains asizeprop (icon-xsdefault,icon-sm).5674880 - A border never changes colour on focus or while active (FOC-14). Every control keeps its resting
border-border/border-inputin every state, and the focus cue isbase.css's background tint everywhere — text entry included. A bordered field group (InputGroup,NumberField,ChipInput,ComboboxChips,PanelSearch, now alldata-field-group) wears the tint on the group;TextEditstays caret-only, and the comment composer box no longer re-borders when active.Input,Textarea,NativeSelect,Select,Combobox,InputOTP(the active slot takes the tint),Questionnaire,DatePickerandSearchableSelectghost triggers, andMediaPlayerControls' seek thumb lose their focus border; the ghostSelectno longer re-borders while open. The invalid state keeps its destructive border in every state, focused or not (thenot-focus:guards are gone fromButton,Badge,Toggle,Checkbox,RadioGroup,Switchand the fields).design-lintgainsno-focus-border, and the geometry focus sweep fails any control whose border colour moves on focus.5674880
📦 npm
@vegastack/design→0.7.46(was0.7.45).@vegastack/design-tokens→0.7.46(was0.7.45).- The design-system registry (
@vegastack/ui) bumps 0.23.39 → 0.23.40.
[0.23.39] — September 26, 2026
📦 npm
@vegastack/design-tokens→0.7.45(was0.7.43).@vegastack/design→0.7.45(was0.7.44).- The design-system registry (
@vegastack/ui) bumps 0.23.38 → 0.23.39.
🐛 Fixed
- Programmatic focus targets (
tabindex="-1") no longer paint the:focus-visiblebackground tint. Before this, clicking the empty part of AppShell's<main>and then pressing any key (Shift or ⌘ alone) tinted the whole page grey. Combobox chips keep the cue.1100891
[0.23.38] — September 26, 2026
🔧 Changed components
- Notion-grade
TextEdit. The editor has no fill in any state (the caret is the focus cue,data-focus-cue="caret") and never widens the page — long words, URLs and inline code wrap, code blocks and tables scroll in their own box (prosegainsmin-w-0 wrap-anywhere). Every menu floats in a<body>portal and flips to stay in view, so no container clips it. The slash menu adds Heading 4, Table and Image (URL and alt text); the bubble menu adds Turn into, Clear formatting and Open / Remove on links; code blocks get a language selector. GFM tables get a table menu (insert, move, delete rows and columns), drag grips to reorder columns and rows, and Tab / Shift+Tab between cells. A ⋮⋮ block handle drags any block or list item (dragHandles, on by default), and ⌘⇧↑ / ⌘⇧↓ move the caret's block. New shortcuts: ⇧⌘X strikethrough. Image-only paragraphs now round-trip.RelativeTimedefaults to the compact house form everywhere — "now", "19m ago", "3h ago", "2d ago", "3w ago", "5mo ago", "1y ago" — withformatchoosingsuffix(default),minimalorlong; the Intl short style with periods ("19 min. ago") is gone andunitStyleis deprecated. Comments: a lighter composer and edit box (bg-muted/30, hairline border), "Add a comment…" as the default placeholder, auto-grow to about twelve lines, a legible disabled send button, and no drag handles.43e36b5
📦 npm
@vegastack/design→0.7.44(was0.7.43).- The design-system registry (
@vegastack/ui) bumps 0.23.37 → 0.23.38.
[0.23.37] — September 26, 2026
🔧 Changed components
- Emoji reactions. New
Reactions(pills with counts that toggle, a hover card naming who reacted, an add button with quick reactions 👍 ❤ 😄 🎉 👀 🙏) andtoggleReactionfor optimistic updates.CommentItemandCommentListtakeonReactionToggleand read each comment'sreactions; the add button joins the hover actions.EmojiPickergains a quick row, a category bar, a Recent section,size="sm", a skeleton and a DS empty state, and loads its data lazily from the newemoji-datalib (EMOJImoved there).a7e0e2b
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.36 → 0.23.37.
[0.23.36] — September 26, 2026
🔧 Changed components
- The
date-timelib's module doc now reaches consumers:shadcn adddrops every comment before a file's first statement, so the doc moves belowDateInput. The code is unchanged.ec9fb47
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.35 → 0.23.36.
[0.23.35] — September 26, 2026
🔧 Changed components
- Every empty list, grid, table and board lane renders the design-system
Emptywith an icon: an Inbox icon and "Nothing here" by default. Change the copy with the newemptyprop ({ icon, title, description, action }) on DataList, on a DataList section and on a Board column;emptyStatestill replaces it outright, and the newDataListEmptyStateis the same state outside a DataList.NoResultsEmptytakes anicon. Board lanes keep theircolumnWidthhowever many lanes are collapsed, instead of stretching into the space a collapsed lane frees.base.cssrenders text-entry controls at 16px or more on touch pointers so iOS Safari never zooms into a focused field; the install docs and consume guide add the viewport'smaximumScale: 1.3c3b145 - TextEdit's markdown output is clean: blank lines typed with Enter no longer serialize as
between extra blank lines — empty paragraphs collapse, so paragraphs are separated by one blank line and the output reloads to itself. Blank lines inside fenced code are kept. Input no longer exportsinputVariants(nothing used it, and its fixed heights broke the data-table squeeze census).aeb7442
📦 npm
@vegastack/design→0.7.43(was0.7.42).@vegastack/design-tokens→0.7.43(was0.7.33).- The design-system registry (
@vegastack/ui) bumps 0.23.34 → 0.23.35.
[0.23.34] — September 26, 2026
🔧 Changed components
- Editing a comment uses the composer's compact box with icon buttons: a round ↑ Save (disabled while the text is empty or unchanged) and a ghost × Cancel, each with a tooltip; Cmd/Ctrl+Enter saves and Escape cancels. TextEdit's tests now cover every markdown element's round-trip (headings, strike, inline code, code blocks, quotes, dividers, task and nested lists, blank lines), commit on blur and unmount, Escape revert, autosave, slash-menu keys, undo/redo, paste and bubble-menu links.
efb06cc
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.23.33 → 0.23.34.
[0.23.33] — September 26, 2026
🔧 Changed components
- PageHeader gains
tabsandviewslots: a list page's view tabs sit on their own row directly under the title, with the layout switch (ViewToggle) pinned to that row's end. The list-page-01 block moves its Mine | Team scope (now defaultTabs) and Grid | List toggle there, out of the FilterBar.2bf27bf
📦 npm
@vegastack/design→0.7.42(was0.7.41).- The design-system registry (
@vegastack/ui) bumps 0.23.32 → 0.23.33.
[0.23.32] — September 26, 2026
🔧 Changed components
- Comments go Linear style:
CommentComposeris a soft filled box with a round ↑ send button (Cmd/Ctrl+Enter sends), anattachmentsslot and no avatar (authoris gone);CommentListgets an Oldest / Newest first toggle (order,onOrderChange) and an empty state whose "Add a comment" reveals and focuses the composer; editing a comment uses a compact box with Cancel and Save. New blockissue-detail-01: a Linear-style issue page composed of the real components.41c25fe - TextEdit has no toolbar: type
/for a slash menu (headings, lists, task list, quote, code block, divider, link) and select text for a bubble menu (bold, italic, strikethrough, inline code, link).slashCommandslimits the menu —TEXT_EDIT_SLASH_COMMANDSis the full set,TEXT_EDIT_COMPACT_SLASH_COMMANDSthe comment-sized one. Hover and focus tint the surface, and every change leaves through oneonCommitpath; thetoolbar,variant,onSave,onCancel,saveLabelandcancelLabelprops are removed. Blank lines round-trip in markdown. Input gainsvariant="ghost"(borderless, tinted on hover and focus) andsize="lg"(heading type) for a title field, andInputGroupInputaccepts Input's props. EditableCellvariant="heading"has no box padding, so a title is edited exactly where it renders. PropertyRow top-aligns its label on the value's first line. Prose list markers inherit the item's colour and use proportional figures, so lists read exactly like body text.9ae6bb2
📦 npm
@vegastack/design→0.7.41(was0.7.40).- The design-system registry (
@vegastack/ui) bumps 0.23.31 → 0.23.32.
[0.23.31] — September 26, 2026
🔧 Changed components
- Dates and times (RelativeTime, DateTime, DueLabel, Duration) render in the body font with proportional figures, like the text around them, instead of tabular figures that read as monospace. Live timers keep tabular figures.
8e5f523 - TextEdit is Notion-style: no border, ground or focus ring, and no view mode — it rests looking exactly like
MarkdownView; click anywhere and type. The Save/Cancel buttons are gone: leaving the editor calls the newonCommit(value)when the document changed, Escape reverts and callsonRevert(), Cmd/Ctrl+Enter callsonSubmit(or commits), andautosavecommits after an idle gap. The formatting toolbar is a compact row under the text, shown only while focused (minimal included).onSave/onCancelstill work as aliases;variant,saveLabelandcancelLabelare ignored. Theproserecipe is now the app's body text:text-sm, body family andforegroundink for paragraphs, lists, list markers, links, quotes and table cells (no relaxed leading, no muted markers), headings on the app scale (#text-lg,##text-base, the rest body size, allfont-heading), and only inline code and code blocks in mono — identical in MarkdownView and TextEdit.3da5a55 - Toasts are tighter: 12px padding (was 16px), 10px between the icon, text and actions (was 12px), and 2px between title and description (was 4px). Close and action buttons are unchanged.
384ff99
📦 npm
@vegastack/design→0.7.40(was0.7.39).- The design-system registry (
@vegastack/ui) bumps 0.23.30 → 0.23.31.
[0.23.30] — September 26, 2026
🔧 Changed components
- Comments:
CommentList(heading with a count, Load earlier, skeleton, "No comments yet"),CommentItem(avatar, name and badge, relative time, "edited", ⋯ Copy link / Edit / Delete, in-place Markdown editing,#comment-<id>highlight, a replies slot) andCommentComposer(avatar and a minimal Markdown editor; Cmd/Ctrl+Enter or Comment posts, with posting and error states).cc7d5ba
📦 npm
@vegastack/design→0.7.39(was0.7.38).- The design-system registry (
@vegastack/ui) bumps 0.23.29 → 0.23.30.
[0.23.29] — September 26, 2026
🔧 Changed components
- Toast actions close their toast: clicking the action (e.g. "Undo") runs it and dismisses the toast;
data: { keepOpen: true }opts out. Tooltips open after 250ms everywhere (TIMINGS.tooltipOpenDelayMs, was 300), and RelativeTime's date tooltips now use that shared delay instead of opening instantly.ffa5257
📦 npm
@vegastack/design→0.7.38(was0.7.37).- The design-system registry (
@vegastack/ui) bumps 0.23.28 → 0.23.29.
[0.23.28] — September 26, 2026
🔧 Changed components
Boardlanes are transparent, like list view: no lane fill, only the column spacing; cards keep their surface. Each lane header shows a collapse icon button (tooltip "Collapse lane") left of its ⋯ menu, the ⋯ menu drops its "Collapse lane" item and hides when a lane has no other actions, and a collapsed lane is a dashed strip with an expand icon (tooltip "Expand lane").4781f95
📦 npm
@vegastack/design→0.7.37(was0.7.36).- The design-system registry (
@vegastack/ui) bumps 0.23.27 → 0.23.28.
[0.23.27] — September 26, 2026
🔧 Changed components
- AudioPlayer: the
floatingpill always sits at the bottom of its flex column (mt-autobuilt in) and centres on the main column beside an end rail —--audio-player-inset-endnarrows and shifts it, and inside AppShell a RecordLayout rail sets it automatically.8334813
📦 npm
@vegastack/design→0.7.36(was0.7.35).- The design-system registry (
@vegastack/ui) bumps 0.23.26 → 0.23.27.
[0.23.26] — September 26, 2026
🔧 Changed components
BoardCard's assignee avatar opens the person hover card on hover (PersonCard: name, email and status badge);BoardCardAssigneetakesemailandbadge("Inactive").7fafdc5
📦 npm
@vegastack/design→0.7.35(was0.7.34).- The design-system registry (
@vegastack/ui) bumps 0.23.25 → 0.23.26.
[0.23.25] — September 25, 2026
🔧 Changed components
- AudioPlayer: the
floatingpill is redesigned — no visible title, back 10s · filled play · forward 10s, tabular times around a thin seek bar with a buffered shade, hover thumb and hover time, a speed menu and a volume popover, evensize-8controls; Space plays or pauses and ←/→ move 5s. Full width and tighter on phones, with volume hidden.4e40109 - Person status badge:
PersonOptiontakesbadge, andPerson(forPersonCard,PersonHoverCardandAvatarStack) andPropertyPersontakebadge— a status right after the name on the same line; a string such as "Inactive" renders as a small muted outlineBadge.SearchableSelectandFilterBarFacetpass it to person options withitemToBadge.PersonBadgeis exported for person cells that compose their own avatar, name and email.98045ec
📦 npm
@vegastack/design→0.7.34(was0.7.33).- The design-system registry (
@vegastack/ui) bumps 0.23.24 → 0.23.25.
[0.23.24] — September 25, 2026
🔧 Changed components
- Board, BoardCard, row menus and PersonOption:
BoardCardtakes astatusslot (your status circle, click to change and Alt-click to finish, as in list rows) in a fixed leading column, with the title, context and meta row sharing one text column; the meta row reads assignee avatar, due, then priority, and thesource/sourceLabelicon is removed; a done title's strike no longer clips. TheBoardcard ⋯ menu drops its Move up/down/top/bottom/"Move to" items (drag and Space + arrows stay) and shows only when a card has actions.RowActionItemgainschecked(a tick for the current choice) andshortcut(a one-key pick such as O/P/B/D/C); row and card menus are at least 12rem wide, submenus 12rem, and person submenus 18rem.PersonOption(avatar, name over email) is the standard for every person list, andSearchableSelectwithitemToSecondaryLabelwidens its popup to 18rem. Card ⋯ menus (MediaCard,BoardCard) are top-aligned with the title's first line.12744e2 - No focus rings anywhere, Tabs included (FOC-13): tab triggers and panels take the same background tint as everything else, and clicking a tab shows nothing. Every clear and dismiss × (FilterBar facet and date clear, Chip remove, SearchableSelect, Combobox and DatePicker clear, Dialog and Sheet close) opts out of Button's press nudge and centres with inset and auto margins, so it never jumps when pressed. Transcript: the search field is capped at
max-w-sm, the speaker name in each turn is an inlineEditableCellwhenonSpeakerRenameis set (a rename applies to every turn with that speaker), andTranscriptSpeakersis deprecated. Empty's icon tile is 40px with a 20px icon (32px / 16px atsize="sm"), and the tile sizes any icon passed in.7d9d485
📦 npm
@vegastack/design→0.7.33(was0.7.32).@vegastack/design-tokens→0.7.33(was0.7.18).- The design-system registry (
@vegastack/ui) bumps 0.23.23 → 0.23.24.
[0.23.23] — September 25, 2026
🔧 Changed components
- Markdown view and edit share one typography:
TextEditgainstoolbarlevels (minimal/standard/full or an action array) that set the schema, a selection bubble menu, a link popover (⌘K), task lists, tables, code blocks and images, markdown paste,onSave/onCancel(⌘Enter/Esc) with toolbar Save/Cancel, optionalautosave, a borderlessghostvariant and a zero-shift placeholder;MarkdownViewtask items take the same DOM through the newprose.taskListrules. New Markdown guide shows every element in view and edit.c6920d0 - Combobox's clear and chip-remove × no longer move when pressed (API-31). RecordLayout gains
RecordLayoutPanels,RecordTabCountandRecordLayoutMainSkeleton; RecordChip gainsRecordChipMenu, the flush picker popover a pill opens. RecordLayout picks the rail from its own width (container query,useRecordLayoutWide) and gainsRecordTabsRow/RecordTabsActions.e706cd9 - Toolbar switches use the default Tabs.
ViewTogglenow renders the default (pill)Tabs(at the bar's h-8 tier, which FilterBar also lifts aTabsList size="sm"scope to) instead of an outlineToggleGroup, keeping its icons (List,Columns3for Board, LayoutGrid) with labels hidden on a phone. TheFilterBarscopeandviewslot guidance, the FilterBar, ViewToggle and DataList docs, and the skill now say every scope or view switch in a table or list toolbar is a defaultTabs, never aToggleGroupor line tabs.78d1263
📦 npm
@vegastack/design→0.7.32(was0.7.31).- The design-system registry (
@vegastack/ui) bumps 0.23.22 → 0.23.23.
[0.23.22] — September 25, 2026
🔧 Changed components
EditableCellkeyboard focus shows only the system focus tint (FOC-13), the same as hover: no focus ring or outline. The 0.7.30 changelog entry now describes the shared box correctly (the text sizes the box and the field is laid over it).f2d4304- SettingsSection takes
actions, right-aligned on the title's row. Row ⋯ menus (RowActionsMenu) are at least 192px and as wide as their longest item, so labels never wrap, and a disabled item'sdisabledReasonis a tooltip on the item (still its accessible description) instead of a wrapped second line.fae1529
📦 npm
@vegastack/design→0.7.31(was0.7.30).- The design-system registry (
@vegastack/ui) bumps 0.23.21 → 0.23.22.
[0.23.21] — September 25, 2026
🔧 Changed components
- Inline edit rework.
EditableCell's edit mode now looks like its view: no border, ring or background, and the field inherits font, size, weight, line height, tracking and colour, so only the caret and the selection show it is editing. The display and the field share one box (the text sizes the box and the field is laid over it), so the field grows with its content, wraps when the value wraps and nothing shifts entering or leaving edit. New props:onSave(the promise API;onCommitstill works),variant(inline·cellfills a table cell and lets clicks beside the text reach the row link ·headingkeeps the title's size and weight and wraps),multiline(⌘/Ctrl+Enter saves),required+requiredMessage,onNavigate(Enter or Tab saves and moves on),tooltipandsaveErrorToast. Saves are optimistic, the trailing spinner shows only after 300ms, and a failure rolls back, is announced and raises a toast with Retry. Read mode is a button labelledEdit {label}; F2 starts editing.useInlineEditgainsmultilineand ignores keys during IME composition.50f37dd
📦 npm
@vegastack/design→0.7.30(was0.7.29).- The design-system registry (
@vegastack/ui) bumps 0.23.20 → 0.23.21.
[0.23.20] — September 25, 2026
🔧 Changed components
- AvatarStack: truly stacked 24px avatars (overlapping, background ring, "+N") as one button that lists everyone, with a hover preview per avatar; PersonCard never truncates. RecordChip:
placeholder={null}shows the icon alone, and the chevron sits as far from the divider as the ↗ does. Clear and remove × controls (DatePicker, SearchableSelect, FilterBar, Chip, SearchInput, AnnouncementBanner, RecordAsideAction) no longer move when pressed: the absolutely centred ones used-translate-y-1/2, which Button's press nudge replaced.1ccbcee
📦 npm
@vegastack/design→0.7.29(was0.7.28).- The design-system registry (
@vegastack/ui) bumps 0.23.19 → 0.23.20.
[0.23.19] — September 25, 2026
🔧 Changed components
- PersonHoverCard:
AvatarStack(stacked avatars, max 5, the rest behind "+N" as rows),PersonHoverCard(hover, focus or tap: a compact 240px card, a 32px avatar with the name and muted email stacked beside it),PersonCardandPersonAvatar; people without an account show initials and the name. Card gainsvariant="outline"(a border, no fill; API-30). RecordAside cards are outline, the inline PropertyList has a fixed 112px label column with values left-aligned, and the rail has layout-matching skeletons. Stat gainsStatSkeleton.e2ef1fc - RecordAside: the record rail's cards —
RecordAsideCard,RecordAsideSection(title, count, a tooltip icon action), full-widthActionListrows on Item (edge to edge, hover tint, no underline) with a one-lineActionListEmpty, andPropertyPerson,PropertyPeople(+N) andPropertyClamp(Show more). PropertyList gainsvariant="inline",PropertyEmptyandPropertySection. People icons are the round lucide set (UserRound, UsersRound, CircleUserRound).e2ef1fc - Row actions:
RowActionItemgains an optionalsubmenuslot — custom content (a searched, paged list such as Assign › members) rendered inside the submenu afteritems.2c9582f - NotificationBell: the count badge is a solid destructive fill (was a faded tint) and the unread dot is solid info blue (was primary ink); both sit inside the button's top inline-end corner with a background ring, so they never spill out when pressed. Counts cap at "99+" with a matching "99+ unread" accessible name.
c6ea931
📦 npm
@vegastack/design→0.7.28(was0.7.27).- The design-system registry (
@vegastack/ui) bumps 0.23.18 → 0.23.19.
[0.23.18] — September 25, 2026
📦 npm
@vegastack/design→0.7.27(was0.7.26).- The design-system registry (
@vegastack/ui) bumps 0.23.17 → 0.23.18.
📚 Docs
- Fix the docs build: an unescaped
addLabelexpression on the DataList page, and a ⓘ the OG font lacks in the RecordLayout description.cccb347
[0.23.17] — September 25, 2026
🔧 Changed components
- Board redesign and the new
BoardCard.Boardlanes now fill the viewport below the board (height="fill", cards scroll inside each 320px lane, the page never scrolls), with sticky headers, a ⋯ menu that collapses a lane to a slim strip (collapsible,collapsedColumns,onCollapsedChange,getColumnActions), "+ Add" at each lane's foot (onAdd,addLabel, per-laneaddable), dashed "Nothing here" / "Drop here" empty zones, per-lane skeletons and load-on-scroll paging withLoadMoreas the fallback, a hidden-scrollbar board with edge fades, and one lane at a time with a lane strip on a phone. Dragging is live (usePointerDraginuse-drag-reorder): the card lifts with a shadow and tilt, other cards make room and it settles; touch picks up on a 250ms long-press; lanes and the board auto-scroll; the keyboard picks up with Space, moves with the arrows, drops with Space and cancels with Escape. Moves are optimistic and a rejectedonMovesnaps back with a toast (moveErrorToast);readOnlyturns moves off. NewBoardCard: a round done tick, a two-line title, a context line, due and priority chips and the assignee's avatar.DataList's board view rendersBoardCards (boardCard) and passesonAddToSection,addLabel,collapsedSections,onCollapsedSectionsChangeandboardHeightthrough, with sectiondefaultCollapsed,droppable,lockedReasonandaddable.RowActionitems takeitemsfor a submenu. Thelist-page-01andboard-01blocks now use aFilterBartoolbar overDataListviews.3f83fa2
📦 npm
@vegastack/design→0.7.26(was0.7.25).- The design-system registry (
@vegastack/ui) bumps 0.23.16 → 0.23.17.
[0.23.16] — September 25, 2026
🔧 Changed components
- StatusIcon and PriorityIcon keep their semantic colour on hover, focus and highlight inside dropdown, context-menu, menubar, select, combobox, command, sidebar, navigation-menu, toggle, button and item rows: both icons carry
data-icon-tone, and the rows' muted-icon default and state recolours skip it. Adddata-icon-toneto any other semantic-coloured icon to get the same behaviour.8d8eb5b
📦 npm
@vegastack/design→0.7.25(was0.7.24).- The design-system registry (
@vegastack/ui) bumps 0.23.15 → 0.23.16.
[0.23.15] — September 25, 2026
🔧 Changed components
- RecordChip: the picker and the ↗ link now sit inside one shared
p-0.5inset, both 24px tall and rounded-full, so their hover and open backgrounds keep the same gap from the pill's border on every side (the pill grows from 28px to 30px). The shell is exported asSplitChipwithSplitChipButton,SplitChipSeparatorandsplitChipIconActionClassNamefor any main-action-plus-icon-action pill; documented under Record Chip.12ba8d9 - RecordLayout: a record page's main column beside a sticky 320px right rail of cards (from
lgup, scrolling on its own), withRecordDetailsSheet— a ghost ⓘ button that opens the details in a Sheet from the right — in its place on small screens.6ae4f3a
📦 npm
@vegastack/design→0.7.24(was0.7.23).- The design-system registry (
@vegastack/ui) bumps 0.23.14 → 0.23.15.
[0.23.14] — September 25, 2026
🔧 Changed components
- FieldDescription
variant="footnote": a centred 12px muted line with a small gap above, the documented footer style for login-01 and the sign-up, forgot password and set password variants.49ce48f
📦 npm
@vegastack/design→0.7.23(was0.7.22).- The design-system registry (
@vegastack/ui) bumps 0.23.13 → 0.23.14.
[0.23.13] — September 25, 2026
🔧 Changed components
- Inbox: infinite scroll —
onLoadMore/hasMore/loadingMore(andloadMoreError,endLabel) page through an IntersectionObserver sentinel, show three skeleton rows while a page loads (no spinner), a ghost "Try again" after a failure and "You’re all caught up" at the end (15 rows a page). Day-group labels drop their dividers and stick within the scroll area with 16px above (not on the first group) and 6px below; rows keep a 1px divider between them only. Titles are regular weight — foreground when unread, muted once read — with the actor and record in medium. The row ⋯ menu sizes to its content (224–320px). notifications-01 uses the new API in place of LoadMore, names its row item "Mute this type" (tooltip "Stop notifications like this") and demonstrates the loading, error and end states.e42fe8c - StatusIcon gains
cancelled(CircleX, muted) and a staticprogress(CircleDashed;animatedkeeps the spinning Loader),blockedbecomes CircleSlash in the warning ink anddonea filled CircleCheck; newPriorityIcon(urgent / high / medium / low / none flags in semantic colours); docs recipes for a Status menu and Priority menu (DropdownMenu + icons + shortcuts O/P/B/D/C and 1/2/3/4/0, Alt-click for quick Done) and icons leading Select/Combobox options.248dd90
📦 npm
@vegastack/design→0.7.22(was0.7.21).- The design-system registry (
@vegastack/ui) bumps 0.23.12 → 0.23.13.
[0.23.12] — September 25, 2026
🔧 Changed components
- SearchableSelect (and so FilterBarFacet): a remote search that is
loadingwith no rows yet shows five Skeleton rows the height of real options (two lines withitemToSecondaryLabel) instead of a blank panel; with rows already shown they stay and a small spinner marks the fetch.adce170
📦 npm
@vegastack/design→0.7.21(was0.7.20).- The design-system registry (
@vegastack/ui) bumps 0.23.11 → 0.23.12.
[0.23.11] — September 25, 2026
🔧 Changed components
- DataList views:
viewis"list","grid"or"board"over the same items, sections, sort, filters, paging, loading, empty and no-results states. The grid renders a MediaCard per row (sections as "Label · n" headings over a 1/2/3-column container grid,gridSize="lg"for a 16:9 image,renderCardto override); the board renders sections as Board lanes withonMove(row, from, to)and a per-sectionloading/loadMore/emptyState.onViewChangemounts the new ViewToggle (Grid | List | Board, labels hidden on a phone) in the FilterBarviewslot and remembers the view for the session;viewspicks the offered views. Columns take athumbnail(32px Thumbnail withthumbnailFallback). New MediaCard (whole-card link, 48px thumbnail, meta, badge, timestamp, ⋯ on hover/focus/touch,lgsize), Thumbnail (32/48px, cover-fit, fallback) and ViewToggle; RowAction entries can be{ type: "separator" }between groups (and an item can takeseparatorBefore), in row, grid-card and board-card menus; the Badge page documents the warning pill.08196ae
📦 npm
@vegastack/design→0.7.20(was0.7.19).- The design-system registry (
@vegastack/ui) bumps 0.23.10 → 0.23.11.
[0.23.10] — September 25, 2026
🔧 Changed components
- Meeting page parts: AudioPlayer gains
variant="floating"(a centred pill) and a global player —AudioPlayerProvider,GlobalAudioPlayer,useGlobalPlayeranduseGlobalPlayerTime— that keeps one recording playing across routes with a title link back; Transcript turns show time, a coloured speaker dot and the name above full-width text,TranscriptSpeakersadds speaker chips with rename (onSpeakerRename), and long transcripts mount progressively (batchSize); newMetaLine,RecordChipandStatusLine; newuseTabsSwipehook for touch swipe between Tabs, documented with the full-width default variant; review-split-01 shows the speaker chips.ffbf133
📦 npm
@vegastack/design→0.7.19(was0.7.18).- The design-system registry (
@vegastack/ui) bumps 0.23.9 → 0.23.10.
[0.23.9] — September 25, 2026
🔧 Changed components
- List toolbars, tables and focus: FilterBar puts search (~320px) left and a Filters (n) toggle, scope Tabs and the view switch right, with compact rounded-md filter chips (tinted when set, a fixed 24px ×, "Status: 2") on a toggled row 12px below that scrolls sideways on a phone (the bottom sheet is gone); new DateRangeFilter with presets; SearchableSelect ticks async options and gains a person option (name plus muted email, both searched); DataList adds sortable columns with an indicator,
compare/sortFirst/sortMode, a standardrowActions⋯ column (32px, always last), un-underlined row links, andnoResults; Empty always renders an icon; TabsList gainssize="sm"; buttons inside a status Alert hover in the family's own tint; Dialog and Sheet open onto the first field, never the ×; and no focus rings anywhere except Tabs — keyboard focus is a subtle background tint (a border tint on text entry).283057d
📦 npm
@vegastack/design→0.7.18(was0.7.17).@vegastack/design-tokens→0.7.18(was0.7.1).- The design-system registry (
@vegastack/ui) bumps 0.23.8 → 0.23.9.
[0.23.8] — September 25, 2026
🔧 Changed components
- notifications-01 is rebuilt on the Inbox component to match the Regent app (Mark all read and ⋯, All | Unread chips, day groups, avatar or icon rows with the unread tint, action chips, "You’re all caught up"); command-search-01 uses CommandFilters, the built-in CommandFooter hints and the wide default size; the alert, checkbox (circle preview) and sidebar docs pages describe the current behaviour.
6353561
📦 npm
@vegastack/design→0.7.17(was0.7.16).- The design-system registry (
@vegastack/ui) bumps 0.23.7 → 0.23.8.
[0.23.7] — September 25, 2026
🔧 Changed components
- InboxItem: unread rows drop the dot for a soft full-bleed tint and a medium title (regular when read); a fixed right column keeps the time on top with the read toggle and ⋯ menu below it on hover or focus.
ca2bd35
📦 npm
@vegastack/design→0.7.16(was0.7.15).- The design-system registry (
@vegastack/ui) bumps 0.23.6 → 0.23.7.
[0.23.6] — September 25, 2026
🔧 Changed components
- Leading icons are muted and follow their row — menu, select, combobox, command, sidebar, navigation-menu, toggle, ghost/outline button, breadcrumb and item-media icons rest at
text-muted-foregroundand go full colour on hover, focus, highlight, selection, active, open and pressed. Destructive rows keeptext-destructive.164e490
📦 npm
@vegastack/design→0.7.15(was0.7.14).- The design-system registry (
@vegastack/ui) bumps 0.23.5 → 0.23.6.
[0.23.5] — September 25, 2026
🔧 Changed components
- Dates & times: one Intl-based module (
@/lib/date-time) — formatRelative, formatDuration, formatDate, formatDateTime, formatDateRange, formatTimeOfDay, formatDueLabel, groupByDay, getTimeZone and thetzcookie script — plusDateTime,Duration,DueLabel,TimeZoneProvider/useTimeZone/TimeZoneScriptbesideRelativeTime(newformatprop). The Relative Time docs page becomes "Dates & times" with a where-to-use table, and the design-system skill says which to pick.323e8c8 - Inbox:
Inbox,InboxFilters,InboxGroup,InboxItem(avatar or muted icon, rich titles, action chips with loading and done states, hover/focus read toggle and menu, grouped counts),InboxEmpty,InboxSkeletonandInboxErrorfor notification panels.af1194d
📦 npm
@vegastack/design→0.7.14(was0.7.13).- The design-system registry (
@vegastack/ui) bumps 0.23.4 → 0.23.5.
[0.23.4] — September 25, 2026
🔧 Changed components
- Sidebar rows breathe and the user menu is one standard —
SidebarMenuputs a 2px gap between rows so a hovered row never touches the active one, andSidebarGroupsits tighter so groups stack closer. Theapp-shell-01user menu is now the standard account menu: avatar, name and role on the trigger; the same row with the email as the menu header; Profile, Settings, a Theme submenu (Light · Dark · System with icons and a check), Keyboard shortcuts, and Sign out — every item with an icon. See App shell 01.5789ff6
📦 npm
@vegastack/design→0.7.13(was0.7.12).- The design-system registry (
@vegastack/ui) bumps 0.23.3 → 0.23.4.
[0.23.3] — September 25, 2026
🔧 Changed components
- Command palette:
CommandFiltersrow for type chips and selects, built-inKbdkey hints inCommandFooter, animated search icon inCommandLoading, the wide 820px palette asCommandDialog's default size, and no blank space below the footer.e5a31c1
📦 npm
@vegastack/design→0.7.12(was0.7.11).- The design-system registry (
@vegastack/ui) bumps 0.23.2 → 0.23.3.
[0.23.2] — September 25, 2026
🔧 Changed components
- FilterBar is a two-row table toolbar — search,
scope,viewandactionson the first row; facets, "More" andonClear's "Clear" on the second, folding into a "Filters (n)" sheet on a narrow bar. An unset facet reads just its label and a set one is a filled pill with a "Clear Status" ×;searchPlacementandanyLabelare deprecated and ignored.a3ddf24 - Overlay footers are as plain as their headers —
DialogFooterandAlertDialogFooterdrop the muted, top-bordered band and sit in the popup's own padding.SheetFooterright-aligns its actions at every width instead of stacking them full width; give a Canceldata-slot="sheet-cancel"to seat it at the start edge. Secondary actions usevariant="secondary":AlertDialogCancel,DialogFooter showCloseButton, andMultiStepForm's Back, Skip and Exit now default to it, and theMultiStepFormActionsrow is right-aligned with no divider. See Sheet and Dialog.f22ce1f
📦 npm
@vegastack/design→0.7.11(was0.7.10).- The design-system registry (
@vegastack/ui) bumps 0.23.1 → 0.23.2.
[0.23.1] — September 25, 2026
🔧 Changed components
- Checkbox takes
shape="circle"— a round check for marking a task or to-do done;squarestays the default for selection and form fields.499e2dd
📦 npm
@vegastack/design→0.7.10(was0.7.9).- The design-system registry (
@vegastack/ui) bumps 0.23.0 → 0.23.1.
🐛 Fixed
- Alert has no focus ring — an app can move focus to an error
Alertso screen readers announce it; the alert no longer shows the focus ring, since it is not an interactive control.SheetContenttakesshowOverlay={false}to drop the backdrop for a non-modal panel docked beside the page.c126f9f - No focus ring on programmatic focus targets —
AppShellContent's<main>(the skip-link and route-change focus target) no longer draws a focus outline, which showed as a line under the app header after navigation. The same applies toSheetContent's popup, theMultiStepFormstep heading and section list, and theSteppersummary; interactive controls keep their focus rings.008bea6
[0.23.0] — September 25, 2026
🔧 Changed components
- Last facelift gaps —
DataList(andDataGrid) reserve the first column and everymobile: "visible"column before fitting the rest, so a trailing Status or actions column no longer leaves a narrow table squeezed while an earlier column that should fold stays.tabsListVariantsandtabsTriggerVariantsdraw horizontal tabs — the list height and the line indicator — whenever thegroup/tabsancestor is not vertical, so route tabs need nodata-orientationat all, and a scrolling route list keeps its indicator inside the scroll box.SearchableSelect's in-panel search keeps its own name inside aFieldinstead of taking the Field label, andsearchLabelis now optional (default "Search"). docs ·ac4341c
📦 npm
@vegastack/design→0.7.9(was0.7.8).- The design-system registry (
@vegastack/ui) bumps 0.22.0 → 0.23.0.
[0.22.0] — September 25, 2026
🔧 Changed components
- Facelift gaps, all opt-in and backward compatible —
EditableCelltakeswrap(a page title wraps instead of truncating) andflush(its text lines up with the line below).DataListcolumns takemergedRender, so a value folded into the first column on a phone keeps its context ("4 to review", not "4") andmergedLayout="line"joins folded values into one compact meta line ("Today · High · Arjun Mehta"), and the list takesrowPropsfor per-rowdata-*attributes, a class and ahighlightedstate that flashes a new row.AudioPlayertakesonSourceExpired: a media error renews an expired signed URL once and resumes at the same position.MarkdownViewtakesheadingOffset, moving every heading down that many levels (capped at h6).tabsListVariantsscrolls a list that carries nodata-orientation(route tabs), and only a vertical list turns the scroll box off.Board's cardhrefnow wins over one on theitemLinkRendertemplate, asDataList's row link already did (the template's own ref still lands), and a card with a menu reserves end padding so a long title wraps before the ⋯ trigger. The Alert page documents a callerrole(role="note") as the way to keep a page-load alert out of every live region, and the agent skill shipped in@vegastack/designnames the new props. docs ·f15f6da
📦 npm
@vegastack/design→0.7.8(was0.7.7).- The design-system registry (
@vegastack/ui) bumps 0.21.2 → 0.22.0.
[0.21.2] — September 25, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.21.1 → 0.21.2.
🐛 Fixed
useAsyncSearchaborts the in-flight request as soon as the query changes, so a slow response for the previous text can no longer fill the list while the new query waits out its debounce.638d754
[0.21.1] — September 25, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.21.0 → 0.21.1.
🐛 Fixed
- A pinned
MultiStepFormActionsrow paints the surface it sits on — a card, a dialog, a sheet or a drawer, not always the page — so it no longer shows as a black band in dark mode. The Tabs page gains the DS-62 recipes Regent's list pages use: vertical tabs that turn into a horizontal line list below an@mdcontainer, and vertical route tabs with counts that become aNativeSelectjump in a narrow column. The docs' code blocks keep their copy button clear of a long first line. docs ·c911726
[0.21.0] — September 25, 2026
🧩 New components
-
Three new blocks: a list page, the notification inbox and the search palette.
- list-page-01: search first, a Status
FilterBarFacet, and Mine | Team and Grid | List switches that never deselect. It shows the same records as aDataListwith row links and row actions, or as industry-grouped grids of linked tiles, pages both views with Load more, and has three empty tiers: nothing yet, no matches and couldn't load (DS-52). docs - notifications-01:
InboxSheet. The rail row and the bell carry the unread count in their names. The sheet has All | Unread, Today and Earlier groups of linked rows with the neutral unread dot, "Mark all read" announced once, Load older, and loading, empty, caught-up and error states (DS-56). docs - command-search-01:
CommandSearchhas scope chips (Alt+←/→ from the input), recents, results grouped by type, and aborted stale searches. It has loading, no-results and error states, footer hints fromformatShortcut, and ⌘↵ / Ctrl+↵ to open in a new tab (DS-57). docs ·1f8da35
- list-page-01: search first, a Status
🔧 Changed components
-
The board-01 block pages its Backlog and gives each card its own menu (DS-80).
📦 npm
@vegastack/design→0.7.7(was0.7.6).- The design-system registry (
@vegastack/ui) bumps 0.20.0 → 0.21.0.
📚 Docs
-
The
vegastack-design-systemagent skill now lists the facelift components and blocks:SearchableSelectmultipleand server search,FilterBarFacet,DataListpaging, row links, sections and row actions,LoadMore,useAsyncSearch,SortableListtiles,Transcriptwith a dockedAudioPlayer, and a starter-blocks roster fromapp-shell-01tostatus-pages-01.4178c7f -
The multi-step form guide gains a "Full-page flow" example with the step in the route.
🐛 Fixed
-
Fixes from the consolidated review of the facelift design-system work (Regent #136–#140).
- Two-line rows (Command, Select, Combobox and the three menus): the second line is read once, as the row's description, and no longer repeated inside its name. A caller's own
aria-describedbystill wins, and the second line then stays in the name. docs - CommandLoading reads "Searching…" by default. With a
progressvalue it stays a labelledprogressbar, so the value reaches assistive technology. docs - LoadMore: when the last batch arrives the footer keeps the focus its button had, instead of dropping it to the page, and reads its
endLabel(or "End of list"). It is now a client component. - SortableList and Board: a row or card action that removes its row moves focus to the row that took its place (its handle, or its menu when the row is locked). SortableList takes the accessor as
getItemActions, Board's name;menuItemsis deprecated. Board takesactionsLabel, a lane'sloadMoretakes the footer's labels, and a read-only lane keeps a card's own actions (only the Move items go). docs - RowActionsMenu: a disabled single icon action is described by its
disabledReason, not only in its tooltip. - FilterBar: facets have their own seat (
facets), after the search and before the chips; an "Add filter" option with aneditoropens it on the new chip;FilterChiptakesdefaultEditorOpen. FilterBarFacet: the pinned "Selected" group comes first and is taken when the list opens, so toggling a row no longer moves it; the facet is the bar's height (h-8);removeLabel,selectedGroupLabelandmoreGroupLabeloverride its strings. docs - FilterBuilder (
filter-bar-managed):fieldPicker="searchable"(withfieldPickerProps) searches the fields, an option value with more than seven options is a search picker and a several-values editor isSearchableSelect multiple, and a row error is tied to the field picker whenever the operator takes no value. docs - SearchInput: a value reset from outside (a host's "Clear filters") drops the typed value's pending
onValueCommitted. - SearchableSelect: a server search with no rows yet shows its loading line;
groupOrderputs named groups first; its fallback name no longer overrides a<label for>; it takesaria-invalidandaria-describedby. - TextEdit: a disabled Base UI
Fielddisables the editor. - RelativeTime:
formatOptionswithdateStyleortimeStyleno longer throws for another year's date or withwithTime. - useAsyncSearch: Load more waits out a typed query's debounce instead of pairing the new query with the old cursor.
- Transcript: matches after characters whose lower case is longer are highlighted exactly; segments that only add matches keep the reader's position without a new announcement; a press on the scrollbar pauses follow; the loading line is a status.
- AudioPlayer: a rejected lazy
srcor a failed media load shows the player's own error line (loadErrorLabel); the loading glyph isSpinner. AttachmentProgress speaks a clamped percent. - VegaStackProvider: a custom
toaster={<Toaster limit timeout />}sets the provider's queue, and aToasteron another manager brings its own provider. - AppShell takes
keyboardShortcut(orfalse), forwarded to the sidebar. SidebarStateScript takes anonce. The sidebar docs render a collapsible menu row as theSidebarMenuItem, show the unavailable item'sTBDbadge and reason, and format the shortcut per platform. - DataList section rows use the group-label face; SettingsSection titles use the heading face; Board keeps one stable ref per card.
- Blocks: command-search-01 keeps Enter on its scope chips and Try again, follows only http(s) links, debounces its search, announces a scope change and a settled result count once, and has all ten scopes; the Mod+K shortcuts in command-search-01 and app-shell-01 ignore text fields; list-page-01 searches on the settled query, adds an Industry facet, sets
mobileon every column, takesreadOnly, and moves focus to the search after "Clear filters", as board-01 does; both use one control height per row; notifications-01 and list-page-01 takeloading/error(and notifications-01loadMore) instead ofstatus/loadOlder; review-split-01 keeps its player docked in its column and names its tab count; counts in tabs are muted tabular numbers with a spoken suffix. - Docs: Stat shows linked tiles; Dropzone's upload queue has per-file fields and a save retry; Pagination describes its
<a>links; list-page-01 keeps view state in guardedsessionStorage. vegastack-design doctorpoints retiredtext-h1/text-h2at the page and section heading faces.- Migration: the
data-slotvaluesnotification-bell-dot,row-actionandrow-actions-triggerare nownotification-dot,row-actions-menu-actionandrow-actions-menu-trigger. Copied blocks are yours: re-add list-page-01 or notifications-01 to take the prop renames. From 0.19.0 (not in its notes): a mixed checkbox shows a minus, disabled checkboxes and radios dim outside a Field too, andCardandEmptyare client modules.5f33be1
- Two-line rows (Command, Select, Combobox and the three menus): the second line is read once, as the row's description, and no longer repeated inside its name. A caller's own
[0.20.0] — September 25, 2026
🧩 New components
-
Three new blocks: a settings hub, the status pages and a two-pane review page.
- settings-02: the settings landing page. It has one section per area, and each area is a container-query grid of whole-tile links, each named by its title and described by one fact. An area that isn't built yet is a plain "TBD" tile, not a tab stop (DS-53). docs
- status-pages-01:
NotFoundPage,ForbiddenPageandErrorPage, each usable in the shell or standalone. Each is anEmptywhose title is the page'sh1and gives one way out. The error page adds "Try again" and, when there is a digest, the reference with a copy button (DS-60). docs - review-split-01:
ReviewSplitputs the summary and action items beside a sticky transcript, with the dockedAudioPlayerat the end of the column. The block measures its own container: the layout and the switch to tabs change together at 56rem, and each panel mounts once (DS-58). docs ·a109931
-
useAsyncSearch — a new hook for server search over a cursor-paged list: the query updates as you type and the request waits for
TIMINGS.searchDebounceMs(a new 300 ms timing in@vegastack/design), a newer request aborts the last and late responses are dropped,loadMorepages with the returned cursor, and a failure keeps the loaded items with a retry. Documented in the components guide.ce43c9b
🔧 Changed components
-
The app-shell-01 and board-01 blocks are modernised as the shell and board references (DS-80).
- app-shell-01: the rail has a workspace menu (a real menu trigger) and a Search row that opens a palette on ⌘K / Ctrl+K, with its hint from
formatShortcut. It also has an Inbox row whose unread count is part of its name, grouped links witharia-current="page", a collapsible "Coming soon" group, and a user menu with the theme choice. The page isAppShellPage›PageHeaderh1 › linked stat tiles, which replace the hand-rolled stat cards. The docs page showsSidebarStateScript. docs - board-01:
AppShellPage›PageHeaderh1 › aFilterBar(search plus an Assignee facet) overBoard. Lanes are named with their count ("Backlog, 3 tasks"), cards link to their task, and filters that match nothing show "No matches" with "Clear filters". docs - Migration: the board's view switch that nothing read and its fixed toolbar widths are gone, and every
href="#"in both blocks is a real route. A copy you already own is unaffected until you copy the block again.a109931
- app-shell-01: the rail has a workspace menu (a real menu trigger) and a Search row that opens a palette on ⌘K / Ctrl+K, with its hint from
-
Board lanes page with
column.loadMore(the shared LoadMore footer inside the lane's scroll), and cards take their own actions throughgetItemActions— listed first in the card's ⋯ menu above the Move items, which is then namedActions for {card}. SortableList rows do the same throughmenuItems, on locked rows too. docs · docs ·ce43c9b -
ComboboxexportsComboboxStatus, Base UI's own polite status region for a list that loads asynchronously. Render it as a sibling ofComboboxListand change its children ("Searching…", then the result count) rather than mounting it conditionally. It is screen-reader-only by default;visibleshows the message as a muted row above the list. It carriesdata-slot="combobox-status"(decision API-27).c087ef9 -
settings-01 is now the reference settings page, and ToggleGroup moves with the up and down arrows when vertical.
- settings-01: an
AppShellPage size="narrow"with aPageHeader, full-width controls, anActionBarsave bar that appears only once something changed (Discard restores the defaults), and "Delete workspace" confirmed in anAlertDialog. docs - ToggleGroup:
orientation="vertical"now reaches the primitive, so the arrow keys follow the column. docs - AppShell: the docs show a static or cached shell kept collapsed on first paint with
SidebarStateScriptanduseSidebarCookieOpen, and the preview's counts usebadgeLabel. docs ·a08665c
- settings-01: an
-
FilterBar gains
FilterBarFacet— a "Status: Open" facet on SearchableSelect, single or multiple, local or server-searched, pinnable and removable — and chips that edit in place: a filter'seditoropens from its label and value (FilterChipTrigger), Escape returns focus to the chip, andonEditorOpenChangereports it. DataList rows become real links withgetRowHref(androwLinkRenderfor a router link): modifier and middle clicks work anywhere on the row, andgetRowLabelnames each selection checkbox. docs · docs ·c4c7dff -
SearchInput and FilterBar send a settled query. SearchInput gains
onValueCommitted, which fires afterdebounceMsof quiet (defaultTIMINGS.searchDebounceMs, 300 ms) and at once on Enter and on clear, whileonValueChangekeeps the field instant; FilterBar'ssearchpasses both through. FilterBar gainssearchPlacement="start"for search-first lists, its chips now read "Label: value" with the colon the docs always promised, andchildrenis gone from its type (it was silently dropped). docs · docs ·ce43c9b -
FilterBuilder edits flat condition rules:
allowGroups={false}, aprefixthat names the rule ("Required when"),labelsfor its own words,conditionErrorfor a row's own message, andsummary="sentence"for a read-only sentence.describeFilterandformatRangeprint a tree or a range as text, fields takeoptionsand aunit, and five value editors ship with it —TextValueEditor,NumberValueEditor,NumberRangeEditor,OptionValueEditor,OptionsValueEditor— picked by default from the operator'svalueShape. docs ·ce43c9b -
DataList gains collapsible
sections(withgetRowSectionandgroupState/defaultGroupState/onGroupStateChange) and arowActionsColumnhelper: one ⋯ menu per row, namedActions for {row}, with link actions, destructive ink, disabled actions that stay reachable and read their reason, and a single-icon shortcut. The parts behind them —SectionRow,SectionToggle,GroupState,RowAction,RowActionsMenu,RowActionMenuItems— are exported fromdata-table-parts. DataGrid's group header now uses the same toggle: its count reads in muted tabular numerals without parentheses ("Open 2", heard as "Open, 2 rows"). docs · docs ·ce43c9b -
The login-01 block is rebuilt as a real sign-in page to the block rules (DS-79).
- login-01: one
h1"Sign in" (CardTitle render={<h1 />}), Email (autoComplete="email") and Password throughPasswordInput(autoComplete="current-password"), each labelled and described by itsField, aFieldErrorper field with focus on the first invalid one, a live destructiveAlertshown only after a rejected sign-in, and a submitButtonthat loads in place without changing width. docs - LoginForm takes your
signIncall; "Forgot password?" and "Sign up" are real links (forgotPasswordHref,signUpHref). The docs page adds sign-up and forgot-password recipes on the same frame. - Migration: the "Login with Google" button and every
href="#"are gone; a copy you already own is unaffected until you copy the block again.f3dbf16
- login-01: one
-
MultiStepForm tells a route-driven host whether to push or replace:
onStepChange(id, { replace })passesreplace: falsefor Next, Back and rail jumps, andtruewhen the form clamps a step it will not admit or follows the address bar's hash. One-argument handlers keep working. docs ·c087ef9 -
SearchableSelect searches on the server and picks several values:
remotestops local filtering,onSearchChange/loading/error/onRetry/loadMoretakeuseAsyncSearchas-is (with "Searching…" announced through the panel's own status region and the Load more footer under the rows),leadingItemsstay first and unfiltered,multiplemakes the value an array ({a}, {b}or{n} selected),itemToDescriptionanditemToDisabledReasonadd a described second line,groupByadds headings andrenderTriggerValuerewrites the trigger text. docs ·c4c7dff
🛠 CLI & tooling
changelog-lintrefuses a bare{…}outside inline code: the docs changelog is MDX, so an unescaped brace evaluates as JavaScript and breaks the public docs build.cd9b28d- The real
shadcn addconsume gate retries ashadcn addthat fails on a transient network error (EAI_AGAIN and friends from ui.shadcn.com) twice before failing, so a runner's flaky resolver no longer blocks a deploy; every other failure still fails on the first attempt.dc8608a - The deploy workflow trusts its container checkout before it checks that the release commit is on
main, so the public distribution proof no longer stops on git's dubious-ownership guard.a62eb9f
📦 npm
@vegastack/design→0.7.6(was0.7.5).- The design-system registry (
@vegastack/ui) bumps 0.19.0 → 0.20.0.
📚 Docs
-
The doctrine now describes the form, toast and overlay behaviour that shipped in the facelift fixes.
- Forms: the skill and design.md say
Fieldwires its control through Base UI Field — label, description and error ids, andaria-invalidfromdata-invalid— so ids are passed only to override, and an explicitaria-*prop merges with the Field's. docs - Toast: one action, one toast — a repeat rewrites the live toast with
toast.update. docs - Elevation: one overlay width table for Dialog, Sheet and CommandDialog
size. docs ·1786767
- Forms: the skill and design.md say
-
The Stat page gains a "Linked stat tiles" example: each count is a whole-tile link to the list it counts.
- Stat: tiles are
Item variant="outline"rendered as a link, in a container-query grid. The name reads label first ("Overdue tasks 3"), the value istabular-numsin the regular font, and aSkeletonholds each tile's box while loading (DS-59). docs - Empty: the docs example's "404 - Not Found" now reads "Page not found". docs ·
a109931
- Stat: tiles are
🐛 Fixed
- Board type-checks in apps without
@types/node: its dev-only lane-name warning declares theprocess.env.NODE_ENVit reads. The sortable-list page quotesActions for {label}as code, and scroll-area documents its props in a table, so the public docs build and export pass again.ad7294f
[0.19.0] — September 24, 2026
🧩 New components
-
LoadMore — the one "Load more" footer for keyset lists: an outline button that keeps its width and focus while the next batch loads, an error line with Try again, and an optional
endLabelonce the list has ended.LoadMoreState(hasMore,onLoadMore,loading,error) is the shape lists, board lanes and data hooks pass around. docs ·d7e9562 -
Transcript is a new component for the text of a recording: timestamped, speaker-labelled lines on MessageScroller's engine.
- The line playing at
currentTimegetsaria-current="true"and stays centred while following. Scrolling the list pauses following, and "Back to current line" brings it back. - Each timestamp is a "Play from 0:15" button that calls
onSeek. WithoutonSeek, timestamps are plain text. TranscriptSearchhighlights matches with<mark>. Enter and Shift+Enter move between them, and each move announces "2 of 5" or "No matches".loadingandemptyStatecover the states before there is any text.- Add it with
shadcn add @vegastack/transcript.b38e9a7
- The line playing at
🔧 Changed components
-
Attachmentnow covers file and media tiles on any surface, not only chat.AttachmentGrouptakeslayout="grid"to wrap tiles into equal columns (the scrolling row stays the default), a newAttachmentProgresspart shows a determinate upload bar built onProgressthat announces its percentage, andmuteddims the media of a file no longer in use. Image media now styles a nested<img>, so the systemImageand its fallback fill the slot. The item now depends on@vegastack/progress(decision API-28).d5e9404 -
AudioPlayer docks, closes, loads its source lazily and seeks from outside.
dockedpins it to the bottom of its scroll column (sticky, bordered, on the popover surface, clear of the bottom safe area) as aregionnamed bylabel.openandonOpenChangehide it with a close button (closeLabel, "Close player") that pauses, reportsfalseand returns focus to the control that opened it; a hidden player stays mounted andinert, and Escape does not close it.srcalso takes a function, resolved once on the first play.loadingshows and announces "Loading audio…" (loadingLabel) once, anderrorrenders an alert with "Try again" (retryLabel,onRetry).actionsRefexposesseek(seconds, { play }),play()andpause(); a seek before the metadata loads is applied when it does.refis still the root element. docs ·10f8d06 -
NotificationBell exports
NotificationDot, the one unread dot for rows, inbox items and nav items (intentdefaultordestructive, decorative), and its dot mode now draws it: the dot's default intent changes from destructive to primary, and itsdata-slotisnotification-bell-dot(the count pill keepsnotification-bell-badge). A newcountLabelwords the count in the accessible name ("Notifications, 3 unread" by default). usePlatform now exportsformatShortcutKeyandformatShortcut, which turn"mod"into ⌘ on macOS and Ctrl elsewhere; ShortcutOverlay uses them and renders the same key labels as before. docs · docs ·10f8d06 -
Board lanes show their total as a muted count, lanes and cards have names, and cards can be links. The lane count is now muted
tabular-numstext instead of aBadge, and it showscount(the lane's total) when the lane has loaded only some of its cards. docs- Names: a lane takes a plain-text
label(required when itstitleis not a string; development warns without one), andgetItemLabelnames each card. Each lane is a region named "Open, 14 tasks" (countLabelsupplies the noun, default "cards"), the Move menu says "Move to In progress", each card's menu control is "Move Write spec", and move announcements name the card and the lane instead of a column id. - Lane states:
loadingshows skeleton cards and marks the lanearia-busy;emptyStatereplaces the default "No cards" drop target;defaultCollapsedstarts a lane collapsed (collapsedstays as its alias). "Drag a card here" shows only where a pointer drag can start. - Cards as links:
getItemHrefrenders a card as a real link (itemLinkRenderswaps in your router's link). Clicks and modifier clicks are the browser's own, Enter follows the link, and Space still lifts the card into move mode. Card content no longer adds a second tab stop throughTruncatedText.88b7a1e
- Names: a lane takes a plain-text
-
DataList and DataGrid page keyset lists with the shared LoadMore footer. DataList gains
loadMore(the footer sits abovefooter, the loaded rows stay while the next batch loads, and the table reportsaria-rowcount="-1"while more rows exist). DataGrid's footer is now LoadMore too: an outline button instead of a ghost one, Try again afterloadMore.error, and no "All rows loaded" caption unless you passloadMore.endLabel.DataGridLoadMoreis a deprecated alias ofLoadMoreState. docs · docs ·d7e9562 -
Page chrome and pickers gain the props the facelift pages need: a framework back link and a page container, zone-correct relative dates, one inline picker tier, Field-wired chips and pickers, and one toast queue.
- PageHeader:
backRendertakes your router's link element for the back affordance,titleLinesclamps or frees the title ("none"wraps),metais a<div>row that can hold controls, and the title renders the page-heading facefont-heading text-2xl font-semibold. docs - AppShellPage (new part): the one page container — page gutters, a
gap-6rhythm and asizemeasure ofnarrow(768px),default(1280px) orfull;AppShellSkeletonuses the same gutters. docs - RelativeTime:
timeZonedecides the calendar day in the label, the server render and the tooltip;capitalize,formatOptionsandwithTimeshape the day label; a label with no tooltip is plain inline text. docs - SearchableSelect and DatePicker:
size="sm"andvariant="ghost"form one inline tier withSelect; clearing returns focus to the trigger. SearchableSelect is named by itsFieldLabelinside aFieldand takesname,requiredandcontentClassName; DatePicker takesclearable,clearLabelandrenderValue. docs - ChipInput: inside a
Fieldthe label, description and error reach the input;nameposts every chip, andmax/maxLabelcap the entries. docs - EditableCell:
renderValueshows a label for an id in display mode. docs - MultiStepForm:
MultiStepFormActions sticky(or"narrow") keeps the action row in view on a long step and reportsdata-stuck. docs - FilterBuilder:
maxDepth={1}shows no group control, operators declare avalueShapeand a shape change clears the value, "Value required" waits for a touch or a submit, and the cap copy is singular for one. docs - Toast and VegaStackProvider: the provider carries the module
toastmanager and aToasterbelow it reuses that provider, sotoast()anduseToastManager()feed one queue with one viewport (OVL-17). docs - AnimatedNumber: the default face is the regular font with tabular digits, not mono. docs
- Migration: a test pinning
font-monoon AnimatedNumber, the PageHeader title classes, a "Value required" message on an untouched FilterBuilder row, or a 24px box around a RelativeTime withtitle={false}sees the new output.70ff12f
- PageHeader:
-
sortable-listkeeps a locked row in the list: a row withdisabled: truenow shows a spacer the size of the handle (data-slot="sortable-list-handle-spacer") and KEEPS its row menu with the Move items disabled, where it used to lose both. New props:lockedReason(the accessible description of a locked row's disabled Move items),renderActions(inline actions before the row menu),actionsLabel(the menu trigger's name) andlayout="grid"(auto-fill image tiles with the handle and actions over the tile's top corners). The row menu trigger is now namedActions for {label}by default, where it wasMove {label}.use-drag-reordergainscolumns(a number or"auto", measured from where items wrap) so ↑/↓ in keyboard move mode step a whole row when a horizontal axis wraps into a grid, anddrag-itemdraws the left and right drop-edge hairlines a horizontal axis reports. The component roster in the shippedvegastack-design-systemskill describes the new surface.5500590 -
TextEdit edits Markdown:
format="markdown"makesvalue,onValueChangeandonSubmitcarry Markdown instead of HTML (through Tiptap's own@tiptap/markdown, a new dependency of the item), and loading a document never firesonValueChange. NewreadOnly(no toolbar,aria-readonly) anddisabled(no toolbar, dimmed,aria-disabled) props;editableis deprecated —editable={false}meansreadOnly. docs ·6fda658
📦 npm
@vegastack/design→0.7.5(was0.7.4).- The design-system registry (
@vegastack/ui) bumps 0.18.0 → 0.19.0.
📚 Docs
- The Dropzone page gains an "Upload queue" example: accepted files upload through a host-owned
uploadFile(file, { onProgress, signal })call and render asAttachmenttiles in anAttachmentGroup layout="grid", each withAttachmentProgress, cancel, retry and discard, while refused files join the queue as error tiles that say why.d5e9404
🐛 Fixed
-
Tables and pagers read and tab the way they should: headers in the sans face, no tab stop per timestamp, pagination links that are links, and pager controls that are buttons.
- DataList and DataGrid: a
monocolumn's header is in the sans face —monostyles body cells only, for codes and IDs (DS-01) — and clipped text or aRelativeTimein a cell is no longer a tab stop (DS-68).headerCellClassis exported from the shared table parts. docs - Pagination:
PaginationLinkis a real<a>wearing the button recipe, so it keeps the link role (A11Y-23); the landmark is named "Pagination" (label) and the ellipsis copy ismorePagesLabel(VOI-1). docs - DataListPager: page controls are buttons with
aria-current="page"on the current page;onPageSizeChangeis optional, and without it there is no rows-per-page chooser (DS-27, DS-71). docs - TruncatedText:
IconText's label slot isicon-text-label, andTableCellText monoistext-sm. docs - useDragReorder (and SortableList, Board): a handle that mounts after its row still owns the pointer drag (DS-70). docs
- Migration: a test that asserts
role="button"on a pagination link,font-monoon a mono column's header, a timestamp in a table as a tab stop,text-xsonTableCellText mono, or theicon-text-sm font-mediumslot now sees the new output; a DataListPager page control is a<button>, not an<a>.4b9f0d0
- DataList and DataGrid: a
-
Sixteen upstream-backed components gain the facelift fixes: counts and current page read correctly in the sidebar, two-line rows link their description, overlays get sizes and scrolling bodies, and built-in copy is sentence case.
- Sidebar:
SidebarMenuButtontakesbadge/badgeLabel, so the count is part of the item's name and the visual badge isaria-hidden. An active link carriesaria-current="page". The trigger reads "Toggle sidebar" (triggerLabel). Mod+B ignores text fields and takeskeyboardShortcut(falseturns it off). Skeleton widths are deterministic.SidebarStateScriptanduseSidebarCookieOpenkeep a collapsed sidebar collapsed on a static shell's first paint, and a controlled provider writes no cookie. docs - Item: a link or button row inside
ItemGroupkeeps its own role, and alistitemwrapper takes the list role. NewItemGroupLabelnames the group. docs - Command:
CommandDialogtakessize; newCommandLoadingandCommandFooter; two-line results;resultsLabel. Default copy is "Command palette" and "Search for a command…". docs - Select, Combobox, DropdownMenu, ContextMenu and Menubar: a two-line row (
ItemTitle+ItemDescription) reads its second line as the row's description, which is where a disabled row gives its reason. docs - Checkbox: the mixed state shows a minus. Checkbox and RadioGroup dim when disabled outside a
Fieldtoo. docs - Tabs:
orientation="vertical"now reaches the primitive, so arrow keys andaria-orientationfollow it. A line tab list scrolls inside itself.tabsTriggerVariantsis exported for route tabs. docs - Sheet and Dialog: side sheets take
size(sm,default,lg,xl). NewSheetBodyandDialogBodyscroll between a fixed header and footer, newSheetActionsits beside the close button, andcloseLabelrenames the close control. docs - CardTitle and EmptyTitle take
render, so a title can be a real heading. docs - Alert: the role is
statusunlessliveis set on a destructive or warning alert. Passlivefor an alert that appears after a user action.AlertActionnow takes its own column beside the text and drops below it on a narrow alert, so a long label never overlaps the title. docs - ToggleGroup: takes
deselectable={false}to always keep one item pressed, andwrap. docs - Badge: exports
BadgeVariant. docs - ScrollArea:
aria-labelnames the scrolling viewport, which becomes a region, andviewportRefreaches it. docs - Migration: the default copy changed ("Toggle sidebar", "Command palette"). Link rows in an
ItemGroupgain a wrapperdiv. An Alert that was assertive at load is nowstatus.1ae9f5b
- Sidebar:
-
Inside a
Field, a Combobox's "Show suggestions" toggle keeps its own name instead of taking the Field's label, so the input is the one element the Field names.- Since 0.18.0 the toggle answered to the field's name too: a screen reader heard a second button with the label, and a
getByLabelquery found two elements. docs - The geometry lane gets back the Select width and ButtonGroup checks and the
textEditInsideFielddynamic-DOM selector that a later merge dropped.a6362db
- Since 0.18.0 the toggle answered to the field's name too: a screen reader heard a second button with the label, and a
-
Inside a
Field, a RadioGroup is the control the Field names and each radio item keeps its own name, instead of every item answering to the Field's label.
[0.18.0] — September 24, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.17.1 → 0.18.0.
🐛 Fixed
-
Field now wires the control inside it — label, description, error and invalid state — and a Select trigger fills its parent by default, with a new
ghostinline variant.- Field renders Base UI's Field underneath (API-26): a control inside a
Fieldgets its label, the ids of theFieldDescriptionandFieldErrorthat are rendered, andaria-invalidfromdata-invalid, with noid,htmlFororaria-*props. An explicitidstill wins, and an explicitaria-describedbykeeps its ids first with the Field's ids after them.FieldTitle's slot is nowfield-title. docs - Input, Select, Combobox, NumberField, PasswordInput, Checkbox, RadioGroup, Switch and Slider read the Field through Base UI; Textarea (now a client component), InputGroup, DatePicker, DateRangePicker and TextEdit render their focusable element through Base UI
Field.Control. - NumberField puts
aria-describedby,aria-labelledbyandaria-invalidon its<input>, never the groupdiv, and an explicitidlabels it inside aField(DS-67). docs - Select: the default trigger is
w-fullinstead ofw-fit(inside a ButtonGroup it still sizes to its content), andvariant="ghost"is the inline, content-width trigger with no border at rest (API-24). docs - Migration: a Select trigger that should size to its content outside a ButtonGroup now needs
variant="ghost"or a width class. A test asserting that a control inside aFieldhas noaria-describedbyoraria-invalidnow sees the Field's ids and state; a selector for[data-slot="field-label"]no longer matchesFieldTitle; and a standalone Textarea, DatePicker trigger or TextEdit now carries a generatedid, as Base UI's Input already did.b5daf25
- Field renders Base UI's Field underneath (API-26): a control inside a
[0.17.1] — September 24, 2026
📦 npm
@vegastack/design→0.7.4(was0.7.3).- The design-system registry (
@vegastack/ui) bumps 0.17.0 → 0.17.1.
📚 Docs
-
The docs and the agent skills now state one convention per concern: numbers, page headings, link buttons, theme choice, empty states, view switches, page spacing and the conventions for components we own.
- Numbers (counts, dates, amounts) are the regular font with
tabular-nums;font-monois for code and identifiers only. docs - One page heading:
PageHeader,font-heading text-2xl font-semibold; a section isfont-heading text-base font-medium. - One link-button recipe:
<Link className={buttonVariants({ variant, size })}>, neverButton render={<Link/>}. docs - Theme choice is a Light / Dark / System radio group in the user menu. docs
- Two new empty-state tiers, "No matches" and "Couldn't load", plus default copy rules. docs
- One view-switch rule: RadioGroup for a form value, ToggleGroup for a view or scope switch, Tabs for page regions, links for URLs.
- A page-rhythm recipe for gutters and gaps. docs
design.mdgains the conventions for components we own and a component / part / block / example decision tree; the public skill gains "Names hide abilities" and "Which component for X".- Registry metadata for button, label, chip, filter-bar-managed, select, command, emoji-picker, chip-input, message-scroller, badge, toggle-group and radio-group now says only what the components do.
- The shadcn-reset decision register is tracked in git, with true counts (180 rows: 108 shadcn, 72 ours).
1f11fbc
- Numbers (counts, dates, amounts) are the regular font with
[0.17.0] — September 24, 2026
🗑 Removed / renamed
-
Demo blocks — the registry no longer serves 28 demo blocks that did not meet the rulebook (27 upstream compositions and our
onboarding-01), and the@dnd-kitdrag engine they needed is gone with them.- Removed:
dashboard-01,login-02…login-05,signup-01…signup-05,sidebar-01…sidebar-16,preview-03andonboarding-01. - Kept:
login-01,app-shell-01,board-01,settings-01and the 68 chart blocks. - Where each recipe went: shells and the sidebar variants →
app-shell-01and the Sidebar page; sign-in →login-01; sign-up → compose it fromlogin-01's frame with Field and PasswordInput; the dashboard andpreview-03showcase →app-shell-01plus the Chart and Stat pages; the getting-started checklist → the Item page's Checklist example. @dnd-kit/*andthesvgleave@vegastack/ui;@atlaskit/pragmatic-drag-and-dropstays the one drag engine, andIcon/BrandIconin@vegastack/designkeep their ownthesvg.- An installed copy of a removed block is yours and keeps working;
shadcn addof a removed name now fails. docs ·27433fe
- Removed:
📦 npm
@vegastack/design→0.7.3(was0.7.2).- The design-system registry (
@vegastack/ui) bumps 0.16.1 → 0.17.0.
📚 Docs
- Item — a new Checklist example composes a getting-started checklist from
Progress,ItemGroup,ItemandStatusIcon. docs ·27433fe
[0.16.1] — September 24, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.16.0 → 0.16.1.
🐛 Fixed
stepperandmulti-step-formkeep the current-step marker (aria-current="step", the step count and focus-follow) when the current step fails validation.StepperStepgains an optionalcurrentflag that marks the step current alongside anerrororwarningstate;MultiStepFormsets it, so a refused step now reads as both current and in error.cefa8f5
[0.16.0] — September 24, 2026
🔧 Changed components
- DatePicker / DateRangePicker bind to a
FieldLabel, and a long EditableCell value truncates with its full text on hover. Both pickers now acceptid,aria-describedbyandaria-invalidand forward them to the trigger button, so<FieldLabel htmlFor>names the picker (clicking the label opens it) and aFieldDescription/FieldErrorand the invalid border work as they do forInput.EditableCell's display stays on one line and ellipsizes to its container — a long page title at 390px no longer overflows — and when the value is actually clipped the display carries it astitleanddata-truncated(measured withuse-overflow, now a registry dependency); the editor still opens with the whole value.ee0cbfd
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.15.0 → 0.16.0.
[0.15.0] — September 23, 2026
🔧 Changed components
DialogContenttakes asizeprop —sm,default,lgorxl, published asdata-size— so a bulk-edit table or an attribute editor gets a wider dialog without aclassNameoverride.defaultkeeps upstream'ssm:max-w-sm;sm,lgandxlstep the cap tomax-w-xs,max-w-2xlandmax-w-5xl. The axis mirrorsAlertDialogContent's (decision OVL-16).f93704eEditableCellinherits the surrounding type instead of fixing its display and editor attext-sm, so it can edit a page title at the heading's size and weight (<h1 className="text-3xl font-semibold"><EditableCell … /></h1>) without descendant-selector overrides. At the 14px body default it looks as before; belowmdthe editor still never drops under 16px.e4c7208
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.14.0 → 0.15.0.
[0.14.0] — September 23, 2026
🧩 New components
-
PasswordInput is back as a thin InputGroup composition with a show/hide toggle.
- The toggle is a
type="button"icon button named "Show password" / "Hide password" witharia-pressed, so it never submits a form. - Every native input prop (
id,name,autoComplete,aria-invalid) and the React 19refland on the inner input, so it works insideField. - Add it with
shadcn add @vegastack/password-input.c2a246e
- The toggle is a
🛠 CLI & tooling
vegastack-design doctorno longer reports apassword-inputimport as retired, and the shipped skills stop listingPasswordInputamong the retired components — it is a registry component again.c2a246e
📦 npm
@vegastack/design→0.7.2(was0.7.1).- The design-system registry (
@vegastack/ui) bumps 0.13.0 → 0.14.0.
[0.13.0] — September 23, 2026
🧩 New components
-
DataList never forces a horizontal scroll, and the new DataListPager pages it.
- DataList columns take the same responsive posture as DataGrid —
minWidth(default 120) andmobile: "visible" | "hidden" | "merge"(default"merge") — now declared once on the shared column layout. In a narrow container, overflow columns stack into the first cell,hiddencolumns are dropped and counted in a line the table is described by, andvisiblecolumns never hide (if they still do not fit, the table is squeezed). See Fitting the width. - A merged value always wraps and wears its own column's face, so a
monoor end-aligned first column no longer pins the stack to one line. If the table still overflows after that — a long unbroken value, a one-line mono id, severalvisiblecolumns — it is squeezed (data-squeezed): every cell, and the text a customrenderputs in it, may break rather than scroll. Atruncatespan or awhitespace-nowraprow wraps instead (an auto-layout table cannot truncate), and text-like parts grow taller to hold a wrapped label: a Badge (orbadgeVariants()on your own element, or ToolCallChip), a Chip or Tag, a link-variant Button (orbuttonVariants({ variant: "link" })) and EditableCell's value. Controls and fixed-size content are never wrapped, so their label never spills their box — recognised by what reaches the DOM: every other Button and any element styled withbuttonVariants(),toggleVariants(),navigationMenuTriggerStyle(),tabsListVariants()orstepperNodeVariants(), a nativebutton/input/select/textarea, the control roles, an Avatar and a Kbd (held on one line at full width). The row grows taller instead; only a kept part wider than the container itself still scrolls the table (see Fitting the width). A Checkbox, Switch or Radio in the last column keeps 12px of end padding, so its 24px pointer target no longer scrolls the table. - When the sorted column is merged or hidden, a "Sorted by …" line under the table states the order its header can no longer show. DataGrid does the same in its toolbar, describes the grid by both lines, and now gives merged values their header as a screen-reader prefix.
- DataList always renders one
data-list-rootstack, so its status lines never land in your own grid or flex container. - Sortable headers use the plain header's
text-sm font-mediumand foreground ink in DataList and DataGrid, and their label now lines up with the column's values (it sat about 5px in): the start of a start column, the end of an end column, where the sort arrow leads instead of trailing.SortHeaderButtontakes the column'salign. A selected row isbg-muted/50, so asecondaryBadge on it stays visible. - New DataListPager for DataList's
footerslot: a controlled range summary ("1–15 of 40"), a rows-per-page Select (15 / 30 / 50 by default), and windowed Pagination that hides on a single page. The page list narrows with the pager's width (data-layout: full, compact, minimal); a page count that outgrows its layout (three digits at exactly 240px, four or five digits wider) steps down one more (re-checked on any width change, however small, and when the page list's own content changes size, as when a web font swaps in), number slots grow to hold their number, and at the last step the position reads "N / M" (data-short) and may truncate while screen readers still hear "Page N of M" — so the page list never scrolls sideways. The range and the rows-per-page chooser each stay on one line, which puts the pager's floor at about 200px. ANaN,undefinedor negativetotalreads as 0, and apageSizeof 0 or less shows the firstpageSizesentry instead of adding a bogus option. - Chip's height and EditableCell's display height are now floors (
min-h-7/min-h-8), not fixed boxes: identical on one line, and a wrapped label grows the box instead of spilling it.stepperNodeVariantsoutput carries agroup/stepper-nodehook.72741b7
- DataList columns take the same responsive posture as DataGrid —
🔧 Changed components
-
Cards and floating surfaces draw a real border instead of a ring outline, destructive menu rows clear AA, three components mirror under RTL, and FilterBar and DatePicker fit their space.
- Border, not ring (BRD-1).
card,dialog,alert-dialog,popover,hover-card,select,combobox,dropdown-menu,context-menu,menubar(content and sub-content) andnavigation-menuswap upstream'sring-1 ring-foreground/10box-shadow for a 1pxborder border-border, and the floatingsidebarswaps itsring-sidebar-borderoutline forborder border-sidebar-border, soSettingsCardandBoardcolumns follow. The ring read as a stray outline and disappeared in forced-colours mode. A border takes 1px of layout on each side: under Tailwind'sborder-boxsizing a surface with a fixed width or height keeps that size and its content area shrinks by 2px, while a content-sized surface grows by 2px.Board's drop-over highlight now recolours that border (data-drop-over:border-primary/50). See Elevation. - Destructive menu rows (A11Y-13). A focused
variant="destructive"item indropdown-menu,context-menuandmenubarreadstext-destructive-texton its/10wash (it read 3.99:1 in light).QuestionnaireErrortakes the same-textink asFieldError. - Logical direction.
PageHeader's actions andFilterBar's search and trailing slot push withms-auto, andDatePicker's preset rail divides withborder-e, so all three mirror under aDirectionProvider. - FilterBar never overflows.
SearchInput's clear button sat in an addon whose box ended about 4px outside the input (upstream's inline-end-0.3remmargin), so a search filling a narrowFilterBarpushed the bar 4px past its container at 320px. The addon now stays inside; the clear button keeps its position. - DatePicker's popup is one surface. The calendar inside
DatePicker/DateRangePickeris transparent, so it no longer paintsbg-backgroundover the popup'sbg-popover(a visible second tone in dark). Withpresets, the rail now sits beside the calendar fromsmup, as documented — it had stacked above it at every width — and its one divider sits between rail and calendar instead of doubling the popup's edge.72741b7
- Border, not ring (BRD-1).
🛠 CLI & tooling
-
vegastack-design doctornow fails on retired design-system vocabulary in your own source, withfile:lineand the replacement for each.A utility the shadcn reset retired compiles to nothing — no build error, no type error — so a
text-h2heading silently renders as body text. The new check scans the project's code and stylesheets (skippingnode_modules,.next,distand thecomponents.jsonuidirectory) fortext-h1…text-h4,text-label,text-label-sm,text-mono-label,text-code,text-strong,text-display-*, the-subtlesteps of the four status families (destructive,success,warning,info), every--alpha-*name the system ever shipped (the reset's nineteen plus five retired before it, such as--alpha-fill-hover), the retired--opacity-*and--z-raised/--z-overlay/--z-toastnames,shadow-overlay,backdrop-blur-glass, and imports of the retiredicon-button,segmented,password-input,progress-indicator,field-inline,floating-surface,section-headerandsonnercomponents. Only real usage is reported: an import counts only when it resolves into yourcomponents.jsonuidirectory (or@/components/ui/…), so your own@/components/layout/section-headeris left alone;//and/* */comments and prose strings are skipped, while JSX text — an apostrophe, a URL, a glob — never hides the real usage after it on the same line, and neither does Tailwind's!modifier (hidden!); a class you define yourself (@utility text-h2 { … }or a.text-strongrule) or a custom property you declare (--z-header: 30) is yours; another library's--z-indexor--alpha-channelis not ours to call retired; and the kept--tag-*-subtletints are not reported. Every replacement is in Migrating to the shadcn reset.72741b7
📦 npm
@vegastack/design→0.7.1(was0.7.0).@vegastack/design-tokens→0.7.1(was0.7.0).- The design-system registry (
@vegastack/ui) bumps 0.12.2 → 0.13.0.
📚 Docs
-
The shipped
vegastack-design-systemandvegastack-design-auditagent skills now name the retired vocabulary and the rules lint enforces.The design-system skill's Don't list adds no arbitrary text sizes (
text-[13px]), no ring as a surface edge, and a table of every retired utility, token and component with what to write instead, pointing atvegastack-design doctorto find them. The audit skill runsdoctorin its first pass and states one rule for copied-in components: don't edit one; if you must, it becomes yours andcheck-updatesreports it as drifted.72741b7 -
The reduced-motion comment in
base.cssnow states the!importantrule precisely: the reset is the only hand-written!importantin the token CSS, and Tailwind's!modifier in component class strings is a separate, counted allowance indesign-lint.No token value, selector or rule changed — this is a documentation correction inside the shipped stylesheet.
72741b7
🐛 Fixed
AppShellSkeletonno longer causes a hydration mismatch. Its nav rows used upstream'sSidebarMenuSkeleton, which picks a random width per mount, so the server HTML and the client's hydration never agreed in aloading.tsx. The rows now take their widths from a fixed cycle — same shape, same 50–90% band, identical on server and client.72741b7
[0.12.2] — September 23, 2026
🛠 CLI & tooling
-
The Version PR head is read from REST and the exact-SHA comparison retries.
gh pr view --json headRefOidserved a stale head for minutes on release run 35763347504 — the branch ref was already at49486dab0while the PR API still reported6a359a602. The comparison against the branch ref had no retry of its own (only the PR lookup above it did), so a Version PR that had been generated correctly was left behind a red run.The head now comes from
repos/{owner}/{repo}/pulls/{n}(REST answered correctly and immediately throughout that incident) and the comparison retries on the same bounded schedule. The check itself is unchanged: two genuinely different shas still fail, because that comparison is the exact-SHA protection boundary.The negative harness gained the mutation that boundary never had — replacing the branch-ref lookup with the PR's own head, making the comparison vacuous. 36/36 rejected.
Both reads now tolerate a failing call, because under
set -ea transientgh apierror aborted the step at the command substitution before the retry could do anything, with no message at all. An empty read never compares equal, so it falls through to the same bounded retry.9ad0966
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.12.1 → 0.12.2.
[0.12.1] — September 23, 2026
🛠 CLI & tooling
-
A release whose changesets all bump nothing no longer fails the coordinator.
release-detect.mjsdecided the Version PR path by counting files in.changeset/. The empty-frontmatter changeset is the documented form for a change that bumps no published package, andchangeset versionconsumes it without writing a version — so Changesets opens no PR, while the workflow had been told to expect one. It retried, hard-errored, and skipped the deploy; and because the changeset stays pending, every later push tomainfailed identically.The output is now
has_version_bumpand means what the workflow actually asks: at least one pending changeset declares a bump for a package Changesets will version. Frontmatter is read at the same ref as the file list, and packages in the Changesetsignorelist are excluded, since naming one bumps nothing and would dead-end the same way.This removes the failure. It does not change deploy policy: a docs-only push still does not deploy on its own — it never has — and now simply succeeds and waits for the next release.
dd9b308 -
Browser assertions test whether something is painted, not how Chromium spells "nothing".
Chromium serialises a fully transparent colour as
rgba(0, 0, 0, 0)or asoklab(0 0 0 / 0)depending on the colour space the value came through, and this token system's colours routinely come through OKLCH. Fourteen sites compared against the first spelling as a literal string.Two of them asserted equality and broke loudly when the spelling changed — one of them flipped red and then green again across three CI runs on identical source. The other ten asserted
not.toBe("rgba(0, 0, 0, 0)")to mean "something is painted here", which passes on an element that lost its fill entirely whenever Chromium spells the nothing differently. A Badge, Button, Switch track, Toast action or Bubble surface could have gone completely unpainted with its test green.packages/ui/test/color.tsnow answers the question by meaning rather than by spelling, and its own contract is pinned incolor.browser.test.tsx— including the original defect as an executable claim, and a real element read in the engine without naming which spelling it produced. No call site changed meaning: every file was green before and after, so this removes the possibility rather than uncovering an instance.229add9
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.12.0 → 0.12.1.
📚 Docs
-
The registry-auth banner no longer paints over the site header on a phone.
Fumadocs'
Banneris a fixed-height sticky box — it writes the sameheightinto its own inline style and into--fd-banner-height, which every sticky offset below it is measured from. The notice was long enough to need six lines at 320px inside that 48px box, so 72px of it overflowed and, atz-40over the header'sz-30, rendered on top of the VegaStack logo row. The layout boxes never overlapped and there was no horizontal scroll, which is why nothing caught it: the defect was content overflowing its own container, not a broken grid.The trailing enumeration — the Base UI shadcn project, the
@vegastacknamespace, the Cloudflare Access service token — is now shown fromlgup, which is measured rather than guessed: the full sentence occupies six lines at 320px, three at 480px, two at 768px and one from 1024px. Below that the lead sentence and its link to the registry setup are unconditional, so the notice itself is intact at every width and the detail is deferred to the page that performs it, one tap away. The banner stays one line and 48px everywhere, and the wide layout is unchanged.823b3bc
[0.12.0] — September 22, 2026
🧩 New components
-
MultiStepForm — a guarded, branching flow around Stepper, with no opinion about your fields.
- It owns the flow and refuses to own your form.
stepsdeclares labels, branches and guards; eachMultiStepFormStepdeclares one body, and only the current one renders. A body is whatever you put in it — a Field form, a review table, an upload surface, nothing. That boundary is what keeps it at zero new dependencies: a guard is an ordinary function returning a promise, so React Hook Form, Zod or a hand-written check all plug in from the app side, and none of them becomes a dependency of the design system. - Guards, forwards and backwards.
beforeNextandbeforeBackreturntrueto pass or a sentence to refuse, and may be async — while one runs the step isloadingin the rail and the action is a loading Button. The flow never advances optimistically.canGoNextis the cheap synchronous gate for the case with no reason worth showing. - A refusal sits beside the control it blocks, and its weight follows its cause. A check that ran and failed renders an assertive Alert and marks the step; a gate not yet satisfied renders a quiet polite line and leaves the rail alone. Either is tied to the forward button through
aria-describedby, so it reads out with the control and persists — which a toast cannot do (WCAG 3.3.1). The one failure a toast belongs to is the check that could not run at all, and that arrives asonTransportError. - Conditional steps. A step whose
whenisfalseleaves the rail, the count and the sequence, so "step 3 of 4" stays true when a branch drops one — and a branch closing under the current step rewinds rather than stranding it. - One predicate decides four behaviours. Mark a step
satisfiedwhen its data already exists, and reachability follows: which steps a#step=…deep link may open, whether the rail starts complete, whether jumping is offered, and which layout a phone gets. There is nomode="create" | "edit"to keep in sync. lockseals what has been committed. Once a locking step is passed, Back is disabled for good and neither a jump nor a forged hash can reopen what came before it.- Optional steps, resume and deep links.
optionaladds the affix and a Skip that recordsskippedrather than complete.urlSyncwrites a namespaced#step=<id>so the browser's Back moves a step, and no router adapter is needed.persistKeyrestores the position and the steps passed for the life of the tab — opt-in,sessionStorage, and never for payment data. layout="panel"is the shape for a dialog. Inside a Dialog, Sheet or Drawer the nav and the action row hold their place while the step body becomes the one scrolling region, so the frame cannot grow past the viewport and take its own footer with it.MultiStepFormExitguards unsaved work.dirtyis yours to define; while it is true, leaving raises an AlertDialog first and the browser warns on a refresh or a closed tab — the half no component can fake. With nothing dirty it simply callsonExit, because a confirmation nobody needs is the fastest way to teach people to dismiss confirmations unread.- Phones get the layout their flow can honour. Reachable steps become a tappable section list that drills into a step; a strictly linear flow keeps the rail, which collapses itself to a line and a bar, because rows would promise navigation the flow does not offer.
- The multi-step form guide is rewritten from a recipe you assemble into a map of which component to reach for, and Stepper's page now leads with the case that needs no buttons at all.
76fa8d3
- It owns the flow and refuses to own your form.
🔧 Changed components
-
Typography: a global Geist-spec ramp replaces per-component type decisions
The system had no typography contract at all — the shadcn reset resolved TYP-1…TYP-9 and TYP-11 as shadcn, which left every size, weight, line-height and letter-spacing to Tailwind's stock values plus 229 local decisions across 112 component files. Nothing was globally declared, and nothing carried letter-spacing at any size.
Four new decisions (MK, 2026-09-22), all declared once and inherited everywhere:
- TYP-15 — heading-tier optical metrics. At
text-lgand above, line-height and letter-spacing follow Geist's heading spec, declared as per-size--text-*--line-heightand--text-*--letter-spacingin the@theme inlinebridge. Tracking runs −0.012em at 18px to −0.06em at 72px. Geist's copy tier carries zero letter-spacing, and this system's body sizes are its copy tier, sotext-xs/text-sm/text-baseare untouched and render byte-identically. SIZES do not move, so TYP-1 stays shadcn and a pasted shadcn snippet still renders at upstream's size. - TYP-16 — Geist rendering.
-webkit-font-smoothing: antialiasedonbody. Geist is drawn for it; without it the same weight renders heavier and softer than the identical weight elsewhere. - TYP-17 — a declared 14px default body size, on
bodyand never onhtml.remresolves against the root, so anhtmlsize would rescale every token and override the reader's browser font-size preference. Previously unclassed text fell back to 16px while components were 14px. The docs shell keeps its 16px reading size. - TYP-18 — no arbitrary font size. Upstream's
text-[0.8rem](buttonsm,togglesm,calendar) andtext-[0.625rem](questionnaire) now sit on the ramp. Upstream's ladder does scale type with control size and is KEPT — thesmhalf-step resolves down totext-xsrather than flattening up to 14px.
Also enforced: TYP-10 ("tabular figures on code and data") had been ours since the reset with no gate at all, and
number-fieldshipped proportional digits whose value jittered on every stepper press. It now carriestabular-nums, and three newdesign-lintrules —raw-tracking,arbitrary-text-sizeandtabular-figures— hold all of the above, each with negative-specimen coverage inverify-design-lint-structural.Block heading weight is normalised to
font-semibold; chart figure labels keepfont-bold.Markdown surfaces.
prose.rootnever declared a font family, and neither of its two consumers (MarkdownView,TextEdit) sets one — so prose inherited whatever surrounded it, and inside any mono container the whole tree rendered in Geist Mono: headings, paragraphs, table cells, and the1./2.markers of an ordered list, since::markerinherits font properties from its element. The recipe now declaresfont-sans, making mono the exception it names explicitly (code,pre,pre code) rather than something prose falls into by accident.No uppercase, anywhere.
design.md§ Voice & content has always said sentence case for everything and TYP-7 resolves as shadcn ("No uppercase"), but twelvefont-mono text-xs uppercase tracking-wideeyebrows had survived across the docs shell, plus theterminalandcode-blockheader labels and the OG card. Two were a correctness bug rather than a style one: the home page rendered real CSS custom-property names through the transform, so--text-lgdisplayed as--TEXT-LG. All of it is removed and gated by a newuppercase-transformrule, which bans the CSS transform rather than uppercase text — if a string is uppercase, write it that way. That also removed the only justification for positivetracking-*, which existed to make uppercase legible, soraw-trackingnow allowstracking-widestalone (the menu shortcut hint).code-blocknow showstsxas given instead ofTSX, andterminalshowsTerminal.The principle applied throughout: mono is for code, uppercase is for nothing — content that is code keeps
font-mono, content that is language isfont-sansin sentence case.The docs site. Fumadocs'
.prosewritesfont-sizedirectly rather than through a utility, so its headings tracked the ramp's sizes but never its letter-spacing,h1rendered at weight 800 (h1 strongat 900) andh3at 1.6 leading. All four now reference the ramp variables at weight 600. Two arbitrary sizes baked into Fumadocs' own class strings — 15px on the sidebar, tabs and accordion, 13px on code blocks — are pulled onto--text-sm, putting every piece of docs chrome at the same 14px as the product layer. Prose body stays 16px; it is a reading surface.76fa8d3 - TYP-15 — heading-tier optical metrics. At
🛠 CLI & tooling
-
The package-exports gate resolves its sibling from the tarball under test, not from npm.
verify-package-exportspacks both public packages and installs them into a throwaway consumer under npm and again under pnpm.@vegastack/designdepends on@vegastack/design-tokensby semver range, andversion-syncrewrites that range on the Version Packages PR — so on a release that movesdesign-tokens, the range names a version npm does not have yet, because that release is what publishes it. npm hoisted the local tarball and passed; pnpm resolved independently, went to the registry and failed the whole gate.The consumer now overrides that dependency to the tarball just built — the top-level
overrideskey for npm,pnpm-workspace.yamlfor pnpm, which in pnpm 11 no longer readspnpm.overridesfrom package.json. Because an override could equally hide a range pointing at the wrong sibling, it is paired with an assertion that the declared range equals^<the design-tokens version being shipped beside it>, which is the formversion-syncwrites. The gate keeps failing on a genuinely wrong range; it stops failing on a version that does not exist yet.dbc1190 -
The Version Packages PR refreshes
pnpm-lock.yamlafter rewriting internal dependency ranges.version-syncrewrites the semver range@vegastack/designdeclares on@vegastack/design-tokenswhenever a bump moves that package out of the pinned range, but nothing regenerated the lockfile afterwards — andpnpm-lock.yamlrecords the range as a specifier. The generated branch therefore carried a manifest and a lockfile that disagreed, and its ownPR qualityrun died atpnpm install --frozen-lockfilebefore reaching a single gate.version-packagesnow runspnpm install --lockfile-only --ignore-scriptsbetweenversion-syncandsync-changelog.linkWorkspacePackagesis on, so the pair resolves locally: the refresh needs no network and does not need the newdesign-tokensto exist on npm, which it does not at the moment this runs.The failure was latent from the moment the two packages' versions diverged and only fires when a bump leaves the
^range — which is why every previous release passed. No new gate: the check that should have caught this is the Version PR's own required run, and it did.a71cd3a -
The release scope guard knows about the lockfile, and is specific about it.
verify-release-output-scopeis an allowlist, andpnpm-lock.yamlwas not on it — because until this releaseversion-packagesnever produced a lockfile change. It does now, so the guard rejected a legitimate release output.The lockfile gets its own class with its own predicate rather than being waved through beside the changelogs: it is the one release output where a new third-party dependency could hide, which is the threat the allowlist exists for. The
specifier:/version:lines under a@vegastack/*key are neutralised — the same move already made for the package manifests — and everything else must be byte-identical. Verified against this release's real lockfile diff (exactly one line) and against a synthetic third-party addition and re-resolve, both rejected.2ef219d
📦 npm
@vegastack/design→0.7.0(was0.6.1).@vegastack/design-tokens→0.7.0(was0.5.0).- The design-system registry (
@vegastack/ui) bumps 0.11.3 → 0.12.0.
⚠️ Breaking
-
Stepper is rebuilt: the rail now carries the progress, and the refusal message moves out of it.
- Removed:
blockedReasonandblockedReasonId. They rendered under the current step's label — a one-column-wide ribbon in a horizontal rail, and nothing at all once the rail collapses — so the single placement they had was the one layout they did not fit. A reason the flow cannot advance now belongs beside the control it blocks: an Alert for a check that failed, a quiet line for a gate not yet satisfied, wired to your own Next button'saria-describedby. - Removed: the root is a
<div>, not the<ol>. The list is now nested inside it asdata-slot="stepper-list", so a ref or a selector aimed at the old root resolves to the wrapper. Every otherdata-slotsurvives. - Changed: the navigable step is a plain row, not a link-styled
Button.data-slot="stepper-trigger"is the target anddata-slot="stepper-label"stays on the label itself; anything selecting the old innerButtonclasses will not match. - New states.
loadingis what an async advance gate occupies while it runs — the state the previous four could not express without a checking step pretending to be idle.warningis a step that is passable but carries something to know, andskippedis one passed over rather than failed.optionalon a step renders an affix beside its label. - New: numbered nodes and a rail that fills in. Connectors behind the flow take
bg-primaryand those aheadbg-border— read from each step's own state, never from its index — so the component is its own progress bar. A completed step's ordinal gives way to a check; colour appears only forwarninganderror, whose labels take the family's-textink per A11Y-13 while the node fill carries its-foreground. - New:
orientation="auto"(the default) flips to vertical atverticalFromsteps (6), because a rail long enough to crush its own labels reads better down the page.collapse="auto"(the default) replaces the rail with the current step's name, its position and a Progress bar below a width derived from the step count — a container query, so a rail inside a narrow dialog collapses on a wide screen too. Focus follows the process into whichever of the two is laid out. - New:
size(default,sm),labelPosition(below,inline) andshowCount.inlinesets each label beside its node with the connector running on from it, from the same DOM and reading order as the default.76fa8d3
- Removed:
-
Toast is the one notification engine —
Sonneris retired, and Toast gains position, anchored toasts, custom bodies and a band of its own above the modal scrim (OVL-10, OVL-15, COL-23).-
Removed: the
sonnerregistry item, its docs page and thesonnerdependency. Upstream ships bothtoast(Base UI) andsonner, and the shadcn reset shipped both; two engines for one job meant two live regions and two fixed viewports competing for the same corner. Toast was already the default —providerhas always mounted it — so the migration is the import and the call shape:- import { Toaster } from "@/components/ui/sonner"; - import { toast } from "sonner"; + import { Toaster, toast } from "@/components/ui/toast"; - toast.success("Event created", { description: "Sunday at 9:00 AM" }); + toast.add({ type: "success", title: "Event created", description: "Sunday at 9:00 AM" });toast()/toast.success()/toast.error()/toast.info()/toast.warning()becometoast.add({ type, title, description });durationbecomestimeout(and0, notInfinity, disables auto-dismiss);action: { label, onClick }becomesactionProps: { children, onClick };toast.dismiss(id)becomestoast.close(id)— with one real difference:close()with no id closes the FRONTMOST toast, where sonner'sdismiss()closed every one.toast.promisekeeps its{ loading, success, error }shape. A copy you already installed keeps working; it just stops receiving updates. Upstream'sdashboard-01block now fires our manager. -
New:
positiononToaster— six logical corners (top/bottom×start/center/end, defaultbottom-end). Inline names are logical, sobottom-endis bottom-right in an LTR document and bottom-left in an RTL one. The stack's growth direction and its swipe direction follow the corner, so a toast always enters and leaves through the edge it is pinned to. -
New: anchored toasts.
ToastPositionerandToastArrowwrap the two Base UI parts upstream wraps neither of, so a confirmation can position against the control that caused it instead of stacking in the corner. -
New: custom bodies. A toast carrying
data.renderdraws its own content and keeps the surface, the stack, swipe-to-dismiss,Escapeand the viewport's live region — an avatar or a thumbnail no longer means opting out of the toast contract. -
Fixed: a toast fired while a modal is open is no longer hidden behind the scrim. The viewport moves to a
z-60band, one above the singlez-50overlay band every other surface shares. It has to be a band rather than DOM order, because the viewport mounts with the provider before any dialog exists. Nothing else leavesz-50. -
Fixed: a toast with only a description read in the secondary ink. Base UI renders
Toast.Titleasnullwhen there is no title, sotoast.add({ description })— the shape upstream's own examples use — painted the toast's only line, its primary message, in muted grey. The description now takes the default ink exactly when it leads, and stays muted under a title.76fa8d3
-
[0.11.3] — September 22, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.11.2 → 0.11.3.
📚 Docs
- The reference-consumer starter is no longer part of the project workflow. The
shipskill's post-release step, the Quickstart, Troubleshooting and Production checklist guides, the repo map and the internal operations guide no longer reference it, and the guides state what is actually true of themselves rather than deriving their authority from a repo nobody can run. Release verification is now the npm versions plus the production-boundary probe. Historical records indocs/ledger/anddocs/plans/are untouched: they record what was decided at the time.80e127a
[0.11.2] — September 22, 2026
🔧 Changed components
-
The indeterminate loading mark is now lucide
Loadereverywhere, in place ofLoaderCircle.- Spinner, Toast's loading icon and
Sonner's loading icon move fromLoader2Icon— an alias of lucide'sLoaderCircle— toLoaderIcon, so they match StatusIcon'sprogressand upstream's owndashboard-01block. One loader shape across the system. - Every component that composes
Spinnerinherits the new mark, including Button and Toggle in theirloadingstate. - Recorded as decision ICO-8 and enforced by design-lint's new
loader-markrule, so the upstream mark cannot return one file at a time.38ad929
- Spinner, Toast's loading icon and
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.11.1 → 0.11.2.
🐛 Fixed
-
A loading Toggle no longer paints its label underneath the spinner.
- The label wrapper was
display: contents, which generates no box and therefore accepts noopacity, soopacity-0had nothing to apply to and the label stayed fully visible. It is now a realinline-flexbox inheriting the control's owngap, matching Button. - This is the same defect that was fixed on Button; Toggle was the only other component carrying the pattern.
38ad929
- The label wrapper was
[0.11.1] — September 22, 2026
📦 npm
@vegastack/design→0.6.1(was0.6.0).- The design-system registry (
@vegastack/ui) bumps 0.11.0 → 0.11.1.
🐛 Fixed
- Publish concrete dependency ranges so fresh npm and pnpm consumers can install the package outside the VegaStack workspace.
997dfb8
[0.11.0] — September 22, 2026
🧩 New components
-
SearchInput now provides a token-safe clear action with controlled, uncontrolled, keyboard, and native form behavior across supported browsers.
- Use the new reusable SearchInput for consistent search semantics and styling.
- FilterBar now composes SearchInput while preserving its existing controlled search API.
6ed3e37
🔧 Changed components
-
Avatar fallback initials now use the smaller
text-xssize across all three avatar diameters.- Preserve the existing
sm,default, andlgdiameters and public API. - Keep explicit consumer typography overrides and standalone group-count typography unchanged.
cfe049d
- Preserve the existing
📦 npm
@vegastack/design→0.6.0(was0.5.0).- The design-system registry (
@vegastack/ui) bumps 0.10.0 → 0.11.0.
📚 Docs
- Teach consumers to use SearchInput for token-safe, cross-browser search clearing and FilterBar-compatible search behavior.
14c88ce
🐛 Fixed
- Loading Buttons now hide their label through a real layout box, preventing the centered spinner from overlapping visible text while preserving the button’s size and accessible name.
e7097d9 - Command dialogs now stay centered inside short viewports while their results scroll and composed footers remain visible.
1689201
[0.10.0] — September 19, 2026
🛠 CLI & tooling
upstream:paritynow compares the canonical file and the patched upstream file with the generated provenance header excluded, so a version bump no longer breaks byte parity for every patched component.a918731version-syncnow walks the whole component-contracts document when it rewrites public dependency ranges instead of iterating three named categories, so a category added later — chart blocks, libs, the animated-icon shared contract — cannot be left at the previous range and failverify-component-contractson the Version Packages PR.c3053c3
📦 npm
@vegastack/design→0.5.0(was0.4.1).@vegastack/design-tokens→0.5.0(was0.4.0).- The design-system registry (
@vegastack/ui) bumps 0.9.1 → 0.10.0.
⚠️ Breaking
-
The shadcn
base-novareset — the runtime and token bridge are rebuilt on shadcnbase-nova, with no compatibility layer.The optional
lucide-reactpeer range moves to^1.47.0, matching the version the system is built and tested against; a consumer on an older lucide should upgrade alongside this release.cnis plaintwMergeagain, because the custom font-size class group it extended no longer exists, and seven shared class-string recipes are deleted with no alias:surfaceInteractive,surfaceInteractiveGroup,fillInteractive,FillTone,fieldControl,fieldControlGroupandselectedChipVariants. Each described a system — a three-rung surface ladder, a per-tone fill map, a shared text-entry chrome, a raised-chip recipe — that the reset deleted; replace each with the literal it expanded to, or compose the upstream component it was re-deriving.cn,TIMINGS,FLOATING,mergeRefs,prose/proseClassName, the icon runtime, the Tailwind preset and thevegastack-designCLI are unchanged. The shipped agent skills are rewritten for the new vocabulary.Who this affects: every consumer. Nothing is deprecated first — an import of a removed export fails to resolve. The complete break, with a replacement for each removed export, is the migration guide § 6.
72ae827 -
The shadcn
base-novareset — the token contract is shadcnbase-nova'sneutralbase plus our recorded additions, and every deleted family is gone with no alias.94 resolved tokens per theme, down from 185. Deleted: the surface ladder (
--surface-1/2/3), the 19-entry--alpha-*ladder, the--opacity-*ladder,--size-*,--icon-*,--panel-width-*,--layout-*,--z-*,--shadow-overlay,--radius-xs,--radius-sharp,--overlay,--muted-foreground-faint,--font-family-pixel, the whole role and display type scale (text-h1…text-h4,text-label*,text-code*,text-mono-label,text-display-*), and every-subtle/-hover/-activestep on the four STATUS families (the ten--tag-*trios keep theirs). Each is now a plain Tailwind utility. Kept and ours: the four status families in upstream's owndestructiveshape — each with a-foregroundink for the solid fill and a-textink for the page and the family's own tint — the 8-hue chart palette plus--chart-single, the 10-hue tag palette, the brand pair, the media trio, the Geist families, and the--duration-*/--motion-ease-*pairs behind themotion-*utilities.Who this affects: every consumer, including ones that never touched a component. A deleted token compiles to nothing rather than failing, so a page keeps rendering and quietly looks wrong. The searches that find every holdover, and the write-instead table for each family, are the migration guide §§ 3–5 and § 11.
72ae827 -
The shadcn
base-novareset — every component shadcn ships is now upstream's own file plus a recorded patch, and there is no compatibility layer.62 upstream components were reset onto pinned
shadcn@4.21.0base-nova, so their props, variants and behaviour are upstream's; 13 are new here (aspect-ratio,button-group,calendar,carousel,direction,drawer,input-group,input-otp,menubar,native-select,questionnaire,sonner,panel-search); 10 are retired onto an upstream replacement (icon-button,otp-input,password-input,checkbox-group,field-inline,segmented,split-button,progress-indicator,onboarding-checklist,floating-surface); and the 10 marketing components are removed outright with their scope mechanism and tokens. Button'svariant × toneaxis becomes upstream's flatvariant,IconButtonbecomesButton size="icon*",Fieldcomposes its label, description and error as children, the toast manager istoast.add/toast.close/toast.promise,Sheetmoves off Base UI Drawer onto Dialog, and a long list ofsizeaxes is gone. Visible without touching any code: one 2px focus outline instead of the ring glow, a hand cursor on every control, shadcn's own neutral, no surface ladder, and Tailwind's stock radius, shadow and type scales. The registry ships 689 items, including 100 blocks.What is ours is 61 recorded exceptions, every one traceable: a component's docs page closes with a
## Deviationssection naming the decision IDs behind its patch. One of them is new to this release:TooltipContentandDropdownMenuContent(and soDropdownMenuSubContent) take an optionalcontainer, forwarded to their portal, so chrome drawn over a fullscreen surface renders inside it instead of behind it — which is what makes a fullscreen player show its control labels and its settings menu again. Leavingcontainerunset is upstream's default.Who this affects: every consumer. Re-pull every copied-in component; a retired import fails to resolve, and each retirement's prop map — including what did not survive it — is in the migration guide § 7.
72ae827
[0.9.1] — September 15, 2026
🧩 New components
- Selection controls — Give ToggleGroup, Segmented, and pill Tabs a persistent semantic boundary, and use Segmented for the documentation preview's device modes.
6ec9d54
🛠 CLI & tooling
- Verification and release — Run deterministic affected component tests once on pull requests and automate the authorized publish-and-deploy chain.
6ec9d54 - Version PR checks — Use GitHub's native approval gate for the bot-created pull-request run so its
PR qualityresult satisfies protected-main status checks without a duplicate dispatch.22cc795 - Version PR verification — Route generated version metadata through static and positive output checks without misclassifying provenance-only registry rewrites as component edits.
ed2aa8f - Release automation — Restore a generated Version Packages PR so protected
mainneeds no automated direct-push bypass.3dd6a85
📦 npm
@vegastack/design→0.4.1(was0.4.0).- The design-system registry (
@vegastack/ui) bumps 0.9.0 → 0.9.1.
[0.9.0] — September 15, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.8.2 → 0.9.0.
🐛 Fixed
- Toast — center the copy and controls vertically in single- and two-line notifications,
give the labelled action the same quiet ghost treatment as dismissal, and complete the docs
Scope and part API sections.
docs ·
31e915d
[0.8.2] — September 12, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.8.1 → 0.8.2.
🐛 Fixed
- Dropzone docs — keep the three-state drag preview within 320px by giving its narrow layout
an explicit zero-minimum grid track before the three-column breakpoint.
docs ·
c371d09
[0.8.1] — September 12, 2026
🛠 CLI & tooling
- Geometry diagnostics — name the elements and computed paint geometry that own a horizontal
overflow, so a cross-engine failure identifies its cause instead of reporting only the page width.
9390ce4
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.8.0 → 0.8.1.
[0.8.0] — September 12, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.5 → 0.8.0.
🐛 Fixed
- Keep Dropzone drag feedback inside narrow WebKit viewports. The drag-state stroke now sits
one stroke-width inside the surface and uses explicit border-box sizing. This avoids WebKit counting
the stroke's two edges as horizontal scroll overflow at 320px while preserving the design system's
independent focus-visible outline.
docs ·
f4fec28
[0.7.5] — September 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.4 → 0.7.5.
🐛 Fixed
- Dropzone — paint drag feedback with an inset overlay that cannot expand WebKit scrollable
overflow.
e5ba405
[0.7.4] — September 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.3 → 0.7.4.
🐛 Fixed
- VideoPlayer WebKit contract — separate pointer-leave hiding from the intentional
focus-retention behavior in the browser test.
e45defd
[0.7.3] — September 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.2 → 0.7.3.
🐛 Fixed
- WebKit release contracts — keep Dropzone drag outlines inside the viewport and make
AudioPlayer media-time tests use an engine-independent writable clock.
7c5a9e8
[0.7.2] — September 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.1 → 0.7.2.
🐛 Fixed
- Docs-shell verification — assert native modal isolation directly instead of requiring a
platform-dependent focus-guard escape after the injected defect.
9a1c6ec
[0.7.1] — September 11, 2026
📦 npm
- The design-system registry (
@vegastack/ui) bumps 0.7.0 → 0.7.1.
🐛 Fixed
- Docs-shell verification — make the modal native-inert negative proof remove every outside
descendant and keep the injected defect active for the full focus walk.
ae881cc
[0.7.0] — September 11, 2026
🧩 New components
-
--duration-indeterminate(1200ms) and themotion-indeterminateutility — the one sanctioned looping animation, the sweeping segment of an indeterminateProgress. Its keyframes open and close on the same resting frame, so the globalprefers-reduced-motionreset leaves a static 35% segment rather than a bar that reads as complete. docs ·7915a71 -
surfaceInteractiveandfillInteractive— the two hover/pressed recipes, exported from@vegastack/designso no component writes ahover:bg-*literal again.surfaceInteractive(hover:bg-surface-2 active:bg-surface-3) is for a control on a known ladder surface;fillInteractive.<tone>(hover:bg-<tone>/(--alpha-hover) active:bg-<tone>/(--alpha-pressed)) is for one on an unknown backdrop or hovering in its own hue. TheFillTonetype ships with them. docs ·b2c2e96 -
A layout scale, and
--chart-single.--layout-header-height,--sidebar-width-mobile,--layout-overlay-max-heightand--panel-width-sm|md|lggive the shell and the overlay family named dimensions instead of per-component literals, and--chart-singlenames the one-series chart colour. docs ·b2c2e96 -
CheckboxGroup — shared state for a set of checkboxes, with first-class "select all". Base UI ships the parent/child arithmetic (
allValuesplus aparentchild gives the mixed state and the whole-set toggle) and the system had no wrapper for it, so DataGrid, DataList and every permissions block computed it by hand. There is noCheckboxGroupItem— a child is a plainCheckboxwith avalue.useInlineEdit— the click-to-edit machine: draft, commit, cancel, focus restoration and the double-commit guard (Enter closes the edit, which unmounts the input, which fires blur, which would commit a second time).FieldInlineandEditableCellhad each written it, and the copies had already drifted — only one re-armed the guard when a controlled host flippededitingon, and only one restored focus after a keyboard commit. It owns no DOM and no persistence, so it also serves a cell editor that is aSelectpopup with no text input at all. docs ·9fbeb65 -
28 new
lucide-animatedicons adopted — the mirror moves 439 → 467. Upstream had drifted 28 items ahead of the pinned manifest, and becausetooling/mirror-animated-icons.mjsfails closed on an unexpected upstream item count,--refreshcould not be run at all. The new items arebinary,cigarette,cigarette-off,circle-gauge,cloud-backup,cloud-sync,external-link,hat-glasses,leaf,leafy-green,link-2,monitor-cog,palette,plane-landing,plane-takeoff,server,server-cog,server-crash,ship-wheel,shredder,soup,spray-can,ticket,tree-deciduous,tree-pine,user-plus,waves-arrow-downandwaves-arrow-up— eachshadcn add @vegastack/icon-<name>, each generated by the mirror rather than authored, and each inheriting the singlecreateAnimatedIconcontroller with its reduced-motion gate. None replaces an existing icon: the refresh removed nothing, the 439 pinned upstream and generated-module hashes are byte-identical, and every new item maps to its ownlucide-reacticon module rather than to an alias of one already mirrored. Registry items go 568 → 596.tooling/verify-animated-icons.mjsgains two sanctioned Motion easings ([0.25, 1, 0.5, 1],[0.65, 0, 0.35, 1]), two durations (0.12s, 0.32s) and a narrow staggered-duration form whose base and per-index step are each held to a sanctioned vocabulary; its--self-testgrows to seventeen mutations, two of them new — an off-vocabulary stagger step, and a one-digit glyph edit inside one of the newly adopted modules. docs ·2035023 -
media-player-controls— the shared media transport, lifted out ofaudio-player(which owned the video player's controls too). It carries the control surface anduseMediaShortcuts: ONE keyboard map for both players (Space/K play, J/L and arrows skip, M mute, F fullscreen), scopedsurfacevscontrolsso a shortcut can never steal a key from the focused control. TheassignRef/getMediaDuration/clampTimehelpers live here as well, instead of in a copy per player.audio-playerdrops from 1,431 lines to 305. docs ·aa5fa0d -
use-media-query— the system's onematchMediasubscription, onuseSyncExternalStorewith a caller-declaredserverFallback. Five files each hand-rolled the sameuseState(false)+useEffectshape, and every one of them reportedfalseon the server, so a phone rendered the DESKTOP branch of every JS-driven layout until an effect ran. ShipsusePrefersReducedMotionas its named reduced-motion reader;useIsMobileandusePlatform's touch half are now one-liners over it. docs ·3663f8f -
floating-surface— the shared floating-overlay module every anchored overlay now composes: onePortal → Positioner → Popup (→ Viewport)composer, one theme-scope hand-off across the portal boundary, one arrow, and four painted surfaces (panelat the 16px tier,menuat list density,tooltipas the inverted ink chip,navigationfor the morphing mega-menu). It also ownsmenuItemVariants— the one list-item recipe behind menu items, select options, combobox options and command rows — andPanelSearchFrame, the in-panel search row.7915a71 -
toast— the toast surface on Base UI's Toast primitive, replacing the sonner wrapper. It ships the whole vocabulary rather than a configured library root:ToastProvider,Toaster,Toastand every part (ToastRoot,ToastContent,ToastTitle,ToastDescription,ToastAction,ToastClose,ToastViewport,ToastPortal,ToastPositioner,ToastArrow), plususeToastand the imperativetoast(). Stacking with expand-on-hover, swipe-to-dismiss,F6into the viewport landmark andEscapeon the focused toast come from the primitive; promise toasts and a customrendercome with it. The surface is the floating-family recipe at 16px padding and a typed toast wears Alert's exact tint recipe, so the two status surfaces read as one design. Six types (default · success · error · warning · info · loading) — the strings follow the engine, which writesloading/success/errorinpromise()itself, while the tokens follow the house families. The live-region policy is enforced in code:errorandwarningannounce urgently, everything else politely, derived from the type. docs ·d5e2de2 -
geo-dataanddrag-item— the first tworegistry:libitems: plain.tsmodules that install under a consumer'slibalias and are pulled in automatically as dependencies.geo-dataholds the ISO 3166-1 country list, the states/provinces map and their two lookups, so a consumer installing both geography selects copies the data once (region-select.json67 KB → 7.4 KB).drag-itemholds the one visual recipe for ause-drag-reorderitem — drop-edge hairlines, lift dim, pending shimmer — whichBoardandSortableListhad each copied. docs ·45cde26 -
searchable-select— the one Select-shaped Combobox preset: a full-width trigger, the shared in-panel search row, a check on the selected row, a--anchor-widthpanel and an optional clear control.CountrySelectandRegionSelectare now thin data-fed wrappers over it. Two rules it exists to hold: selection runs through Base UI'svalue/onValueChangeand nothing else (the oldRegionSelectcomputed the value inside each row's click handler with the root deliberately unwired, so keyboard Enter and a pointer click reached it by two different paths), and the clear control is a SIBLING of the trigger, never a child, because an interactive control may not contain another. docs ·45cde26 -
data-table-parts— the chromeDataListandDataGridhad each grown separately: the sort header, the select-all / per-row selection arithmetic, the skeleton rows, the empty row and the column class rules, twice each. They now come from one registry item, installed automatically with either renderer throughregistryDependencies.SortableHeadandSortHeaderButtoncompose the systemButtoninstead of a hand-rolled<button>, and emitaria-sorton every sortable column ("none"included). The doctrinal split is unchanged —DataListstays presentational,DataGridkeeps its engines.6d25185 -
use-overflow— the system's one overflow measurement, live across resizes AND content growth: it observes the element and its children, so a table widening inside a fixed viewport is not missed. It backsTruncatedText's clipping check, theTablescroll region's focusability,ComparisonMatrixandTerminal's command pane. docs ·6d25185 -
chip— THE labelled pill, and the only one.hue(10 decorative tag hues, or neutral) ×size(sm28px inline ·md32px control-scale) ×active(the neutral chip's promotion to thesurface-2selection rung), withonRemovemounting a round ghostIconButton size="xs"whose real border box is 24×24.Tag,FilterChip,ComboboxChipand ChipInput's chips are all this one primitive composed through Base UIrender. docs ·7915a71 -
use-announcer— the one polite live region. DestructureannounceandAnnouncerfromuseAnnouncer()and render theAnnouncerelement once per component. It keeps the region mounted and observed from first paint, re-keys it per call so an identical consecutive announcement is still spoken, and holds its state outside the host so announcing no longer re-renders a whole DataGrid. docs ·7915a71
🔧 Changed components
-
Animated icons — the host element is now an
inline-flex<span>rather than a block-level<div>, so an icon placed in a line of text no longer breaks the line box, andAnimatedIconComponenttypes its host asHTMLSpanElement. Reduced motion is now a live subscription to(prefers-reduced-motion: reduce), so turning the preference on settles every mounted icon immediately instead of only affecting icons mounted afterwards. Motion's own hooks cannot do this: in 12.42.2useReducedMotion()isuseState(prefersReducedMotion.current)— a one-shot read of a module singleton captured at first import, with a standingTODOabout not updating — anduseReducedMotionConfig()layers<MotionConfig>on that same one-shot value. Worse, the OS preference was never consulted at all unless the application happened to mount a<MotionConfig>:useReducedMotionConfig()returnsfalseoutright when the context saysreducedMotion: "never", and"never"is precisely Motion's default context value. The factory now treats the preference as the base value and lets<MotionConfig reducedMotion="always">add reduction on top; the override is one-way, because an explicitreducedMotion="never"is byte-identical to no provider at all and honouring it would switch reduced motion off for everyone who configured nothing. Public icon names, thesizeprop and thestartAnimation/stopAnimationhandle are unchanged. docs ·b2c2e96 -
A list row is 14/500 over 12, disclosures hover with the row wash, and the checklist composes the progress primitive.
ItemTitlemoves from 12px totext-label— the same type Sidebar menu rows, DataList cells, menu items and Message rows already use, because a 12px Item title beside a 14px sidebar row read as two systems;size="sm"keeps the denser 12/12 pair. Accordion and Collapsible triggers droppedhover:underline— underlining on hover is the link affordance — and tooksurfaceInteractivetogether with the padding, inner radius and ≥4px hairline inset that the wash requires, with the accordion panel taking the same horizontal padding so the body stays aligned under its label and row heights unchanged.OnboardingChecklist's segmented bar was a second hand-rolledrole="progressbar"next to the primitive that already draws one, so it now composesProgressIndicator segments; the primitive gainssegmentsFill(segments share the container width instead of a fixed bar width) and accepts a single segment. A navigableStepperlabel is now alink-variant Button instead of aghostButton with its height and padding stripped to imitate inline text. docs ·8c18d2b -
Base UI 1.6.0 → 1.8.0,
@shadcn/react0.2.1 → 0.3.1. Fifteen upgrade deltas were executed and observed in a browser rather than assumed from a green suite; only MessageScroller needed a source change. Its viewport now answers the primitive's newdata-pending-scrollattribute withinvisible, so a server-rendered transcript no longer paints the top of the thread for one frame before jumping to the bottom —visibility: hiddenrather thandisplay: noneon purpose, because the primitive measuresclientHeight/scrollHeightto decide where to scroll and a display-none viewport measures zero. A regression test asserts the attribute never sticks.message-scroller's declared@shadcn/reactrange moves to^0.3.1; it anddate-pickercarry new integrity hashes. Two user-visible upstream changes are kept as shipped: areadOnlySelect/Combobox now opens and browses (reached througheditable-cell), and start/end-aligned popups take their pop-in--transform-originfrom the aligned edge. docs ·5b03e3b -
One key chip, and the display leaves move onto role tokens.
TooltipKbdrendersKbd size="xs"instead of restyling a second<kbd>, so a shortcut hint reads identically wherever it appears — and inherits the OS rewrite.Kbd's three sizes now use one type role (text-code-sm);mdreached the same 12px throughtext-sm, the same pixel size named twice, and the meaninglesspointer-events-noneon a<kbd>is gone.StatusIconsizes become the--icon-inline/default/action/featurerole tokens (14 / 16 / 20 / 24px), the ladder Spinner already uses, instead of rawsize-Nsteps spelling the same four values. ASkeletonline moves to the text radius (rounded-sm): 8px on a 16px bar reads as a pill, not as text. docs ·7915a71 -
Reduced motion is stated once, globally. All seventeen
motion-reduce:copies across eleven components were deleted — the registry now carries zero. Thebase.cssreset owns the rule with the one sanctioned!important, so a per-component restatement adds nothing and is a second copy that can drift. One copy looked load-bearing and exposed a hole in the reset instead: it zeroed animation duration but not delay, soStaggeredTextRevealstill played its words out one by one over the full stagger window. docs ·7915a71 -
Twelve components are server-safe again. Avatar, Button, Collapsible, Field, Progress, Resizable, ScrollArea, Separator, Slider, Switch, Tabs and Toggle carried
"use client"without touching a hook or a handler. A client module poisons every RSC importer downstream —buttonVariantscould not be read from a server component. 84 client leaves in the registry became 72. docs ·7915a71 -
text-xsis mono-only. Seven sites across Attachment, AudioPlayer, Chart and ProgressIndicator were reaching 11px in Geist Sans for density; sans copy now floors attext-sm(12px). docs ·7915a71 -
TruncatedTextgainsfocusable, with aTruncationFocusProviderthat sets it for a whole region. Clipped text becomes a Tooltip trigger and takes a tab stop — in a 50-row table that is 50 extra tab stops layered on a grid's own roving focus, and CSS truncation never hides anything from a screen reader, so the tooltip only ever served sighted keyboard users.IconText,TableCellTextandRelativeTimetake the same prop. docs ·7915a71 -
Button, Select, Sidebar, Toggle, Tabs, Table, DataGrid, DataList, Board, Item, Pagination, NavigationMenu, Combobox, DatePicker, Dialog, Sheet, Popover, HoverCard, Segmented, TagGroup, Bubble, Card, AppShell, EmojiPicker, FieldInline, MessageScroller, NumberField, OnboardingChecklist, ShortcutOverlay, Sonner, Switch, ToolCallChip and the dashboard-01 block — every hover now climbs one rung and every control has a pressed step. Previously only the solid primary Button darkened on
:active; a state probe found 268 elements where pressing changed nothing. Select's trigger hovered only in dark mode; it now hovers in both. The current sidebar row rests onsurface-3so hovering it still moves. ComparisonMatrix and PricingSection stop usinginfo(blue) for the promoted column and the highlighted plan —infois links and informational UI only; promotion is a neutral ladder rung. docs ·b2c2e96 -
A loading Button no longer changes width. The spinner is taken out of flow and stacked over the label, which keeps its box behind
opacity-0— notvisibility: hidden, which would drop the label out of the accessibility tree and leave a pending button with no name; previously a "Save changes" button jumped about 20px the moment a request started. docs ·7915a71 -
IconButton everywhere. The dismiss, pager and toggle controls that were hand-rolled
<button>elements in Alert, AnnouncementBanner, Dialog, Sheet, Pagination, OnboardingChecklist and FilterBar are nowIconButton, and CopyButton, MessageScrollerButton, ColorPicker, EmojiPicker and SplitButton's chevron half compose it too — so they all inherit the matrix, the focus ring, the loading contract and the required accessible name. NewiconButtonGeometry(size, shape)styles an icon-only link: navigation stays a real<a>(PageHeader's back affordance) instead of acquiringrole="button".7915a71 -
One field chrome. The border/hover/focus/invalid/disabled grammar was copy-pasted in Input, Textarea and NumberField, restated a fourth time as slot overrides in Field, and again in Combobox and ChipInput. It is now
fieldControl/fieldControlGroupin@vegastack/design, which Input, Textarea, NumberField, OTPInput, the Select trigger, the Combobox input and its input-group, and ChipInput all spread — so retuning the field is one edit. docsCheckbox, RadioGroup, Switch and the Select trigger hover in both themes, checked included. A state probe found no hover treatment at all on 31 checkbox/radio/switch fixtures, and a ticked control read dead under the cursor while an unticked one moved. They now step through the same neutral border rung every field wears, and a filled control steps through the solid's own darker rungs. docs
NumberField's ± steppers no longer run their hover fill into the field border. The wash was full-bleed, so it met the field's hairline on three sides with a square inner corner against the rounded outer one. It is now an inset chip with its own radius; the button keeps the full pointer target. docs
Labelisinline-flexby default, so it composes into a sentence instead of breaking the line around itself;layout="block"is the explicit opt-in for the stacked form row. docsPasswordInput's eye toggle is an
IconButtonin the ghost recipe, and the eye swap has no motion. It replayedmotion-pop-inbehind a guard whose only job was to stop the animation firing on first paint — a tell that the animation did not belong there.FieldInline and EditableCell run on
useInlineEdit, and FieldInline's rest hover is the shared interactive-surface recipe, so it is visible on a card rather than only on the page ground.AutoSaveInput drops a
cn(className)no-op, sizes its status slot with--icon-defaultinstead of a rawsize-4, and marks its spinnerdecorative. docs ·9fbeb65 -
AttachmentTriggershows a real focus indicator. It droppedoutline-noneand a border tint as its only keyboard cue; the standard outline is drawn inset so the card's radius does not clip it. A border tint is the text-entry treatment, not a button's. docs ·aa5fa0d -
AudioPlayer gains mute and a volume rail in both layouts. Audio previously had no visible volume control at all and mute was reachable only from the M key. The rail is a vertical
Slideropened from the mute button, rendered inline rather than portaled — the video frame is the fullscreen element, so a portal to<body>would put the rail outside it. The seek thumb is now hidden at rest only where a pointer can hover; on touch it stays visible, because otherwise there is no scrub affordance at all. docs ·aa5fa0d -
Imagelazy-loads and decodes off-thread by default.loading="lazy" decoding="async"are now the defaults, matching what MarkdownView already did for its images. Passloading="eager"for an above-the-fold hero, where deferring the fetch delays LCP rather than saving it. docs ·aa5fa0d -
Media chrome is theme-invariant. The video overlay was built on
primary, which flips with the theme — in dark it rendered a near-white scrim behind near-black icons. Scrim, pills and ink now come from--media-scrim,--media-scrim-strongand--media-foreground, which are the same values in both themes, and a compiled-CSS test pins scrim lightness under 0.3 with overlay ink over 0.85. Overlay controls areIconButton variant="ghost" shape="round"on the scrim. docs ·aa5fa0d -
Media controls keep the system's focus outline. The
ring-2 ring-ring/50glow that media invented for itself, and the forced-colours carve-out beside it, are gone; the standard 2px:focus-visibleoutline applies, inset with-outline-offset-2so anoverflow-hiddenframe cannot clip it.tabIndex={0}now appears only on genuinely scrollable regions. docs ·aa5fa0d -
Slidergrows the props the players were faking with descendant selectors.variant(default · media · overlay · bare),orientation(vertical is now supported and is how the volume rail is built),thumb(always · hover · none),marksandshowValue. Every[&_[data-slot=slider-*]]override in the players is deleted. docs ·aa5fa0d -
VideoPlayergainscontrolsVisible.truepins the overlay open andfalsekeeps it closed, for kiosk players — and for static fixtures, since the auto-reveal never fires without a pointer and the docs page therefore never showed the video controls. docs ·aa5fa0d -
Rich text is one recipe, and the bars that promise arrow-key traversal now implement it.
- MarkdownView and TextEdit share the
proserecipe. Both restated the same heading, paragraph, list, quote and code recipe in their own grammar and had already drifted (h4–h6, tables and images existed on one side only). They now wear the same class from@vegastack/design, so rendered markdown and edited rich text are one typography — asserted structurally: a unit test requires every rule of the recipe on both roots, and no typography of their own. TextEdit's fenced-code block stops being a third copy ofCodeBlock's surface, and the editor's links no longer all light up when the editor itself is hovered. - TextEdit's formatting row is a Base UI
Toolbar— one tab stop, arrow keys across three labelled groups,Shift+Tabout. It was arole="toolbar"of eight independent tab stops. - ActionBar is a Base UI
Toolbarwith newActionBarButtonandActionBarSeparatorparts. Compose the actions from them: a toolbar builds its single tab stop from the items that register with it, so a bare<Button>renders but keeps its own tab stop. - ActionBar and MessageScrollerButton use the shared
motion-dock-in/motion-dock-outpair instead of two copies of a recipe that exited more slowly than it entered, with a scale on a bar that slides off its own edge. - MessageScrollerButton defaults to
variant="outline"with no inline colour override — after the Button matrix,outlinealready is a page-coloured face with the one hairline and the surface-ladder hover.2a3fc24
- MarkdownView and TextEdit share the
-
dashboard-01's KPI labels no longer truncate. Every stat label was cut at the 2-column width ("Active agen…", "Tasks compl…", "API calls (24…"). The label now wraps to two lines across the full header and the trend badge sits on the value row, beside a short mono figure; the header breadcrumb collapses at
maxItems={2}so it stays one line at 320px.3663f8f -
LogoRow shows marks, not links. The underline is gone — wordmarks rest in
text-muted-foregroundand lift totext-foregroundon hover, because a wall of underlined text reads as a paragraph of links. Cell seams are logical (-ms-px border-s), so RTL keeps its inner rules instead of doubling the outer edge, andwallColumnsis now a MAXIMUM over anauto-filltrack with an 8rem cell floor: a 4-column wall at 320px gave 80px cells and clipped every mark.3663f8f -
PlanCard's promoted plan is a ladder rung, not a border colour. It now lifts onto
surface-3with its hairline tintedprimaryat--alpha-outline-border. A full-strengthborder-primaryread as the active/invalid state of a form control — the wrong signal on a card someone is being invited to choose.3663f8f -
SettingsSectiongainedtitleAs(h2…h6, defaulth3). A settings page nests sections at different depths, and a hard-coded<h3>everywhere breaks heading navigation. The visual size is unchanged; only the document structure moves. docs ·3663f8f -
StaggeredTextReveal waits for the scroll (
whenVisible, on by default). A reveal below the fold used to finish before anyone scrolled to it. The gate only ever REMOVES the reveal — the server-rendered markup animates and the client pulls off-screen words back before the first paint — so a page whose JavaScript never runs still shows its text.3663f8f -
Testimonial takes its quotation marks from CSS. The quote renders inside a
<q>, so the browser inserts the pair the ACTIVE language uses („…“,« … »,「…」) instead of the hard-coded English“…”— which also kept the marks out of the copied text.3663f8f -
Navigation and layout take one selection recipe and one hover geometry. Tabs, Segmented, Toggle and ToggleGroup move onto the shared
selectedChipVariantsrecipe from@vegastack/design, and a selected chip now hovers and presses again — it used to be excluded from both bynot-data-pressed:*/not-data-[active], so the one chip a user is most likely to click was the one that answered nothing. The Tabslinetrigger's hover wash used to end exactly on the rule the underline indicator rides along, in both orientations; it is held one 4px step off it with a logical margin, so the vertical variant mirrors onto the inline-start rail and RTL follows for free.SidebarProvidergainspersist(defaulttrue) around the cookie write:persist={false}keeps the component out ofdocument.cookieentirely andonOpenChangefires either way, so a host under a consent regime persists the state itself and loses nothing — the docs section is renamed from "SSR persistence" to "Persistence".ScrollArea's viewport is a tab stop only once its content actually overflows, measured on mount and on resize; Board's column viewports inherit it.AppShellContentandSidebarInsetgainlandmark="region", which renders a<div role="region">instead of a<main>for a shell embedded in a page that already owns one.SidebarTriggeris anIconButtonrather than a hand-rolleduseRenderbutton, so it inherits the one box, ink and hover/pressed grammar, andPageHeader's back affordance swaps its physical-ml-2for a logical-ms-2.PageHeader's active favourite star fills withforegroundinstead ofwarningink, which read as caution on a control that means "I marked this". docs ·fdaed05 -
Popover and Select document their modality. Both are modal by default so the page cannot scroll out from under an open panel; pass
modal={false}for a lightweight one. Combobox stays non-modal and says why in its source. docs ·7915a71 -
Overlay motion follows one measured scale. Every floating surface enters and leaves at
duration-fast(150ms); NavigationMenu takesduration-base(200ms) because it resizes between items rather than simply appearing; the modal family — Dialog, AlertDialog, Sheet — isduration-base. Timings were measured against Vercel and Linear rather than chosen. docs ·7915a71 -
Overlay padding has two tiers, not per-surface literals. 24px (
p-6) for Dialog, AlertDialog and Sheet; 16px (p-4) for Popover and HoverCard; menus keep list density. Panel widths come from--panel-width-*, and a viewport-capped popup uses Base UI's--available-heightinstead of a hand-written100dvhcalc.DialogContentand Command size throughsize. docs ·7915a71 -
Popover, HoverCard, Tooltip, DropdownMenu, ContextMenu, Select, Combobox, NavigationMenu — eight lookalike overlays became one module. Each now composes
floating-surfaceinstead of restating its own portal, positioner, popup surface, arrow and theme-scope plumbing.ContextMenuis bound to the same item parts asDropdownMenuthroughcreateMenuParts(Base UI'sContextMenunamespace re-exportsMenu's parts verbatim), so the two menus can no longer drift.7915a71 -
Command, Combobox, EmojiPicker, ShortcutOverlay — the in-panel search row is one recipe. A bordered
Inputinside a bordered popup drew two borders;PanelSearchFrameis a sticky, full-bleed header row with a leading glyph, no box of its own and a hairline below. docs ·7915a71 -
DatePicker, Board and Dropzone. DatePicker and DateRangePicker triggers are
w-fulllike every other form control: the fixedw-56andw-72were the only fixed-width controls in the system and overflowed a 320px content area. The calendar caption is now a realauto 1fr autogrid (navLayout="around") instead of an absolutely positioned nav over apx-7clearance, and the selected day carries a pressed rung (hover:bg-primary-hover active:bg-primary-active) instead of pinning its rest fill. Board's grab cursor appears only where a pointer drag can actually start and its column body height is the newcolumnMaxHeightprop. Dropzone paints the drag-over state on its own surface, so a dropzone wrapping anything other than anEmptyfinally shows one, and a newdragStateprop paints either state for documentation and automated checks. docs ·45cde26 -
Table,DataList,DataGrid,ComparisonMatrix,Terminal— a scroll viewport is now a named, keyboard-reachable region, and it is measured rather than guessed. A wide table could previously only be scrolled with a pointer (axescrollable-region-focusableon the Table page and the dashboard block), whileComparisonMatrixandTerminalcarried an unconditional tab stop that was dead on every instance that fits. Each viewport now measures itself throughuseOverflowand takes a tab stop only while it can actually scroll; name it withscrollLabel(falling back to the table'saria-label) and it is exposed asrole="region", unnamed it stays a plain focusable container. Its focus outline is inset, because the viewport clips its own overflow. Names and roles do not move with the measurement —Terminal's name andgrouprole stay unconditional.TableandTerminalboth stay server-safe: the measurement lives in a'use client'leaf (table-scroll-region.tsx,terminal-body.tsx). docs ·6d25185 -
DataGrid,DataList,Table,PropertyList,Chart— the rest of the table-family pass.DataGridgainscolumnPicker(defaulttrue): the "Columns" picker used to render whenever the grid had any columns — i.e. always, even for a three-column read-only grid — and it now sits in the toolbar's trailing slot beside the hidden-columns hint.DataListandDataGridgain amonocolumn flag (mono numeral face plustabular-nums,nowrapby default) and anowrapflag; together with the shared chrome that is 249 fewer lines across the two files.TableRowno longer tints the header row on hover — it styles every row it renders, including the header row the two renderers build with it, so hovering a header washed it as if it were actionable.PropertyListbecomes a container query: the label track was a fixed 112px regardless of the pane, and is now content-sized above an 80px floor at@xsand stacked below it, with values wrapping instead of truncating.Chartmoves axis labels from 11px to 12px (11px is reserved for mono) with the numerals on the monotext-code-smtier, and the tooltip follows. docs ·6d25185 -
EditableCell, ChipInput, DataGrid, CopyButton, SortableList, Board, Dropzone — all announce through
use-announcer, one live region each, replacing five identical hand-rolled{ text, seq }regions plus CopyButton's. EditableCell's visible status slot is no longer itself a live region, so it stops announcing its own icon swaps.Pagination'sPaginationLinktakes its hover and pressed steps fromsurfaceInteractiveinstead of restatedhover:bg-surface-2/active:bg-surface-3literals. docs ·7915a71 -
Tag, FilterChip, ComboboxChip, ChipInput, TagGroup — all now compose the
chipprimitive. Three consequences are visible: aTagis 28px rather than 20px and aFilterChipis a pill rather than a rounded rectangle (chips arerounded-fullby doctrine); the neutral chip rests onsurface-1and an applied filter sits onsurface-2instead of theaccentalias; and every remove control is the same 24×24 target. That last one fixesComboboxChipRemove, a bare 16px box with no hit-area expansion at all (a WCAG 2.5.8 failure), and retiresTag's::beforehit area, which a nested native<button>clipped and so never actually expanded anything. TagGroup's+Noverflow control is itself a chip, so the whole 28px pill is the pointer target and its hover/pressed steps come from the sharedsurfaceInteractiverecipe rather than a hand-written descendant selector. docs ·7915a71
🗑 Removed / renamed
-
trackis removed. The slider rail, progress track, skeleton and every well aresurface-1; the switch off-track issurface-3, the pressed rung. Three alpha roles are removed with it, because the ladder is now the one hover mechanism and nothing references them:--alpha-fill-hover(the secondary button's/80opacity dim),--alpha-input-hover(the dark-only input hover wash) and--alpha-surface-subtle(the outline button's hover tint, now--alpha-hoverin the family's own hue). docs ·b2c2e96 -
Button
glass, and the seven colour-in-the-name variants. The frostedglassvariant had no product consumer; media chrome uses the theme-invariant--media-*tokens instead.success,warning,info,destructive-outline,success-outline,warning-outlineandinfo-outlinebaked a colour into a name and are replaced by thetoneaxis. docs ·7915a71 -
Button
finishand the--shadow-littoken. The "lit" action finish is retired, so flat-by-default now has no exception at all and the system has exactly one shadow role,shadow-overlay. docs ·7915a71 -
selection:*in form controls — Input, Textarea, OTPInput, NumberField and the Combobox input repainted selected text near-black on near-white. Native selection is what users expect and it respects the OS and accessibility settings. docsshakeSignalon Input, Checkbox, RadioGroupItem, OTPInput, NumberField and ChipInput — the prop lives onField, which owns the shake. docsSpinner label=""as the way to say "decorative" —decorativeis now the sanctioned spelling.label=""still means the same thing; it just says it by passing a value that reads as a mistake at the call site. docs ·9fbeb65 -
mergeRefsno longer ships from theuse-animation-replayregistry item, andmedia-player-controlsno longer exportsassignRef. Both were spellings of the same merge; the one implementation now lives in@vegastack/designand every registry file imports it there alongsidecn.grep -rn 'typeof ref === "function"' packages/ui/registry/uiis 0. docs ·3663f8f -
AlertDialogContent intent— it wrote adata-intenthint and nothing else, leaving two props named for one concept with one of them inert.AlertDialogAction intentis the single owner of a confirmation's tone. docs ·7915a71 -
Hand-written
100dvhoverlay height calcs — replaced by--layout-overlay-max-heightand Base UI's--available-height. docs ·7915a71 -
sonner— the registry item and the dependency. Removing it deletes a rendering engine, the CSS override layer that fought the library's internal greys, its z-index exemption and a:focus-visiblebox-shadow glow the design system bans everywhere else. Toasts are now@vegastack/toast. The toaster-mirror gate stays but points attoast.tsx— the private@vegastack/uipackage still needs a byte-identicalToaster— and thesonner-human-facing-aliascontract exemption is gone, because it existed only while the registry item was namedsonnerand its page was/docs/components/toast; the two now match. docs ·d5e2de2 -
TagHue— the hue vocabulary moved to the Chip primitive and is exported asChipHuefrom@vegastack/chip.Tag'shueprop is unchanged; only the type's name and home moved. docs ·7915a71 -
The attestation stack is deleted — the gate receipt and its guards, the change classifier, route scoping, the Playwright-over-the-docs-export contract and pixel lanes, and the cross-engine smoke selection. CI executes every browser lane instead of inspecting evidence about it. The five docs-shell contracts those lanes carried survive as
tooling/verify-docs-shell.mjs, a release-stage script with a--self-testthat proves each assertion still fails on its own defect.@vegastack/uiis private and neither published package changes.09b2107
🛠 CLI & tooling
-
Animated icons are one factory plus 439 data modules. Every mirrored
lucide-animatedicon used to carry its own copy of the controller — the animation controls, the reduced-motion gate, five pointer/focus handlers, the imperative handle and a block-level host — so a change to any of that meant regenerating 439 files and trusting that all 439 agreed. The controller now lives once increateAnimatedIcon, exported from the new@vegastack/design/create-animated-iconsubpath, and each icon is acreateAnimatedIcon({ … })call describing only its geometry, its Motion variants, and (for 49 icons) its non-default start/stop steps.motionbecomes an OPTIONAL peer dependency — only an animated icon pulls it in, soIcon/BrandIconconsumers are unaffected. The corpus went from 79,078 lines to 12,951 (-84%) and from 2.06 MiB to 0.57 MiB of source; the served registry fell from 4.48 MiB to 2.92 MiB.tooling/mirror-animated-icons.mjsemits the data modules and fails closed on any upstream archetype it cannot model;tooling/verify-animated-icons.mjsasserts the controller contract once against the factory, holds every module to a schema whose central clause is that a data module contains no controller at all, pins each generated module by SHA-256 inpackages/ui/animated-icon-sources.jsonso a hand-edited path or timing value is rejected outright, and carries a--self-testthat proves seventeen distinct regressions are rejected. docs ·2035023 -
Patch/minor alignment across the workspace, and an explicit supply-chain floor. Seventeen packages moved to their current patch or minor, including the three Tailwind siblings brought to a matching 4.3.3. The release-age floor is now explicit (
minimumReleaseAge: 1440+minimumReleaseAgeStrict: true) rather than inherited. Measured on pnpm 11.7.0: inherited, pnpm appends aminimumReleaseAgeExcludeentry topnpm-workspace.yamland installs the too-new version anyway; explicit, the install fails withERR_PNPM_NO_MATURE_MATCHING_VERSIONand writes nothing. The two stalefumadocs-*@16.10.5excludes were written by pnpm that way, not by a human, and are deleted.@hookform/resolversmoves 5.4.0 → 5.9.1 in the same pass:zodResolver's overloads did not accept a schema built by zod 4.5, sofield-form.test.tsx— which exists to fail the typecheck when the RHF/Zod surface drifts — stopped compiling. It did its job.deploy.yml'swranglerVersionpin followsapps/docsto 4.129.0.verify-workflow-security(#88) asserts the two agree, because the deploy action downloads the version named in the workflow and production would otherwise ship through a wrangler this repository never installs or tests against.5b03e3b -
Next 16.2.11 → 16.3.4 for the docs app. The 16.2 line stopped receiving patches in July and two criticals were never backported.
enablePrerenderSourceMapsandexperimental.turbopackFileSystemCacheForBuildare defaults in 16.3 and were deleted.next devin 16.3 writes a managed agent-rules block into anAGENTS.md/CLAUDE.mdin the Next app directory when it detects a coding agent;agentRules: falseturns that off — this repo's agent instructions are authored and reviewed, and a tool-managed block inside a hand-authored file has no owner.5b03e3b -
Security patches across the toolchain.
@vitest/browser4.1.9 → 4.1.11 (critical: provider commands bypassedallowWrite),@tiptap/*3.27.4 → 3.31.3 (prototype pollution + paste XSS),postcss8.5.19 → 8.5.28 (source-map file read),style-dictionary5.5.0 → 5.5.2 (prototype pollution), plus theshadcn4.21 bump that clearspostcssundershadcn/tsup. Measured on the same advisory database on 2026-09-09,pnpm auditgoes from 59 findings (3 critical · 29 high · 25 moderate · 2 low) before to 47 (0 critical · 24 high · 21 moderate · 2 low) after. Every remaining finding is a transitive dev-tool dependency with no path into published output.style-dictionaryis held at 5.5.2 deliberately: 5.5.3 touchescolor/cssalpha precision, which is token CSS output, and 5.5.2 already carries the fix.5b03e3b -
shadcn CLI 4.13.0 → 4.21.0. 4.13.1 fixed three unadvised security bugs — custom registry headers leaking across cross-origin redirects, path traversal for registry items without an explicit target, and flag injection from registry-supplied dependency strings. No GHSA or CVE was ever filed, so no audit tool would have flagged the gap.
shadcn buildoutput is byte-identical andverify-shadcn-consumestill passes against the real CLI. 4.21.0 makes thecnpackage the upstream default for generatedlib/utils; we did not adopt it —cn()keeps coming from@vegastack/design, which is a locked decision, and the change is registry content, not CLI behaviour.5b03e3b -
Playwright 1.61.0 → 1.63.0, and the Linux CI container tag follows it. The
playwrightdevDependency moves in the root workspace and in@vegastack/ui; 1.63 ships Chromium 153.0.8010.12, Firefox 155.0 and WebKit 26.6, and drops Ubuntu 20.04 support. The audit's one API-removal concern,Locator.ariaRef(), is a non-issue: it is absent from the type surface of the 1.61.0 we came from as well as 1.63.0, and nothing in this repo names it.tooling/verify-workflow-security.mjsderives the required Linux job image (mcr.microsoft.com/playwright:v<version>-noble) from the singleplaywrightversion the lockfile resolves, soci.yml,release.ymlanddeploy.ymlnow pinv1.63.0-noble.auto-install-peers=truealso makes pnpm resolve Next's optional@playwright/testpeer even though no manifest declares it, and left alone it stayed on 1.61.0 — twoplaywrightversions in one lockfile, which that gate refuses. Apnpm-workspace.yamloverride pins the phantom peer to the same version, so the container tag keeps exactly one authority.3b37dde -
verify-component-contracts --write-data-attributesrecords each registry part'sdata-*attributes and CSS variables incomponent-contracts.json, extracted from the canonical source through the TypeScript AST, so the docs API tables and the agent markdown export list them. The default mode fails when the contract drifts from the source, and a--self-testdrifts adataAttributesrecord in memory and requires the reconciliation to reject it — so the gate cannot pass by never having run. docs ·7915a71 -
Gate correctness (G1-b) — four fail-opens closed, and focus indication covered again.
packages/ui/test/{contrast,stacking}.cssimported only part of the layer set production ships, so every custom@utilitya fixture wore compiled to nothing and both lanes measured a page no user sees;verify-test-css-layersnow derives the required set from the shipped preset and fails closed.pnpm lintgains a repo-wideprettier --check. A changeset may no longer link a commit — the only sha it can name is a pre-merge one the squash orphans — andchangelog-lint's own probe became reachability rather than mere object existence. Eleven cases in the workflow-security negative harness matched literals that can drift; one of them had never exercised its own rule. The geometry lane gains a focus-indicator assertion over all 541 fixtures that rejects the browser's own ring by name,packages/ui/testfinally type-checks,verify-component-contractsderives the inventory counts from the registry instead of hard-coding them, six token-vocabulary design-lint rules land with negative fixtures, andverify-token-referencesfails closed on a--tokenthat does not exist.b2c2e96 -
CI: the macOS runner class is ONE mac mini running two runner agents that share one home directory, and
pnpm/action-setupdefaulted its bootstrap directory to~/setup-pnpm— which it deletes on every job, and whichPNPM_HOMEalso made the package store. Concurrent jobs raced that deletion intoENOTEMPTY(or a half-linkednode_modulesthat turbo reported asunable to spawn child process) before any repository code ran, and the store never survived a job, so every macOS install re-downloaded the whole dependency graph. Each mac-mini job now bootstraps into${{ runner.temp }}/setup-pnpmand installs with--store-dir "$RUNNER_WORKSPACE/pnpm-store"— per-agent, and persistent — and the three jobs that enabledsetup-node's package-manager cache no longer do, since it cached the directory the next job deleted.verify-workflow-security.mjsasserts all three, with negative-harness cases for each; two of its existing cases that matched the install by literal command line were rewritten to match by shape.c352ba9 -
release-detect --check-npmfails closed instead of fail-open, anddocs/RELEASING.mdmatches reality. The registry probe spawnednpm viewwith cwd = the repo root, wherepackage.jsondeclaresdevEngines.runtimenode 24.20.0; npm enforces that field, does not honour pnpm'sonFail: download, and exitsEBADDEVENGINESon any other Node before it reaches the network. The oldstatus !== 0branch read that as "not published", so every push tomainreported both live public packages as unpublished, forcedpublish=true, ranquality-gate, and armed the OIDC-capablepublishjob on changeset-free pushes (release run 34323665258 showsunpublished: @vegastack/design (none) → 0.3.2with 0.3.2 live). The query now runs in an empty temporary directory carrying a copy of the repo.npmrc, out ofdevEngines' reach but still on the repo's own registry mapping, andnpm view --jsonis parsed into three outcomes: published, absent (a genuineE404— the only answer that may mean unpublished), and unknown (engine refusal, transport, auth, missing npm, unparseable output). An unknown never contributes topublish, is retried once, and exits non-zero when it was the only thing that could have setpublish— fail-closed and loud rather than afalsethat was never established. Nine cases intooling/test/release-detect.test.mjspin it against a stubbed npm; six of them fail against the previous script.docs/RELEASING.mdno longer calls this public repo private in four places, states the real reason provenance is off (npm rejects a self-hosted bundle with E422, not repository visibility), describes the mergedbuild-sign-deployjob rather than the deleted three-job artifact split, and records that releases deliberately create no git tag or GitHub release.AGENTS.mdgains that tag decision and reconciles two deviations from the verification-rebuild plan.af61a44 -
design-lint --docs-shell --emitted-cssreads the BUILT stylesheet, because the shell's off-system values are compiled in by dependencies and never appear in this repo's source. It ships a negative self-test.7915a71 -
CI gates —
pnpm verifycan no longer be neutered in a workflow. The workflow-security gate now matches the command as the ENTIRErun:body (a block scalar that merely contained the line satisfied the presence check while escaping every effectiveness rule), rejects any run body that discards an exit code (set +e,|| true, a bareexit 0), rejects aWEBKIT_LANE/SMOKE_WEBKIT/ cleared-CIenv:at workflow, job or step level (each silently droppedverify:releasefrom three browser engines to two), pins--ignore-scriptsper job in both directions — the npm-OIDCpublishjob now installs without lifecycle scripts, likeversion-pr— and checks--frozen-lockfileline by line. Eleven new negative cases, all observed rejecting. The hover/pressed recipes are now measured for real: the@sourceglob that compiles them was a no-op in both browser lanes, and the one assertion over it was reading its own file. Also: the secret scan's scope is stated honestly (it reads the working tree, never history),release-detectno longer reads an unanswerablegit diffas "nothing changed",workspace-cleanrefuses a path when git cannot say whether it is tracked instead of assuming it is not, and every compiled-CSS lane now imports the same set production ships,tw-animate-cssincluded.47ddb18 -
verify-docs-base-mirrordiffs the rule blocksapps/docs/app/global.csshand-copies frombase.css, which had no gate. It counts@applyas a declaration: the focus ring is expressed only that way on both sides, so filtering@-prefixed lines compared that block as empty against empty and could never fail. It ships a negative self-test.7915a71 -
verify-docs-exportfails the docs build on any JSX tag surviving outside a code fence, any unresolved export placeholder, or any empty API table — the regression guard for the markdown export — and enforces that a page carries either a curated playground or the Story explorer, never both. It ships a negative self-test, so it cannot pass by never having run.7915a71 -
verify-mdx-manifest(docslint) proves the agent export fails closed on the three failures that leave no artefact behind forverify-docs-exportto find: an MDX component no manifest entry classifies, a placeholder whose runtime renderer is missing (nested ones included), and a component registered in the MDX map but absent from the manifest. Before it, the first rendered to a single space and the second to its bare children.7915a71 -
pnpm verify:release— discards the docs build cache before the first export so a stale Turbopack cache can no longer report the docs shell as off-system, and the registry idempotency check now compares before/after instead of demanding an empty tree.cb99022 -
Cross-engine lane — CI now defaults to
WEBKIT_LANE=requireinstead ofauto, so a WebKit that stops launching fails the release lane instead of silently reducing it to two engines.e5d7a7f -
CI/CD: the workflow-security gate now asserts that the workflows DO something, not only that they are shaped correctly. An adversarial review built twelve mutations that the gate passed; three made a workflow report success while executing nothing —
continue-on-erroron the deploy'sverifyjob (a failed sweep still deployed to production), the same key on ci.yml'spnpm verifystep (a green check on a red suite), andif: falseonpnpm verify:release(the step stayed in the file and left the run). The gate now rejectscontinue-on-erroranywhere without a recorded allowlist entry, forbids anif:on any verification step and on the deploy's sweep job, asserts the Cloudflare command exactly (deploy --dry-rundeployed nothing and reported success), pinswranglerVersionagainstapps/docs/package.json, requires the publish-time lifecycle guard and the standalonenpm publishline (afalse &&prefix published nothing), requiresSITE_VISIBILITYin all three workflows,ref: ${{ github.sha }}on every outward checkout,--frozen-lockfileon every install, a concurrency group and a boundedtimeout-minuteson every job, andverify-macos's required steps including thechangeset statuspresence check.publishloses thecontents: writeandpull-requests: writeit never used, every job declares a timeout instead of inheriting the 360-minute default, ci.yml declares the sameSITE_VISIBILITYas the other two so the turbo cache key genuinely matches, and the OIDC scope check is structural rather than a text count. The negative harness grows from 37 to 60 mutations, each of the new ones reproduced against the pre-fix gate first.087015e -
WP1 of the verification rebuild — the 320px reflow, RTL containment, and effective 24px pointer-target contracts now run as a vitest browser lane over the preview fixtures (
packages/ui/test/geometry.browser.test.tsx) instead of@playwright/testover the docs export. No published package changes:@vegastack/uiis private, and the only files touched are its test lane and vitest config.7915a71 -
WP2 of the verification rebuild — test-config and tooling only, no published package change.
7915a71 -
WP4 of the verification rebuild — the contract-derived docs files become build outputs rather than committed artefacts; no published package change.
7915a71 -
The root
CHANGELOG.mdrelease entry is assembled from the changesets. A PR's only changelog artefact is its changeset, whose body opens with one of the eight section emoji;tooling/changelog-assemble.mjsgroups those bodies into the## [x.y.z]entry at version time, insidepnpm run version-packages, andsync-changelogregenerates the docs page as before.tooling/changeset-lint.mjsrejects a body with no marker, two markers, or no text. Nobody hand-edits/CHANGELOG.mdbetween releases — it was one list at the top of one file, and every branch collided on it.7915a71
📦 npm
-
Dependency batches 5–6: the Fumadocs 16.15.8 family, lucide-react 1.42, axe-core 4.13, Playwright 1.63 and recharts 3.10.
Fumadocs 16.11.5 → 16.15.8 (
fumadocs-ui,fumadocs-core,fumadocs-mdx15.4.0,fumadocs-typescript5.4.0,@fumadocs/story1.3.0;fumadocs-twoslashtakes the TS 6-safe 3.3.1 patch rather than 4.0, which needs TypeScript 7) is two migrations rather than a bump.fumadocs-core16.14 replaced Orama with ZBSearch behind the same module path —oramaStaticClientis now a deprecated alias forstaticClient, the client builds its own database, and theinitOramafactory plus the direct@orama/oramadependency are gone.fumadocs-typescript5.4 swapped ts-morph for the native TypeScript 7 API, so the docs' own-props filter reads a property symbol'sdeclarations[].pathinstead ofgetDeclarations()[].getSourceFile().getFilePath(); the generator cache is TypeScript-version bound and was cleared.Two Fumadocs behaviour changes were audited and deliberately left alone. 16.13's global
dtheme hotkey is inert here —RootProvidermounts its window-levelkeydownlistener inside thetheme.enabled !== falsebranch and this site disables fumadocs' theme provider outright, so nothing is registered; the reasoning is now recorded at the call site so re-enabling that provider cannot silently reintroduce a hotkey that swallows a letter on every interactive page. 16.12 stopped force-mounting inactiveTabspanels, so aComponentPreview's hidden Code panel is no longer in the prerendered HTML; nothing depends on it, because the visual-surface contracts read the always- mounted Preview panel and the markdown export reads fixture source from disk, so noforceMountwas added.Nothing in the component sources changed for the icon or chart bumps. The lucide 1.25 → 1.42 rename sweep is a no-op: all 126 distinct lucide names imported across the registry, the design package and the docs app — 125 icons plus the
LucideIcontype — resolve against the installed 1.42.0 module, and lucide keeps every historical rename as a named alias, so there is nothing to sweep. The 439 animated-icon data modules regenerate byte-identical through the factory, with the 28 new upstream icons left unadopted. axe-core 4.12.1 → 4.13.0 expandsaria-prohibited-attrandrole=image, and the browser axe lane reports no new violation. recharts 3.9.2 → 3.10.1 deprecatesLegend'salign/verticalAlignin favour ofposition/offset; noChartLegendcall site passes either andLegendstill injectsverticalAligninto custom content, soChartLegendContentkeeps reading it. Onlychart's registry item changes, and only because its documentation comment records that migration.3b37dde -
@tanstack/react-table8.21.3 → 9.2.4, indata-gridalone. v9 replacesuseReactTablewithuseTableand stops bundling every feature into every table: a table now declares the features it uses, and row models are feature slots rather than table options.data-griddeclares exactly one —rowSortingFeaturewithcreateSortedRowModel()— which turns the sanctioned-exception boundary from a claim into something the module reads back:columnVisibilityFeature,columnOrderingFeatureandrowSelectionFeatureall exist in v9 and none is adopted, because column visibility, column order and row selection aredata-grid's own state, and the APG grid keyboard layer (roving gridcell tabindex, Enter/F2 edit mode, Escape restore) is unchanged this-file-only code.getCoreRowModel()is gone (the core model is automatic) andmanualPaginationwent withrowPaginationFeature, where it was already inert. The four built-in comparators v8 kept permanently in its registry —alphanumeric,basic,datetime,text— are registered explicitly in thesortFnsslot sogetAutoSortFnresolves the same comparator per column as it did under v8. No public prop, type or behaviour ofDataGridchanges;@tanstack/react-virtualis untouched. docs ·36ebf9d -
Vitest 5.
vitest4.1.11 → 5.0.0,@vitest/browser-playwright4.1.11 → 5.0.0 andvitest-browser-react2.2.0 → 2.3.0 across the workspace — the runner under every test in the repository, including the browser suitepnpm verifygates on. Vitest 5 matches locator text and accessible names whole-string by default and makestoHaveTextContentwhole-string equality (toMatchTextContentis the partial/RegExp matcher), which turned ten of 2363 browser tests red. Every one was fixed at the call site rather than by restoring the old substring behaviour: eight now assert the FULL accessible name — a tab's trailing count badge, a link's sr-only "(opens in new tab)", a requirement row's sr-only "Met: " prefix — so a query proves what a screen-reader user hears.getByRole('tab', { name: 'A' })had been resolving a tab namedActivity3. Vitest 5 also consolidates every run artifact under one.vitestdirectory, soworkspace-clean.mjs's tree walk for the old.vitest-attachmentsdirectory is deleted rather than kept. No component source, no published output and no registry item changes; item count stays at 568.2625f2b -
Five mechanical dependency majors.
motion12.42.2 → 13.2.0 (its only import site is the animated-icon factory; the 13.0 removal of the optional@emotion/is-prop-validdependency does not apply — no CSS-in-JS library wraps amotioncomponent here — anduseReducedMotion()is still the one-shotuseStateread the factory deliberately replaces with a liveuseSyncExternalStoresubscription).react-dropzone19.1.1 → 20.1.1, whose only breaking change is a Node 22 floor (this repo pins Node 24.20.0).@atlaskit/pragmatic-drag-and-drop2.0.1 → 3.1.0 plus-hitbox2.0.0 → 2.2.0, whose 3.0.0 renamed every entry point:use-drag-reordernow imports from/adapter/element-adapter,/utils/combine,/closest-edge/attach-closest-edge,/closest-edge/extract-closest-edgeand/typesrather than the deprecated compatibility shims.@testing-library/jest-dom6.9.1 → 7.0.1, which makes@testing-library/doma required peer — now declared explicitly at 10.4.1.globals16.5.0 → 17.12.0, whose 17.0.0 split theaudioWorkletenvironment out ofbrowser; the shared ESLint config usesbrowser+nodeonly. Behaviour of the drag keyboard layer, the live-region announcements, the "Move to…" menu equivalents and the paste-acquisition path is unchanged — all of it is ours, not the engines'. docs ·452df99 -
@vegastack/designexports the field-chrome recipes.fieldControlandfieldControlGroupare the one border/hover/focus/invalid/disabled grammar every text-entry control wears, so Input, Textarea, NumberField, OTP slots, the Select trigger, the Combobox input and ChipInput can stop keeping private copies of it. Three border rungs and no more:border-inputat rest, the neutral--alpha-border-subtleink on hover, theringtint on focus. Hover is guarded bynot-disabled:because a disabled control keeps its pointer events so a Tooltip can explain it. Every element wearing the wrapper recipe must also carry a baredata-field-groupattribute — that is what@vegastack/design-tokens'base.csshooks to paint the forced-colours focus outline on the group, whoseoverflow-hiddenwould otherwise clip the inner input's own.Also
surfaceInteractiveGroup— the group-scoped twin ofsurfaceInteractive, for the one geometry where the two ladder rungs cannot sit on the interactive element itself: a wash painted by an inner chip inset from a container hairline (NumberField's ± steppers). The rungs stay written once. docs ·9fbeb65 -
@vegastack/design-tokens— adds themotion-dock-in/motion-dock-oututilities — the docked-control enter/exit pair for a control that stays mounted at a viewport edge and flipsdata-active(a bottom action bar, a floating scroll-to-edge button). 150ms in onease-emphasized, 100ms out onease-exit, translate and fade, no scale: an exit is never slower than its enter.The pair owns the timing, the fade and the parked
pointer-events: none; the travel distance stays at the call site as ordinarytranslate-*utilities, because it is per-dock geometry and atranslatedeclaration inside the utility would clobber a horizontally-centred bar's composed transform.2a3fc24 -
@vegastack/design— exports theproserecipe — one token vocabulary for rendered rich text, so a surface the system did not author element by element (markdown, a contenteditable, CMS copy) is styled by one class on its root.proseClassNameis the whole recipe;proseis the per-element record it composes from, keyed by theProseElementtype.It is expressed as descendant variants (
[&_h1]:…) because neither consumer can put a class on the elements — ProseMirror owns the editor's DOM, and react-markdown's output is reachable only through an override map — and because an element-level class silently LOSES the cascade to a root-level descendant rule (specificity (0,1,0) against (0,1,1)). Restyle prose by composingprose, never by setting a class on the rendered element.2a3fc24 -
@vegastack/designexportsmergeRefs— fan one DOM node out to several refs (a forwardedrefprop plus one or more internal refs) as a single ref callback, handling both shapes React 19 accepts and skippingnull/undefinedentries. It was exported from theuse-animation-replayregistry item, an odd home for it, while nine registry files hand-inlined the sametypeof ref === "function"merge; ref-as-prop makes "the component needs the node AND has to forward it" the normal case, so it belongs in the package. Typed against React's ref shapes with a type-only import, so the entry stays server-safe.3663f8f -
Exports
selectedChipVariants— the one "raised chip on a muted track" recipe, shared by Tabspill/chip,Segmented, and pressedToggle/ToggleGroup, which had drifted into four different selected looks (bg-background,bg-secondaryplus a hairline,bg-foreground/10). The track issurface-1; the chip is the pressed/selected rung in its alpha form (bg-foreground/(--alpha-ink-tint)), which is what lets a SELECTED chip keep stepping — it strengthens on hover and drops back to the resting tint on press. Ships astrack,item, and two state literals:pressed(Base UIdata-pressed) andactive(Base UIdata-active). docs ·fdaed05 -
The design-system registry (
@vegastack/ui) bumps 0.6.0 → 0.7.0.
📚 Docs
-
The
inputandtextareasummaries no longer promise a focus ring they never had. Both text-entry controls signal focus with a border tint, not an outline — that is the rule in Accessibility and it is what the shared field recipe implements — butcomponent-contracts.json, the machine authority that feeds the shipped design-system skill, still described "a focus-visible ring" for each. Both summaries are corrected, so an agent reading the packaged skill roster is told what the components actually do. The other 114 component summaries were audited for the same class of claim and hold.Doctrine, in the same pass:
react-markdownandremark-gfmare now sanctioned renderer engines rather than an undocumented exception, and two version decisions are written down with their evidence — TypeScript stays at 6.0.3 while no shippedtypescript-eslintsupports TypeScript 7, andtw-animate-cssstays bundled inpreset.cssbecause it is consumer-facing API that Quickstart and Troubleshooting both document.78ed487 -
Alert, Empty, Item, Timeline, Collapsible, Accordion, Announcement Banner, Onboarding Checklist, Progress Indicator, Board and Dropzone — the pages now describe the polite live-region policy, the one
Emptycontainer axis, the group-licensedlistitemrole and the disclosure hover wash. Alert's "Strip variant" and Empty's "Illustration & value tiers" move above API Reference per the page canon, Alert and Announcement Banner gain a "Live announcements" section, and new fixtures cover Card, Announcement Banner, Onboarding Checklist and Timeline. docs ·8c18d2b -
react-day-pickerandnext-themesare now named sanctioned engines in AGENTS.md § Sanctioned dependency exceptions and indesign.md, with their one-file isolation written down. Both were already shipping and neither was on the list.react-day-pickerv10 is the calendar state machine behinddate-picker— Base UI ships no Calendar, and shadcn's Calendar is this package under every base;next-themesis the theme engine mounted only byprovider, whichsonnerreads throughuseTheme()rather than mounting a second time. docs ·5b03e3b -
Every component page now follows the page canon, and a gate says so. All 116 pages carry the canon's frontmatter (
registry,status,since,a11y), the## Installationheading is now## Install, and the five generated sections — install steps, anatomy, API tables, states tested and the per-item changelog — are rendered fromregistry.json,component-contracts.jsonandCHANGELOG.mdon every page instead of being hand-typed on three.tooling/content-lint.mjsenforces the section vocabulary, the section order, "nothing after Do / Don't except the Changelog", and generated-not-typed, with a--self-testthat observes each rule failing;tooling/verify-docs-export.mjsadditionally requires a playground or Story explorer to render under the page's## Playgroundheading. Theregistryfrontmatter field is required and is no longer inferred from the page slug, so a wrong or missing item name fails the build rather than composing the wrongshadcn addtarget. TheAutoTypeTablealias forApiTableis gone. On the Command page the live dialog demo moved to ⌘J, because the docs site itself owns ⌘K and both dialogs were opening at once. docs ·7153311 -
A component page's
statusandsinceare now machine authorities, not hand-typed prose. Both are recorded per component inpackages/ui/component-contracts.json, written onto the page bypnpm design:derived, and compared against the contract bytooling/content-lint.mjs, so a page cannot claim a status or an origin version the contract does not hold.statuswas 116 identicalstablestrings with nothing behind them; every component is genuinelystable— each has a registry item, a docs page, a preview and required test coverage, and no ledger, changelog or source records a deprecation or a preview-quality component.sincewas derived once fromgit log --followand is now pinned data, never computed: the values were verified against /CHANGELOG.md's enumerated release lists (0.2.0 1/1, 0.3.0 12/12, 0.4.0 12/12, 0.5.0 2/2) and againstpackages/ui/registry.jsonas it stood at each release commit (0.1.0 82/82). That check corrected one page — MediaPlayerControls now readssince: 0.7.0, not 0.5.0:--followhad walked into theaudio-playersource it was extracted from, while the item itself has never shipped. A component authored between releases carries the next version, andtooling/version-sync.mjsre-stamps it at version time with the version actually being released, so a different bump than the author guessed cannot publish a wrongsince. docs ·c352ba9 -
API tables are flat and expanded, on every page at once. One table per exported part — name, the literal union (
"default" | "secondary" | …, notunion), the@defaultvalue, the description — instead of collapsed accordion rows. This one lands everywhere immediately: the renderer is registered under the legacyAutoTypeTablename the 107 unmigrated pages author, so no page body had to change for it. Own props only, and a part with no own props of its own gets one sentence instead of the 138 "(no own props)" placeholder rows that filled 18 pages. A second table lists thedata-*attributes and CSS variables the part exposes. example ·7915a71 -
Badge — a "Minimal — the dense-table treatment" section, real pixel sizes on the Sizes section, and a playground that gained the
borderedswitch and emitsdot={false}when the switch disagrees with the variant's own default. docs ·7915a71 -
Button — the page is rebuilt around the matrix: a variant row, a tone row, and the full
variant × tonegrid with the forbiddensolid+destructivecell called out. The "Lit finish" section is gone. IconButton gains Tones and Shape sections; SplitButton gains Tones. docs ·7915a71 -
Colors — a new surface-ladder specimen renders both themes side by side with the rungs and their alpha twins; the sidebar section now says the rail is aliases, not a second palette. docs ·
b2c2e96 -
Doctrine corrections in
design.md— the hairline is described as the derivedforegroundalpha rather than a solid border; Buttonsecondaryas the rung-1 fill over a transparent base border rather than a card fill plus border; Input/Select/Textarea as transparent with a dark-only wash rather than asecondaryfill; and the switch off-track assurface-3rather than the removedtracktoken. The Sidebar page's active-row description matches the shippedsurface-3rest /surface-2hover behaviour. docs ·b2c2e96 -
Progress, RelativeTime, TruncatedText, Kbd, StatusIcon, Skeleton — each page now explains the mechanism rather than the class name: why an indeterminate bar is a segment, why the first paint is an absolute date, why a grid turns tab stops off, and where reduced motion is actually handled. A duplicate empty heading on the Progress page is gone. docs ·
7915a71 -
Elevation — "the two roles" is now "the one role"; every foundations page that named
shadow-litwas corrected. docs ·7915a71 -
Fullscreen preview is the system
Dialog. The old overlay declaredrole="dialog" aria-modal="true"and had no focus trap — Tab walked straight out into the hidden chrome behind it. Copy Prompt moved once into the page header (it was repeated six times on the Button page), the hero preview renders through the same frame as every other example, a skip link is now the first tab stop on every page, and the icon-gallery tile is a real labelled button instead of 439 nameless focusabledivs. accessibility ·7915a71 -
llms.txtcarries the registry roster and the skill roster — every installable item with its page and itsshadcn addtarget, and the public agent skills — so an agent can go from "I need a data grid" to the page and the install command without scraping. guide ·7915a71 -
The markdown export is real markdown. The per-page
.mdroute andllms-full.txtpreviously emitted<AutoTypeTable …/>and<ComponentPreview …/>verbatim — 107 of 110 component pages and 260 occurrences inllms-full.txt— so an agent reading the docs saw no props and no example code at all. Every MDX component now renders to markdown: the exact fixture source the Code tab shows, the flat prop tables, the install steps, the do/don't pairs. Browser-only surfaces are replaced by a one-line note rather than dropped silently. guide ·7915a71 -
One page canon, for humans and agents alike — the infrastructure, and three reference pages. The canon defines a fixed page shape whose machine-readable half is generated from the two authorities rather than typed: Install from
registry.json(theshadcn addcommand, the registry dependencies and the sanctioned engines), Anatomy from the contract's newdataAttributes, the states-tested table from the contract'sstates, and a per-item Changelog filtered out of/CHANGELOG.md. This release ships those generated sections as MDX components and places them on three reference pages (button, dialog, data-grid); the remaining 107 pages keep their current bodies and are migrated to the canon in the next release. The canon table isdesign.md§Docs canon — the target shape, not a description of every page today. canon ·7915a71 -
Per-family variants are documented as eight tokens, not six —
subtle-hoverandsubtle-active(the soft fill's hover and pressed steps, precomposed at build time and AA-gated against-text) were missing from the Colors page, and fromdesign.md, which called the ramp seven tokens. docs ·b2c2e96 -
The docs shell obeys the design system it documents. Fumadocs' chrome and the typography plugin are compiled against Tailwind's stock theme, so headings, sidebar titles and prose
<strong>rendered at weight 600–900 in a system whose ladder is 400/500, cards usedrounded-xl, and popovers used the stock shadow ladder. All of it is remapped to system values once. Demos also sat on the 15px/28px prose base because the product type scope re-bound the--type-*vars but not the inheritedfont-size. foundations ·7915a71 -
Theming and Colors no longer teach a removed token — both pages used
bg-primary/(--alpha-surface-subtle)as the worked example of an override flowing through the@theme inlinebridge. That alpha role was deleted with the ladder, so the example compiled to nothing; both now show thefillInteractiverecipe's real washes. docs ·b2c2e96 -
The shipped design-system skill is refreshed for Button's
variant × tonematrix, the singlexs · sm · md · lgsize vocabulary, andIconButtonas the only icon-only path. No runtime code changed. guide ·7915a71 -
Field, Checkbox, FieldInline, Textarea, Motion — every page that described the error as
role="alert", the shake as per-control, or the textarea focus treatment as a removed outline now says what the code does. Textarea gains thesizeexample it never had, NumberField gains its invalid fixture and folds its "Scope" table into Usage, and Label documentslayout. docs ·9fbeb65 -
Docs —
design.mdgains a §Prose section (one recipe, why it is expressed as descendant rules, and the cascade trap that makes an element-level typography class a silent no-op inside a prose root) and its §Motion vocabulary gains the docked pair with the rule the pair encodes: an exit is never slower than its enter. Thecomponentskill's motion-mechanism matrix gains a fourth row for a control that stays mounted at a viewport edge and flipsdata-active.text-edit's "Scope" moves into its canon position and its keyboard table documents the toolbar's arrow-key traversal. docs ·2a3fc24 -
design.md§Charts and §Brand & marketing. A single series is drawn in foreground ink (--chart-single), not a categorical hue: the ramp encodes which series is this, a distinction that does not exist when there is only one — hue starts at two series. Two new §Brand & marketing subsections: Promotion is a ladder rung plus a tinted hairline, neverinfoand never a full-strength chromatic border; Marketing leaves records the three conventions the leaf components encode — quotation marks from CSS rather than characters, a logo wall of marks rather than links, and a scroll reveal that fails open. Thecomponentskill §4 states the responsive ladder explicitly: container query → viewport breakpoint →useMediaQuery, and a JS media branch must declare its server answer.3663f8f -
Plan and ledger — withdrew the verification rebuild plan tooling line-count target, dropped WP3b with the measured reasoning, and recorded the post-rebuild audit including the decision to leave
mainunprotected while the audit epic merges.5d2c705 -
Ship skill — corrected a false claim about CI on the Version PR: runs on the changesets bot branch sit at
action_requiredand never execute, so the publish is gated byquality-gateon main before and after the merge, not by checks on that PR.5aba886 -
Docs
- Corrected the shipped 0.1.0 changelog entry: the initial release carried 83 components, not
75 (and the docs showcase had 99 pages, not 91). Recomputed against the release commit with the
repository's own path-derived classifier, which counts
icon-buttonas the component it is. The animated-icon and hook figures were already right. - Replaced the hand-maintained runner roster in the Linux CI runbook — and its two copies elsewhere —
with the one
gh apicommand that returns live truth, keeping only the per-host facts that command cannot return. - Reconciled the hover/pressed rule in AGENTS.md and design.md with what
tooling/design-lint.mjsactually enforces (hover-without-pressed), and added thesr-onlyaccessible-name separator to design.md § Accessibility as doctrine. verify-component-contractsno longer tells the reader to run a command that cannot fix a driftedexpectedCountskey.- Versioned the audit epic's orchestration briefs into the repo, and recorded the epic's review
rounds — three Codex, the rest independent Opus reviewers — in the ledgers.
c68a35b
- Corrected the shipped 0.1.0 changelog entry: the initial release carried 83 components, not
75 (and the docs showcase had 99 pages, not 91). Recomputed against the release commit with the
repository's own path-derived classifier, which counts
-
Docs —
AGENTS.mdis a rulebook again (150 lines): locked decisions as one-liners with dates, the three verification loops, and the four artefacts a PR carries. The CI history it used to carry moved todocs/ledger/operator-review.md, and thecomponent,review, andshipskills now describe only machinery that exists.d65088b
🐛 Fixed
-
Animated icons — the reduced-motion effect ran after every render in all 439 icons, because it was written without a dependency array. It now runs when the preference changes, once, in the factory. docs ·
b2c2e96 -
AppShell's skip link now targets its own content region. The skip link andAppShellContentboth hard-coded the idmain-content, so a page holding more than one shell published that id twice and EVERY skip link resolved to the first region — measured on theapp-shelldocs page, where four embedded previews each carried it and the documented "Tab once, press Enter" flow landed the reader in the wrong preview from every frame but the first.AppShellnow mints the id once withReact.useId()and shares it toAppShellContent, with a newcontentIdprop for when the id has to be known (a deep link, an externalaria-controls, a test harness). SettingidonAppShellContentmoves the element but does not rewire the link; that is whatcontentIdis for. Sharing a generated id is whyapp-shell.tsxnow carries a client boundary at the shell root —createContext/useContextare unavailable under thereact-servercondition — which is whereSidebarProvider's own client context already lives; page content passed aschildrenstill renders on the server. docs ·64ba720 -
Release readiness — close the modal focus leak across Dialog, AlertDialog and Sheet; isolate the animated-icon gallery to its own route; make the docs-shell and export checks fail closed over both fixes; and stabilize the complete cross-engine suite with trusted interactions, settled animation baselines, and sequential browser processes.
515da26 -
The notification badge pops for real activity only.
NotificationBellread a mount ref during render and flipped it in an effect that scheduled no re-render, so the pop-in class first landed on whatever unrelated re-render happened next: a parent state change animated the badge with no new notification behind it. The previous count is now held in state and the cue is replayed throughuseAnimationReplaywhen the count rises after mount and the badge visibly changes — so it never fires on mount, never on a re-render, and never for 100 → 101 (both read"99+"). TheTimelinehero fixture, which sat under the 24px pointer-target floor, is lifted off it. docs ·8c18d2b -
tiptap 3.31.3 loaded
prosemirror-modeltwice, and TextEdit's suite died on it.@tiptap/pm@3.31.3declaresprosemirror-modeldirectly and resolved to 1.25.11, while its siblings stayed on the 1.25.9 the pre-bump lockfile already carried —pnpm installonly re-resolves what it must. ProseMirror compares node types by object identity, so a fragment built by one copy is unconvertible by the other (RangeError: Can not convert <> to a Fragment).pnpm dedupecollapses the tree to a single 1.25.11 and removes 34 packages.packages/ui/vitest.config.tsalso stops pre-bundlingclsxandtailwind-merge, which are@vegastack/design's dependencies rather than@vegastack/ui's and printed a resolve failure on every run.5b03e3b -
The
providerunit test no longer races sonner's toast timer. Sonner's toast store is a module singleton, so a toast fired by one test kept rendering — close button and all — into the next test in the same file for its full 4 s lifetime, and an unscopedgetByRole("button")there became a strict-mode violation under load. The toast is now dismissed and drained where it is fired, and the theme probe is queried by accessible name. Test-only; no component changed.5b03e3b -
An indeterminate
Progressno longer reads as 100% complete. Base UI writes no inline width whenvalueisnull, so an indicator styled only for the determinate case inherited the track's full width — an upload in progress looked finished. It is now a 35% segment sweeping the track on the one sanctioned looping utility,motion-indeterminate, whose keyframes rest on the same frame at both ends so reduced motion leaves a static segment rather than a full bar, andaria-valuenowis omitted. docs ·7915a71 -
The
prefers-reduced-motion: reducereset zeroes delays too. It gainsanimation-delay: 0s !importantandtransition-delay: 0s !important. Zeroing duration alone left a staggered entrance sequencing over its real-time delay window, which is motion; with the delay zeroed the whole sequence lands at once, and no component needs amotion-reduce:restatement of its own. docs ·7915a71 -
RelativeTimeno longer renders an empty first frame. A relative label needsDate.now(), which the server cannot reproduce, so it used to render""until hydration — a visible pop and a layout shift on every row of a list. Server and hydration render now agree on the absolute date ("Mar 15, 2025"), derived from the target instant alone, and the swap to the relative label is a text change inside a box that is already the right size. docs ·7915a71 -
Docs — the icon gallery was unusable by keyboard and inert on touch. Each tile was a focusable
<div>with no role, so all 439 were reachable and announced as nothing; each is now a real<button>. A tile also drives its icon through a ref, and holding the ref suppresses every trigger the icon provides for itself, including its tap-to-playpointerdown— the tile replaced hover and focus but not that one, so on a phone nothing in the gallery ever moved. Tiles now carry the tap driver too, under the same pointer-type rules. docs ·b2c2e96 -
The surface-ladder specimen showed the wrong swatches — the "alpha twins" panel labelled two swatches
--alpha-hover/--alpha-pressedwhile painting the opaquesurface-2/surface-3rungs, so it demonstrated the opposite of the twins' claim. It now paints the realforegroundcomposites over three hosts (page, card, well) in both themes, with the opaque rung beside each wash for comparison. docs ·b2c2e96 -
Text-entry focus under forced colours — Input, Textarea, NumberField, OTPInput and TextEdit signal focus with a border tint and
outline-none. Windows High Contrast replacesborder-coloroutright, so a focused field showed no indicator at all. The token layer now paints a real2pxoutline underforced-colors: active, once, for every text-entry control. docs ·b2c2e96 -
Text on the soft media scrim is gated at AA —
media-foregroundonmedia-scrimwas checked only against the 3:1 non-text floor while the token contract permitted labels on it, so the contract was wider than its enforcement. The pair is now gated at 4.5:1; it measures 5.22:1 over the white worst case, so nothing moves today and a future scrim retune that thins it under AA fails the build instead of silently demoting its labels. Themedia-scrim,media-scrim-strongandmedia-foregrounddescriptions state where text is allowed and which floor enforces it. No token value changes. docs ·b2c2e96 -
Media chrome no longer inverts in dark — the video scrim and its controls were built from
primary, which flips with the theme, so in dark the scrim rendered near-white with near-black icons. New theme-invariant--media-scrim,--media-scrim-strongand--media-foregroundtokens keep overlay chrome dark-scrim + light-ink in both themes. docs ·b2c2e96 -
Four controls shipped with utilities silently destroyed, and a
Field > Textareawas unlabelled. In five places two adjacent class-string literals were concatenated with no separating space, so JavaScript welded them into one word and the utility on both sides of the seam vanished. The Switch had no track colour in either state — measuredbackground-color: rgba(0, 0, 0, 0)andpadding: 0pxunchecked and checked, with on/off conveyed only by thumb position on abackground-coloured thumb; only a hovered checked switch painted, so the control appeared under the cursor and nowhere else. The switch thumb ran on Chromium's default curve instead of--motion-ease-standard; a focused OTP slot wore the global 2px focus ring that text entry exists to suppress; and the NumberField steppers rendered at full--foregroundwith no hover step. All four are repaired, anddesign-lintgained a structuralclass-gluerule that rejects the seam at the AST — the existing rules read one literal at a time and could not see it, which is whytransition-pairingpassed on an element with no ease token.<Field label="…"><Textarea /></Field>produced a textarea with noid, noaria-labelledby, noaria-describedbyand noaria-invalid: the<label for>pointed at nothing, the error message was not linked, and axe reportedlabelat critical.Textareanow renders through Base UI'sField.Control, like every sibling control, so the wiring and the destructive border tint arrive automatically. Standalone use is unchanged.Also fixed:
aria-invalidwas accepted and inert on a standaloneOTPInput(it landed on the root, never on the slots) and on a standaloneNumberField(the group's:has()selector cannot match the group's own attribute); a read-onlyEditableCellwith aselecteditor rendered the raw stored value where the editable cell rendered the option label; and aborderlessFieldshowed no resting border when invalid. docs ·02ba364 -
Text-entry focus under forced colours, on a field with addons, where the outline was being clipped. A bordered field group — Input's prefix/suffix wrapper, NumberField's stepper group, ChipInput, the Combobox input-group — clips with
overflow-hiddenso its addons follow the rounded corner, and the inner input's outline is offset outward into that clip. It was drawn and then cut, so an addon field still had no visible focus under the forced palette. The group carries the outline now (via a baredata-field-groupattribute thatbase.csshooks) and the control inside stands down, so the two never double-ring. docs ·9fbeb65 -
Text-entry focus — a text field's border is its only focus channel, and two other states were taking it. An
aria-invalidfield kept its destructive border when focused, and aField borderlesscontrol kept its transparent one, so both showed no focus indicator at all (WCAG 2.2 §2.4.7). The invalid tint infieldControl/fieldControlGroupand on TextEdit's container now stands down onfocus/focus-within,borderlessflattens only while unfocused, andInput'soutline-hidden— lost to a missing space in a string concatenation — applies again. The focus tint is now contrast-gated as the composite users actually see: 4.04–4.51:1 light, 6.31–7.72:1 dark. docs ·cff5ccb -
Two shipped WCAG 1.4.3 failures in brand and status colour, and the three gates that could not see them. The
destructiveBubblewas the only place in the registry that used a solid FILL token as body text —text-destructiveoverbg-destructive/(--alpha-soft-surface)measured 5.24 / 4.31 / 4.44:1 in light and 2.56 / 2.37 / 1.78:1 in dark across rest/hover/pressed, and its light ladder inverted (hover L 0.874, pressed L 0.883) because the pressed step jumped to a precomposed token sitting on a different ground. It is now the same soft recipe the four soft Buttons use —bg-destructive-subtle/-hover/-activewithdestructive-textink — measuring 5.80 / 5.13 / 4.69 light and 6.14 / 5.02 / 4.61 dark, monotone in both. Thetintedvariant's pressed step, which composited to L 0.921 against asurface-3hover at 0.922, moves to--alpha-ink-tint-strongso a press is visible. ThectaButton painted its 0.75rem/400 mono label intext-brand, a 3.5:1 MARKER value, measuring 3.41 / 3.33 / 3.21:1 in light on the public docs playground; the family now shipsbrand-text, the page-readable half every chromatic family already has, and the label re-measures 5.93 / 5.80 / 5.59 light and 11.41 / 10.90 / 10.13 dark. The hovered-link dim (--alpha-link-hover, on thelinkButton, every rendered rich-text link throughprose, and PropertyList) compositedsuccess/info/warningink to 4.03–4.11:1 in light at 80% and is now 88%, re-measured 4.74–4.83:1.--alpha-soft-hoverand--alpha-soft-surfaceare removed: their only consumer was that Bubble line, and--alpha-soft-hoverwas a second, 13pp different answer to the rolesd-hooks.mjs'sSUBTLE_HOVER_ALPHAalready owns.--font-display/--font-pixelare now bridged into@theme inline, so D17's sanctioned Geist Pixel flourish is reachable. Gates:contrast-check.mjsmeasuresbrand-textand the link-hover composite (both observed failing on the pre-fix theme);design-lintgainsfill-token-as-textandfield-group-pairing, both with negative fixtures;verify-token-referencesanddesign-lintnow coverpackages/design/src, where every shared recipe lives and where a bogus token previously exited 0; andverify-docs-base-mirrornow mirrors the::view-transition-*reduced-motion companion rule the docs copy had silently lost. docs ·18e2208 -
A
Timeline's last row lost the bottom of its pointer target.TimelineItem's<li>usescontent-visibility: autofor render skipping, which brings paint containment with it, so anything a child paints outside the box is clipped and stops being hit-testable.timeline-contentdropped its bottom padding on the last item, and a trailingRelativeTime— whose 24px pointer target comes from a 4pxbefore:-inset-y-1overhang — had that overhang fall outside the clip: the effective target collapsed to the row's own 23px, under the WCAG 2.2 SC 2.5.8 floor. The last row now keeps 4px of bottom padding, exactly the depth of that hit area. Trailing whitespace under the final row grows by 4px; nothing else moves. docs ·6a5a49e -
Pointer targets — nine WCAG 2.2 §2.5.8 misses recorded by the geometry lane are closed at the root.
IconText's row and aMarkerrendered as a link or a button each carry an invisible hit area, so a clipped row and an action marker measure 24px to the pointer and not one pixel differently to the eye. In the docs, the attachment demo composed its actions above the card trigger — the reverse of the documented order, which made the remove button unclickable — the scroll-fade demos now reflow at 320px instead of scrolling the page sideways, and the message-scroller outline entries sit on a target-sized pitch. The lane itself learned two facts it was missing: aninertcontrol accepts no pointer action, and arole="tabpanel"is not a target. docs ·1b11aa1 -
The video player's tooltips and settings menu are visible in fullscreen. They portaled to
<body>, which the browser does not paint inside a fullscreen element — the playback-speed and quality menus were unusable in fullscreen.MediaPlayerControlstakes aportalContainerandVideoPlayerpasses its frame; the volume panel already avoided this by rendering inline. docs ·aa5fa0d -
The video overlay's seek rail rests at its own thickness and thickens on engagement again. Its
h-1tied on specificity with the shared track'sh-1.5, so Tailwind's sort order picked the default 6px rail and the hover/focus thickening had nothing to thicken from. docs ·aa5fa0d -
A viewer's chosen playback speed no longer resets while the media plays. The media-element effect listed the consumer's
onTimeChangeamong its dependencies;timeupdatefires ~4×/s and re-renders the controls, so a player given an inline callback re-applieddefaultPlaybackRateseveral times a second and 2× snapped back to 1×. docs ·aa5fa0d -
Fixed — the geometry contract lane (
packages/ui/test/geometry.css) imported the token theme and base layers but neverutilities.css, so every@utilityin the system —scroll-fade-*,scrollbar-thin,motion-pop-in, the newmotion-dock-*pair — compiled to nothing and all 523 fixtures were measured against a layout no user ever sees. A custom utility with no definition is an empty rule, not an error, so the gap only ever made the contracts weaker. The lane now imports the same layer production does.2a3fc24 -
useIsMobilereportedfalseon the server, so SSR rendered the desktop layout on a phone until the effect ran — Board enabled pointer drag and then disabled it. It now renders theserverFallbackthe caller declares.usePlatform'sisTouchwas frozen at the post-hydration value; the primary pointer can change mid-session — a 2-in-1 detaching its keyboard — and a drag affordance gated onisTouchhas to follow it, so that half is now the live(pointer: coarse)query. docs ·3663f8f -
ParticleField froze the brand colour of the theme it mounted in. The ink was read once, into a
const, inside an effect keyed on nothing the theme touches — so a light-mounted field kept the light--brandafter a toggle to dark until something forced a remount. It now reads the canvas's own resolvedcolorper frame, and the single static reduced-motion frame repaints on a theme change too.3663f8f -
The collapsed-breadcrumb trigger was a 20×20 pointer target. Its visible box was the 20px
BreadcrumbEllipsisglyph and nothing expanded it, so the one control that reveals a trail's hidden segments sat under the WCAG 2.5.8 24×24 CSS px floor — measured 20.00×20.00 on all three breadcrumb fixtures.BreadcrumbCollapsed's trigger now carries a transparent::beforeexpansion (relative before:absolute before:-inset-0.5) that brings the EFFECTIVE target to exactly 24×24 with no change to the visible glyph and no change to the trail's line height; 2px per side stays insideBreadcrumbList's 6px gap, so it never reaches into a neighbouring segment.BreadcrumbEllipsisis decorative and stays 20px — its doc comment now says the wrapping trigger owns the target, and the manual-composition example demonstrates it. docs ·fdaed05 -
Four navigation and layout accessibility defects.
BreadcrumbPageannounced the current page as a disabled link — it rendered<span role="link" aria-disabled="true">, so screen readers described a non-interactive segment as a dimmed link; it is a plain<span aria-current="page">, and the ARIA misuse was inherited from shadcn. Focus rings were clipped on scroll viewports —ScrollArea's andMessageScroller's viewports and the sidebar rail offset their focus outline OUTWARD under a clipping ancestor, so the ring was cut in half or lost; all three inset it now. Every shell and sidebar docs fixture rendered a duplicate<main>inside the docs page's own, failing axe'slandmark-no-duplicate-main; they renderlandmark="region". AndTabsContent,BreadcrumbCollapsed's trigger andAppShell's skip link each restated the global:focus-visiblerule — two copies of one rule can only drift, so the copies are gone. docs ·fdaed05 -
The DatePicker caption dropdown was a 21px-tall pointer target.
captionLayout="dropdown"renders a transparent<select>stretched over the caption root (absolute inset-0), and that root — anitems-centerchild of a 32px row — collapsed to its 21px line box, so the real control measured 50.36×21.00 against the system's 24px effective-target floor (WCAG 2.2 §2.5.8). The root now takesself-stretch, handing the select the row's full height. Nothing in that root paints, so the month/year label and its chevron are pixel-identical. Its entry in the geometry lane'sEXCLUDEDmap is deleted. docs ·45cde26 -
registry:libfiles are now modeled as shadcn-transformed in the shipped consume verifier.shadcn addremoves a JS/TS file's entire leading comment prologue as it writes it, for every file type it touches — butverify-registry-item.mjslisted onlyregistry:ui,registry:hook,registry:pageandregistry:component, so the firstregistry:libitems (geo-data,drag-item) compared the copy-in against unstripped source and failed post-write verification with a line-count mismatch — i.e. the gate reported a TOCTOU signal for a transform the CLI is sanctioned to perform.check-updatesreads the same set, so its diffs were affected identically.45cde26 -
Toasts appear again, and three appearance-probe defects close. The docs site mounted the registry copy-in
Toasterunder the package provider'sToastProvider, and each toast module owns a module-scope manager — so the viewport listened to one store while every preview'stoast()wrote to the other, and the Toast page had been silently dead since toasts moved to Base UI. The copy-in now brings its own provider, a gate refuses aToasterwhose provider comes from a different module, and a browser test pins both halves of the rule. Tabs' count badge stacks its ink wash on whatever the trigger paints, which put muted ink at 3.43:1 on a selected pill in dark; it takes body ink now, the token gate learned to check a wash painted on a ladder rung, and the rendered-contrast lane covers every variant in both themes. DatePicker'sdata-dayhook is a stableYYYY-MM-DDinstead of a locale-formatted string, which is what made a prerendered calendar throw a hydration error in every browser whose locale was not the build host's, and the page now says prerendering a formatted date needs an explicitlocale. NumberField's addon slots hold an interactive control off the field's hairlines and keep its focus ring out of the clip, so the money recipe's currencySelectstops painting into the rule.1e439d9 -
A table's selection checkbox did not own its own 24px hit area.
TableHeadandTableCellcollapsed the trailing padding of a checkbox column tope-0, so the checkbox's 6px::beforeoverhang fell outside its own cell — the neighbouring header's sort control owned part of the centred 24×24 square. Both cells now usepe-2, the least that contains the target, and the sort control no longer carries a negative inline margin (SortableHeadnarrows its own cell padding instead, so the label alignment is unchanged). docs ·6d25185 -
Two focus rings that never painted. Charts were focusable with no visible ring — recharts'
accessibilityLayermakes the plot<svg>a tab stop, andChartContainer's own.recharts-surfaceoutline reset then poisoned--tw-outline-styleon the very element that takes focus, so even the global:focus-visiblerule resolved tooutline-style: none. The reset is now scoped to:not(:focus-visible). And theDataGridroving cell's ring was clipped, because the cell lives inside the table's scroll viewport, which clips its overflow — it is now inset.PropertyListvalues stop truncating for the same reason: theoverflow: hiddenthattruncateimplies was clipping the focus ring of any link inside a value. docs ·6d25185 -
The 320px contract check no longer races a re-rendering fixture — a fixture that re-renders on its own timer (relative-time reschedules a
setTimeout) could detach between the visibility assertion andscrollIntoViewIfNeeded, failing the sweep with "Element is not attached to the DOM" on a different subset of Chromium projects each run — on unmodifiedmainas well. The scroll is now a bounded retry that re-resolves the locator; the assertions, the RTL and 24px target-floor checks, and the fixture selection are unchanged.b2c2e96
⚠️ Breaking
-
Seven animated icons drop a deprecated handle alias; an eighth renames its handle type.
BotMessageSquareHandle,ConciergeBellHandle,KeyIconHandle(on bothkey-circleandkey-square),RefreshCCWIconWIcon(onrefresh-cw),ActivityIconHandle(onsquare-activity) andZapHandlewere@deprecatedaliases left behind by upstream naming quirks; each of those icons still exports its<Name>IconHandleand only the alias is gone.chevron-firstis the different case and is a rename, not an alias removal: upstream had copy-pasted adisplayNamefrom another icon, so the primary interface was calledChevronsDownUpIconHandleandChevronFirstIconHandlewas the@deprecatedalias of it. The exported component symbol is authoritative, so the name that survives is the one that matches it —ChevronsDownUpIconHandle→ChevronFirstIconHandle. Consumers of the old name must rename; no compatibility alias is kept. docs ·b2c2e96 -
Alertisrole="status", notrole="alert", andItemtakesrole="listitem"only inside anItemGroup. Every alert used to be an assertive live region, so a page rendered with three static alerts interrupted a screen reader three times before the user had read anything. The banner is now polite for every intent; pass the newliveprop for a banner raised by a user action, and adestructiveorwarningintent then escalates to the assertivealertrole.AnnouncementBannerdrops itsrole="status"at load and takes the sameliveprop. Tests assertinggetByRole("alert")on a static banner should readgetByRole("status"). Separately,Itemappliedrole="listitem"to every non-renderrow, so a standaloneItemwas an axearia-required-parentcritical andTimelinehad to documentrole="none"as a workaround;ItemGroupnow provides the context that licenses the role, and outside one a row carries no role at all. Remove anyrole="none"passed to work around the old default. docs ·8c18d2b -
Emptyhas one container axis.variant: plain | card | dashedreplaces theborderedflag crossed withsurface, a pair that could ask for a dashed card and resolved it only by letting tw-merge pick a winner. Rename map:bordered→variant="dashed",surface="card"→variant="card",surface="transparent"(the default) →variant="plain". Thedata-borderedanddata-surfaceattributes are replaced bydata-variant.EmptyTitlealso takes anasprop, so the hard-coded<h3>no longer guesses at the host page's heading outline — passas="h2"when the empty state replaces a page body, oras="p"when the surrounding card already carries the heading. docs ·8c18d2b -
Badge speaks the same variant vocabulary as Button.
variantis nowsolid · soft · outline · minimal:subtleis renamedsoft, with no alias — a stalevariant="subtle"is a type error. The three sizes become three REAL heights,sm16px ·md20px ·lg24px;smused to bemdwith 2px less horizontal padding, which is a padding value, not a size.minimalbecomes ink only — no fill, no border, no horizontal padding — so it sits flush in a table cell instead of faking a pill, and it now carries a leading dot by default, because a badge with no container has nothing but colour left to signal status with (WCAG 1.4.1). Passdot={false}to opt out, or the newiconprop to take the dot's place. docs ·7915a71 -
Kbddefaults toos="other". It reads nonavigator— that is what keeps it server-safe — so the platform is the caller's to resolve: runusePlatform()and pass the answer down. The old default shipped mac glyphs to a Windows majority.TooltipKbdtakes the sameosprop. docs ·7915a71 -
borderandinputare translucent.borderis derived asforegroundat--alpha-border(8% light / 14% dark) so one hairline reads on the page, on a card, inside a well and on a dark band. Anything that assumed an opaque border value should read the variable instead. docs ·b2c2e96 -
Light cards are page-coloured.
cardwasoklch(0.985)against a0.994page — a grey slab no reference system draws. It is now the page colour, separated by the hairline alone; dark keeps its one-step lift.popoveriscardin both themes. docs ·b2c2e96 -
Surface tokens are now one ladder.
surface-1/surface-2/surface-3— the rest-fill/well, hover and pressed/selected rungs — arrive with the theme-invariant alpha twins--alpha-hover(7%) and--alpha-pressed(10%).secondary,mutedandaccentwere a single OKLCH value under three names, so no hover or pressed state could be seen on a card. They are now aliases of ladder rungs and have no independent values:secondary=muted=surface-1,accent=sidebar-accent=surface-2,sidebar=card,sidebar-border=border,sidebar-ring=ring. Existingbg-muted/bg-accent/bg-sidebar-*utilities keep compiling and keep their rest appearance; onlyaccentmoves (one rung darker, because it is the hover rung). Name the rung in new code. docs ·b2c2e96 -
Buttonhas no icon size tier.size="icon"/icon-xs/icon-sm/icon-lgare gone; every icon-only action isIconButton, which makes the missingaria-labela type error and now ownsshape="square" | "round".7915a71 -
Button is two axes, not fifteen variants.
variantis now the SHAPE —solid·soft·outline·ghost·link·cta— and the newtoneprop is the HUE —neutral(default) ·destructive·success·warning·info. Every recipe is written once as ten class strings and reads the hue from--btn-*custom properties, so all thirty cells share one hover/pressed grammar. Rename map:default→solid,secondary→soft,destructive→soft -
disabledisaria-disabled, not the native attribute. Button, IconButton and SplitButton keep their pointer events and stay focusable when disabled, so a Tooltip can explain why the action is unavailable. Base UI still suppresses activation. Code assertingelement.disabledshould readaria-disabledinstead. docs ·7915a71 -
One size vocabulary:
xs · sm · md · lg. The tier every component calleddefaultis nowmd, matching the--size-*tokens it was always built from. This is a rename across Button, IconButton, SplitButton, Badge, Input, Textarea, Select, Combobox, Avatar, Card, Item, Empty, Kbd, Dialog, Switch, Checkbox, RadioGroup, Toggle, ToggleGroup, Segmented, Stat, Spinner, StatusIcon, Progress, ProgressIndicator, OTPInput, NumberField, Attachment, ChipInput, Pagination and Sidebar. There is no alias —size="default"is a type error. docs ·7915a71 -
Helper text moved below the control, and
Fieldowns validation feedback. A field's description now renders under the control with the error under that — above the control it pushed the input away from its own label, and a wrapped description put two lines of prose between the two things the eye pairs. The invalid shake moved with it:Input,Checkbox,RadioGroupItem,OTPInput,NumberFieldandChipInputno longer takeshakeSignaland no longer shake on their own —Fielddoes, for every control it wraps, soTextareagains the behaviour it never had. A bare<Input aria-invalid>outside aFieldstill tints its border; wrap it in aFieldfor the motion, or moveshakeSignalonto theField. docsInline validation announces as
role="status", notrole="alert".FieldErrorandFieldInline's error are polite live regions: the person just typed or submitted and is looking at the field, andalertinterrupts whatever the screen reader was saying.alertstays reserved for something that arrives without being asked for. Tests assertinggetByRole("alert")on a field error should readgetByRole("status"). docsForm controls keep their pointer events when disabled.
disabled:pointer-events-noneis gone from Input, Textarea, NumberField, OTPInput, Select, Combobox, ChipInput, Checkbox and RadioGroup, matching the Button contract — an unavailable control must stay hoverable so a Tooltip can say why.cursor-not-allowedand the dim stay; Base UI suppresses activation either way. docs ·9fbeb65 -
MediaPlayerControlsmoved out ofaudio-playerinto its own registry item. It was exported fromaudio-playereven though the video player was its main consumer. Runshadcn add @vegastack/media-player-controlsand import from@/components/ui/media-player-controls;video-playerdeclares it as a registry dependency, so a freshshadcn add @vegastack/video-playerpulls it in automatically. docs ·aa5fa0d -
Slider's internals are no longer restyled from outside. Anything reaching into
[&_[data-slot=slider-track]]/-indicator/-thumbto build a media rail should passvariant="media"orvariant="overlay"withthumb="hover"instead. The internal slots keep theirdata-slotnames, but their rest appearance is now thevariant's to decide. docs ·aa5fa0d -
Menu items take
tone, notvariant.DropdownMenuItem,ContextMenuItemand their checkbox/radio siblings usetone="destructive", matching Button's tone axis. The state attribute moves with the prop: items exposedata-tone, notdata-variant. docs ·7915a71 -
In-panel search fields are a
searchbox. Every panel-search row renderstype="search", soShortcutOverlay's filter (and any other field inside the shared row) exposes thesearchboxrole rather than a generic textbox. Selecting one by role in a test or script must change with it. docs ·7915a71 -
Sheetruns on Base UI'sDrawer. It gains swipe-to-dismiss, snap points (snapPoints/snapPoint/onSnapPointChangepass straight through) andSheetVirtualKeyboardProviderfor bottom sheets containing fields.sidemoves fromSheetContentto theSheetroot, because it selects the dismiss gesture as well as the pinned edge, and asideon the content could disagree with the gesture.SheetContentnow sizes throughsize—sm · md · lg · fullfrom the shared--panel-width-*vocabulary replacesclassNamewidth overrides, and one tier means a width on aleft/rightsheet and a height on atop/bottomone. Swipe is always an addition:Esc, the close button and a backdrop press still close the panel. docs ·7915a71 -
Toasts run on Base UI, and the
toast()API changed with them.sonneris removed from the system and the registry item is renamedsonner→toast.toast()now takes a title plus Base UI's options:action: { label, onClick }becomesactionProps: { children, onClick },durationbecomestimeout(and0, notInfinity, disables auto-dismiss), and ids are strings.toast.messageis gone — it wastoast().toast.customnow renders the toast BODY inside a real toast, so a custom notification keeps stacking, swipe-to-dismiss,Escapeand the live region instead of opting out of them. Resolving a loading toast istoast.update(id, …)rather than re-firing with the same id.Toasterloses sonner's props:positionvalues are logical (bottom-end, notbottom-right),expandis gone because the stack expands on hover by design, andoffset/mobileOffset/themeare gone — the viewport carries the safe-area insets itself and reads the theme from the cascade.VegaStackProvideralways mounts the toast context now:toaster={false}still suppresses the visible viewport — the part that must not mount twice — but the providertoast()writes into is unconditional, so a host rendering its own<Toaster />shares one queue.@vegastack/designgainsTIMINGS.tooltipOpenDelayMs/TIMINGS.tooltipCloseDelayMs, which the provider applies toTooltip.Providerso every tooltip in an app shares one rhythm.@vegastack/design-tokensgains a third z band,--z-toast(60): the toast viewport mounts with the app provider, before any dialog exists, so DOM order alone would put every later-opened dialog on top of it — and a toast fired from inside a modal must stay visible. Sonner supplied that from its own private z-index, which is why elevation doctrine carried a library-shaped exception; it is now a token with exactly one caller. docs ·d5e2de2 -
CountrySelect/RegionSelectinternals.COUNTRIES,Country,getCountryByCode,REGIONS_BY_COUNTRY,Region,getRegionsByCountryandhasRegionsare no longer exported fromcountry-select/region-select, andregion-select-data.tsis gone — importCOUNTRIES,REGIONS,getCountryByCodeandgetRegionsfrom@/lib/geo-data(getRegionsByCountry→getRegions;hasRegions(c)→getRegions(c).length > 0).RegionSelectno longer clears by re-selecting the current state; clearing is the explicitclearablecontrol on the trigger, on by default. Both selects now render a wrapper, sodata-slot="country-select"/"region-select"is on the wrapper and the trigger carries the-triggersuffix. docs ·45cde26 -
Table cells wrap by default. Every head and cell carried
whitespace-nowrap, so one long value forced the whole table to scroll instead of wrapping at a word boundary. Body cells now useoverflow-wrap: anywhereover a--table-cell-min-widthfloor, and scrolling is reserved for tables that are genuinely wide. Two column shapes opt back out automatically —align="end"figures and the newmonocolumns — andDataListColumn.nowrap/DataGridColumn.nowrapoverride the inference either way. A layout that relied on single-line cells should setnowrap: true(orwhitespace-nowrapon a rawTableCell). Two further breaks land with it:DataGrid'smobileposture now defaults to"merge"rather than"hidden", so overflow columns stack into the primary cell instead of disappearing —mobile: "hidden"is still available, and when it drops anything the toolbar states "N columns hidden"; andTable'scontainerClassNameis removed, because it did exactly whatcontainerProps.classNamedoes. docs ·6d25185 -
useDragReorderanduseFileDropreturnAnnouncer, notgetLiveRegionProps(). Render<reorder.Announcer />/<drop.Announcer />in place of<span {...reorder.getLiveRegionProps()} />. The props-getter shape could not keep the region mounted across an announcement, which is the property that makes it audible. docs ·7915a71 -
Paginationno longer hard-codes its accessible name. It renders a plain<nav>(norole="navigation") andaria-labeldefaults to "Pagination". A page with more than one pager must name each one — two identically named landmarks are an axelandmark-uniquefailure. docs ·7915a71
[0.6.0] — August 31, 2026
🔧 Changed components
- AudioPlayer — rebuilt transport with a responsive, two-line mobile layout. On a wide player
it is a single line: play/pause, rewind and forward (±15s), an
elapsed / durationreadout, a flexible seek, and a fixed-width tappable speed control that cycles the playback rates (1x → 1.25x → 1.5x → 2x → 0.5x). Audio carries no volume control — mute stays on the M key. On a narrow (mobile-width) player it reflows to two lines: the seek bar withelapsedanddurationpinned to either edge in a smaller font on top, and a centred play/pause flanked by rewind and forward on the bottom, with an optional transcript control (newonTranscriptClick, lucideaudio-lines) on the leading edge and the speed control on the trailing edge. The chrome and progress fill move from the brand-bold primary to a subdued secondary emphasis, and the waveform seek now fills continuously instead of one bar at a time. VideoPlayer's overlay controls are unchanged. docs ·43eb359
📦 npm
@vegastack/design→0.3.2— refreshes the shipped Audio Player skill reference for the reworked transport and its two-line mobile layout. No runtime code changed.@vegastack/design-tokensis unchanged. The design-system registry (@vegastack/ui) bumps 0.5.0 → 0.6.0.
[0.5.0] — August 28, 2026
🧩 New components
- AudioPlayer, VideoPlayer — media players sharing one custom transport: grouped
play / seek / mute / settings controls, a 128-bar waveform seek on audio, a smoothly expanding
video progress rail, a contained volume rocker, larger overlay actions, state-aware fullscreen,
and keyboard shortcuts.
docs ·
334cb4c
🔧 Changed components
- ProgressIndicator — new value-display variants render the percentage inline or contained
within the glyph.
334cb4c - Sidebar — the desktop sidebar and its footer now stay pinned to the viewport while page and
navigation content scrolls.
docs ·
334cb4c - Switch — the invalid state no longer applies a destructive track border;
aria-invalidremains the semantic cue and the wrapping Field owns the error message. Consumer-visible restyle. docs ·334cb4c
🐛 Fixed
- DatePicker, ComparisonMatrix, PricingSection, Tabs, FieldInline — manual-QA
remediation across range selection, compact navigation, overflow handling, and responsive pricing
and comparison layouts.
docs ·
334cb4c
🛠 CLI & tooling
- Cross-engine smoke and the full three-engine suite now treat WebKit as host-conditional — it
runs where it can launch and is skipped with an auditable banner where it cannot (macOS 26.6.2
cannot launch Playwright's WebKit). Chromium + Firefox coverage is never blocked, and a Mac on
macOS 26.2–26.5 still enforces WebKit. Control with
WEBKIT_LANE=auto|off|require.b6b1bd0
📚 Docs
- Improved the published examples for dropzones, menus, charts, mobile shells, and the dashboard
starter.
334cb4c
📦 npm
@vegastack/design→0.3.1— ships the regenerated component-roster skill reference (the two new players, the ProgressIndicator variant note, updated counts). No runtime code changed.@vegastack/design-tokensis unchanged. The design-system registry (@vegastack/ui) bumps 0.4.1 → 0.5.0.
[0.4.1] — July 28, 2026
🐛 Fixed
- Stepper — horizontal step content is now width-constrained so long labels truncate instead of overlapping adjacent steps at the 320px responsive contract width. docs
[0.4.0] — July 27, 2026
🧩 New components
- ActionBar, ChipInput, EditableCell, FilterBuilder (
filter-bar-managed), NumberField, ShortcutOverlay, Stepper, and Timeline — eight additions from the CRM commission (plan 2026-07-26), each with complete docs, state coverage, accessibility tests, and registry integrity metadata. components - SortableList, Board, Dropzone, and DataGrid — the four dependency-gated commissions, unblocked by MK's 2026-07-27 sanction of four engines (Pragmatic drag and drop, react-dropzone, TanStack Table, TanStack Virtual). Reorderable rows with a lossless keyboard Move menu; a kanban board with cross-column card movement and per-move lock reasons; a paste-capable file drop surface; and the full-parity grid — multi-key sort, column picker with responsive revelation, collapsible grouping, keyboard-continuous load-more, opt-in virtualization, and APG cell navigation with inline editing. components
- useListNav, usePlatform, useDragReorder, and useFileDrop — four new registry
hooks: roving-tabindex keyboard navigation for lists and grids (RTL-aware arrows,
homeEndScope, overlay suppression); SSR-safe platform detection ({ os, isTouch }) forKbd's modifier rewriting and touch gating; the reorder seam over Pragmatic drag and drop (pointer drags, commit-per-step keyboard move mode, server-refusable moves); and the file-drop seam over react-dropzone (drop + paste + browse, typed rejection reasons). guide
🔧 Changed components
- Kbd — mac modifier glyphs now pair the visual glyph with visually hidden spoken names ("Command", "Option", …), so screen readers no longer hear "place of interest sign" or nothing. docs
- Table — new
containerClassName/containerPropsforwarded to thedata-slot="table-container"scroll wrapper, the attachment point for sticky headers, fixed-height viewports, and virtualizers. docs - DataList —
DataListPropsnow extendsTableProps(the spreadsheet voice and container hooks type-check), columns gain a per-cellcellClassNamehook, andrenderreceives an optional thirdDataListCellContextargument. All additive. docs - FieldInline — additive controlled edit mode (
editing/onEditingChange) and atabIndexoverride for the display element, soEditableCelland grid hosts compose it instead of forking it. No behaviour change for existing consumers. - ColorPicker and EmojiPicker — internal refactor onto the shared
useListNavhook; the API and visuals are unchanged, with one behavioural correction riding along: EmojiPicker's horizontal arrow keys become RTL-aware, matching ColorPicker. Safe to take or skip. docs - Button — the
active:translate-y-pxpress nudge is removed frombuttonVariants, so every component composing Button (IconButton, SplitButton, toolbars, pickers) loses it in one place. Pressed feedback is now colour-only via the existingactive:bg-*states, and the motion foundations doctrine records press-motion as a deliberate exclusion. docs
🛠 CLI & tooling
verify-registry-depsgains a fail-closed npm-range check: a registry item pin the installed version cannot satisfy now failsregistry:buildinstead of passing silently.
📚 Docs
- The npm dependency pins the installed versions could not satisfy are reconciled to
packages/ui/package.json:lucide-reactwas declared at both^1.20.0and^0.525.0across a major boundary (41 items), and@shadcn/reactat^0.1.0against an installed^0.2.1. table.mdxno longer claims the Table parts add no props;data-list.mdxdocuments that a columnrenderis invoked as a plain function (hooks belong in a returned component element).- The docs homepage and 404 page now pass
nativeButton={false}to every Button rendered as a link, matching the Base UI contract for non-buttonrendertargets;button.mdxand the bundled design-system skill document the pattern, andmotion.mdxrecords the press-nudge removal.
[0.3.0] — July 24, 2026
🧩 New components
- AnnouncementBanner, CodeBlock, ComparisonMatrix, NavigationMenu,
OnboardingChecklist, PricingSection, PropertyList, RuledBand, Segmented,
Stat, TagGroup, and ToolCallChip — twelve production-ready additions with complete
docs, state coverage, accessibility tests, responsive previews, and registry integrity metadata.
components ·
6c60d53
🔧 Changed components
- MarkdownView — remote image sources are now same-origin by default; explicitly allow trusted
remote origins with
allowedImageOrigins. The renderer and copied-file verifier also enforce contained paths and digest-pinned post-write checks. docs ·6c60d53 - Provider and all portal-owning components now share an explicit theme-scope contract, while
server-safe modules are verified under React's real
react-servercondition. docs ·6c60d53
🛠 CLI & tooling
vegastack-design skills installnow bundles and safely installs the four public VegaStack agent skills for Claude Code and Codex, with atomic conflict handling, symlink containment, dry-run, and single-surface options. guide ·6c60d53vegastack-design check-updatesnow verifies installed file bodies and the complete target set; matching provenance headers can no longer hide edited content, and removed or renamed targets fail--fail-on-update. guide ·6c60d53- Registry builds now prune stale artifacts, reconcile exact authority/index/manifest sets, and
prove real shadcn consumption against locally packed npm artifacts before release.
integrity ·
6c60d53
📦 npm
@vegastack/design0.2.0 — adds bundled public agent skills, the skills installer, CommonJS-compatible exports, and the explicit./theme-scopeclient subpath while preserving a server-safe root.6c60d53@vegastack/design-tokens0.2.0 — publishes the unified DTCG-backed doctrine, strong type and effect roles, exact dark/marketing parity, and verified ESM/CommonJS/package exports.6c60d53- Private
@vegastack/uiadvances to 0.3.0 to stamp the 538-item registry; it remains private and is never published to npm.6c60d53
📚 Docs
- The public design doctrine, component contracts, homepage catalog, API reference, agent guidance,
internal SSO corpus, metadata, and release/cutover runbooks now derive from current machine
authorities and build successfully in both private and public visibility modes.
design doctrine ·
6c60d53
🐛 Fixed
- State-specific accessibility coverage now exercises open, expanded, selected, disabled,
highlighted, loading, empty, error, success, and removable states across the applicable
component contracts; all token contrast pairs pass WCAG 2.2 AA gates in both themes.
accessibility ·
6c60d53 - Release and deploy workflows now use the reviewed-merge/manual-dispatch approval model supported
by the private GitHub Team repository, retain isolated OIDC and repository secrets, and probe the
Cloudflare Access boundary after deployment.
6c60d53
⚠️ Breaking
vegastack-design verify --post-writenow requires--expected-integrity; use the exact integrity-pinned command printed by the pre-write verification. MarkdownView also requires an explicit allowlist for cross-origin images. verification · MarkdownView ·6c60d53
[0.2.0] — July 19, 2026
🧩 New components
- Provider — the app-root wrapper (theme, toasts, tooltip coordination, text direction);
install once via
shadcn add @vegastack/provider, composes thesonnerToaster item. docs ·c7de692
🔧 Changed components
- Checkbox, Switch — Story explorer controls narrowed to human-usable props (the raw
Base UI prop graph serialized to ~24 MB per page, over Cloudflare's 25 MiB asset limit).
No API change.
checkbox ·
switch ·
45c7cf8 - dashboard-01 (block) — component files now target
app/dashboard/components/(the page's relative imports were broken on clean installs) andpage.tsxships the default export Next requires for route files.ac6288a - use-animation-replay (hook) — stray duplicate header line removed (caused a false drift
flag in
check-updates).ac6288a
🛠 CLI & tooling
vegastack-design check-updatesis header-optional: the shadcn CLI strips provenance headers on copy-in, so copies are now identified by filename against the registry index and compared by alias-normalized content. New≈ driftstatus (differs — upstream update or local edits);--fail-on-updatefails onupdate+drift. guide ·ac6288a- Release workflow is path-routed: the container pixel gate runs only when component-visual
code changes; the release job only when something is publishable.
e8a8450
📦 npm
@vegastack/design0.1.1 —tw-animate-cssis a real dependency (was an optional peer that pnpm never installed, breaking every fresh consumer build atpreset.css). Published via npm OIDC trusted publishing — the first fully-automated release.9532d42@vegastack/design-tokensstays 0.1.0 (no changes).
📚 Docs
- New Guides section — Quickstart,
Registry access & auth, Working with components, Provider setup, Theming, Production
checklist, Troubleshooting. Every command executed for real against the reference starter
before being written down; 29 adversarial-review findings applied.
7307231·11836b6 - Sidebar sections labeled; doc pages carry a server-rendered "Last updated" stamp.
a323cba - Lifecycle guides for both audiences: Internal projects (the two-speed update model: npm = global on dep bump, registry = frozen until reviewed pull; the token-ordering trap) and Client projects (delivery model, handover checklist, zero-lock-in dependency table). Repo also gains a maintainer README. The internal guide is now SSO-only and intentionally absent from the public corpus; the client-project guide remains public.
[0.1.0] — July 18, 2026
🧩 New components
- Initial release: 83 components, 439 animated-icon items, 2 hooks
(
use-mobile,use-animation-replay), and thedashboard-01starter block — all Base UI + Tailwind v4, semantic-token-only, WCAG 2.1 AA, distributed as copy-in via the private registry atdesign.vegastack.com/r/*(Cloudflare Access service-token auth, Sigstore-signed manifest). components ·8a5bb2a
📦 npm
@vegastack/design0.1.0 —cn(), the icon runtime (./icons), the Tailwind v4 preset (./preset.css), token CSS re-exports, and thevegastack-designCLI.@vegastack/design-tokens0.1.0 — the zero-dependency DTCG token contract (theme.css,base.css,utilities.css,tokens.json).
📚 Docs
- At the 0.1.0 release, the Fumadocs showcase had 99 pages, live previews rendering the real
shipped source, and complete desktop/mobile VRT coverage; it was deployed behind Cloudflare
Access SSO. Current route and baseline counts are verified dynamically rather than inferred
from this historical release entry.
8a5bb2a
How you receive updates
Two delivery paths, because the system is hybrid — the full operating model per audience: Internal projects · Client projects.
Packages (@vegastack/design + @vegastack/design-tokens) are normal npm dependencies — bump them
with your package manager when a new version publishes. They share a Changesets linked group:
releases that touch both give them the same version; otherwise versions may differ (e.g.
design 0.1.1 alongside design-tokens 0.1.0 — nothing changed in the tokens).
Components are pulled, never pushed. They are copied into your repo by the shadcn CLI, so an upstream change does not silently rewrite your files. You decide when to re-pull, review the diff, then overwrite.
# 1. See what changed upstream (content comparison against the live registry)
vegastack-design check-updates
# 2. Review the diff for a component before taking it
shadcn add @vegastack/button --diff
# 3. Pull the update in, overwriting your copy
shadcn add @vegastack/button --overwriteStatus is by content, not version number
check-updates identifies your copies by filename against the registry index and compares
alias-normalized content (a provenance header, when present, is only a fast-path pin). A
component reads up to date when the global version bumped but that component's content
didn't change — you only re-pull what actually moved. ≈ differs means the bytes diverged — an
upstream update or your local edits; add --diff disambiguates.
Every registry item carries a meta.integrity SHA-256 plus a provenance
header in its registry content, and the consume flow supports a fail-closed
verify (vegastack-design verify). If a hash doesn't match, the add is
rejected rather than silently trusted.