Major Changes
-
b7122c0: feat(DST-1545): replace
ActionBar.Buttonwith a plain<Button>via aButtonContextcascade.ActionBarnow provides aghost/defaultcascade to its toolbar, so authors place a standard<Button>inside the bar and it adapts to the toolbar look automatically, with the full Button API available (disabled,loading,slot,size="icon"). This mirrors the pattern already used byPanel.HeaderandButtonGroup.Breaking:
ActionBar.Buttonis removed. Replace<ActionBar.Button>…</ActionBar.Button>with<Button>…</Button>. For icon-only actions use<Button size="icon" aria-label="…">, which also fixes an accessibility defect where the old wrapper silently droppedaria-label, shipping unlabeled icon buttons. The duplicatedactionButtontheme style is gone (it hand-mirrored Button'sghostvariant at the default size, minus the press and loading affordances a real<Button>now brings).// Before <ActionBar selectedItemCount={3} onClearSelection={clear}> <ActionBar.Button onPress={edit}> <Pencil /> Edit </ActionBar.Button> </ActionBar> // After <ActionBar selectedItemCount={3} onClearSelection={clear}> <Button onPress={edit}> <Pencil /> Edit </Button> </ActionBar>
-
b7122c0: feat(AppLayout): switch to page-level scroll
AppLayoutno longer owns an interior scroll container. The document
(<html>/<body>) scrolls the whole page; the sidebar sticks via
position: stickyand the top header stays pinned through
TopNavigation's own sticky positioning.Why page-level scroll
- Mobile URL bar collapses on scroll. With interior scroll, Safari
and Chrome mobile keep the URL bar expanded forever, wasting ~8% of
the screen. Only document scroll lets the browser hide it. - Pull-to-refresh works. Interior scroll disables it.
- Browser scroll restoration on back/forward only works reliably
for the document, not interior containers. Interior scroll produces
subtle "lost scroll position" bugs. Cmd+Ffind-in-page scrolls the document, not an interior
container, so matches outside the viewport scroll into view
correctly.- Anchor links (
#section), iOS status-bar tap (scroll-to-top) and
native keyboard nav (PgUp/PgDn/Space/Home/End) all
behave predictably. IntersectionObserverwith default root, scroll-snap, sticky
elements,scroll-margin-top— all simpler when there is one
scroll container.
Breaking changes
- Code reading
mainRef.current.scrollTop(or similar) will no
longer see user scroll. Readwindow.scrollY/
document.documentElement.scrollTopinstead. - Styles assuming a fixed-height main region (
height: 100%on
direct children of<AppLayout.Main>, for example) will no
longer be bounded by the viewport. Usemin-h-dvhor remove the
constraint.
Known trade-offs
- Pure app-shell look via
position: stickycan flicker on iOS
Safari momentum scroll. Cosmetic, usually acceptable. - Sticky elements may show a brief re-paint when overlays close.
Not a correctness bug.
- Mobile URL bar collapses on scroll. With interior scroll, Safari
-
b7122c0: feat([DST-1407]):
Drawerenforces one open at a time.Opening a sibling
<Drawer>while one is already open dismisses the first.
Applies to desktop and mobile. The dismissed Drawer'sonOpenChange(false)
is invoked so controlled-state consumers stay in sync.A
<Drawer.Trigger>nested inside an already-open Drawer is treated as a
sub-flow: the nested Drawer opens over its parent and the parent stays
mounted. Dismissing the parent in that situation would also unmount the
nested trigger and tear down the new Drawer.Migration: No API change. If a flow relied on multiple simultaneous
sibling drawers, refactor to a single drawer with switchable content, or
use<Modal>for layered interactions. -
b7122c0: chore(DST-1164): remove the unused
Breakoutcomponent and the now-deadalignAPI fromContainer.Breakouthad 0% usage across all scanned production repositories, so it is removed entirely (component, stories, tests, and docs).Container'salignprop, together with its internalgridColumn/gridColsAligngrid setup, only ever took effect via a[data-breakout]child, so it became dead code onceBreakoutwas gone and is removed as well.Migration: remove any
alignprop from<Container>usages. ThecontentLength,alignItems, andspaceprops are unchanged. -
b7122c0: refa([DST-1324]): Breaking change: Rename Inset's
space/spaceX/spaceYprops top/px/pyThe padding props on
Insetare renamed to align withPanel's existing API, so that across the design systemspacealways means gap between children andp/px/pyalways mean inner padding. Previously,spacecarried two different meanings depending on the component, which was a source of confusion.Migration:
Before After <Inset space="…" /><Inset p="…" /><Inset spaceX="…" spaceY="…" /><Inset px="…" py="…" />The discriminated union shape is unchanged:
pis mutually exclusive withpx/py. Token vocabularies are unchanged (InsetSpacingTokensforp,PaddingSpacingTokensforpx/py). -
b7122c0: feat(DST-1360): introduce
AppShell,Page,Page.Header, andPage.Content; removeAppLayoutRenames
AppLayouttoAppShelland removes its three pass-through subcomponents (AppLayout.Sidebar,AppLayout.Header,AppLayout.Main) —<Sidebar>,<TopNavigation>, and<Page>now sit directly inside<AppShell>(each owns its grid area, so child order does not matter).AppShellabsorbsSidebar.Providervia thedefaultSidebarOpenprop; render your own<Sidebar.Provider>around<AppShell>for controlled state,variant, orsizeand it is detected and used instead of the internal one.Adds
<Page>— the<main>landmark with page padding (p, orpx/py; defaultsquare-relaxed) and vertical rhythm between sections (space; defaultgroup). The page's<main>is named by its<h1>viaaria-labelledby; when there is no<Title>, passaria-label(or your ownaria-labelledby) instead. With none of these,<Page>warns in development so the landmark is never silently unnamed. Like<Panel>,<Page>forwards standard HTML attributes (id,data-*, event handlers) and arefto its<main>.Adds
<Page.Header>— a slot-based title/description/actions header that mirrorsPanel.Header— and an optional<Page.Content>(with its ownspace) for when the rhythm between sections should differ from the header-to-content gap. The page heading outline now falls out of the defaults:<Title>inPage.Headeris anh1,<Title>inPanel.Headeranh2,<Title>inPanel.Collapsibleanh3(override per<Page>withheadingLevel).Migration:
-<Sidebar.Provider defaultOpen> - <AppLayout> - <AppLayout.Sidebar>…</AppLayout.Sidebar> - <AppLayout.Header>…</AppLayout.Header> - <AppLayout.Main>{content}</AppLayout.Main> - </AppLayout> -</Sidebar.Provider> +<AppShell defaultSidebarOpen> + <Sidebar>…</Sidebar> + <TopNavigation>…</TopNavigation> + <Page> + <Page.Header> + <Title>Billing</Title> + <Description>Manage your plan and invoices.</Description> + <Button variant="primary">Upgrade plan</Button> + </Page.Header> + {content} + </Page> +</AppShell>
-
b7122c0: refactor(DST-1548): rename
Card.BodytoCard.ContentAligns the Card body sub-component with
Panel.ContentandPage.Contentso all three container primitives expose the main body region under one name.Breaking change:
Card.Body(CardBody) is removed. Rename usages toCard.Content:- <Card.Body>...</Card.Body> + <Card.Content>...</Card.Content>
The
bleedprop and padding behavior are unchanged. The internaldata-card-bodyattribute has been removed to matchPanel.Content.The
Cardtheme slot key is renamed frombodytocontentin the@marigold/systemThemetype and in@marigold/theme-rui. Theme authors overriding this slot must rename their key accordingly. -
b7122c0: refa([DST-1549]): Breaking change: Rename compound member
Tabs.TabPanel→Tabs.PanelTabsnow exposes.List,.Item, and.Panel, so every compound member follows
one predictable naming rule.<Tabs.TabPanel>is removed (hard rename, no deprecated alias).Migration:
Before After <Tabs.TabPanel id="…"><Tabs.Panel id="…"> -
b7122c0: fix(DST-1559): remove dead and mis-named props from
TextField,FileTrigger, andLoader.TextField: drop themin/maxprops. They were never forwarded to the underlying<input>(react-aria filters them out of both the input and wrapper props), so they had no effect. Numeric constraints belong onNumberField.FileTrigger: remove the mis-named singularacceptedFileTypeprop. Its key did not match react-aria'sacceptedFileTypes, so file-type filtering silently never applied. Use the inheritedacceptedFileTypesinstead.Loader: fix theloaderTypeJSDoc, which documented a non-existentcyclevalue. The accepted values arexloaderandcircle(defaultcircle).
-
b7122c0: refactor([DST-1283]): Breaking Change — Remove
<Multiselect>(and thereact-selectdependency) from@marigold/components.Use
<TagField>instead. -
b7122c0: fix(DST-1353): remove
width="fit"from Select, ComboBox, and AutocompleteBREAKING CHANGE: The
fitvalue for thewidthprop is no longer accepted onSelect,ComboBox, andAutocomplete. These components use a popover with virtualized rendering, where the react-aria Virtualizer controls item sizing and ignores CSS layout. This caused dropdown content to be clipped whenwidth="fit"was used. Affected usages should switch to an explicit width value instead. -
b7122c0: fix(DST-1556): rename SectionMessage's controlled visibility prop from
closetoopen<SectionMessage>'s controlled visibility prop was namedclosebut its truthiness meant visible ,close={true}showed the message andclose={false}hid it, the opposite of what the name and docs implied. It's nowopen, matching the polarity used by<Dialog>,<Drawer>,<Tray>, and<Sidebar>.Before After close={isVisible}(truthy = visible)open={isVisible}(truthy = visible)close={!isDismissed}open={!isDismissed}onCloseChange={setX}(receives current value)onOpenChange={(open) => ...}(receivesfalseon dismiss)A new
defaultOpenprop (defaulttrue) sets the initial visibility in uncontrolled mode. The uncontrolled default behavior (visible, self-dismisses via the close button) is unchanged. -
b7122c0: feat(DST-1246): update Switch component layout and sizing to align with Checkbox and Radio
The Switch component previously rendered its label on the left and toggle on the right, which was inconsistent with Checkbox and Radio where the control sits on the left. When used together in forms, this created a visually misaligned layout.
Layout: Toggle now renders before the label (control on the left, label on the right), matching Checkbox and Radio. This ensures consistent visual alignment when Switch is used alongside other boolean controls in form layouts.
Sizing: Reduced the default track size from 24x40px to 16x28px and thumb from 20px to 12px. This brings the Switch closer in visual weight to Checkbox/Radio (16px), making it fit better in the flow of forms.
Settings variant: A new
variant="settings"mirrors the default layout — label and description on the left, toggle on the far right. This is the common pattern used on settings/preferences pages. The variant is propagated toBooleanFieldso that grid columns and description placement adjust accordingly.Description support: Switch now accepts a
descriptionprop (help text rendered below the control), matching Checkbox's existing support. The description text aligns with the label text using CSS grid + subgrid, automatically adapting to any control size without hardcoded padding. Properly wired witharia-describedbyfor accessibility.Form support: The
nameprop passes through to the underlying input for HTML form submission.Shared BooleanField: Extracted a reusable
BooleanFieldwrapper used by both Checkbox and Switch for consistent description rendering andaria-describedbywiring. Uses CSS grid with subgrid to align description text with label text across both components.Breaking changes
Restoring the old Switch behavior
The default Switch layout has changed: the toggle is now on the left and the label on the right (previously reversed). If you need the old layout (label left, toggle right), use the new
variant="settings":- <Switch label="Wi-Fi" /> + <Switch label="Wi-Fi" variant="settings" />
The
size="large"prop has been removed. The default size is now smaller (16x28px track). There is no built-in way to get the old large dimensions (24x40px track) — if needed, create a custom size variant in your theme'sSwitch.styles.ts.Custom theme migration
This release introduces a new required theme component
BooleanFieldand changes the layout model of theCheckboxandSwitchcontainer slots from flexbox to CSS grid. Custom themes must be updated or Checkbox/Switch will throw a runtime error.1. Add
BooleanFieldto your theme (required)BooleanFieldis a new multi-slot theme component used internally by bothCheckboxandSwitchto render descriptions. If your theme does not include it, anyCheckboxorSwitchwith adescriptionprop will throw:Error: Component "BooleanField" is missing styles in the current theme.Add the following to your theme's component styles:
import { cva } from '@marigold/system'; export const BooleanField = { container: cva({ base: 'grid gap-x-2', variants: { variant: { default: 'grid-cols-[auto_1fr]', settings: 'grid-cols-[1fr_auto]', }, }, defaultVariants: { variant: 'default' }, }), description: cva({ base: 'mt-0.5', variants: { variant: { default: 'col-start-2', settings: 'col-start-1', }, }, defaultVariants: { variant: 'default' }, }), };
container: Defines the 2-column grid layout wrapping the control and its description. Thedefaultvariant usesgrid-cols-[auto_1fr](control left, label right). Thesettingsvariant usesgrid-cols-[1fr_auto](label left, control right).description: Styles the description text wrapper. Placed under the label column viacol-start-2(default) orcol-start-1(settings).mt-0.5adds vertical spacing between the label row and description.
Then export it from your theme's component index file:
export { BooleanField } from './BooleanField.styles';
2. Update
Checkboxcontainer slot (required if customized)The
Checkboxcontainer slot changed from flexbox to CSS grid with conditional subgrid support:Before:
container: cva({ base: 'cursor-pointer read-only:cursor-default gap-2' }),
After:
container: cva({ base: [ 'grid grid-cols-[auto_1fr] gap-x-2 items-center', 'cursor-pointer read-only:cursor-default', 'group-data-[booleanfield]/booleanfield:grid-cols-subgrid group-data-[booleanfield]/booleanfield:col-span-full', ], }),
Key changes:
gap-2changed togap-x-2(column gap only, since row gap is now handled byBooleanField.description)grid grid-cols-[auto_1fr] items-centerreplaces theflex items-centerthat was previously hardcoded in the componentgroup-data-[booleanfield]/booleanfield:grid-cols-subgridandgroup-data-[booleanfield]/booleanfield:col-span-fullenable subgrid when inside aBooleanFieldwrapper, so the description aligns with the label
3. Update
Switchcontainer slot (required if customized)The
Switchcontainer slot also changed from minimal styles to CSS grid with subgrid:Before:
container: cva({ base: 'disabled:cursor-not-allowed disabled:text-disabled-foreground', }),
After:
container: cva({ base: [ 'grid gap-x-2 items-center', 'disabled:cursor-not-allowed disabled:text-disabled-foreground', 'group-data-booleanfield/booleanfield:grid-cols-subgrid group-data-booleanfield/booleanfield:col-span-full', ], variants: { variant: { default: 'grid-cols-[auto_1fr]', settings: 'grid-cols-[1fr_auto]', }, }, defaultVariants: { variant: 'default' }, }),
Key changes:
- Added
grid gap-x-2 items-center(replacesflex items-center gap-2that was previously hardcoded in the component) - Grid columns moved to
variantto support both default and settings layouts - Added subgrid support for BooleanField integration
-
b7122c0: refa([DST-1162]): Breaking changes: The
Cardcomponent has been refactored into a compound component pattern.What changed:
- The previous prop-based API (
padding,space, etc.) has been removed. - Content must now be composed using explicit sub-components:
Card.Header,Card.Body,Card.Footer, andCard.Preview. - A
CardContextis now required — sub-components will throw an error if used outside of a<Card>.
Migration:
// Before <Card> <SomeContent /> </Card> // After <Card> <Card.Header>Title</Card.Header> <Card.Body><SomeContent /></Card.Body> <Card.Footer>Actions</Card.Footer> </Card>
- The previous prop-based API (
-
b7122c0: refa([DST-1281]): Breaking change:
<Tooltip>no longer acceptsopen. Controlled visibility is only supported on<Tooltip.Trigger>(open/onOpenChange). Removes the internal React context that previously forwardedopenfrom<Tooltip>to the trigger.
Minor Changes
-
b7122c0: feat(DST-1381): add
master/adminaccess variants toLinkandMenuItemthat mark actions requiring elevated access rights with an icon (lock = master, key = admin).Badgerenders itsmaster/adminvariants with the same icons.The components render the icon as a decorative
<svg>colored by the theme's access foreground tokens (which also keeps it visible in forced-colors mode). OnLinkandMenuItemthe restriction is exposed to assistive technology through a visually hidden "Master"/"Admin" text label rendered after the visible label, so restricted links and menu items carry the access level in their accessible name.Badgerenders no extra label because its visible text already is the access level, which rules out double announcements by design.Note that
variantis a single axis: an access variant cannot be combined with another variant (e.g.destructiveonMenuItem). For destructive actions that are access-restricted, the access variant takes precedence (variant="master"), with the destructive nature conveyed by the action's label and confirmation flow. See the Admin & Master Mark pattern docs. -
b7122c0: feat(DST-1460): animated open/close caret in
Accordion.HeaderAccordion.Headernow uses the newMorphCareticon, which smoothly animates between the closed (down) and open (up) states by morphing its SVGdpath. Respectsprefers-reduced-motion. The unusedChevronDownicon has been removed. -
b7122c0: feat(DST-1551): add
DateRangePickercomponentNew
<DateRangePicker>lets users enter or select a start–end date range through a single field, mirroring<DatePicker>'s API and behaviour. Two date inputs (start/end) sit in one field group with a calendar button that opens a<RangeCalendar>in a popover on desktop and a tray on small screens. Supports per-input paste (ISO/EU/US formats),granularity(inline time segments),visibleDuration(up to three months), and the usual Marigold field props (disabled,readOnly,required,error,errorMessage,description,minValue,maxValue,dateUnavailable,width,variant,size). Adds a matchingDateRangePickertheme entry totheme-rui. -
b7122c0: feat([DST-1134]): add
<RangeCalendar>component (alpha)Adds a new
<RangeCalendar>for selecting a contiguous or non-contiguous date range, built on react-aria's<RangeCalendar>with Marigold conventions (disabled,readOnly,error,dateUnavailable,allowsNonContiguousRanges). Supports up to three side-by-side months viavisibleDuration, stacking vertically below thesmbreakpoint; the same responsive stacking now applies to multi-month<Calendar>for parity.descriptionanderrorMessageroute through<FieldBase>so the help/error UI matches the rest of the form-component family (TriangleAlert icon + HelpText container). Ships as an alpha component with a stub docs page under the form section. -
b7122c0: feat(DST-765): add
<SegmentedControl>componentAdds a new
<SegmentedControl>for compact, single-select view switching and quick filters. It is built on react-aria'sRadioGroup/RadioField/RadioButtonwith aSelectionIndicator, so it is a real form field:value/defaultValue/onChange, thenameattribute (submits like a radio group),required,error+errorMessage,description,readOnly, and validation all work exactly like the other Marigold form components (label/description/error route through<FieldBase>). The selected segment is marked by an animated indicator that slides between options.Options are declared via the compound API
SegmentedControl.Option(also exported asSegmentedControlOption), each with avalue:<SegmentedControl label="View" defaultValue="list"> <SegmentedControl.Option value="list">List</SegmentedControl.Option> <SegmentedControl.Option value="grid">Grid</SegmentedControl.Option> </SegmentedControl>
Two variants —
default(abg-controltrack with a raisedui-surfacethumb, mirroring theSwitch) andghost(track-less, with a translucent ghost-Button-style indicator for dense toolbars) — at a singledefaultsize (matching theh-controlInput height). Hover and focus reuse the sharedui-*utilities (ui-state-focus,ui-state-hover-ghost); the indicator slides between options (ease-out-quint) and respectsprefers-reduced-motion.To make segments divide the available width equally, use the standard
widthprop — e.g.width="full". There is no separatefullWidthprop.When the options exceed the available width the control scrolls horizontally instead of compressing the segments, keeping the selected option scrolled into view (reduced-motion aware). A scroll-driven edge fade signals there is more to scroll where supported, falling back to a native scrollbar otherwise.
ToggleButtonGroupnow logs a dev-only warning when used withselectionMode, steering single-select use cases towardsSegmentedControl(it remains for independent on/off actions in toolbars). -
b7122c0: feat([DST-1429]):
Cardnow exposes aPanel-aligned padding API.What changed:
Cardacceptsp/px/pyprops (mutually exclusivepvspx+py), resolving to CSS custom properties--card-pxand--card-pyon the container. Defaults tosquare-regular.- A new
spaceprop controls the gap between slots, resolving to--card-gap. Defaults toregular. Card.BodyandCard.Footeraccept an opt-inbleedprop to skip horizontal padding for tables, media, or full-width action bars.- Internally,
Cardswitched from CSS grid withgrid-template-areasto a flex column withgap-y. JSX order now determines visual order — placeCard.Previewfirst when used. - Slot theme styles (
header,body,footer) no longer hardcodepx-4/py-*; padding lives in the component layer and is driven by the CSS variables above. Card.Previewautomatically escapes the container's vertical padding when used as the first or last child via negative margins.
Why:
Cards previously had no consumer-controllable padding API and no default padding on the container — content rendered as direct children of
<Card>was visually broken. The new API mirrorsPanel's padding model so the two surfaces behave consistently.Migration:
- Wrap bare children in
<Card.Body>. Bare children inside<Card>are no longer rendered with horizontal padding; this matchesPanel's composition contract. - If you used
Card.Previewfor media at the top, keep doing so — it stays edge-to-edge. - No changes needed for the canonical composition (
Preview+Header+Body+Footer).
-
b7122c0: feat(DST-1373): adopt the slot-configuration pattern in
CardCard.Headeris now a slot provider: drop a<Title>and an optional<Description>directly inside it and the header wires up the heading level, id, accessible name, and theme classes automatically. A bare<Title>placed directly inside<Card>(noCard.Headerwrapper) is also picked up by the root, so title-only cards can skip the header and still get the right padding andaria-labelledbywiring.<Card>itself now renders an<article>landmark and is automatically labelled by its<Title>viaaria-labelledby, or by an explicitaria-label. A newheadingLevelprop (default3) controls the underlying heading tag for the document outline.The theme
Cardslot map gainstitleanddescriptionentries — the typography previously carried on theheaderslot has moved totitle. Variant text color now flows through a new--card-accentCSS custom property, somasterandadmincards pick up the matching accent automatically. Raw<Stack>/<Headline>composition insideCard.Headerstill renders but does not pick up the slot wiring; prefer<Title>/<Description>going forward. -
b7122c0: feat(DST-876): add Card usage guidelines
Renames the
Card.Previewslot toCard.Mediaacross components, theme, and docs. This is a breaking change: consumers using<Card.Preview>, thedata-card-previewselector, or thepreviewtheme slot key must migrate toCard.Media,data-card-media, and themediaslot key respectively.Adds a "Usage" section to the Card docs covering when to use cards, media slot guidance.
-
0e2c676: fix(DST-1680): keep the mobile
ComboBox/Autocompletetray in the accessibility tree.On small screens both components render a
Trayinstead of RAC'sPopover. When the listbox opened,useComboBoxcalledariaHideOutside([inputRef.current, popoverRef.current].filter(el => el != null))— and because nothing claimedPopoverContextand the tray (with its input) portals in a later commit, both refs werenull.ariaHideOutsidehides everything outside the elements it is given, so an empty list hid the whole document, including the tray itself: screen readers could not reach the tray's dialog, search input or options at all.popoverRefnow points at the tray — the dialog element while open, and the container the tray portals into while closed — so the hide pass keeps the tray visible. Background isolation is unchanged; it comes from the tray's ownModal, which runsariaHideOutside(..., { shouldUseInert: true }). As a side effectuseComboBox's blur handling works again, since it checks whether focus moved intopopoverRef.currentbefore closing.Traynow also accepts arefto its dialog element. -
a9fdcff: feat(DST-944): add
addUndoToast, gateFileFieldremovals withonBeforeRemove, and make destructive confirmations settle reliably.useToastgainsaddUndoToast({ title, onUndo, onCommit }), which reports a destructive action as done and sends the real request only if the user does not take it back. It owns the parts that are easy to get wrong, so a caller cannot lose a deletion to a drifting timer, an unguarded commit, or a toast that never closes. The Destructive Actions pattern explains each one.addToastnow acceptsonClose, called when the toast closes for any reason: its timeout ran out, it was dismissed, it was closed throughremoveToast, or the queue was cleared. It also acceptscloseButton: false, which a toast that never auto-dismisses (warning,error,timeout: 0) ignores, since that would leave no way out of it.<FileField>acceptsonBeforeRemove, called with the file a remove button is about to drop. Returnfalse, or a promise resolving tofalse, to keep it, which is what puts a confirmation in front of the built-in remove buttons. A handler that throws keeps the file.Each
FileFieldremove button is now named after its file ("Remove agb-2026-08-01.pdf") instead of all announcing "Remove file", so the rows of a document list are distinguishable to a screen reader.FileField.ItemtakesremoveLabelto set that name when you compose items yourself.Dialog.Triggerno longer blocks Escape by default. An unsetkeyboardDismissablewas inverted into "keyboard dismiss disabled", so every dialog opened from a trigger trapped the keyboard unless the prop was passed explicitly, contradicting both the documentation and the default inTrayandDrawer. PasskeyboardDismissable={false}to opt out. This is a behaviour change for anyone who relied on the previous default.ConfirmationDialoganduseConfirmationgain four fixes around the same flow:- Closing a confirmation without pressing a button (Escape) now resolves
useConfirmationascancelled. Previously the promise never settled, so anawait confirm(...)silently stalled and its continuation never ran. ConfirmationDialogfocuses the cancel button by default whenvariant="destructive", so a reflexive Enter takes the safe path. PassautoFocusButton: 'action'to focus the confirm button instead.ConfirmationProvidernow forwards theautoFocusButtonfrom aconfirm()config, which it accepted but dropped, and its confirm button falls back to the localizedconfirmmessage rather than a hardcoded "Confirm" next to a translated "Cancel".ConfirmationDialogProps['variant']is typed as'destructive' | (string & {})so the variant that drives this behaviour is discoverable, rather than an untyped string.
The action buttons call their handler before closing the dialog so that an owner watching
onOpenChangesees the decision before it sees the close. This is a behaviour change for anyone relying on the previous ordering. They are still called with no arguments.See the new Destructive Actions pattern for when to confirm and when to offer undo.
- Closing a confirmation without pressing a button (Escape) now resolves
-
b7122c0: feat(DST-1369): adopt the slot-configuration pattern in
Dialog,Drawer, andTrayThe three overlay components now follow the same slot-configuration pattern as
PanelandCard. Each publishes the slot contexts at its root, so the title, description, and action primitives pick up the overlay's theme classes wherever they are dropped:Dialog.Title/Drawer.Title/Tray.Titleare thin wrappers over<Title slot="title">.- New
Dialog.Description/Drawer.Description/Tray.Descriptionwrap<Description slot="description">. - New
Dialog.Header/Drawer.Header/Tray.Headerare optional layout wrappers that group a title and description. A bare<Title slot="title">(or<*.Title>) without a header is a first-class, accessible authoring form —aria-labelledbyresolves to it automatically.
The compound-component API is unchanged. The
<header>element that previously wrapped the title is gone; the title now carries the header chrome directly, with no change to the rendered visuals. -
b7122c0: feat(DST-1635): add a
bleedprop toDrawer.Contentso edge-aware children can span the full Drawer width.What changed:
<Drawer.Content bleed>drops the Drawer's horizontal content padding and publishes a--bleed-pxcustom property, mirroringPanel.Content'sbleed.- The horizontal padding shared by the sectioned overlay surfaces (
ui-panel-header/ui-panel-content/ui-panel-actions) now comes from a single--ui-panel-pxtoken, and a bledDrawer.Contentre-publishes that exact token as--bleed-pxso the two can't drift. - Edge-aware children stay aligned with the Drawer title while their dividers/backgrounds reach the Drawer edges:
Accordionreads--bleed-pxdirectly, andTable's edge-cell padding now falls back to--bleed-px(after the Panel-only--panel-px), so it aligns inside a bled Drawer too.
Why:
Placing an
<Accordion>(orTable) inside<Drawer.Content>previously trapped it inside the content padding, so item dividers and hover/selection backgrounds could not reach the Drawer edges.bleedgives the same full-width alignmentPanel.Content bleedalready offered.Impact:
- Default behavior is unchanged: without
bleed,Drawer.Contentkeeps the paddedui-panel-contentand--bleed-pxstays unset (children resolve their inset to0px). The--ui-panel-pxtoken resolves to the same24pxthe surfaces used before, so Dialog/Drawer/Tray are visually identical.
-
b7122c0: feat(DST-1282): scroll the Tabs row horizontally when it overflows
When more tabs are rendered than fit the available width,
Tabs.Listnow scrolls
horizontally instead of wrapping onto multiple lines or pushing the page wide. Tabs
keep their natural width (shrink-0) and snap gently into place (proximity) as you
scroll, with the adjacent tab kept peeking past the edge so the scrollability stays
discoverable. A vertical mouse wheel scrolls the row horizontally (pointer users
without a trackpad), without hijacking normal page scroll. Horizontal overscroll is
contained so it does not trigger browser back/forward gestures, and scrolling is
smooth for users who allow motion. On browsers that support scroll-driven animations
the overflowing edges fade out (ui-scroll-mask-x); elsewhere it falls back to a
plain scroll container. When all tabs fit, nothing changes visually.The sliding selection indicator stays correct while react-aria scrolls an off-screen
tab into view (the scroll container is alayoutScrollmotion element). No runtime
API change.Breaking change (
@marigold/system): theTabsthemeRecordgains a new
requiredtabsListScrollslot. It is deliberately required so a theme cannot ship
tabsList(whosew-maxtriggers the overflow) without the scroll container that
makes it behave. Custom themes that define aTabsblock must add atabsListScroll
entry to type-check. -
b7122c0: feat(DST-1480): forward arbitrary HTML attributes on
<Panel><Panel>now extendsHTMLAttributes<HTMLElement>(minusclassName/style) and spreads the remaining props onto its root<section>, matching the<Card>API. Consumers can now passid,data-*, event handlers, and other standard attributes directly to a Panel.A consumer-supplied
aria-labelledbyis preserved instead of being overwritten withundefinedwhen no<Title>is present — the slot-ownedtitleIdstill wins when a<Title>renders. This mirrors the fallback adopted byCardin DST-1373. -
b7122c0: feat(DST-1483): remove ActionButton in favor of a slot-aware Button (rename ActionGroup → ButtonGroup)
The beta-only
<ActionButton>is removed.<Button>is now slot-aware: it adapts
automatically inside a button container, so you write<Button>everywhere instead
of learning a second button component.<ActionButton>is removed. Use<Button>; it adapts inside<ButtonGroup>and
<Panel.Header>. Opt a button out of the cascade withslot={null}.<ActionGroup>is renamed to<ButtonGroup>, mirroring the existing
ToggleButtonGroup → ToggleButtonContext → ToggleButtontrio.- A single Marigold-owned
ButtonContextdrives the cascade (replaces
ActionButtonContext+ActionGroupContext). RAC's ownButtonContext
(close/increment/decrementslots) is untouched. - Uniform precedence: a local prop (
variant,size,disabled) always wins
over the container. This drops the formerActionGroupsize-group-wins outlier. <ButtonGroup>cascadesvariant: 'secondary'when unset, the same baseline
as a standalone<Button>. Slot-aware parents override it where they want
lower emphasis:<Panel.Header>cascadesvariant: 'ghost'+size: 'small',
so a labelled header action stays readable. An icon-only action (a bare-icon
<Button>, an<ActionMenu>kebab) setssize="icon"to render as a square.<ButtonGroup>now owns a structuralflex gap-1layout (orientation-aware), so
a standalone cluster is spaced correctly —<ActionGroup>had no layout of its
own. A container's positional className (e.g. Panel's[grid-area:actions]) still
rides along and positions the group.- Overlays (
Popover,Modal,Tray,Drawer) resetButtonContextat their
content root, so a header/group cascade can't leak through the portal into an
overlay'sslot="close"orDialog.Actionsbuttons. <SelectList.Option>cascadesvariant: 'ghost'to a nested<Button>,
<LinkButton>, or<ActionMenu>, so a trailing in-row action reads as
low-emphasis chrome without an explicitvariant.
Migration
<ActionButton>→<Button>(itsdefaultvariant maps tovariant="ghost").<ActionGroup>→<ButtonGroup>.ActionButtonContext/ActionGroupContext→ButtonContext.<ActionMenu>keeps its public name. Its trigger is now a slot-aware<Button>
that inherits the cascade instead of hardcoding a variant: it renderssecondary
on its own (the standalone<Button>baseline, matching the pre-unification look)
andghostinside<Panel.Header>,<SelectList.Option>, or a<ButtonGroup>.
Avariantset on the<ActionMenu>still wins.
-
b7122c0: feat(DST-1492): add
usePageFocusto move focus to the page<h1>on route changeOn client-side navigation focus lingers on the clicked link or falls back to
<body>, so screen-reader and keyboard users get no signal that the screen changed.usePageFocusimplements the standard SPA fix: given the current route key (typically the pathname), it makes the page<h1>programmatically focusable (tabIndex={-1}) and focuses it on each change.The first render is skipped so the initial load never steals focus, and it is a no-op on a page with no
<h1>(anaria-label-only<Page>).<Page>stays router-agnostic: the route signal comes from the app router (RouterProvider). Call the hook from a component that persists across navigations (the layout / shell level), since the skip-first-render guard is per mount.import { Page, usePageFocus } from '@marigold/components'; const PageFocus = ({ pathname }: { pathname: string }) => { usePageFocus(pathname); return null; }; <Page> <PageFocus pathname={location.pathname} /> <Page.Header> <Title>Billing</Title> </Page.Header> {/* … */} </Page>;
-
b7122c0: feat(DST-1509): add
collapseAttoTag.GroupTag.Groupnow accepts acollapseAt={n}prop: the firstntags render as usual, and the rest collapse behind a "Show N more" / "Show N less" toggle, matching the behavior already available onCheckbox.GroupandRadio.Group.Unlike those groups,
Tag.Group's tags are a RAC collection (selection, removal, keyboard navigation), so collapsed tags stay mounted inside the<TagList>and are hidden via the nativehiddenattribute rather than being pulled out into a separate<Collapsible>. This keepsonRemove,removeAll, andemptyStateworking unchanged, and the collapsed count automatically shrinks as tags are removed. If a hidden tag is part of the initial selection, the group expands automatically.collapseAtonly applies to static children; dynamic collections (items+ a render function) are unaffected.When both
collapseAtandremoveAllare used together, "Show N more" bottom-aligns next to the last visible tag, while "Remove all" stays in its own trailing column so it never mixes into the tag wrap. -
b7122c0: Add relative date presets to
Calendar,RangeCalendar,DatePicker, andDateRangePickervia a newpresetsprop. On desktop the presets render as a quick-selection list beside the calendar. On small screens the grid renders first with a "Quick selection" row: inline calendars open the preset list in a bottom sheet, while the pickers switch their existing sheet to the list in place. Ships built-in localized presets (today,yesterday,tomorrow,this-week,next-7-days,next-30-days,last-7-days,last-30-days,this-month,this-quarter), supports custom presets with value resolvers, and exportsuseDatePresets/useDateRangePresetsfor userland compositions. -
b7122c0: feat(DST-1533): add
Table.FootercomponentNew
<Table.Footer>renders a semantic<tfoot>after<Table.Body>for summary rows like totals, counts, or averages, composed from<Table.Row>and<Table.Cell>just like the body. Supports astickyprop that pins the footer to the bottom of the viewport while scrolling, mirroring sticky table headers. Adds a matchingfootertheme entry totheme-rui. -
b7122c0: feat(DST-1568): add
erroranderrorMessagesupport toSwitchandCheckboxWhen
erroris set, the field is marked invalid and theerrorMessageis shown in place of thedescription, wired to the input viaaria-describedby. When both are unset, rendering is unchanged.Internally,
SwitchandCheckboxnow render theirHelpTextinside the RAC field (SwitchField/CheckboxField), relying on RAC's nativearia-describedbyandFieldErrorContextwiring instead of re-plumbing it by hand. No other public API change. -
b7122c0: feat(DST-1609): two-level sidebar navigation with
Sidebar.RailandSidebar.RailItemAdds a two-level navigation mode to the sidebar: a persistent rail of icon-first
top-level destinations next to a panel showing the active section's sub-navigation.
ASidebar.RailItemwrapping aSidebar.Navis a section that shows a panel. One with
only anhrefis a direct link, and one insideSidebar.Footerpins to the bottom of the
rail. Itsactiveprop overrides href matching for pages the URL can't identify.
Collapsing (toggle or Cmd/Ctrl+B) hides the panel while the rail narrows to an icon
strip, so top-level navigation always stays available. On small screens the rail
renders as the same single-column drawer as the plain sidebar: sections drill in
(opened at the active section) and links close the drawer.<AppShell>switches to a full-width top bar automatically when a rail is present
(pure CSS via:has()), so the brand never moves when the panel collapses.Sidebar.Togglegainsvariant="rail"for its top-bar placement between the
brand and the breadcrumbs.- New theme tokens:
--spacing-topbar(the shell's shared vertical datum for the top bar
height, sidebar brand row, and rail sticky offset), the rail column widths
--spacing-rail/--spacing-rail-collapsed/--spacing-rail-panel, and
--spacing-touch-target(44px minimum row height on small screens, shared by the
drawer's nav rows and the existing Tray-mode ListBox/Menu options). - Idle single-column nav labels darken a step (new
--color-secondary-boldtoken,
charcoal-700) so they clearly out-rank the quiet group-label captions. - Keyboard: the rail supports arrow-key (and Home/End) movement on top of its flat
tab order, and the section panel's tab stop re-syncs to the current page when the
route changes, so Tab re-enters at the active item. - The
TopNavigationbottom edge is now an always-on border. The non-reusable
ui-scroll-edgeandui-sidebar-seam-headerutilities are removed, so the sticky
bar and the sidebar header carry a plain border instead. - The
AppShellheader row is now sizedauto(was a fixed3.5rem), so a shell
without aTopNavigationno longer reserves an empty header band, so the row
collapses to the height of its content. - The shell's viewport-height claims (
AppShellgrid, sidebar and rail asides) read
the new--ui-viewport-heightcustom property with a100dvhfallback. Set it on a
wrapper to render the shell inside a bounded container (embedded previews, demos)
instead of the browser viewport. Nothing changes when it is unset.
-
b7122c0: fix(DST-1621): keep
AccordionstickyHeaderpinned when the header has actionsAccordion.Headernow accepts anactionsprop for content shown next to the title, such as buttons. Previously actions were added by wrappingAccordion.Headerin a layout component, which shrank the sticky header's containing block to the header row sostickyHeadercould no longer pin the header while scrolling. Passing actions throughactionskeeps the sticky wrapper a direct child of the item, so the header stays pinned together with its actions. -
b7122c0: feat(DST-1634): standardize
Inputtrailing-action alignment and makesize="icon"a publicButtonAPI.The
Inputleading icon is clamped to 16px so it no longer overlaps the placeholder. Every trailing action (clear button, chevron, loading spinner, or a custom icon button) now sits in a control-sized centered box flush to the edge, so its icon aligns at the same inset as the leading icon acrossInput,SearchField,ComboBox,Autocomplete,TagField, andDatePicker.Button'ssize="icon"is now public and documented as the way to build an icon button, composing with anyvariant(for examplevariant="ghost" size="icon").Migration:
variant="icon"was never a real Button variant. It silently rendered a default button, so any usage from older docs should switch tosize="icon"(with a variant, for examplevariant="ghost" size="icon"). -
b7122c0: feat(DST-1643): add a
fullscreensize toDialog<Dialog size="fullscreen">fills the viewport (minus a small margin) at every breakpoint, giving content-heavy picks room for search, filters, and a long scrollable list while the title and actions stay fixed. The existingxsmall/small/medium/largesizes are unchanged. -
b7122c0: feat([DST-901]): styleProps for
width,maxWidth,height,space,spaceX,spaceY,pr,pl,pt,pbnow accept both numeric scale values (4) and their string equivalents ("4"). The public types are now declarative (Scale | Fraction | WidthKeyword, etc.) instead of being derived from the internal class-name maps.Components that previously resolved
width,maxWidth, andheightvia class-name lookup (Form, Calendar, legacy Table column header / select-all cell, Slider, Scrollable, Switch, Grid) now resolve them through CSS custom properties (createWidthVar/createHeightVar) targeting--width,--max-width,--height. Those variables — along with--container-widthand--field-widthalready used byFieldBase— are registered as non-inheriting (@property … inherits: false) in the RUI theme so they cannot leak into descendants.createWidthVargained support for the previously missing keywords (svh,lvh,dvh,px,container), and a newcreateHeightVarhelper was added. Both share a common factory and a base keyword set, so they remain trivially in sync.The runtime class-name maps
width,maxWidth,height,gapSpace,paddingSpace,paddingSpaceX,paddingSpaceY,paddingRight,paddingLeft,paddingTop,paddingBottomare no longer exported from@marigold/system. These were internal utilities consumed only by@marigold/components. Use the prop types (WidthProp,HeightProp, …) and the CSS-var helpers (createWidthVar,createHeightVar,createSpacingVar) instead. The corresponding TypeScript prop types are unchanged. -
b7122c0: feat(DST-990): enrich
<Menu>with selection visuals, keyboard shortcuts, and dividers<Menu>gains richer building blocks for advanced menus:- Selected-item visuals. In
selectionMode="single"or"multiple", items show a leading checkmark and a highlighted row, aligned like<ListBox>. Command menus (noselectionMode) render exactly as before. - Keyboard-shortcut hints via a new shared
<Keyboard>primitive (a sibling to<TextValue>and<Description>). It renders a<kbd>key-cap on its own and adapts to its container, so inside aMenu.Itemit becomes a muted, right-aligned hint wired to react-aria'saria-describedby. - Dividers. Drop the shared
<Divider>between<Menu.Item>s to separate groups with arole="separator"line.
Breaking (
@marigold/system): theMenurecord in theThemetype now requires akeyboardkey. Custom themes implementingMenumust add it to keep compiling. All@marigold/componentsadditions are backward compatible. - Selected-item visuals. In
-
b7122c0: refa([DST-1298]): Refactor Divider component: API, styling, and docs
We fixed the vertical orientation of the divider, which previously didn't work.
Added new Divider stories and updated the Divider docs. -
b7122c0: feat(DST-1641): add
ErrorState, the error sibling ofEmptyState: same anatomy (title,description,action,headingLevel), plus typed DOM passthrough (role,tabIndex,ref) for error-boundary fallbacks. -
b7122c0: feat([DST-761]): export
useLandmarkfrom@marigold/components.Marigold now re-exports React Aria's
useLandmarkhook (and itsAriaLandmarkRole/AriaLandmarkPropstypes) so consumers can register custom regions as ARIA landmarks without adding@react-aria/landmarkas a direct dependency.import { useRef } from 'react'; import { useLandmark } from '@marigold/components'; const ref = useRef<HTMLElement>(null); const { landmarkProps } = useLandmark( { role: 'search', 'aria-label': 'Site search' }, ref );
A new accessibility guide on landmarks and a dedicated
useLandmarkreference page have been added to the documentation. -
b7122c0: feat(DST-1587): share wheel-to-horizontal-scroll between
TabsandSegmentedControlThe wheel handler that lets pointer users without a trackpad reach overflowing
Tabsis now a shareduseWheelScrollXhook, andSegmentedControladopts it too. Wheeling vertically over an overflowingSegmentedControltrack now scrolls it horizontally, matchingTabs. No public API change on either component. -
b7122c0: fix([DST-1363]): make
<TagGroup>errorMessagerender and bridge it to form validation<TagGroup>acceptederrorMessagevia<FieldBase>but never rendered it because RAC'sTagGroupdoes not populateFieldErrorContext. The internal<HelpText>short-circuits on a missing context, so the error path was a silent no-op and<Form>-level validation never reached the user.Bridges
<TagGroup>to validation the same way<SelectList>does:useFormValidationState+useFormValidationon a hidden<select>(replacing the previous<input type="checkbox">shim), with<FormContext>inheritance forvalidationBehavior. The shared hidden control lives atHiddenSelection/and is now consumed by both<SelectList>and<TagGroup>so future fixes only happen once.New props on
<TagGroup>:error,required,disabled,validate,validationBehavior,form. Public API normalised to Marigold's convention —isInvalid/isRequired/isDisabledare removed andonSelectionChangeis renamed toonChange.selectionModenow defaults to'multiple'.disablednow propagates to each<Tag>via context so interaction is blocked alongside the form-disabled state. -
b7122c0: feat(DST-1257): add universal
nonespacing token- Introduce
NoSpacingToken = 'none'shared across all spacing token families - Add
'none'toSpacingTokens,PaddingSpacingTokens, andInsetSpacingTokens - Add
--spacing-none: --spacing(0)CSS custom property to the theme
'none'now works wherever a spacing token is accepted:Stack/Inlinegap (space="none"),Insetaxis padding (spaceX="none"/spaceY="none"), andInsetrecipes (space="none") — useful for wrappers that should render without adding any spacing (e.g. an edge-to-edgeTableinside a containing component). - Introduce
-
b7122c0: feat(DST-1646): add
OverflowRegion, a single-row layout primitive that hides trailing items instead of wrapping when horizontal space runs out, and restores them as space returns. Hidden items stay mounted (state preserved) but are removed from paint, tab order, and the accessibility tree. Like the other layout primitives it has no theme layer: space its items with thespaceprop (inherited from a parentInline/Stackwhen unset, likeInline), label it with the forwarded region props (role,aria-label, ...), and pair it with theindicatorrender prop or theonOverflowChangecallback so hidden items stay reachable through another surface, e.g. quick filters demoting into a filter panel. -
b7122c0: fix(DST-1573): default
<Page>and<Page.Content>spacing toregularThe
spaceprop on<Page>and<Page.Content>— which controls the vertical rhythm between a page's children (the<Page.Header>and the<Panel>s/sections below it) — now defaults toregular(spacing(6), 24px) instead ofgroup(spacing(12), 48px). The previous default produced too much whitespace between sections. Consumers can opt back into the larger gap withspace="group". -
b7122c0: feat(DST-1326): introduce
Panel.CollapsibleHeader,Panel.CollapsibleTitle, andPanel.CollapsibleDescription. The collapsible mirrorsPanel.Header— a header wrapper with a title plus an optional description — and the whole visual surface is a single click target: title and description render as spans inside the trigger<button>, with the accessible name wired viaaria-labelledbyand the description viaaria-describedby. The chevron icon uses a reusableMorphCaretthat animates via SVG path morphing (honoursprefers-reduced-motion). -
bb23186: feat(DST-1700):
--bleed-pxis the documented way to align content with a container's padding, and the overlay surface anatomy no longer borrows Panel's name.What changed:
- The
ui-panel-header/ui-panel-content/ui-panel-actionsutilities and their--ui-panel-pxtoken are nowui-surface-header/ui-surface-content/ui-surface-actionsand--ui-surface-px. They style the shared anatomy of sectioned overlay surfaces (Dialog,Drawer,Tray,Sidebar) — never thePanelcomponent, which has its own per-instance--panel-px. They are now named afterui-surface, the role those containers already wear. Tablederives its edge cell padding from--bleed-pxalone:var(--bleed-px, var(--cell-x-padding)). It previously readvar(--panel-px, var(--bleed-px, var(--cell-x-padding))).- A bled
Card.ContentandCard.Footernow publish--bleed-px(set to the Card's--card-px), which they never did. - Every non-bled content area (
Panel.Content,Panel.CollapsibleContent,Card.Content,Card.Footer,Drawer.Content) clears--bleed-pxwithinitial. Custom properties inherit through the whole subtree, so without the reset a container nested inside a bled one would read the outer container's padding and inset its edge-aware children twice. --panel-px/--panel-py/--panel-gap, their--card-*and--page-*counterparts, and--bleed-pxare documented as read-only public API under Reading container spacing.
Why:
--panel-pxis set on thePanelroot, so it inherits into the whole subtree whether content is bled or not. Reading it first meant the chain always resolved at step one inside a Panel and the remaining branches were unreachable: aTablein a non-bledPanel.Contentwas inset by the Panel's padding on top of the content area's own padding, putting its edge cells 12px from the border while the Panel title sat at 12px — offset twice. Every bleedable container already publishes--bleed-px, and because that is declared on the bled element rather than on a container root — and cleared again on every non-bled one — it is only in effect where edge alignment actually applies.Impact:
- A
Tablein a bledPanel.Content,Panel.CollapsibleContentorDrawer.Contentis unchanged. - A
Tablein a non-bledPanel.Contentnow uses the ordinary cell padding for its first and last cell instead of the Panel's horizontal padding. Docs steer tables tobleed, so most tables are unaffected. TableandAccordioninside a bledCard.ContentorCard.Footernow align with the container title, where previously they got no edge alignment at all.- A
TableorAccordionin a non-bled container nested inside a bled one no longer picks up the outer container's padding. Dialog,Drawer,TrayandSidebarare visually unchanged. If you override--ui-panel-pxor apply theui-panel-*utilities directly, rename them; the token was undocumented, so nothing else in the public API moves. It also only ever existed in18.0.0-rc.*, never in a stable release, so only prerelease consumers are affected.
- The
-
b7122c0: feat([DST-753]):
SectionMessageexposes anannounceprop and uses react-aria'sLiveAnnouncerto notify assistive technology.What changed (DST-753):
<SectionMessage>accepts a newannounce?: booleanprop. When set, the message text is sent to a shared, always-mounted live region maintained by@react-aria/live-announcer. Priority ispoliteforinfo/success/warningandassertiveforerror.announcedefaults totrueforvariant="error"andfalsefor all other variants, preserving today's behavior for the common error case while letting consumers opt in for confirmations and informational updates.- The wrapper element no longer carries
role="alert"for the error variant. Announcements are now delegated to the singleton live announcer instead. - Re-announcing the same message uses the React
keypattern: pass a changingkeyto force a remount.
Why:
The previous implementation only announced the
errorvariant, and it did so by addingrole="alert"to a conditionally rendered element. Per the WAI-ARIA spec and MDN guidance,role="alert"should be on an element that already exists in the DOM before its content is injected, and it should not contain interactive elements. Marigold'sSectionMessageviolated both constraints (the alert was mounted together with its content, and it can contain close buttons and action links), making announcements unreliable on some screen reader / browser combinations.The new implementation uses
@react-aria/live-announcer, which maintains persistent polite and assertive live regions at the document root. This is the same mechanism used across React Spectrum and avoids the conditional-rendering and interactive-content pitfalls of inlinerole="alert". It also unifies the API: opt in to announcement for any variant with a single prop.Additional cleanup bundled with this release (beyond DST-753):
- Close button now matches the rest of the system. The previous theme defined a
closeslot forSectionMessagewith bespoke overrides (size-8,[&_svg]:size-6,text-foreground, negative margins) that produced a visibly larger close button than every other close button in the design system. The component now renders the shared<CloseButton>with no overrides, so it gets the same 16px icon, focus ring, hover-opacity, and rounded-full styling as Dialog, Drawer, etc. Thecloseslot has been removed from theSectionMessagetheme type. - Component cleanup. Dropped a stale
useButton(props, buttonRef)call that was applying div-level props to a button, the unusedbuttonRef, and the{...buttonProps}spread on<CloseButton>. TheButtoninsideCloseButtonalready provides all keyboard/press semantics. - Theme variant order normalized.
info(the default) is now listed first across thecontainer,content, andiconslots intheme-rui, matching the variant table in the docs and the existingdefaultVariantssetting.
Docs:
- New anatomy SVG matching the Card / Sidebar / SelectList style; title and close button marked as optional, with content rules (no period in title, don't repeat title in body).
- Two realistic announcement demos: a bulk-archive form (polite, with RAC
validateand thekeyre-announce pattern) and a server-availability save error (assertive). - Added focus-management guidance for dynamic appearance and post-dismiss.
- Added form-summary placement rule pairing
<SectionMessage>with field-level validation. - Added action constraints (one primary action, verb+noun labels, descriptive link text).
- Added two-line body rule with a link-out overflow pattern for longer content.
- Folded the previous Position subsection into Usage; removed redundant Do/Don't tiles; renamed subsections to Dismissal / Actions / Announcements.
- Drive-by: typo fix in the
feedback-messagespattern doc.
Migration:
- Code relying on
getByRole('alert')or[role="alert"]selectors to find renderedSectionMessageerror nodes needs to be updated. The message text itself is still rendered as before; only the wrapper role is gone. - Consumers who previously wrapped a dynamic
<SectionMessage>in their own<div role="status">or<div aria-live="polite">can replace that wrapper with<SectionMessage announce>. - Custom themes that defined a
SectionMessage.closeslot will now see a type error. Remove the slot. Close button styling now flows entirely from theCloseButtontheme. - The SectionMessage's close button is visually smaller after this release (matches every other close button in Marigold). If you previously relied on the larger size, that was an inconsistency, not a feature.
-
b7122c0: feat(SelectList): standardized API, item layout, and visual distinction from ListBox (DST-1076)
<SelectList>has been refined into a first-class form field for picking one or many items from a visible list of rich two-line rows. This release contains breaking renames and a tightened type surface.Breaking changes
SelectList.Item→SelectList.Option. The option semantic matchesSelect.Optionand the HTML<option>mental model. Update any<SelectList.Item>usage to<SelectList.Option>.SelectList.Actionhas been removed. Drop your<ActionMenu>or<IconButton>directly inside<SelectList.Option>— the component positions, sizes, and styles the nested control automatically viaButtonContext. Limit: one action per option (multi-button groups will arrive with a futureActionGroup).- Leading-image slot has been removed. Compose images inside
<Text slot="label">(or anywhere in children) as you see fit. selectionMode="none"is no longer accepted.SelectListis a form field; the default is now"single".onChangeis strictly typed perselectionMode:(key: Key | null) => voidfor single,(keys: Key[]) => voidfor multiple. The shape matchesSelect<T, M>. PassingsetStatedirectly may require adapting the callback.
Other changes
- Selection indicator — single-select rows render a visible radio circle; multi-select renders a checkbox.
- Label & description slots — use
<Text slot="label">and<Text slot="description">inside<SelectList.Option>. The row skeleton isselection · label + description · action (optional). - Dev-mode warning when
textValueis missing on an option whose children aren't a plain string. - Own theme entry —
SelectListships a dedicated theme component. The theme exposes first-classlabel,description, andactionentries; slot styling no longer uses descendant selectors. Consumers with custom themes must add or update aSelectListentry.
Documentation
The SelectList docs page is rewritten around the new API. Adds an anatomy diagram, a decision table for choosing between
<SelectList>and lighter controls (<Radio.Group>,<Checkbox.Group>,<Select>,<Combobox>,<TagField>), and dedicated sections for multi-selection, per-row actions (decision-help and configuration patterns), horizontal orientation, and empty state. Replaces selected prose with Do/Don't tiles. Tightens the accessibility section to what's specific to SelectList (keyboard model, label requirement,textValuefor rich rows).Migration
- <SelectList selectionMode="none"> - <SelectList.Item id="free"> - <SelectList.Action> - <IconButton aria-label="Info"><Info /></IconButton> - </SelectList.Action> - Free - </SelectList.Item> - </SelectList> + <SelectList selectionMode="single"> + <SelectList.Option id="free"> + Free + <IconButton aria-label="Info"><Info /></IconButton> + </SelectList.Option> + </SelectList>
-
b7122c0: feat(DST-1513):
<Select>'srenderValuenow receives a seconddetailsargument with the selectioncount, and works when options are provided as static<Select.Option>children (previously it was silently skipped unless the options came from theitemsprop).This makes it easy to summarise a multi-select trigger instead of listing every value, e.g.
renderValue={(items, { count }) => `${count} selected`}. The default trigger rendering is unchanged: withoutrenderValue, a multi-select still lists the selected values. -
b7122c0: feat(DST-1404): add
renderValueprop to<Select>for custom trigger rendering. When provided, the callback receives the selected items and replaces the default trigger render. Useful when the trigger should look different from the option (e.g. avatar plus name in the trigger, avatar plus name plus role in the dropdown). The placeholder still renders when nothing is selected. -
b7122c0: feat(DST-1322): add
currentprop toSidebar.Navfor automatic active item detectionSidebar.Navnow accepts acurrentprop that resolves the active leaf automatically — pass the current pathname (string) for smart segment-aware matching, or a predicate(href, key) => booleanfor full control. Removes the per-itemactive={pathname === '/...'}boilerplate. The per-itemactiveprop onSidebar.Itemstill works as a local override. -
b7122c0: feat(DST-1370): migrate
ContextualHelp,SectionMessage, andEmptyStateto the slot-configuration pattern-
SectionMessage.Titlenow renders a semantic heading (<h3>by default) instead of a<div>, fixing an a11y gap. The level is configurable via the newheadingLevelprop on<SectionMessage>. When a title is present, the container becomes arole="group"labelled by the title viaaria-labelledby. -
New
<SectionMessage.Description>sub-component for a short summary between title and content. -
ContextualHelp.Titlenow usesslot="title", so the popover dialog gets a properaria-labelledby. The title tag changes from<h3>to<h2>(same asDialog.Title); visual appearance is unchanged. -
New
<ContextualHelp.Description>sub-component. -
The
Themetype now requires adescriptionkey on theSectionMessageandContextualHelpstyle records; themes defining styles for these components must add it. -
EmptyState'stitlenow renders as a semantic heading (<h3>by default, configurable via the newheadingLevelprop), and itsdescriptionrenders through the<Description>primitive (same DOM as before, now sitting 4px below the title to match the description rhythm of the other components). The flat-props API is unchanged. -
All three roots now also publish a
ButtonContext, completing the slot-configuration set. It scopes action buttons (e.g. those placed inSectionMessage.Content, theEmptyStateaction, orContextualHelpcontent) to a clean baseline so they never inherit a surrounding container's button cascade (such as aPanel.Header's ghost/small look). No variant or positioning is imposed, so existing usage renders unchanged.
-
-
b7122c0: feat(DST-1098): persistent idle sort indicator on sortable columns
Table.ColumnwithallowsSortingnow shows a Lucidearrow-down-upicon when the column is sortable but not currently the active sort column. The active ascending/descending icons (SortAscending/SortDescending) are unchanged.
Patch Changes
-
b7122c0: feat(DST-1461):
Accordionaligns likeTableinside a bledPanel.What changed:
- A bled
Panel.Content/Panel.CollapsibleContentnow publishes a--bleed-pxcustom property (set to the Panel's--panel-px). Non-bled content is unchanged and does not set it. - The
defaultAccordionheaderandcontentinset themselves by--bleed-px(via--accordion-x-padding, which falls back to0px). So inside a bled Panel the item dividers span edge-to-edge while the header/content align with the Panel title. - In a bled Panel the full-width header (and its focus ring) is inset off the Panel border by one spacing step, matching
Panel.Collapsible.
Why:
Dropping an
<Accordion>into<Panel.Content bleed>now gives full-width item dividers and header/content aligned with the Panel title — the same behaviorTablealready had, with no new Accordion prop or variant.Impact:
- Standalone Accordions are unchanged (
--bleed-pxis only set by a bled Panel container, so the inset resolves to0px). - Accordions inside a non-bled
Panel.Contentare also unchanged — the inset stays0px, so header/content keep aligning with the dividers as before (no double indent). - Only Accordions inside a bled Panel gain the inset. The
cardvariant is unaffected; it keeps its ownpx-4.
- A bled
-
85e9a45: Fix
Autocomplete's publiconSubmittype, which declared(value, key)while the implementation, JSDoc, and both docs demos all used(key, value). The type now matches the actual(key, value)signature — no runtime behavior changes. -
b7122c0: style(DST-1586): scale breadcrumb separators with the size variant and emphasize the current page
The chevron separator now scales with the breadcrumb size (
small/default/
large) instead of rendering at a fixed 16px, so it stays a quiet mark between
crumbs; gaps tighten onsmallaccordingly. The current page reads a tier above
the trail — medium weight in theforegroundink — matching how the sidebar
marks the active item.The
<Breadcrumbs>chevrons drop their hardcodedsize={16}prop: the theme's
Breadcrumbsitemslot now owns the separator size ([&_svg]:size-*, which
wins over the SVG's width/height), so the number in the component was dead and
misleading. Themes that don't set a separator size fall back to the icon's
default size. -
b7122c0: fix(DST-1659): draw the
Checkboxbox from theme tokens instead of raw Tailwind literalsThe checkbox icon painted itself with
bg-whiteandborder-black. Both now come from the token layer:bg-surfacefor the fill andborder-control-borderfor the affordance edge, matching how every other control draws its boundary.@marigold/theme-ruialready overrides both slots inCheckbox.styles.ts, so the rendered markup and the visual result are unchanged there. The literals only leaked in custom themes, where a consumer could not recolor the box edge or fill without fighting a hardcoded value. This was the last raw color in the component layer. -
b7122c0: feat([DST-1395]): SelectList horizontal layouts now automatically flip to a vertical stack when the wrapping container is narrower than
40rem(~640px). -
b7122c0: feat(DST-1286): Panel renders a
data-panelattribute on its rootThe root
<section>rendered by<Panel>now carries a valuelessdata-panelattribute. External stylesheets and host pages can use it as a stable selector (e.g.:not(:has([data-panel]))) to detect whether a Panel is present without depending on Tailwind utility classes. -
b7122c0: fix([DST-1295]): replace
gapbetweenCheckboxGroupandRadioGroupitems with per-item padding so the full space between items is clickable. Vertical items now meet the 24px target-size minimum; horizontal spacing keeps visual parity. StandaloneCheckboxis unaffected.Also align the label and icon: switched the inner row layout from
items-centertoitems-startso the icon stays on the first line when the label wraps.Radiolabels now useleading-4to matchCheckbox, andRadio's icon-to-label gap moves from an inlinegap-[1ch]to the theme-drivengap-x-2for parity withCheckbox. -
b7122c0: feat([DST-1339]): FileField gains a
size="small"compact layout renders as a single-row input-height control (upload button + file list) instead of the full drop-zone, suited for space-constrained forms. -
b7122c0: refactor(DST-1367): Panel adopts the slot-configuration pattern
Panel.Headeris now a single<Provider>that configuresHeadingContext,TextContext,ActionButtonContext, andActionGroupContextfor everything nested inside it. Consumers drop slot-aware primitives directly into the header —<Title>,<Description>, and any of<ActionButton>,<ActionGroup>,<ActionMenu>,<LinkButton>— and the Panel injects level, ids, ref wiring, grid-area positioning, and the action cascade via context.The compound sub-components that paired with
Panel.Headerare removed because slot-aware role primitives subsume their responsibilities:<Panel.Title>→ use<Title>.<Panel.Description>→ use<Description>.<Panel.HeaderActions>→ drop the wrapper; the action primitives themselves (<ActionButton>,<ActionGroup>,<ActionMenu>,<LinkButton>) land in the actions grid cell via the context className thatPanel.Headerpublishes.
Multiple actions belong inside an
<ActionGroup>— the cluster claims one cell and renders as a toolbar. A raw<Button>insidePanel.Headeris intentionally not slot-aware: it stays as a footgun so the action primitives are the obvious choice for header chrome.Panel.CollapsibleHeaderadopts the same shape:<Panel.CollapsibleTitle>/<Panel.CollapsibleDescription>are removed in favour of plain<Title>and<Description>inside the header.Panel.CollapsibleHeaderpublishes slot-keyedHeadingContextandTextContextinside its disclosure trigger so the primitives render as spans (matching the heading-inside-button constraint), while the structural<hN>semantics come fromPanel.CollapsibleHeaderitself.<Description>now honourselementTypefrom its surroundingTextContextslot config.Panel.HeaderdeliverselementType: 'p'so the description renders as a paragraph;Panel.CollapsibleHeaderdeliverselementType: 'span'so it nests cleanly inside the disclosure trigger button. Elsewhere,<Description>continues to render as RAC's default<span>.<Headline>now defaults to opting out of any surroundingHeadingContextslot config (slotdefaults to the no-slot opt-out instead ofundefined). This avoids "A slot prop is required" runtime crashes when a bare<Headline>is rendered inside a container that publishes a slot-keyedHeadingContext— such as a<Panel>that publishes itstitleslot at the root. An explicitslotprop on<Headline>still overrides the default. -
b7122c0: refactor(DST-1374): use
<TextValue>and<Description>for selection-container itemsConsumer-facing JSX in component stories and documentation demos for
<Select>,<SelectList>,<ListBox>,<Menu>,<ComboBox>, and<Autocomplete>now composes item content with the<TextValue>and<Description>primitives instead of hand-written<Text slot="label">/<Text slot="description">. The primitives are drop-in replacements that render the same RAC<Text>with the same default slot values, so rendering,aria-describedbywiring, and accessibility are identical.<Menu.Item>gains first-classlabelanddescriptiontheme slots, mirroring<SelectList.Option>.MenuItemmerges the Marigold theme classNames into RAC'sTextContextso nested<TextValue>/<Description>pick up Menu styling without losing RAC's slot wiring. Menu items adopt a two-column grid layout (icon column + content column) so descriptions render below labels; existing plain-text and icon+text menu items are unaffected.The
Menutheme type in@marigold/systemis extended with requiredlabelanddescriptionslot keys. Consumers maintaining a custom theme that overridesMenuwill need to add these two slots to satisfy the type.@marigold/theme-ruiis updated accordingly in this release.No public API change on
Select.Option,SelectList.Option,ListBox.Item,Menu.Item,ComboBox.Option, orAutocomplete.Option. -
b7122c0: fix([DST-1410]): restore RangeCalendar build by using
createWidthVarfor the width prop. The component still imported thewidthruntime map from@marigold/system, which DST-901 removed when it migrated dimension props to CSS variables. This brokemainfor everyone — typecheck, unit tests, and Storybook tests all failed on every open PR. Apply the same migration pattern Calendar already uses:w-(--width)className plusstyle={createWidthVar('width', width)}. -
2b9df4c: docs(DST-1427): promote
EmptyStatefrom beta to stableEmptyStatehas been in beta since January. v18 is where its surface settled, with the title becoming a semantic heading behind a configurableheadingLeveland the description moving onto the sharedDescriptionprimitive, so the badge comes off now that the API being frozen is the one 18.0.0 ships.title,description,actionandheadingLevelare covered by the usual semver guarantees from here on.variantandsizestay open ended, as they do on every themed component:theme-ruidefines no values for either today, and anything it adds later widens the accepted set rather than narrowing it. No code changes. -
b7122c0: feat(DST-1439): give
SectionMessagea neutral surface with a muted variant borderSectionMessageno longer fills its background with a per-variant tint (bg-info,bg-success,bg-warning,bg-destructive). It now sits on a neutralui-surfacewith neutral title and body text. The severity is carried by a muted per-variant colored border (the accent mixed halfway into the neutral border) plus the colored icon.This is a visible design change for every variant. Because the surface stays neutral, standard
<Button>and<Link>actions placed inside read correctly instead of floating on a colored fill. On the old tint the default<Button>(variantsecondary, which has its ownui-surfacefill) rendered as a foreign chip on the colored container for every variant except error. The muted border sits at the container edge, away from the content, so it signals the variant without touching the actions. The border color is set throughui-surface's--ui-border-colorhook, which is registered as a non-inheriting custom property, so it stays scoped to the container and does not leak into nested action borders.Variants stay distinguishable without relying on color alone through the colored border, the distinct icon shape and color, and the title. The bordered, in-flow treatment keeps an inline message visually distinct from the floating, shadowed
<Toast>. No API changes. -
b7122c0: fix(DST-1467): make
MorphCaretSSR-safeThe shared
reducedMotionconstant inutils/reducedMotion.tssampledwindow.matchMediaat module evaluation, so the server bundle always resolved tofalsewhile a client withprefers-reduced-motion: reduceresolved totrue— producing a React 19 hydration mismatch (silently absorbed in production, logged as an error in dev).MorphCaretnow reads the preference viauseReducedMotion()frommotion/react, matchingSidebarToggleIconandTrayModal. The obsoleteutils/reducedMotion.tshas been removed. -
b7122c0: fix(DST-1480): only set
aria-labelledbyon Panel when a Title is present<Panel>previously renderedaria-labelledby={titleId}whenever noaria-labelwas given, even if no<Title>was present. That left the<section>landmark pointing at an id no element carried, producing a broken/empty accessible name.The guard now also checks
hasTitle, soaria-labelledbyis only applied when a<Title>actually renders. This mirrors the stricter guard adopted byCardin DST-1373. -
b7122c0: fix(DST-1501):
<Panel p={number}>now applies paddingA numeric
p(e.g.p={4}) silently produced no padding: the value was suffixed with-x/-yand resolved to a non-existentvar(--spacing-4-x). A scale value is now applied directly (calc(var(--spacing) * 4)) on both axes, matchingpx/pyand<Page>. Named inset tokens are unchanged. -
b7122c0: Extract
resolveInsetAxeshelper to centralise inset-padding axis resolution.The
p→px/pyresolution logic (branching on whether the value is a numeric scale or a named token) was copy-pasted acrossPage,Panel, andCard. This duplication caused the<Card p={number}>silent bug (resolving to a non-existentvar(--spacing-4-x)), the same class of bug that had already been fixed independently inPanel(DST-1501) andPage(DST-1360).- Adds
resolveInsetAxes({ p, px, py, defaultInset })to@marigold/systemalongsidecreateSpacingVar. - Adopts the helper in
Card,Panel, andPage, fixing the live<Card p={number}>bug as part of the refactor. - Fixes the same numeric-
pbug inSelectList(inline, since its conditional-axis pattern differs).
- Adds
-
b7122c0: chore(DST-1503): Migrate
Checkbox,Switch, andRadiooff the deprecated react-aria-components single-element exports (Checkbox/Switch/Radio) to the*Field+*Buttoncomposition introduced inreact-aria-components@1.18.0. This removes thets(6385)deprecation warnings with no change to the public API, behavior, or visual output. -
b7122c0: chore(DST-1517): HelpText renders the shared Description component
HelpTextnow renders the description through the sharedDescriptioncomponent
instead of react-aria's raw<Text slot="description">.Descriptionis just
<Text slot="description">, so the rendered output andaria-describedbywiring
are unchanged. The win is a singleDescriptionbuilding block shared by both the
slot-configuration containers and the form fields, so the two cannot drift apart.
No names, output, or behaviour change. -
b7122c0: refactor(DST-1534): build the
Calendaryear dropdown on react-aria'sCalendarYearPickerThe year dropdown now consumes react-aria's
CalendarYearPickerrender-prop (mirroring how the month dropdown already usesCalendarMonthPicker), replacing the hand-rolled year list and its localizedaria-labelworkaround. This was unblocked by react-aria's June 2026 fix that makesmaxValueinclusive, so the boundary year is reachable. Unbounded calendars keep the ±20-year window and bounded ranges stay fully reachable at both ends. When only one bound is set, the open side now widens to keep that bound reachable instead of staying at a fixed ±20 years. -
b7122c0: refactor(DST-1546): replace the bespoke TagGroup "Remove all" wrapper with a plain
<Button>via aButtonContextcascadeTagGroupnow provides alink/smallButtonContextaround its internal
RemoveAllrender, so the "Remove all" action is a bare Marigold<Button>
instead of the raw react-ariaButtonwith hand-rolled link styling. This
mirrors the cascade pattern already used byActionBarandPanel.Header.The change is internal-only.
TagGroupRemoveAllis not part of the public API
(TagGrouprenders it itself), the authoring API (removeAll/onRemove) is
unchanged, and there is no behavioral or accessibility change.The redundant
removeAlltheme style is removed fromTag.styles.ts(the
linkvariant atsize="small"reproduces it), and the now-unusedremoveAll
key is dropped from theTagtheme type. -
b7122c0: chore(DST-1547): move z-index classes out of theme style files into component implementations
Per the z-index management rule (
CLAUDE.md),z-*utilities belong in component implementations, never in theme*.styles.tsfiles. The local focus/drop/sticky stacking classes forCalendar/RangeCalendar,LegacyTable,ListBox,Table,ToggleButtonandSegmentedControlhave been moved from theirtheme-ruistyles into the matching componentclassName(viacn()), preserving the exact modifiers and important flag. No visual or stacking change. -
b7122c0: fix(DST-1553): drop the dead
'small' | 'medium' | 'large'literals fromTabssizeThe
sizeandvariantprops onTabsresolved to nothing after the RUI theme
size variants were removed (2025-03-04).sizenow accepts a plainstring
(matchingLabelandHelpText) instead of advertising specific values that no
theme backs. The props stay in place as theme hooks, so a consumer theme can
define its ownsize/variantvariants without the misleading built-in union. -
b7122c0: fix(DST-1565): stop the DatePicker and DateRangePicker popover from stretching to the trigger width
The shared
Popoverforces its width to at least the trigger's viamin-w-(--trigger-width). That is right for field dropdowns (Select, ComboBox) whose list should line up with the field, but wrong for a calendar, whose width is its own. A newmatchTriggerWidthprop (defaulttrue, so every existing popover is unchanged) lets DatePicker and DateRangePicker opt out, so the calendar sizes to its content instead of the full field width. -
b7122c0: refactor(DST-1585): drive SegmentedControl's reduced-motion scroll from CSS
SegmentedControl now gates its selection-reveal scroll animation with the same
CSS approach as Tabs: the scroll container carriesmotion-safe:scroll-smooth
and the component'sscrollTousesbehavior: 'auto', which follows that CSS —
animating when motion is allowed and jumping instantly under reduced motion.
This replaces the previous JSwindow.matchMedia('(prefers-reduced-motion)')
check. Behavior is unchanged; the initial mount reveal stays instant. No API
change. -
b7122c0: style(DST-1602): drop the removed elevation shadow from SelectionIndicator
SelectList'sSelectionIndicatorno longer appliesshadow-elevation-border,
which is removed fromtheme-ruiin the accompanying change. The indicator keeps
itsborderand surface fill. -
68122ff: refactor(DST-1627): extract a shared
CalendarBodyfor theCalendar/RangeCalendargrid layoutThe multi-month and single-month layout markup was duplicated verbatim in
CalendarandRangeCalendar, so every layout tweak had to be made twice and the copies could silently drift. Both components now render that markup through one internal<CalendarBody>, which reads the theme classes, visible months, min/max value, disabled state and a newisRangeflag off the existingCalendarContext, keeping the touch pointerup guard (DSTSUP-257) range-only. Pure refactor: the rendered DOM, the styling and the behavior are unchanged. -
b7122c0: fix(DST-1629): apply a
SelectListitem'sactionslot styling to a trailing MarigoldButton,LinkButton, orActionMenuso the action spans both rows and stays centered. The action slot className was only provided on RAC'sButtonContext, which Marigold'sButtonignores, so the action auto-placed into the title row and stretched it, pushing the description down. The className now flows through the MarigoldButtonContextthat these components read. -
b7122c0: fix(DST-1630): match the Panel collapsible header caret to the Accordion chevron. It rendered at the default 24px in the foreground color, while Accordion uses a 16px
text-secondarycaret, so the two collapsible patterns looked inconsistent. The Panel caret now renders at 16px and its color is driven by a new themeablecollapsibleIconslot (defaulting totext-secondaryin the RUI theme). -
b7122c0: fix(DST-1632): center the section
Loadertogether with its label and give theTabledrag handle edge spacing.The section
Loadersized its container to a fixed square, so a labelled loader overflowed the box and the section wrapper centered the box instead of the spinner-and-label group. The spinner now carries the fixed size and the container is content-sized, so the whole group centers as one. TheTabledrag cell had no padding, leaving the grip flush against the row edge. It now uses the shared cell edge padding and its column matches the checkbox column width, so the grip lines up with its header and the first cell. -
b7122c0: fix(DST-1645): align preset quick-selection rows with the tray nav row on small screens. The preset listbox rows reuse the shared
ListBoxp-1focus-ring gutter, which insets them narrower than the full-width nav row. On small screens the list now negates that gutter (-mx-1) so rows span the tray edge-to-edge and line up with the nav row, while the gutter still keeps each row's focus outline from being clipped by the list's overflow. -
e0f9c05: fix(DST-1647): honor the router's
useHrefin sidebar linksSidebar.ItemandSidebar.RailItemrendered the rawhrefprop straight onto
their anchor, which shadowed the value produced byRouterProvider's optional
useHref. Applications served from a prefix, such as a Next.jsbasePath, ended
up with sidebar markup pointing at an unprefixed URL, so middle click and "copy
link address" resolved to the wrong page. Both components now render the
transformed href and keep handing the unprefixed path tonavigate, matching how
React Aria's ownuseLinkbehaves. Consumers that do not passuseHrefsee no
change, because the default leaves the href untouched.The
RouterProviderdocs now coveruseHrefnext tonavigate, and the
component gained a matching Storybook story and prop description. -
b7122c0: fix(DSTSUP-256): show
cursor-not-allowedon disabled TagFieldThe hidden trigger button inside TagField had
cursor-pointerhardcoded, so hovering a
disabled TagField showed the text/caret cursor instead ofnot-allowed— inconsistent with
Select and ComboBox. Addeddisabled:cursor-not-allowedto the trigger button so the cursor
now matches the rest of the form components. -
b7122c0: fix(DST-1354): restore collapsing
Table.EditableCelledit triggerThe overlay/ring affordance introduced in #5250 (DST-1275) did not read as editable in user testing: sighted users did not associate the hover ring with inline editing, and there was no discoverable trigger for keyboard or touch. This change reverts that approach and restores the explicit pencil edit button.
The trigger collapses to zero layout space at rest (
w-0 overflow-hidden) and expands on row hover or keyboard focus, so static layout remains clean while the affordance is discoverable the moment the user interacts with the row. When expanded, the wrapper switches tooverflow-visibleso the button's focus outline is not clipped. The cell itself stays clickable as a touch target. Enabled editable cells always truncate their content to stay aligned with column headers and match the single-line editing controls; disabled cells behave like a regularTable.Cell. -
b7122c0: fix(FieldBase): forward
isInvalid,isRequired, andisDisabledto RAC components passed viaasWhen
FieldBaserenders through a React Aria Components element (e.g.as={RACComponent}), validation props are now forwarded so the underlying RAC element receives them. Plain DOM elements continue to skip these props to avoid unknown attribute warnings. -
b7122c0: fix(Radio): apply width via CSS variable instead of raw class name
Radiobuilt itswidthclass name from the raw token directly (e.g.cn(width || groupWidth || 'w-full'), wherewidthcould be a literal"1/2"), which Tailwind can't statically detect at build time, so no CSS was ever generated — settingwidthhad no visible effect.Radionow follows the same pattern already used byFieldBase/TextField/NumberField: a staticw-(--field-width)class with the actual value injected viacreateWidthVar. IndividualRadioitems inside a sizedRadio.Groupnow inherit the group's already-computed width instead of recomputing it, which previously caused a double-shrink (e.g.width="1/2"rendering at 1/4 instead of 1/2). -
b7122c0: fix(DST-1352): use correct outline for focus + error state in compound fields
-
b7122c0: fix: stop exposing
styleonLabelLabelonly removedclassNamefrom its react-aria props, leavingstylein
the public interface. It now omits both, matching theclassName/style
removal convention used across the design system (e.g.Description,
TextValue). Theme the label viavariant/sizeinstead. -
b7122c0: chore(DST-1364): migrate
ListBoxitem label/description styling off descendant selectorsListBoxnow exposeslabelanddescriptionas first-class theme entries, andListBox.Iteminjects their classNames into react-aria'sTextContext(merging rather than replacing, so RAC'saria-describedbywiring is preserved) instead of styling[slot=description]via a descendant selector onitem. This also benefitsSelect.Option,ComboBox.Option, andAutocomplete.Option, which re-exportListBox.Item.The
Themetype in@marigold/systemnow requireslabelanddescriptionkeys on theListBoxrecord, so custom themes implementingListBoxmust add these entries. No public API change in@marigold/components; visually identical exceptdescriptionnow explicitly setsfont-normal(parity withSelectList). -
b7122c0: fix(DST-1660): keep a loading
Button's accessible nameA
<Button loading>previously had no accessible name at all. The label is kept mounted so the button doesn't change width when the spinner is overlaid, but it was hidden withinvisible(visibility: hidden), which removes a subtree from the accessibility tree — not just from the screen. The spinner's ownaria-labeldid not substitute, because that is a child widget's name rather than text content the button can take its name from.The effect: a screen reader announced "Delete, button" before the press and roughly "dimmed, button" the moment the action started, so the user lost the identity of the operation they were waiting on. That is a WCAG 2.1 §4.1.2 (Name, Role, Value) failure at Level A, and it was worse than a plain
disabledbutton, which keeps its name.The label is now hidden with
opacity-0. It reserves the exact same layout box, so nothing moves — verified by comparinggetBoundingClientRect()for every button across all three surface grounds, withdisplay: noneas a control to confirm the measurement detects real shifts. Rendering is unchanged; this is purely a fix to what assistive technology reports. -
b7122c0: chore: extract shared
useMergedTextSlotshelper for RACTextContextslot stylingListBox.ItemandSelectList.Optionboth mergedlabel/descriptiontheme classNames into react-aria'sTextContext(spreading the parent slot first to preserve RAC'saria-describedbyid). That accessibility-critical logic — and itsSlottedContextValuetype — now lives in a singleuseMergedTextSlotshook that both consume. No public API or visual change. -
b7122c0:
Panel.Titlemay now be used as a direct child ofPanelwhen the Panel has only a title (no description, no actions) —Panel.Headeris the layout wrapper for title + description + actions, but a title-only Panel doesn't need it. Accessibility (aria-labelledby) and horizontal panel padding still resolve correctly.Panel.DescriptionandPanel.HeaderActionscontinue to require aPanel.Headerwrapper. No change to existing usages. -
b7122c0: test(DST-1329): add comprehensive unit and play-test coverage for
Paneland its sub-components (Header, Title, Description, HeaderActions, Content, Footer, Collapsible, CollapsibleHeader, CollapsibleTitle, CollapsibleDescription, CollapsibleContent, Context). No runtime changes. -
b7122c0: fix(DST-1622):
ProgressCircleandLoaderrespect a consumer-providedaria-label/aria-labelledby.What changed:
ProgressCircleno longer overwrites a caller's accessible name. The built-in localized "loading" message is now only a fallback, applied solely when the consumer provides neitheraria-labelnoraria-labelledby.Loader(BaseLoader) uses the same fallback: the localized message is applied only when there is noaria-label,aria-labelledby, or visiblechildrenlabel — and it no longer emits a redundantaria-labelnext to a consumer'saria-labelledby.- A fullscreen
Loadernow stays reliably named: when the consumer supplies their ownaria-labelledby, the overlayDialogreferences that element directly instead of the intermediate loader node (the accessible-name spec does not follow a secondaria-labelledbyhop, which otherwise left the modal unnamed).
Why:
Previously the
aria-labelwas set after{...props}was spread, so a caller passingaria-label(oraria-labelledby) had no effect and every progress circle announced the same generic "Loading…" string. This prevented labelling a spinner for its context (e.g. "Sending reminders" in a bulk-action flow).Impact:
- Callers that pass no label (e.g. the spinners inside
Button,ComboBox,SearchInput) are unchanged — they still get the localized fallback. - Callers that pass
aria-label/aria-labelledbynow get their own accessible name.
-
b7122c0: fix(ProgressCircle): resolve named
sizetokens to a numeric SVG dimensionA named
sizetoken (default|large|fit) was forwarded unchanged to the
underlying<SVG>element, which renderswidth/heightas`${size}px`—
producing invalid attribute values likewidth="defaultpx"and emitting console
errors. The SVG now resolves named tokens to the pixel dimension of their theme
size-*class (default→ 80,large→ 144) for itswidth/heightattributes
and stroke-width math, so the stroke stays proportionate and the rendered output is
unchanged.fitis content-sized and has no intrinsic dimension, so it falls back to
the<SVG>default of24.The stroke-width comparison also switched from a string compare (
size <= '24') to a
numeric one, which additionally fixes multi-digit sizes that were previously
mis-classified (e.g.size="100"computed astrokeWidthof2instead of4). -
b7122c0: docs: improve
AutoTypeTableprop renderingCentralizes the display of design-system aliases in the component docs'
prop tables. Props whose types reference aliases from@marigold/system
or@marigold/types(e.g.SpacingTokens,Scale,WidthProp,
NonZeroPercentage) now render with a meaningful summary in the main
cell and the full list of resolvable literal values on row expand —
instead of a wall of literals in the cell and a redundant alias name on
expand.Before:
- Cell:
SpacingTokens<Tokens>(a fabricated generic, inconsistent across components) - Expand:
SpacingTokens | Scale | undefined(same alias names, no new info)
After:
- Cell:
SpacingTokens | Scale(accurate, derived from the real type) - Expand:
"96" | "80" | ... | "tight" | "related" | 0(every concrete value)
Under the hood this replaces 27 per-prop
@remarks \TypeName`JSDoc overrides with a single fumadocs-typescript transform in the docs site, so future components pick up the same behavior automatically. A@remarks` tag on a prop still wins as an escape hatch.Multiselect.widthandComboBox.widthnow useWidthProp['width']
directly instead ofFieldBaseProps<'label'>['width']— structurally
identical, no runtime change. - Cell:
-
0e2c676: chore(DST-1680): update React Aria to the 1.20.0 line.
What changed:
react-aria-components1.19.0 → 1.20.0, which pinsreact-aria3.51.0 andreact-stately3.49.0.@internationalized/date→ 3.12.3 and@react-types/shared→ 3.36.1. Both are required rather than cosmetic: RAC 1.20.0 declares^3.12.3/^3.36.1, and pnpm will not move an in-range dependency unless the specifier changes, so leaving the old floors keeps a second (runtime-bearing)@internationalized/datecopy in the tree.- The remaining declared floors now match the versions actually installed, which are also the latest published ones:
@react-aria/form→^3.2.1,@react-aria/live-announcer→^3.5.1,@react-stately/form→^3.3.1. Every other@react-aria/*and@react-stately/*entry was already current.
Impact:
No API change in Marigold components. Consumers pick up the upstream 1.20.0 fixes, including Table focus restoration,
FocusScoperestore-without-scrolling, and DatePicker focus handling in Firefox.One upstream behaviour change did need handling on our side: RAC 1.20.0 exposed a latent problem in the mobile
ComboBox/Autocompletetray, which lost its place in the accessibility tree. See the separate entry for that fix.@react-types/{button,checkbox,grid,table}deliberately stay on their type-only lines. All four latest minors pull@react-spectrum/provider— three as a direct dependency andgrid3.4.0 as a peer dependency — which drags in@adobe/react-spectrumand splits the i18n and overlay contexts. See the hold rule in.github/renovate.json. -
f331a41: chore: remove the leftover
react-selectdependency<Multiselect>was removed in v18 and it was the only thing importingreact-select, but the dependency itself was never dropped. It is now gone from@marigold/componentsand from the root workspace, along with theexternalentry intsdown.config.tsand theoptimizeDepspre-bundle entry invitest.config.shared.tsthat only existed for it. Nothing imports it anymore, so this is install-size only — no runtime change. -
b7122c0: fix(DST-1464): keep wide content inside
<AppLayout.Main>from overflowing the viewport. The shell grid usesgrid-cols-[auto_1fr]and the main grid item defaulted tomin-width: auto, so any content wider than the available track (most visibly a<Select selectionMode="multiple">with several long selected items) pushed the main column past the viewport and added a horizontal scrollbar. Addingmin-w-0toAppLayoutMainlets the1frtrack actually shrink, and children liketruncateon the Select trigger can now clip at the right place. -
04e22ab: fix(DST-1679): stop
SelectListfrom advertising two props it silently ignores, and stopSelectList.Optionfrom double-warning about a missingtextValue.What changed:
layoutandkeyboardNavigationBehaviorare no longer part ofSelectListProps. Both were publicly typed but discarded at runtime: the list hardcodeslayout="grid"after spreading consumer props, and RAC deriveskeyboardNavigationBehavior: 'tab'from that grid layout. They now sit inRemovePropsalongsideselectionMode, which is the correct pairing for a prop the component owns.SelectList.Optionno longer warns about a missingtextValue. RAC'sGridListalready warns on exactly that condition and its warning can't be suppressed, so one authoring mistake produced two console lines. ThetextValuefallback for plain-string children is unchanged.
Why:
layoutwas a prop that lied — the docs props table even published it as"stack" | "grid"defaulting to'stack'while the component always rendered a grid. Passing it type-checked and did nothing, and it couldn't be removed later without a breaking change.The duplicate warning was also a false positive in one case: RAC accepts an
aria-labelin place of atextValue, so an option named that way is perfectly accessible, but the local warning fired on it anyway while RAC stayed silent.Impact:
No runtime behavior change.
layoutandkeyboardNavigationBehaviorwere already no-ops, so code passing them keeps working identically — it just no longer type-checks, which is the point.layout="grid"stays hardcoded because it is load-bearing: it gives the list arrow-key navigation on both axes, so a horizontal list moves with Left/Right and still moves with Up/Down after the container-query flip to a vertical stack. Under RAC's"stack"default a row captures Left/Right to walk its own focusable children and horizontal navigation stops working; a new test now guards that. -
b7122c0: feat([DST-1396]): mobile-optimized pagination layout
Paginationnow hides the numbered page buttons on small viewports (max-sm) and spreads the previous/next navigation buttons across the full width usingjustify-between. This produces a cleaner, touch-friendly pagination on mobile while preserving the full layout on larger screens. -
b7122c0: style(DST-1586): remove the overshoot from the sidebar toggle icon animation
The sidebar toggle icon's panel/chevron animation eased with a spring-like
overshoot bezier. It now settles onease-out-quint, matching the theme's
motion register — fast start, smooth stop, no bounce. -
b7122c0: fix(DST-1642): keep the Slider thumb from being clipped at the track ends
The Slider thumb is centered on the track position, so at its min/max values it overhangs the track ends by half its width. Inside a scroll container such as
Drawer.Content— whoseoverflow-y: autopromotesoverflow-xto a clipping value — the overhang was sliced off, leaving a half-circle thumb (and it will be clipped unconditionally onceDrawer.Contentgains ableedprop with no horizontal padding). The track is now inset by half the thumb width so the thumb always stays within the Slider's own box, independent of any ancestor padding. -
6cfcea4: fix(DST-1686): make
controla ground-adaptive track fill, and give theSliderrail the same token asSwitchandSegmentedControlThe Slider rail was the wrong token, painted twice. It used
bg-border— the token for structural lines (dividers, grid lines, table rules) — where theSwitchgroove and theSegmentedControltrack both usebg-control. On top of that,Sliderapplied itstrackstyle to two exactly-overlapping elements (theSliderTrackand an inner raildiv), so the translucentbg-bordercomposited with itself and the rail rendered at ~0.26 effective alpha instead of 0.14 — measuring#c0bfbeon white where the token specifies#dddddc. The redundant inner element is gone; theSliderTrackitself is the rail. Geometry is unchanged (both wereh-2at the same position) andtouch-none,select-noneand the disabled cursor stay on the interactive track element.controlis now translucent (charcoal-950 / 16%, was the opaquecharcoal-300). A track is not painted on one known background — it appears on a white Card, the gray page ground, amutedfill, and inside a hovered Table or ListBox row — and a fixed palette step drifts across those. charcoal-300 measured 1.53:1 on white but only 1.21:1 inside a hovered row, where the groove half-dissolved into the row it sits in. At 16% the four grounds land within 0.02 of each other (1.41–1.43:1), so a track weighs the same wherever it goes. Same rationale asborderin DST-1672.The three tracks (
Switch,SegmentedControl,Slider) read a touch lighter on white surfaces as a result:#d8d8d7rather than#d4d0ce. The trade-off is that the white Switch thumb and SegmentedControl indicator now vary against the track by ground (1.43:1 on white, 1.79:1 on a hovered row) where the opaque step held a flat 1.53:1 — track-vs-ground and thumb-vs-track cannot both be constant while the thumb is opaque, and ground legibility wins because it decides whether the control reads as a control at all.The
SegmentedControlindicator is larger. The frame around the selected thumb went from 4px to 3px (inset-y-[3px]on the indicator,p-[3px]on the list), so the thumb is 30px tall instead of 28px. Since the thumb's own 1px rim occupies the innermost pixel of that frame, 2px of bare track is what you see.The
SegmentedControlthumb's focus ring is now the sharedui-state-focus. It previously hand-rolled only the outline, so it missed the other half of that utility — firming--ui-border-colorto the opaque ring colour — and read noticeably lighter than every other focused control. A focused thumb and a focusedInputnow resolve identically: 3pxoutline-ring/50atoutline-offset-0, plus a 1px opaqueoklch(0.52 0.008 54)rim. The ring stays outside the thumb, and 3px is the minimum frame that clears it: the list is a scroll container clipping at its padding box, so that padding is the only room an outset ring can grow into. Below 3px the first and last thumbs get a visibly shaved ring.The
SegmentedControltrack's corners are now concentric with the indicator's. Both used the sharedrounded-surface, but two rounded rectangles nested with a gap only look like parallel arcs when the outer radius is the inner radius plus that gap. With the thumb at 8px and 3px of frame around it, the track needs 11px; at 8px its corner read visibly tighter than the arc it frames. The track is nowcalc(var(--radius-surface) + 3px)— derived from the token, so it stays concentric if the radius is retuned. This is a deliberate exception to usingrounded-surfaceeverywhere.The
SegmentedControlindicator's resting rim is re-derived from the track.ui-framedraws its rim as an outset ring, so the indicator's rim lands on the track and composites overbg-control— it reads denser than the samecontrol-bordertoken does on a field. The old compensation subtracted a flat 0.08, hand-tuned against the previous opaquecharcoal-300track, which left the rim at an effective 0.31 against a field's 0.26. It is now derived from the track's own alpha (--control-alpha, newly exposed on the token), so a resting indicator rim and a restingInputedge render the same on every ground, and retuning the track cannot silently detune the rim.If you use
bg-controlin your own code, note that it is now translucent: two stacked elements that both carry it composite into a darker track than the token specifies, and anything that paints a translucent edge over a track has to account for the track underneath it. -
b7122c0: feat(DST-1366): introduce slot-configurable primitives
Adds three text-bearing role primitives —
Title,Description,TextValue— and three action primitives —ActionButton,ActionGroup,ActionMenu— that participate in slot-keyed context. Text/heading slots use React Aria'sHeadingContext/TextContextdirectly; action slots use Marigold-owned contexts (ActionButtonContext,ActionGroupContext,ActionMenuContext) consumed viauseContextProps.Titlewraps RAC's<Heading>withslot="title"andlevel={2}as defaults, both overridable byHeadingContext. Thelevelprecedence is default ← context ← local, so a container can publish{ level: 4 }and drive a stretch of nested<Title>s to<h4>without each call site setting it.DescriptionandTextValueforward straight to RAC's<Text>withslot="description"andslot="label"defaults respectively, letting<Text>consumeTextContexton its own. None of the three carry typography props. Styling cascades from the surrounding container (or selection item) viaHeadingContext/TextContext. Consumers drop these into containers without anyslotwiring. The container provides level, layout (e.g. a grid area), size, variant, color, and any other styling through a singleProvider.ActionGroupis its own top-level component (own folder, own docs page, own Storybook entry) — there is noActionButton.Groupcompound. It cascadessize,variant, anddisabledto nested<ActionButton>,<LinkButton>, and<ActionMenu>triggers viaActionGroupContext, with explicit per-prop precedence:size: group wins (visual uniformity within a cluster).variant: local wins (so a single destructive action can sit inside an otherwise uniform group).disabled: local wins; the group provides the default. Writingdisabled={false}on a child re-enables it inside an otherwise-disabled group.
ActionMenuis rebuilt to compose its ownMenuTrigger+<ActionButton>+Popover/Tray+ RACMenurather than delegating to Marigold'sMenu. The trigger uses<ActionButton>so an outerActionButtonContextcascades to it. Marigold'sMenuis untouched.LinkButtonis now slot-aware: it picks upActionButtonContextandActionGroupContextso a navigating action can sit alongside<ActionButton>inside an<ActionGroup>and inherit the same cascade. Adestructive-ghostvariant is added to match<ActionButton>. Context is consumed read-only (viauseSlottedContext) to sidestep the anchor/button ref-type mismatch thatuseContextPropswould have created. The read-only consumption now also absorbsclassNamefromActionButtonContext(mirroring<ActionButton>'suseContextProps-driven className merge) so positional classes published by a parent container — e.g. a grid-area class injected viaActionButtonContext— reach the rendered anchor. This lets<LinkButton>participate in container-driven layouts the same way<ActionButton>does.The container-driven layout pattern this enables comes with a corresponding convention: positional
classNameflows through slot contexts and is absorbed at the first layout boundary.<ActionGroup>enforces the convention at its own boundary by scrubbingActionButtonContextfor its descendants — it republishes an empty value so nested<ActionButton>s and<LinkButton>s do not individually re-claim a positional class that was meant for the group as a whole. Cascading props (size,variant,disabled) still reach the children viaActionGroupContext, which they read independently. This convention scales to every future container that adopts the slot-configuration pattern.<ActionBar>'s legacy top-levelActionButtonslot is internalized and re-exposed asActionBar.Button. Existing consumers that already use<ActionBar.Button>are unaffected.Typography prep:
HeadlineexportsHeadlineSize,TextexportsTextSizeandTextVariant. The aliases aren't yet consumed by other primitives, but exposing them now lets a future typography-token PR replace runtime classes without rewriting consumer-facing prop types. -
b7122c0: fix: apply
alignXfromTable.Columnto first column cellsTableCellContentused a truthy check oncolumnIndex, causing it to skip thealignXlookup whencolumnIndexwas0(first column). Replaced with a nullish check so all columns correctly inherit their alignment. -
b7122c0:
Titlelevelnow accepts the string form (level="2") in addition to a
number, matchingHeadline. The two heading primitives now share onelevel
type. Non-breaking: existing numericlevel={2}usage is unaffected. -
b7122c0: fix: make Select and Menu overlay appear above Drawer on small screens
On small screens,
SelectandMenurender their options in aTray(bottom sheet). TheTrayoverlay hadz-40in the theme while theDraweroverlay usesz-50, so the tray rendered behind an open drawer and was unreachable.Moved the
z-indexfrom the theme style file into theTrayModalcomponent implementation (matching the project's z-index architecture rule), and raised it toz-50. Both theDrawerandTrayportal todocument.body; at equal z-index, DOM order determines stacking. TheTrayis always mounted after theDrawer, so it correctly appears on top. -
b7122c0: chore(deps): update
react-aria-components,@react-aria/*,@react-stately/*,@react-types/*, and@internationalized/*packages to their latest versions. -
b7122c0: fix(DST-1355): widen
variantandsizeprop types onLoaderandProgressCircleto accept arbitrary strings via| (string & {}). Matches the pattern already used byButton,Panel, and other components, and lets consumer themes register their own variant/size tokens without TypeScript errors while preserving IDE autocomplete for the built-in RUI values. -
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
-
Updated dependencies [b7122c0]
- @marigold/system@18.0.0