UNPKG

@ariakit/react

Version:

Toolkit for building accessible web apps with React

523 lines (355 loc) 71.9 kB
# @ariakit/react ## 0.4.32 ### Faster keyboard navigation on composite widgets Moving through items with arrow keys no longer re-renders the [`Composite`](https://ariakit.com/reference/composite) component itself when using roving tabindex. This reduces the scripting cost of each keystroke on large collections and benefits everything built on composite widgets, such as [`Menu`](https://ariakit.com/reference/menu), [`Combobox`](https://ariakit.com/reference/combobox), [`Toolbar`](https://ariakit.com/reference/toolbar), and [`Tab`](https://ariakit.com/reference/tab). ### Fixed `Command` stuck pressed state when losing focus mid-press When rendering a non-native element (such as `render={<div />}`), the [`Command`](https://ariakit.com/reference/command) component — and components built on it, such as [`Button`](https://ariakit.com/reference/button), [`Checkbox`](https://ariakit.com/reference/checkbox), [`CompositeItem`](https://ariakit.com/reference/composite-item), and their derivatives — now clears its pressed state (`data-active`) when the element loses focus while <kbd>Space</kbd> is held, mirroring how native buttons cancel the Space activation when they lose focus before the keyup. Additionally, a Space keyup bubbling up from a focused child no longer dispatches a synthetic click on the element, and calling `event.preventDefault()` in a custom `onKeyUp` handler no longer leaves the element stuck looking pressed. ### `PopoverArrow` box-shadow ring detection Fixed [`PopoverArrow`](https://ariakit.com/reference/popover-arrow), including components built on it such as [`TooltipArrow`](https://ariakit.com/reference/tooltip-arrow), [`MenuArrow`](https://ariakit.com/reference/menu-arrow), and [`HovercardArrow`](https://ariakit.com/reference/hovercard-arrow), to draw the popover's box-shadow ring for any positive ring width. Previously, widths whose text contained the digit 0, such as `10px` or `0.5px` from the Tailwind `ring-[10px]` and `ring-[0.5px]` utilities, were not detected, and the arrow rendered with no stroke at all. The arrow stroke now also matches the ring color instead of the popover's inherited text color, so the arrow blends into the outline. This includes `inset` rings and rings without an explicit color, which default to `currentColor` following CSS. ### Radio `onChange` event on arrow-key selection Selecting a native [`Radio`](https://ariakit.com/reference/radio) or [`FormRadio`](https://ariakit.com/reference/form-radio) with arrow keys now delivers a real `change` event with `event.target.checked` already set to `true`, matching pointer and Space selection. Previously, the handler received the focus event while `checked` was still `false`, which silently broke handlers gated on `event.target.checked`. Since arrow-key selection now replays the browser's native activation, `onClick` handlers also fire when a native radio is selected with arrow keys, matching native radio group behavior. ### Other updates - Improved the performance of the composite store's [`next`](https://ariakit.com/reference/use-composite-store#next), [`previous`](https://ariakit.com/reference/use-composite-store#previous), [`up`](https://ariakit.com/reference/use-composite-store#up), and [`down`](https://ariakit.com/reference/use-composite-store#down) functions, which now scan the rendered items without copying arrays in the most common cases. - Fixed [`ComboboxItemValue`](https://ariakit.com/reference/combobox-item-value) highlighting the wrong characters for item values whose Unicode normalization changes the string length, such as Hangul, kana with dakuten, and decomposed (NFD) strings. Matching remains diacritic-insensitive, and the `data-user-value` spans now cover exactly the matched characters without detaching combining marks from their base letters. - Fixed [`CompositeItem`](https://ariakit.com/reference/composite-item) and components built on it, such as [`Tab`](https://ariakit.com/reference/tab) and [`SelectItem`](https://ariakit.com/reference/select-item), crashing the app with a "Maximum update depth exceeded" error when a `NaN` value was passed to the `aria-posinset` or `aria-setsize` props. The `useStoreStateObject` hook now compares snapshot values with `Object.is`, so the fix also covers any direct consumer of that hook. - Fixed [`CompositeItem`](https://ariakit.com/reference/composite-item) crashing with `Cannot access 'rowId' before initialization` when rendered inside a [`CompositeRow`](https://ariakit.com/reference/composite-row) — or a derived row component such as [`SelectRow`](https://ariakit.com/reference/select-row) or [`ComboboxRow`](https://ariakit.com/reference/combobox-row) — that receives the `aria-posinset` prop. - Fixed [`CompositeItem`](https://ariakit.com/reference/composite-item) and components based on it, such as [`SelectItem`](https://ariakit.com/reference/select-item) and [`ComboboxItem`](https://ariakit.com/reference/combobox-item), leaving DOM focus stuck on the item with virtual focus enabled when the item received focus before the composite element was available, instead of redirecting focus to the composite element. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) and components built on it such as [`Popover`](https://ariakit.com/reference/popover) and [`Menu`](https://ariakit.com/reference/menu) hiding on close before the [`backdrop`](https://ariakit.com/reference/dialog#backdrop) element's exit transition ends: the backdrop's fade-out was skipped entirely when only the backdrop was animated, and cut short when its transition was longer than the panel's. - Fixed [`Dialog`](https://ariakit.com/reference/dialog), including components built on it such as [`Popover`](https://ariakit.com/reference/popover) and [`Menu`](https://ariakit.com/reference/menu), so focusing, clicking, or right-clicking elements returned by [`getPersistentElements`](https://ariakit.com/reference/dialog#getpersistentelements) no longer closes the dialog before it has received focus, such as when it's rendered with [`autoFocusOnShow`](https://ariakit.com/reference/dialog#autofocusonshow) set to `false`. - Fixed [`PopoverArrow`](https://ariakit.com/reference/popover-arrow) — including arrows built on it such as [`MenuArrow`](https://ariakit.com/reference/menu-arrow) and [`TooltipArrow`](https://ariakit.com/reference/tooltip-arrow) — detaching from the anchor in RTL contexts when the popover flips or otherwise changes placement while open. - Fixed [`Portal`](https://ariakit.com/reference/portal), including components built on it such as [`Tooltip`](https://ariakit.com/reference/tooltip) and [`Popover`](https://ariakit.com/reference/popover), to avoid leaking duplicate portal containers in React development StrictMode. - Fixed [`Portal`](https://ariakit.com/reference/portal), including components built on it such as [`Dialog`](https://ariakit.com/reference/dialog) and [`Popover`](https://ariakit.com/reference/popover), destroying and recreating the portal node when the [`portalRef`](https://ariakit.com/reference/portal#portalref) prop changes identity, such as when passing an inline callback. The ref now re-fires against the same portal node, so the portal content is no longer remounted on parent re-renders. - Fixed the [`focusOnMove`](https://ariakit.com/reference/composite#focusonmove) prop being ignored on [`SelectList`](https://ariakit.com/reference/select-list) and components built on it such as [`SelectPopover`](https://ariakit.com/reference/select-popover). - Fixed arrow keys on a closed [`Select`](https://ariakit.com/reference/select) freezing the page when multiple [`SelectItem`](https://ariakit.com/reference/select-item) components without a [`value`](https://ariakit.com/reference/select-item#value) prop follow the active item, and moving to an item without a [`value`](https://ariakit.com/reference/select-item#value) when exactly one follows it. Items without a [`value`](https://ariakit.com/reference/select-item#value) are now skipped correctly, including when [`focusLoop`](https://ariakit.com/reference/select-provider#focusloop) wraps around the list. - Fixed [`TabPanel`](https://ariakit.com/reference/tab-panel) not updating its own `tabindex` when a single panel is reused with a dynamic [`tabId`](https://ariakit.com/reference/tab-panel#tabid) pointing to the selected tab. The tabbable-children check now re-runs when the [`tabId`](https://ariakit.com/reference/tab-panel#tabid) changes, so the panel joins the tab sequence when the newly selected tab's content has no tabbable elements and leaves it when the content has one. - Fixed [`Tab`](https://ariakit.com/reference/tab) not becoming the active item on the first [`setSelectedId`](https://ariakit.com/reference/use-tab-store#setselectedid-1) call after a [`SelectPopover`](https://ariakit.com/reference/select-popover) or [`ComboboxPopover`](https://ariakit.com/reference/combobox-popover) containing the tabs opens or toggles. - Fixed [`ToolbarContainer`](https://ariakit.com/reference/toolbar-container) so composing Enter keydowns in nested text fields don't cancel IME commits or move focus back to the container. - Updated dependencies: `@ariakit/react-components@0.3.1` ## 0.4.31 This version improves React form behavior and composite separators, including safer generated field name paths, form submission and validation that continue in background tabs, and explicit separator orientation handling. ### Composite separators honor explicit orientation Fixed [`CompositeSeparator`](https://ariakit.com/reference/composite-separator) to honor an explicit `orientation` prop instead of always using the composite store-derived default. This also fixes components built on it, including [`ToolbarSeparator`](https://ariakit.com/reference/toolbar-separator), [`MenuSeparator`](https://ariakit.com/reference/menu-separator), [`SelectSeparator`](https://ariakit.com/reference/select-separator), and [`ComboboxSeparator`](https://ariakit.com/reference/combobox-separator). ### `form.names.*` paths no longer crash on symbol access Fixed [`useFormStore`](https://ariakit.com/reference/use-form-store) [`names`](https://ariakit.com/reference/use-form-store#names) values throwing `Cannot convert a Symbol value to a string` when an absent symbol key was read from them. This happened whenever something probed a symbol on a raw name — most notably when React reads `Symbol.iterator` to reconcile a name rendered as a React child, but also `Object.prototype.toString.call(name)` and `Array.from(name)`. Absent symbol keys now resolve to `undefined`, matching plain-object semantics, so those probes degrade gracefully. The documented string coercion keeps working, so coerce a name before rendering or inspecting it outside Ariakit props: ```tsx <p>This field submits as {`${form.names.email}`}.</p> ``` ### Form submission no longer stalls while the tab is hidden [`useFormStore`](https://ariakit.com/reference/use-form-store)'s [`submit`](https://ariakit.com/reference/use-form-store#submit) and [`validate`](https://ariakit.com/reference/use-form-store#validate) no longer stall while the document is hidden — for example, when auto-saving a draft on `visibilitychange`. They previously awaited a `requestAnimationFrame`, which browsers pause in background tabs, so the submission only completed once the tab was brought back to the foreground. ### Other updates - Fixed [`ComboboxDisclosure`](https://ariakit.com/reference/combobox-disclosure) to honor `event.preventDefault()` in `onMouseDown` before moving focus to the [`Combobox`](https://ariakit.com/reference/combobox) input. - Fixed [`ComboboxItem`](https://ariakit.com/reference/combobox-item) so non-paste Ctrl/Cmd character shortcuts preserve focus and the combobox value when virtual focus is disabled, while paste shortcuts still route to the input. - Fixed [`ComboboxItemValue`](https://ariakit.com/reference/combobox-item-value) so overlapping user input matches are rendered without duplicated text. - Fixed [`Combobox`](https://ariakit.com/reference/combobox) inline autocomplete so decomposed Unicode input no longer produces misspelled completion values. - Fixed [`Composite`](https://ariakit.com/reference/composite) keyboard paging and [`Combobox`](https://ariakit.com/reference/combobox) scroll behavior for elements rendered inside a same-origin iframe. - Fixed [`Composite`](https://ariakit.com/reference/composite) base-element arrow key navigation in RTL composites, including components built on it such as [`Toolbar`](https://ariakit.com/reference/toolbar) and [`TabList`](https://ariakit.com/reference/tab-list). - Improved [`FormControl`](https://ariakit.com/reference/form-control) and components built on it, such as [`FormInput`](https://ariakit.com/reference/form-input), [`FormCheckbox`](https://ariakit.com/reference/form-checkbox), and [`FormRadio`](https://ariakit.com/reference/form-radio), to avoid redundant form store subscriptions and item lookups while fields update. - Fixed [`Form`](https://ariakit.com/reference/form) stealing focus into an invalid field when its items changed after a successful submission with [`resetOnSubmit`](https://ariakit.com/reference/form#resetonsubmit) set to `false`, so [`autoFocusOnSubmit`](https://ariakit.com/reference/form#autofocusonsubmit) again focuses the first invalid field only as a result of a failed submission. - Fixed [`FormPush`](https://ariakit.com/reference/form-push) to focus the newly added field when pushing into arrays with existing values or arrays that start empty. - Fixed [`useFormStore`](https://ariakit.com/reference/use-form-store) to ignore `__proto__` and `constructor` path segments in field names, preventing form state objects from being corrupted through prototype replacement. - Fixed [`Form`](https://ariakit.com/reference/form) to focus the first invalid field in document order when invalid fields mount out of registration order. - Fixed nested [`Hovercard`](https://ariakit.com/reference/hovercard) components so pressing Escape closes the topmost card even when focus is on another element. This also applies to components built on [`Dialog`](https://ariakit.com/reference/dialog). - Fixed [`Hovercard`](https://ariakit.com/reference/hovercard) so it stays open when hovering content rendered inside an open shadow root. This also applies to components built on it, such as [`Tooltip`](https://ariakit.com/reference/tooltip) and [`Menu`](https://ariakit.com/reference/menu). - Fixed text field detection for elements rendered inside same-origin iframes. This fixes [`Composite`](https://ariakit.com/reference/composite) keyboard navigation for iframe text fields, including components built on it such as [`Toolbar`](https://ariakit.com/reference/toolbar), and prevents [`Command`](https://ariakit.com/reference/command) and [`Combobox`](https://ariakit.com/reference/combobox) from treating iframe text fields as non-text fields. - Improved public JSDoc comments for component and store options. - Reduced extra [`Menu`](https://ariakit.com/reference/menu) renders when menu items re-register without changing the initial focus target. - Fixed [`MenuItemCheckbox`](https://ariakit.com/reference/menu-item-checkbox) to initialize boolean fields from [`defaultChecked`](https://ariakit.com/reference/menu-item-checkbox#defaultchecked) and controlled [`checked`](https://ariakit.com/reference/menu-item-checkbox#checked) props when the menu store has no default value. - Fixed menubar and menu bar stores to reflect navigation updates immediately before initialization. - Fixed a portaled [`Popover`](https://ariakit.com/reference/popover) or [`Dialog`](https://ariakit.com/reference/dialog) not receiving focus when reopened while a non-focusable element (such as a `display: none` file input) comes before the first focusable element in its content. This also fixes [`FormLabel`](https://ariakit.com/reference/form-label) focusing such a hidden element instead of the visible control. - Fixed [`RadioGroup`](https://ariakit.com/reference/radio-group) so tabbing back into a group focuses the checked [`Radio`](https://ariakit.com/reference/radio) after another unchecked [`Radio`](https://ariakit.com/reference/radio) has received focus. - Fixed [`SelectPopover`](https://ariakit.com/reference/select-popover) typeahead to skip disabled offscreen [`SelectItem`](https://ariakit.com/reference/select-item) placeholders. - Fixed [`ToolbarContainer`](https://ariakit.com/reference/toolbar-container) so pressing <kbd>Backspace</kbd> or <kbd>Delete</kbd> on a focused container with an empty text field no longer steals focus from the field on the next typed character. - Updated dependencies: `@ariakit/react-components@0.3.0` ## 0.4.30 ### Improved `Dialog` performance Opening a modal [`Dialog`](https://ariakit.com/reference/dialog) now marks and disables the elements outside the dialog in a single tree walk instead of two, tracks the dialog state with fewer store subscriptions, and finds the initial focus target without checking every tabbable element inside the dialog. The scroll lock, the backdrop z-index synchronization, and the root-dialog bookkeeping also moved from passive effects to the layout phase, removing several forced style recalculations and layouts from the open path and applying the scroll lock before the dialog is first painted. This also benefits components built on top of [`Dialog`](https://ariakit.com/reference/dialog), such as [`Popover`](https://ariakit.com/reference/popover), [`Menu`](https://ariakit.com/reference/menu), and [`SelectPopover`](https://ariakit.com/reference/select-popover). ### Fixed `DisclosureContent` over-waiting to unmount with mixed transitions and animations [`DisclosureContent`](https://ariakit.com/reference/disclosure-content) (and components built on top of it, such as [`Dialog`](https://ariakit.com/reference/dialog) and [`Popover`](https://ariakit.com/reference/popover)) could keep [`unmountOnHide`](https://ariakit.com/reference/disclosure-content#unmountonhide) content mounted longer than necessary when a transition and an animation were both applied and the longest delay and longest duration belonged to different properties. The unmount timeout is now the longest per-property end time (`delay + duration`) across the transitions and animations, instead of the longest delay added to the longest duration, which could overestimate the real end time and keep [`unmountOnHide`](https://ariakit.com/reference/disclosure-content#unmountonhide) content mounted longer than necessary. A leftover `duration` or `delay` with no matching transition or animation (such as an `animation-duration` while `animation-name` is `none`) is also ignored now. ### Other updates - Added an associated panel lookup to [`useTabStore`](https://ariakit.com/reference/use-tab-store), improving [`Tab`](https://ariakit.com/reference/tab) performance when resolving controlled panels. - Improved [`TooltipAnchor`](https://ariakit.com/reference/tooltip-anchor) to avoid re-rendering default description tooltip anchors when the tooltip content id changes. - Updated [`VisuallyHidden`](https://ariakit.com/reference/visually-hidden) to hide content with the modern `clip-path: inset(50%)` technique instead of the deprecated `clip` property. The same technique now applies to the other elements Ariakit hides visually, such as the [`Select`](https://ariakit.com/reference/select) value mirror and the [`Dialog`](https://ariakit.com/reference/dialog) dismiss button. - Fixed [`CheckboxCheck`](https://ariakit.com/reference/checkbox-check) to avoid passing invalid function children to React while unchecked. - Fixed the [`ComboboxGroup`](https://ariakit.com/reference/combobox-group) development error message to name the correct component. - Improved [`ComboboxItemValue`](https://ariakit.com/reference/combobox-item-value) rendering performance. - Fixed [`Command`](https://ariakit.com/reference/command) and the components that build on it, such as [`Button`](https://ariakit.com/reference/button), staying stuck in the active (`data-active`) state when the Space key is released while the Meta key is held, or after the element becomes disabled between keydown and keyup. - Fixed [`CompositeTypeahead`](https://ariakit.com/reference/composite-typeahead) so typeahead text from one composite no longer affects another composite rendered on the same page. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) cleanup so stale nested dialog effects no longer restore page accessibility state while a newer effect is active. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) to reset outside-interaction focus tracking when reused dialogs reopen. - Fixed [`FormPush`](https://ariakit.com/reference/form-push) and [`FormRemove`](https://ariakit.com/reference/form-remove) incorrectly matching sibling array fields whose names share a prefix (for example `tags` and `tags2`) and throwing when a field name contained regular expression special characters. - Fixed [`useFormStore`](https://ariakit.com/reference/use-form-store) nested value updates so path segments like `-1`, `Infinity`, and `NaN` are treated as object keys instead of array indexes. - Improved [`Hovercard`](https://ariakit.com/reference/hovercard) so nested hovercards no longer reinstall document mousemove listeners when they mount or unmount. - Fixed [`MenuItemRadio`](https://ariakit.com/reference/menu-item-radio) to avoid forwarding the `defaultChecked` prop to rendered elements. - Fixed [`Popover`](https://ariakit.com/reference/popover) to ignore stale async positioning updates after a newer positioning effect has started. - Fixed merged refs in React components and [`Portal`](https://ariakit.com/reference/portal) to preserve React 19 callback ref cleanup functions while still detaching refs that don't return a cleanup. - Updated dependencies: `@ariakit/react-components@0.2.0` ## 0.4.29 ### Fixed `Combobox` dropping characters when the popover resizes while typing The [`Combobox`](https://ariakit.com/reference/combobox) component with [`autoSelect`](https://ariakit.com/reference/combobox#autoselect) enabled no longer loses typed characters when the popover is resized as the user types. This could happen with a virtualized list on mobile devices, where the keyboard's autocomplete bar repeatedly changes the available viewport height. Each resize re-rendered the list and re-applied the auto-selection, briefly moving focus away from the input and dropping keystrokes. ### Composite items keep their enclosing store Fixed [`CompositeItem`](https://ariakit.com/reference/composite-item) to register on the enclosing [`Composite`](https://ariakit.com/reference/composite) store when rendered as the same element as a component that sets its own composite context, such as a [`MenuButton`](https://ariakit.com/reference/menu-button) inside a [`MenuProvider`](https://ariakit.com/reference/menu-provider). This keeps the item reachable with the arrow keys in one- and two-dimensional composite widgets. The [`CompositeItem`](https://ariakit.com/reference/composite-item) can now omit the explicit `store` prop and still register on the enclosing composite: ```tsx {5-7} const composite = Ariakit.useCompositeStore(); <Ariakit.Composite store={composite}> <Ariakit.MenuProvider> <Ariakit.CompositeItem render={<Ariakit.MenuButton />}> Menu </Ariakit.CompositeItem> <Ariakit.Menu> <Ariakit.MenuItem>Edit</Ariakit.MenuItem> </Ariakit.Menu> </Ariakit.MenuProvider> </Ariakit.Composite>; ``` ### Components no longer throw on events with a non-element target Several components attach global event listeners that read `event.target`/`event.relatedTarget` and call methods like `contains()` and `hasAttribute()` on them. When third-party code dispatched an event whose target was a non-element `EventTarget` (such as `window` or an `XMLHttpRequest`), those calls threw a `TypeError`. This affected [`Dialog`](https://ariakit.com/reference/dialog) (its interact-outside and Escape-to-close listeners), [`HovercardDisclosure`](https://ariakit.com/reference/hovercard-disclosure) (its focusout listener), and the shared `isFocusEventOutside` and `isPortalEvent` helpers used by [`Focusable`](https://ariakit.com/reference/focusable), [`Combobox`](https://ariakit.com/reference/combobox), [`Composite`](https://ariakit.com/reference/composite), and [`Portal`](https://ariakit.com/reference/portal). ### Other updates - Improved performance of components that subscribe to internal store state by upgrading the underlying [`@ariakit/store`](https://www.npmjs.com/package/@ariakit/store) package. - Fixed [`Button`](https://ariakit.com/reference/button) to preserve React form pending state when submitted with the keyboard. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) to preserve closing animations when using [`unmountOnHide`](https://ariakit.com/reference/dialog#unmountonhide) with the controlled [`open`](https://ariakit.com/reference/dialog#open) and [`onClose`](https://ariakit.com/reference/dialog#onclose) props and no explicit store. - Documented that [`removeValue`](https://ariakit.com/reference/use-form-store#removevalue) preserves array length by replacing removed items with `null`. - Fixed runtime `process.env.NODE_ENV` checks in published package output, including test-only behavior and development warnings. - Fixed rendering many [`Menu`](https://ariakit.com/reference/menu) components on the same page potentially causing a "Maximum update depth exceeded" error. [`MenuItem`](https://ariakit.com/reference/menu-item) elements now register only while the menu is visible, instead of registering on mount even while it's hidden. - Fixed [`Tab`](https://ariakit.com/reference/tab) to move focus to the selected tab after a controlled [`selectedId`](https://ariakit.com/reference/tab-provider#selectedid) update while a tab has DOM focus. - Fixed [`TooltipProvider`](https://ariakit.com/reference/tooltip-provider) to avoid a re-entrant loop when multiple tooltips are forced open at the same time. - Updated dependencies: `@ariakit/react-components@0.1.2` ## 0.4.28 - Release artifacts now include npm trusted publishing provenance. - Updated dependencies: `@ariakit/react-components@0.1.1` ## 0.4.27 - Fixed [`Menu`](https://ariakit.com/reference/menu) to respect the [`autoFocusOnShow`](https://ariakit.com/reference/menu#autofocusonshow) prop when set to `false` or when a callback returns `false`, while still allowing arrow keys to move focus into an already-open menu. - Updated dependencies: `@ariakit/react-components@0.1.0` ## 0.4.26 This version focuses on bug fixes across [`Dialog`](https://ariakit.com/reference/dialog), [`Portal`](https://ariakit.com/reference/portal), [`Radio`](https://ariakit.com/reference/radio), [`Combobox`](https://ariakit.com/reference/combobox), and [`MenuButton`](https://ariakit.com/reference/menu-button), along with improvements to how portals behave inside fullscreen elements, how dialogs handle events in popup windows, and how [`Radio`](https://ariakit.com/reference/radio) groups automatically generate unique `name` attributes. ### Fixed events not handled in popup windows [`Dialog`](https://ariakit.com/reference/dialog) and components that extend it, such as [`Menu`](https://ariakit.com/reference/menu) and [`Popover`](https://ariakit.com/reference/popover), now handle events correctly when rendered in a popup window opened via `window.open()`. [`hideOnEscape`](https://ariakit.com/reference/dialog#hideonescape), [`hideOnInteractOutside`](https://ariakit.com/reference/dialog#hideoninteractoutside), and focus restoration now use the content element's `ownerDocument` instead of the main window's `document` for event listeners. ### Fixed `Portal` not rendering inside fullscreen elements [`Portal`](https://ariakit.com/reference/portal) was always appended to `document.body`, which made it invisible when an ancestor element entered fullscreen mode via the Fullscreen API. Portals are now automatically moved to `document.fullscreenElement` when it's active, and back to `document.body` when fullscreen is exited. ### Auto-generated `name` attribute for `Radio` [`Radio`](https://ariakit.com/reference/radio) now automatically uses the [`RadioGroup`](https://ariakit.com/reference/radio-group) store's `id` as the default [`name`](https://ariakit.com/reference/radio#name) attribute when no explicit `name` prop is provided. This ensures consecutive [`RadioGroup`](https://ariakit.com/reference/radio-group) components have unique names, preventing the browser from treating all radio inputs as a single group and fixing Tab navigation and form submission issues. ### Other updates - Fixed [`Combobox`](https://ariakit.com/reference/combobox) pressing Enter from submitting a parent form when the popover is open but has no matching items. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) not removing `data-enter` when closed after using the [`render`](https://ariakit.com/reference/dialog#render) prop to wrap the dialog element in an outer element. - Fixed [`Dialog`](https://ariakit.com/reference/dialog) not restoring focus to the disclosure element when the dialog was opened and closed quickly in succession. - Fixed [`MenuButton`](https://ariakit.com/reference/menu-button) `aria-haspopup` attribute changing from `"menu"` to `"dialog"` when opening a menu that contains a combobox. - Fixed [`render`](https://ariakit.com/guide/composition) prop merging when the rendered element passes falsy `className` or event handler values such as `undefined` or `null`. - Fixed `Math.random()` being called unconditionally when creating composite stores ([`useTabStore`](https://ariakit.com/reference/use-tab-store), [`useComboboxStore`](https://ariakit.com/reference/use-combobox-store), [`useSelectStore`](https://ariakit.com/reference/use-select-store), etc.), even when an explicit `id` prop was provided. This was causing Next.js build errors with `cacheComponents` enabled. - Updated dependencies: `@ariakit/react-core@0.4.26` ## 0.4.25 ### Clicking outside no longer restores focus to the disclosure element [`Dialog`](https://ariakit.com/reference/dialog) and components that extend it (such as [`Menu`](https://ariakit.com/reference/menu) and [`Popover`](https://ariakit.com/reference/popover)) no longer restore focus to the disclosure element when the dialog is closed by clicking or right-clicking outside. This aligns with native HTML `<dialog>` and `popover` behavior where trigger buttons don't receive focus when you interact outside. Focus is still restored normally when the dialog is closed by other means, such as pressing Escape, selecting a menu item, or calling `store.hide()` programmatically. In these cases the disclosure element is now focused with default browser scrolling instead of `preventScroll`. ### Improved Safari focus behavior for buttons, checkboxes, and radio buttons On Safari, buttons, checkboxes, and radio buttons don't receive focus on mousedown like other browsers. Previously, this was handled by manually focusing the element in a `mousedown` handler. Now, an explicit `tabIndex` attribute is set on these elements in Safari, which causes the browser to focus them natively. This results in more predictable focus behavior and fewer timing-sensitive workarounds. ### Other updates - Fixed a race condition in [`Dialog`](https://ariakit.org/reference/dialog) where the deferred auto-focus could steal focus from the disclosure element after the dialog was closed. - Fixed [`formStore.setError()`](https://ariakit.org/reference/use-form-store#seterror) and [`formStore.setFieldTouched()`](https://ariakit.org/reference/use-form-store#setfieldtouched) failing to set values on nested array field paths such as `items.0.name`. - Fixed [`SelectItem`](https://ariakit.com/reference/select-item) store item `children` property reflecting the [`value`](https://ariakit.com/reference/select-item#value) prop instead of the actual rendered text content. - Fixed [`MenuButton`](https://ariakit.com/reference/menu-button) to preserve [`accessibleWhenDisabled`](https://ariakit.com/reference/menu-button#accessiblewhendisabled) behavior when composed with a rendered [`Button`](https://ariakit.com/reference/button). - Fixed [`Menu`](https://ariakit.org/reference/menu) with [`modal`](https://ariakit.org/reference/menu#modal) and [`getPersistentElements`](https://ariakit.org/reference/menu#getpersistentelements) so focusing a persistent [`MenuButton`](https://ariakit.org/reference/menu-button) doesn't immediately move focus back to the menu. - Fixed [`TabPanel`](https://ariakit.com/reference/tab-panel) not re-evaluating tabbable children when the panel becomes visible. - Fixed components not dropping their internal `aria-labelledby` when `aria-label` is passed. - Updated dependencies: `@ariakit/react-core@0.4.25` ## 0.4.24 This release improves React combobox and form reliability, including preserved combobox input and popover scroll position during result updates, more predictable focus behavior after filtering selects on iOS Safari, proper isolation of [`FormRadio`](https://ariakit.org/reference/form-radio) groups inside nested composite widgets, safer handling of explicitly undefined [`id`](https://ariakit.org/reference/select-item#id) props, and better generic typing for [`CheckboxProvider`](https://ariakit.org/reference/checkbox-provider) wrappers. ### Improved `CheckboxProvider` generic typing This fixes TypeScript errors when wrapping [`CheckboxProvider`](https://ariakit.org/reference/checkbox-provider) in generic React components. Controlled and uncontrolled checkbox group wrappers now type-check correctly without requiring non-null assertions on values such as `defaultValue`. Before, generic wrappers often needed a non-null assertion to satisfy the provider props: ```tsx {15} interface CheckboxCardGridProps<T extends string | number> extends Pick< Ariakit.CheckboxProviderProps<T>, "value" | "setValue" | "defaultValue" > {} function CheckboxCardGrid<T extends string | number>({ value, setValue, defaultValue, }: CheckboxCardGridProps<T>) { return ( <CheckboxProvider value={value} setValue={setValue} defaultValue={defaultValue!} /> ); } ``` Now the same wrapper can type-check without the workaround: ```tsx {4} <CheckboxProvider value={value} setValue={setValue} defaultValue={defaultValue} /> ``` ### Fixed `Combobox` input scroll position resetting When a [`Combobox`](https://ariakit.org/reference/combobox) input's text overflowed its width, the input's horizontal scroll position reset to the beginning each time the results changed. This happened because the virtual focus mechanism briefly moved DOM focus to the active item and back when [`autoSelect`](https://ariakit.org/reference/combobox#autoselect) was enabled, causing browsers to reset the input's internal `scrollLeft`. The scroll position is now preserved across these focus transitions. ### Fixed `FormRadio` registering to ancestor composite stores [`FormRadio`](https://ariakit.org/reference/form-radio) items nested inside components like [`TabPanel`](https://ariakit.org/reference/tab-panel) were incorrectly registering to the tab store, causing arrow keys in the tab list to navigate to radio items instead of other tabs. [`FormRadioGroup`](https://ariakit.org/reference/form-radio-group) now resets the composite context for its children, preventing form radio items from being picked up by unrelated parent stores. ### Other updates - Fixed [`ComboboxPopover`](https://ariakit.org/reference/combobox-popover) scroll position resetting when items change in multi-select mode (e.g., during infinite scroll). - Fixed [`SelectItem`](https://ariakit.org/reference/select-item) stealing focus from the combobox input when the selected item reappears after filtering, which dismissed the keyboard on iOS Safari. - Fixed components crashing with "Maximum call stack size exceeded" when the [`id`](https://ariakit.org/reference/select-item#id) prop is explicitly passed as `undefined`. - Updated dependencies: `@ariakit/react-core@0.4.24` ## 0.4.23 - Fixed [`ComboboxDisclosure`](https://ariakit.org/reference/combobox-disclosure) so pressing Escape closes [`ComboboxPopover`](https://ariakit.org/reference/combobox-popover) when the popover starts open and the [`Combobox`](https://ariakit.org/reference/combobox) input is auto-focused. - Updated dependencies: `@ariakit/react-core@0.4.23` ## 0.4.22 - Fixed [`MenuItemRadio`](https://ariakit.org/reference/menu-item-radio) so controlled reset states are reflected correctly by the menu item and [`MenuItemCheck`](https://ariakit.org/reference/menu-item-check). - Updated dependencies: `@ariakit/react-core@0.4.22` ## 0.4.21 - Fixed an error when trying to reach focusable elements within iframes. - Fixed issues with React 19 types. - Updated dependencies: `@ariakit/react-core@0.4.21` ## 0.4.20 - Fixed `RefObject` types for React 19. - Updated packages to target ES2018 (previously ES2017). - Updated dependencies: `@ariakit/react-core@0.4.20` ## 0.4.19 - Updated `Array` types to `ReadonlyArray` for better compatibility. - Fixed the automatic border width on [`PopoverArrow`](https://ariakit.org/reference/popover-arrow) to account for the device pixel ratio. - Updated dependencies: `@ariakit/react-core@0.4.19` ## 0.4.18 ### Improved Combobox performance Thanks to [@iamakulov](https://github.com/iamakulov), the [Combobox](https://ariakit.org/components/combobox) component now opens ~30% faster by removing unnecessary calls to an internal function that adds global event listeners. See the [pull request](https://github.com/ariakit/ariakit/pull/4860) for more details. ### Other updates - Improved [`PopoverArrow`](https://ariakit.org/reference/popover-arrow) default appearance when using semi-transparent borders. - Updated dependencies: `@ariakit/react-core@0.4.18` ## 0.4.17 - Restored support for React 17 in [`PopoverArrow`](https://ariakit.org/reference/popover-arrow). - Updated dependencies: `@ariakit/react-core@0.4.17` ## 0.4.16 ### Improved `PopoverArrow` The [`PopoverArrow`](https://ariakit.org/reference/popover-arrow) component now attempts to infer its border width from the popover’s `box-shadow` style when all lengths are `0px` and the spread radius exceeds `0px` (e.g., `box-shadow: 0 0 0 1px black`), which is commonly known as a "ring". If the border width cannot be inferred, you can use the new [`borderWidth`](https://ariakit.org/reference/popover-arrow#borderwidth) prop to define it. This ensures a consistent size regardless of the arrow's size, which wasn't achievable before when manually setting the CSS `stroke-width` property. In addition, the arrow’s SVG path has been slightly modified to be more angled in the pointing direction. Note that you can always provide your own SVG using the `children` prop. ### Scrolling behavior when closing dialogs and popovers When hiding a dialog or popover, the [`finalFocus`](https://ariakit.org/reference/dialog#finalfocus) element will no longer scroll into view. This change prevents scrolling issues when the element lies outside the viewport and mirrors the behavior of native HTML dialog and popover elements. ### Other updates - Fixed [`data-focus-visible`](https://ariakit.org/guide/styling#data-focus-visible) attribute removal on lower-end devices. - Fixed [Select](https://ariakit.org/components/select) not passing down the [`disabled`](https://ariakit.org/reference/select#disabled) prop to the native select element. - Fixed [Dialog](https://ariakit.org/components/dialog) initial focus behavior in Safari for non-focusable elements. - Fixed `valid` state not updating on [Form](https://ariakit.org/components/form). - Fixed [`moveOnKeyPress`](https://ariakit.org/reference/composite#moveonkeypress) being triggered with composition text commands. - Updated dependencies: `@ariakit/react-core@0.4.16` ## 0.4.15 - Fixed a regression on [Hovercard](https://ariakit.org/components/hovercard) that sometimes prevented it from closing when other popups were opened. - Fixed typings for [`onSubmit`](https://ariakit.org/reference/use-form-store#onsubmit) and [`onValidate`](https://ariakit.org/reference/use-form-store#onvalidate). - Improved JSDocs. - Updated dependencies: `@ariakit/react-core@0.4.15` ## 0.4.14 ### Improved performance on composite widgets Composite item components such as [`ComboboxItem`](https://ariakit.org/reference/combobox-item) and [`SelectItem`](https://ariakit.org/reference/select-item) now render 20-30% faster compared to Ariakit v0.4.13. This enhancement should decrease the time needed to render large collections of items in composite widgets and improve the Interaction to Next Paint (INP) metric. We're working on further optimizations to make composite widgets even faster in future releases. ### Combobox auto-scroll The [`Combobox`](https://ariakit.org/reference/combobox) component now scrolls the list to the top while typing when the [`autoSelect`](https://ariakit.org/reference/combobox#autoselect) prop is disabled. The behavior is now consistent with the [`autoSelect`](https://ariakit.org/reference/combobox#autoselect) prop, except the first item won't be automatically focused. ### Other updates - Fixed the [`item`](https://ariakit.org/reference/use-collection-store#item) method to prevent it from returning items that have been removed from the collection store. - Fixed the [`item`](https://ariakit.org/reference/use-menu-store#item) method when keeping different menu stores in sync. - Added [`id`](https://ariakit.org/reference/use-composite-store#id) prop to composite stores. - Fixed composite typeahead functionality when rendering virtualized lists. - Fixed [`SelectValue`](https://ariakit.org/reference/select-value) to display the [`fallback`](https://ariakit.org/reference/select-value#fallback) when the value is an empty array or string. - Fixed an issue where composite widgets might not navigate to the correct item when pressing <kbd>↑</kbd> while the composite base element was focused. - Improved JSDocs. - Updated dependencies: `@ariakit/react-core@0.4.14` ## 0.4.13 ### Accessible composite widgets with invalid `activeId` We've improved the logic for composite widgets such as [Tabs](https://ariakit.org/components/tab) and [Toolbar](https://ariakit.org/components/toolbar) when the [`activeId`](https://ariakit.org/reference/composite-provider#activeid) state points to an element that is disabled or missing from the DOM. This can happen if an item is dynamically removed, disabled, or lazily rendered, potentially making the composite widget inaccessible to keyboard users. Now, when the [`activeId`](https://ariakit.org/reference/composite-provider#activeid) state is invalid, all composite items will remain tabbable, enabling users to <kbd>Tab</kbd> into the composite widget. Once a composite item receives focus or the element referenced by the [`activeId`](https://ariakit.org/reference/composite-provider#activeid) state becomes available, the roving tabindex behavior is restored. ### Other updates - Fixed regression in [`focusShift`](https://ariakit.org/reference/composite-provider#focusshift). - Fixed [Radio](https://ariakit.org/components/radio) to prevent `onChange` from triggering on radios that are already checked. - Fixed [`DisclosureContent`](https://ariakit.org/reference/disclosure-content) setting an incorrect `animating` state value during enter animations. - Improved JSDocs. - Updated dependencies: `@ariakit/react-core@0.4.13` ## 0.4.12 ### Tab panels with scroll restoration Ariakit now supports scroll restoration for the [`TabPanel`](https://ariakit.org/reference/tab-panel) component. This allows you to control whether and how the scroll position is restored when switching tabs. To enable scroll restoration, use the new [`scrollRestoration`](https://ariakit.org/reference/tab-panel#scrollrestoration) prop: ```jsx // Restores the scroll position of the tab panel element when switching tabs <TabPanel scrollRestoration /> ``` By default, the scroll position is restored when switching tabs. You can set it to `"reset"` to return the scroll position to the top of the tab panel when changing tabs. Use the [`scrollElement`](https://ariakit.org/reference/tab-panel#scrollelement) prop to specify a different scrollable element: ```jsx // Resets the scroll position of a different scrollable element <div className="overflow-auto"> <TabPanel scrollRestoration="reset" scrollElement={(panel) => panel.parentElement} /> </div> ``` ### Full height dialogs and on-screen virtual keyboards A new [`--dialog-viewport-height`](https://ariakit.org/guide/styling#--dialog-viewport-height) CSS variable has been added to the [Dialog](https://ariakit.org/components/dialog) component. This variable exposes the height of the visual viewport, considering the space taken by virtual keyboards on mobile devices. Use this CSS variable when you have input fields in your dialog to ensure it always fits within the visual viewport: ```css .dialog { max-height: var(--dialog-viewport-height, 100dvh); } ``` ### Overriding composite state for specific methods The [`next`](https://ariakit.org/reference/use-composite-store#next), [`previous`](https://ariakit.org/reference/use-composite-store#previous), [`up`](https://ariakit.org/reference/use-composite-store#up), and [`down`](https://ariakit.org/reference/use-composite-store#down) methods of the [composite store](https://ariakit.org/reference/use-composite-store) now accept an object as the first argument to override the composite state for that specific method. For example, you can pass a different [`activeId`](https://ariakit.org/reference/use-composite-store#activeid) value to the [`next`](https://ariakit.org/reference/use-composite-store#next) method so it returns the next item based on that value rather than the current active item in the composite store: ```js const store = useCompositeStore({ defaultActiveId: "item1" }); const item3 = store.next({ activeId: "item2" }); ``` It's important to note that the composite state is not modified when using this feature. The state passed to these methods is used solely for that specific method call. ### Other updates - Fixed the ability to <kbd>Tab</kbd> out of a nested [Menu](https://ariakit.org/components/menu) within a modal [Dialog](https://ariakit.org/components/dialog). - Fixed CJS build on Next.js. - Enhanced performance on [Dialog](https://ariakit.org/components/dialog) backdrops. - Fixed [`Tab`](https://ariakit.org/reference/tab) to pass the [`rowId`](https://ariakit.org/reference/tab#rowid) prop when used with other composite widgets. - Improved JSDocs. - Updated dependencies: `@ariakit/react-core@0.4.12` ## 0.4.11 ### Tabs inside animated Combobox or Select When rendering [Tab](https://ariakit.org/components/tab) inside [Combobox](https://ariakit.org/components/combobox) or [Select](https://ariakit.org/components/select), it now waits for the closing animation to finish before restoring the tab with the selected item. This should prevent an inconsistent UI where the tab is restored immediately while the content is still animating out. See [Select with Combobox and Tabs](https://ariakit.org/examples/select-combobox-tab). ### Other updates - Updated [Combobox](https://ariakit.org/components/combobox) to immediately reset the [`activeId`](https://ariakit.org/reference/use-combobox-store#activeid) upon closing the popover. - Removed delay when applying the [`data-focus-visible`](https://ariakit.org/guide/styling#data-focus-visible) attribute. - Fixed mouse down on [`MenuButton`](https://ariakit.org/reference/menu-button) hiding the menu on Safari. - Improved JSDocs. - Updated dependencies: `@ariakit/react-core@0.4.11` ## 0.4.10 - Fixed a regression introduced in `v0.4.8` that set the default value of the [`accessibleWhenDisabled`](https://ariakit.org/reference/tab#accessiblewhendisabled) prop to `false` on [`Tab`](https://ariakit.org/reference/tab). - Updated dependencies: `@ariakit/react-core@0.4.10` ## 0.4.9 ### New `useStoreState` hook The [`useStoreState`](https://ariakit.org/reference/use-store-state) hook is now part of the public API. Previously used internally by dynamic `useState` hooks from Ariakit store objects, it is now available in the `@ariakit/react` package to ensure compatibility with the new React Compiler. The following snippets are equivalent: ```js const combobox = useComboboxStore(); const value = combobox.useState("value"); ``` ```js const combobox = useComboboxStore(); const value = useStoreState(combobox, "value"); ``` Besides working better with the new React Compiler, [`useStoreState`](https://ariakit.org/reference/use-store-state) is more flexible than `store.useState` as it accepts a store that is `null` or `undefined`, in which case the returned value will be `undefined`. This is useful when you're reading a store from a context that may not always be available: ```js const combobox = useComboboxContext(); const value = useStoreState(combobox, "value"); ``` ### New `ComboboxValue` component A [`ComboboxValue`](https://ariakit.org/reference/combobox-value) component is now available. This _value_ component displays the current value of the combobox input without rendering any DOM elements or taking any HTML props. You can optionally pass a function as a child returning any React node based on the current value: ```jsx <ComboboxProvider> <Combobox /> <ComboboxValue>{(value) => `Current value: ${value}`}</ComboboxValue> </ComboboxProvider> ``` ### `aria-selected` on composite items Composite items like [`ComboboxItem`](https://ariakit.org/reference/combobox-item) no longer have the `aria-selected` attribute autom