Changelog
Published versions of @radui/ui. Minor and patch sections follow SemVer. Source: CHANGELOG.md in the repository.
Release
v1.1.1
Patch Changes
- d0b3237Document Separator keyboard and ARIA accessibility references.
Release
v1.1.0
Minor Changes
-
d955028: feat(badge): soft label style by default
Badges get a new look:
- Default variant is now
soft(wassolid): a translucent tint of the badge's color with vivid text. Passvariant="solid"to keep the previous default. - Small rounded corners (
--rad-ui-control-radius-sm) instead of a pill, and the mono font at weight 600, for every variant. - Without
color, the soft badge uses the neutral gray scale.
Every color scale clears WCAG AA (4.5:1 or better) in both light and dark. A new theme variable,
--rad-ui-badge-soft-text-weight, tunes the soft text mix per appearance. - Default variant is now
-
dc6aaa7: feat(build): publish per-component Clarity style sources
Each component's Clarity styles now ship in the package and can be imported from
@radui/ui/styles/clarity/<Component>/<file>.clarity.scss. For example,@radui/ui/styles/clarity/Button/button.clarity.scss.- The folder structure matches the source, so relative
@useimports between components still resolve when you compile a single file with Sass. @radui/ui/themes/default.cssis unchanged and remains the recommended way to load the full Clarity theme.- The docs site uses these files to show the exact styles that ship in each released version.
- The folder structure matches the source, so relative
-
0bcb1ef: feat(theme): default
Themeappearance to dark<Theme>now rendersdata-rad-ui-theme="dark"when noappearanceis passed. Previously it defaulted to'system'and followed the OS color scheme.To keep the old behavior, pass it explicitly:
<Theme appearance="system">...</Theme>appearance="light"andappearance="dark"are unchanged. -
944003e: feat(meter): add headless Meter primitive
-
3aabdde: feat(label): add Label component
@radui/ui/Labelrenders a native<label>that names a form control (htmlFor, or by wrapping it).- Matches Radix Label: double-clicking the label doesn't select its text, while controls nested inside it still get their own clicks.
- Supports
asChild,customRootClassand ref forwarding. - Clarity and baremetal styles included.
-
8d5c556: feat: right-to-left support for menus, Select, Combobox, NavigationMenu, TabNav and ScrollArea
- DropdownMenu, ContextMenu: new
dirprop. In RTL, ArrowLeft/ArrowRight open and close submenus, submenus open on the left, and the portaled content (and submenus) carriesdir.rtlis deprecated in favor ofdir="rtl". - NavigationMenu, TabNav: new
dirprop; arrow keys follow reading direction. - Select, Combobox:
diron the root now also applies to the portaled listbox. - ScrollArea: horizontal scroll math and scrollbar placement use the computed direction (logical CSS insets, RTL scroll range).
- DropdownMenu, ContextMenu: new
Patch Changes
-
9ff88bf: fix(a11y): Command separators and RadioGroup arrow keys
- Command:
Command.Separatoris now hidden from assistive technology (aria-hidden, norole="separator"). A separator isn't an allowed child of thelistboxit renders inside, so axe reported a criticalaria-required-childrenviolation. - RadioGroup / RadioCards: all four arrow keys move between options, whatever the
orientation, as the WAI-ARIA radio pattern specifies. Radix and Base UI do the same.orientationstill setsaria-orientation.
- Command:
-
76087cc: standardize asChild support across remaining compound card, steps, avatar group, scroll area, and table parts
-
0221f95: standardize overlay portal
containerandforceMountAPIs across menu, combobox, and hover card primitives -
0af1268: fix: Drawer focus management and stable Toast manager functions
- Drawer: focus now moves into the drawer when it opens, and Tab stays trapped inside. The focus manager used to engage as soon as
data-stateflipped toopen, but the themes keep the popupvisibility: hiddenuntil the entrance transition starts. Initial focus found nothing focusable and silently failed, so keyboard and screen-reader users were left on the page behind the modal. It now waits until the popup is actually visible (capped at about 0.5s). - Toast: the functions from
useToastManager()(add,close,update,promise,dismiss,dismissAlland the variant helpers) are stable for a given manager, as in Base UI. They used to be recreated on every render. An effect keyed on one of them, such as an unmount cleanup callingdismissAll, then re-ran on every render and dismissed each toast as soon as it was added.
- Drawer: focus now moves into the drawer when it opens, and Tab stays trapped inside. The focus manager used to engage as soon as
-
6ae9df7: fix(tooltip): keep the Clarity tooltip dark in dark mode
The Clarity tooltip used
--rad-ui-surface-inverse, which flips to near-white underdata-rad-ui-theme="dark", so tooltips rendered as a light box on dark pages. Tooltip content and arrow now read new--rad-ui-tooltip-background,--rad-ui-tooltip-text, and--rad-ui-tooltip-borderaliases: inverse in light mode (unchanged), and a raised dark surface with a soft border in dark mode. Override those aliases on a Theme container to customise tooltip colors. -
5a38eef: docs(guides): add a troubleshooting guide for SSR, portals, and focus
New
docs/app/docs/guides/troubleshootingpage covering the integration failures that are otherwise spread across guides, organised as symptom / likely causes / what to try:- hydration mismatches on overlay and theme components
- portal containers rendering in the wrong place or missing theme tokens
- focus not restoring after a dialog, popover, or menu closes
- focus traps that are too narrow or too wide
- overlays flashing or never opening on first paint
- scroll lock leaking after an overlay unmounts
Registered in
docsNavigationSectionsand cross-linked from the existing SSR & No-JS Fallback guide.
Release
v1.0.1
Patch Changes
- 0257be0Emit CommonJS component bundles and root index files so every
package.jsonexport target exists in the published package.
Release
v1.0.0
Major Changes
-
56ab619: Tailwind CSS v4 support. Tailwind v3 is no longer supported.
The Tailwind preset is now a CSS file instead of a JavaScript config object. Import it after Tailwind itself:
@import "tailwindcss"; @import "@radui/ui/themes/tailwind-presets/default.css";Breaking changes:
-
The
@radui/ui/themes/tailwind-presets/default.jsexport has been removed. Upgrade Tailwind to v4 before upgrading Rad UI. -
The preset covers colours only, as before. Spacing, radius, shadow, blur and typography keep Tailwind's own defaults.
-
The colour ramp no longer contains
<alpha-value>. Opacity modifiers keep working (bg-gray-500/90), so nothing needs replacing. -
Tailwind v4 dropped its default border colour, so bare
bordernow inheritscurrentColorinstead ofgray-200. To restore the old default, add this to your base layer:@layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: oklch(from var(--rad-ui-color-gray-200) l c h); } } -
var(--color-gray-500)no longer resolves. Readvar(--rad-ui-color-gray-500)instead, which is what v3 emitted anyway.
Applying v4's utility renames to your own markup:
shadow-sm→shadow-xs,shadow→shadow-sm,blur-sm→blur-xs,blur→blur-sm,backdrop-blur-sm→backdrop-blur-xs,backdrop-blur→backdrop-blur-sm,outline-none→outline-hidden,bg-gradient-to-*→bg-linear-to-*, and barering→ring-3. Runningnpx @tailwindcss/upgradehandles most of this automatically. -
Minor Changes
-
3fec6d3: Add a LiveRegion primitive for accessible status announcements.
-
02254e0: table resizable columns
-
56ab619: Release
TextFieldand add theme styles forFieldset.TextFieldis now published. Previously it shipped with styles and tests but was missing from the export list, so@radui/ui/TextFielddid not resolve.Fieldsetnow renders with Rad UI styling in both themes instead of the browser default, and itsinvalidanddisabledprops have a visual effect.
-
63d644a: Support React 19 in
asChildcomponents and enable Tailwind alpha modifiers on design tokens.React 19
asChildrefs — components that read a ref off the child element (Primitive,PopoverPrimitiveArrow,TooltipTrigger,AccordionHeader) accessedelement.refdirectly. React 19 promotedrefto a regular prop and leftelement.refas a deprecation getter, so everyasChildrender logged "Accessing element.ref was removed in React 19" and consumers on React 19 saw warnings from components they never opted into. These now read the ref through a sharedgetElementRefhelper that branches on the React major version, because both access paths warn: React 18 warns onprops.ref, React 19 warns onelement.ref. Behaviour is unchanged on both versions.React 19 peer range — the
reactandreact-dompeer dependencies widened from^18.2.0to^18.2.0 || ^19.0.0. Without this, an application on React 19 could fail installation while this release claims to support it.React 19 ref teardown — React 19 lets a callback ref return a teardown function, which React then calls instead of passing
null.composeRefsfanned the node out to every ref but discarded those return values, so a composed ref silently swallowed every teardown an inner ref had registered. It now retains them per ref and replays them on detach, falling back to the oldnullcontract for refs that register no teardown. React 18 has no way to run a teardown, so its behaviour is untouched.Tailwind alpha modifiers — the generated Tailwind preset emitted
var(--rad-ui-color-*)for every ramp step, so Tailwind's<alpha-value>placeholder could never be substituted and opacity modifiers such asbg-gray-50/90silently produced no color. The preset now emitsoklch(from var(--rad-ui-color-*) l c h / <alpha-value>), which enables the full/<opacity>syntax across every palette and step. This changes the CSS your build emits for preset-derived color utilities, and it relies on relative color syntax (Chrome 119+, Safari 16.4+, Firefox 128+). That is inside this project's stated support policy of the latest two stable versions of each evergreen browser, but it does raise the effective floor for apps pinning older browsers, so it is released as aminorrather than apatch.Token adjustment —
--rad-ui-text-mutedin the Clarity design system now resolves to--rad-ui-color-gray-950instead of--rad-ui-color-gray-800, bringing muted text closer to the secondary text ramp for legibility.
Patch Changes
-
68d9ea9: Expose stable Accordion anatomy data slots for root, item, header, trigger, content, and content inner parts.
-
2fc2c28: Add a composable Breadcrumb component with accessible navigation semantics.
-
1cb98d6: Add Button and Link keyboard interaction and ARIA reference docs.
-
1899f88: Add Checkbox keyboard interaction and ARIA reference tables to the docs.
-
d8d348c: Document CheckboxCards keyboard interactions and ARIA references.
-
0e2c3a8: fix(checkbox): expose protected anatomy and state data attributes
-
55ee49a: Accessibility and Clarity cleanup across core controls: fix menu/navigation semantics and dismissal, form prop/state routing for Checkbox, Radio, TextArea, Fieldset, Text, and Slider, Avatar/Skeleton sizing, and contrast/alignment issues in the Clarity theme.
-
1ea8953: Document CheckboxGroup keyboard interactions and ARIA references.
-
5ff13c7: Expose stable
data-slotanatomy markers on CheckboxGroup parts. -
ae43686: Add an automated Clarity design-system audit gate and close the first tranche of findings it surfaced.
The gate (
npm run check:clarity, wired into CI) fails on raw colors, token fallbacks, hand-coded shadows,filter: drop-shadow, and literal duration, spacing, radius, typography, and sizing values. It carries a per-rule budget that acts as a ratchet: CI fails only when a rule goes above its recorded count, and that count drops as findings are fixed. Rules added later start at zero.All color, shadow, and motion findings are now zero. Popover's navy shadow and Toast's hand-picked status hexes were replaced with tokens, which fixes their dark-mode rendering; the Radio family shared one control shadow; AvatarGroup referenced an undefined
--rad-ui-text. Two dense type sizes that had no token (0.8125remand0.9375rem, previously literals in 13 components) are now generated scale steps behinddenseandcomfortablealiases.No rendered value changes: each swap was verified against compiled CSS.
-
10dbaf0: Document Collapsible keyboard interactions and ARIA references.
-
3406f13: fix(collapsible): publish measured content dimensions when forceMounted
With
forceMount,CollapsiblePrimitive.Contentis already present, so in the zero-duration branch bothsetHeight(undefined)andsetIsPresent(true)bail out as no-ops and no re-render is scheduled. The measurement from that pass lives only in refs, so the measured size never reached the CSS variables and--radix-collapsible-content-height/--rad-collapsible-content-heightwere left at their initial0.That matters most under
prefers-reduced-motion, where the resolved transition duration is0— so the affected path is the default for those users:<CollapsiblePrimitive.Root defaultOpen> <CollapsiblePrimitive.Content forceMount>…</CollapsiblePrimitive.Content> </CollapsiblePrimitive.Root>Before, the content height variable read
0px; it now reads the measured height. -
1853344: Add Combobox large-list documentation with an incremental loading example.
-
b45059a: Document Command keyboard interactions and ARIA references.
-
90f0db6: docs(contributing): add component docs templates for Accessibility and Features sections
Adds two contributor guides alongside the existing
component-docs-anatomyandcomponent-docs-stylingtemplates:component-docs-accessibility— keyboard table format, ARIA/role and state attribute requirements, and a checklist that ties each section back to the keyboard interaction spec and screen reader testing guide.component-docs-features— the short scannable Features bullet list, with rules for deriving bullets from the shipped public API.
Both are also registered in
docsNavigationSections, titledComponent Docs: AccessibilityandComponent Docs: Featuresto match the existingComponent Docs: Anatomy/Component Docs: Stylingentries. -
4e3b3fe: Document CSS variable fallback and partial token override expectations for Rad UI styled layers.
-
05c0ccb: Add
defaultOpentoDialog.Rootand fix controlled/uncontrolled handlingDialogPrimitive.Rootnow acceptsdefaultOpenfor uncontrolled usage and switches from hand-rolleduseState+useEffectsyncing to the existinguseControllableStatehook.This also fixes controlled mode. Previously
openwas seeded into local state and re-synced by an effect, so a controlled dialog still opened when the trigger was clicked even though the parent heldopen={false}— the effect only re-ran whenopenitself changed. WithuseControllableState, a definedopenis authoritative: the dialog reports the request throughonOpenChangeand waits for the parent to update.AlertDialoginherits the same fix. ItsonOpenChangetest asserted the old behaviour (clicking the trigger opened a controlled dialog) and now asserts correct controlled semantics: the request fires, the dialog stays closed until the parent rerenders withopen={true}. -
55d0806: fix(dialog): merge consumer props through getItemProps on Action and Cancel
DialogPrimitiveActionandDialogPrimitiveCancelspread consumer props after the props returned bygetItemProps, so a consumer handler replaced the library's own handler instead of running alongside it.On
Action, passing anonClicksilently suppressed the close: the internalhandleOpenChange(false)was overwritten, so the dialog stayed open.Both parts now pass consumer props into
getItemPropsand composeonClickexplicitly, so the consumer handler runs first and the dialog still closes:{...getItemProps({ ...props, onClick: (e) => { onClick?.(e); handleOpenChange(false); } })}DialogPrimitiveContextTypealso widensgetItemProps,getReferencePropsandgetFloatingPropsto accept optional user props, and the default context value returns those props unchanged instead of{}. -
ebcab1f: Expose Dialog trigger
data-state,data-disabled, andaria-expandedattributes from the primitive. -
79e711d: Add Disclosure keyboard interaction and ARIA reference documentation.
-
51a45ac: Expose stable Disclosure anatomy data slots and initialize
defaultOpen. -
87604b7: Document Drawer keyboard interactions and ARIA references.
-
7d338d9: docs(drawer): add the component documentation page and align the internal context type
-
1ec9c6d: fix(drawer): resolve peekStyle temporal dead zone in DrawerContent
DrawerContentreferencedpeekStyleinside thegetFloatingPropscall before the variable was declared further down the component body. Theconstdeclarations sat below an earlyreturn, so the reference landed in the temporal dead zone. The file failed to type-check, which broke four Drawer test suites at compile time.Hoisting the
peekOffset/peekStylepair above thegetFloatingPropscall fixes the ordering.getFloatingPropsis also cast to its user-props signature, matching the convention already used by Dialog, Combobox and Menu. -
54b05e7: Add a Fieldset component with native legend semantics, grouped form-control slots, accessibility docs, and state data attributes.
-
f2c6bfb: Document HoverCard keyboard interactions and ARIA references.
-
4095f30: Expose stable
data-slotanatomy markers on HoverCard root, trigger, content, and arrow parts. -
285c5c8: Document Live Region keyboard behavior and ARIA references.
-
0f04b58: docs(a11y): add ARIA references for select, combobox, and dropdown menu
-
465e6d1: Document NumberField keyboard interactions and ARIA spinbutton references.
-
66a89d5: Expose Popover anatomy data slots for root, anchor, trigger, content, close, and arrow parts.
-
db4c358: Collapsible primitives respect
prefers-reduced-motionAdds a
usePrefersReducedMotionhook and uses it to resolve the Collapsible height transition.transitionDurationwas previously defaulted to300inCollapsiblePrimitive.Root, which meant the content type could not tell "the consumer asked for 300ms" apart from "nobody asked for anything". The default is removed and the value is resolved inCollapsiblePrimitiveContent:const resolvedTransitionDuration = transitionDuration !== undefined ? transitionDuration : prefersReducedMotion ? 0 : DEFAULT_TRANSITION_DURATION;So an unset
transitionDurationnow collapses the animation entirely whenprefers-reduced-motion: reduceis set, rather than still animating for 300ms. An explicittransitionDurationalways wins, which is the escape hatch for anyone who needs to keep animating.The hook is SSR-safe: it returns
falseuntil mounted, then follows the media query, and supports bothaddEventListenerand the legacyaddListenerAPIs.Note that
transitionDurationonCollapsiblePrimitiveRootPropsis unchanged for consumers — it was already optional at the prop level; only the internal default is gone. -
f83f617: Document Progress keyboard behavior and ARIA references.
-
16ed885: Render indeterminate Progress without determinate ARIA value attributes.
-
09da0e2: Keep Progress.Indicator out of the accessibility tree as a second progressbar while preserving its styling data attributes.
-
669e5d8: Document Radio keyboard interaction and ARIA reference tables.
-
06d4f89: Expose stable Radio state and anatomy data attributes.
-
903a35d: Expose stable RadioGroup anatomy markers through
data-slotattributes on root, label, item, and indicator parts. -
e0a9b2d: Document Slider and RadioCards keyboard interactions and ARIA references.
-
e6dde24: Add ScrollArea keyboard interaction and ARIA reference tables to the docs.
-
0ccabb4: fix(scrollarea): hide the scrollbar while a document overlay is open
An overlay (Dialog, Menu, Popover, Combobox, Drawer) covers the page and locks body scroll, but a
ScrollAreawithtype="always"still painted its scrollbar and thumb on top of it.Adds a document-level overlay registry and suppresses the scrollbar and thumb while any overlay is open:
const isVisible = !overlaySuppressesScrollbar && (type === "always" || (type === "auto" && isOverflowing) || (isOverflowing && (type === "scroll" || type === "hover") && scrollbarVisible));The registry is ref-counted and mirrors its state onto
data-rad-ui-overlay-openon<html>, so overlays stack correctly (two open overlays decrement back to zero rather than clearing early).ScrollAreapicks the state up viauseDocumentOverlayOpenState, which also observes the attribute andbody's inlinestyle, so overlays from other libraries that only lock body scroll are covered too.resetDocumentOverlayOpenForTestsis wired into the globalafterEachinsetupTests.tsso the registry cannot leak between suites. -
ffab89f: Expose stable Select anatomy markers through
data-slotattributes on root, trigger, content, group, item, item text, and item indicator parts. -
8f8574c: Add Select documentation for large option lists.
-
ed757fa: Improve Splitter handle accessibility with separator range attributes, Home/End keyboard resizing, and matching docs tables.
-
6c18fcb: docs(a11y): add ARIA references for alert dialog, context menu, and menubar
-
279d111: Document Switch keyboard interactions and ARIA pattern references.
-
bfd1085: Expose stable Switch anatomy data slots and keep the thumb visual-only for assistive technologies.
-
5f1af85: Expose stable Tabs anatomy markers through
data-slotattributes on root, list, trigger, and content parts. -
8284dc5: Added TextArea accessibility docs covering keyboard behavior and native multiline textbox semantics.
-
8ca42a5: Expose TextField anatomy through stable
data-slotattributes. -
88c11a3: docs(toast): add keyboard interaction and ARIA reference tables
-
ebe6ba7: Document Toggle and Toggle Group keyboard interactions and ARIA references.
-
32d31e8: Expose Tooltip anatomy data slots for root, trigger, content, and arrow parts.
-
91d18f6: Document Tree keyboard interactions and ARIA tree view references.
-
dc01059: docs(Tree): add large-data branch mounting guidance and correct item API docs.
Release
v0.6.0
Minor Changes
- fa1a1d5scroll area visual types
- 1535c08new toast component
Patch Changes
- 3a9ef46Add configurable
loopfocus-wrapping options for Tabs, Disclosure, Tree, and NavigationMenu, including NavigationMenu content panels. - b7d4406Restore Menubar horizontal keyboard navigation when focus is inside open menu content.
- b7d4406Preserve inline AspectRatio styles while applying its ratio value.
- b577317popover - improve radix api parity, modal focus behavior, arrow styling, and positioning stability