Repository navigation
Minor Changes
-
d88791c:
AutocompletetakesselectedText, a function of the count that words the "N selected" text amultipleautocomplete's toggle shows, so the toggle can follow the page's language. The text was English with no override. -
5cd6580: Breaking:
@chassis-ui/css0.12 is required (the peer range is>=0.12.0 <0.13.0). No prop, export
or class name of this package changes, and no component renders a class whose rule 0.12 changes;
a page changes where its own classes do. A border width or style class, such as.border-xlor
.border-style-dashed, setsborder-widthandborder-styleon its element only, no longer the
inherited--cx-border-widthand--cx-border-stylethat reached every border inside it; on a
component it changes the outline, and the component's variable, such as--cx-card-border-width,
changes its dividers too. Inchassis.cssa longhand utility wins over a shorthand on the same
element:place-items-center align-items-baselinealigns to the baseline, and-m-2xl mb-0gives
a bottom margin of0. The dividers of a card, popover, toast, tabs and table read
--cx-border-style, and the inner corners of a card, accordion, popover and toast follow the
component's own radius and width. The changelog of@chassis-ui/css0.12.0 has the full list. -
5c03ff5:
Table,StaticTableandDataGridwithsmnow render compact, with half the cell padding: chassis-css has no size modifier for a table, so the.smrule ships in this package'sstyle.css. Before,smrendered a class nothing styled.Breaking:
Drawerno longer renders anonmodalclass, andNavTitleno longer rendersnav-title. chassis-css styles neither (its.nonmodalapplies to.dialogonly). A stylesheet that targets either class should select the drawer by.drawer:not(:modal)and the title by a class of its own passed throughclassName. -
6fd1de8: The native fields are typed with the attributes their element has (#88).
- Breaking:
SelectPropsextendsSelectHTMLAttributes, notInputHTMLAttributes:readOnly,type,pattern,min,max,checkedand the other input attributes a<select>ignores are type errors now.readOnlyrendered areadonlyattribute the select didn't honour: it still changed and firedonChange. - Breaking:
FileInputPropshas novalueordefaultValue. A file input's value is the files chosen, and a string given threwInvalidStateErrorat mount. - Breaking:
ColorInputPropsandRangeInputPropshave noplaceholderorreadOnly, which HTML doesn't apply totype="color"ortype="range";RangeInputdocumented a read-only state it never had.
- Breaking:
-
c6878bf:
NavItemandPaginationputclassName, the ref and every other attribute on one element, as the other components do.NavItemgainslinkPropsfor its link's own attributes, andPaginationgainslistClassNamefor its.paginationlist.Breaking: a
NavItemwith a link now puts its attributes (id,data-*,aria-*,style, …) on its<li>, besideclassNameand the ref; onlyhref,active,disabled,component,asChildandonClickstill go to the link, and anything else for it, such astargetor a router link'sto, moves tolinkProps.Pagination'sclassNamenow goes to the<nav>, beside the ref and the other attributes; pass the list's classes aslistClassName. -
2229e27: A
Navitemsentry withouthrefnow renders its link as a<button>, soactiveanddisabledapply to it, and entries take anonClick. Such an entry rendered plain text that dropped both. -
e7eae98:
TableRowtakes anid, the row's key as it is given, asDataGridRowandTreeItemalready do. A row's only key was React'skey, which React makes a string, so with the docs'key={row.id}on rows whose ids are numbers,selectedKeys={new Set([1])}selected nothing,disabledKeys={[2]}disabled nothing andonSelectionChangereported"1". Write<TableRow id={row.id}>:idwins over akeygiven beside it. The docs' examples now use it.
Patch Changes
-
23f9bdb: A
Modal,DrawerorPopovergivenaria-labelledby={undefined}keeps its automatic name (#66): theModalTitleorDrawerTitle, or aPopover's title or trigger. A wrapper that forwards an optional prop (<Modal aria-labelledby={props.labelId}>) passes the key with no value, and thatundefinedwrote over the computed label, leaving an unnamed dialog. A caller's ownaria-labelledbystill names the dialog in place of the title. -
cf85250: A
ToastorNotificationthat autohides now resumes its countdown after a hover or focus with the time that was left, as the docs describe. Leaving restarted the wholedelay, so a toast hovered 200 ms before it would have hidden stayed for another fulldelay. -
473139d:
<Button asChild pressed>around a link givenrole="button"now rendersaria-pressed, as<Button component="a" role="button" pressed>does. That is the documented way to make a router link a toggle;Buttonlooked for the role in its own props, whereasChildnever puts the child's, so the pressed state was not announced. -
cd8c7e6: A
Buttonkeeps anaria-pressedof the caller's whenpressedisn't given:<Button aria-pressed="mixed">is a tri-state toggle again, and a toggle whose state is kept outsidepressedis announced as one.pressedwas written after the caller's attributes asaria-pressed={undefined}, which removed the caller's on a<button>, an<input>and an element given button semantics.pressedstill wins when both are given. -
47f3e51:
Button,CloseButtonand a clickableLinkrendered on a plain tag (component="span","div") keep atabIndexand arolethe caller passes, as they already did on a<button>or an<a>. Therole="button"andtabIndex={0}they add to such a tag were written over the caller's, so atabIndex={-1}for a roving tab stop in a toolbar, or arole="menuitem", was lost. A disabled one takestabIndex={-1}for anytabIndexgiven, as a disabled<button>stays out of the tab order. -
2db54ba: A
CalendarorRangeCalendargiven anaria-labelkeeps the name react-aria composes for it with the visible month:aria-label="Event date"names the widget "Event date, March 2024" again, as a screen reader announces on entering it. The raw prop was spread after react-aria's and replaced that name with "Event date" alone. -
3859a34: A
Carouselno longer callsonSlidor announces "Slide N of M" when it mounts underReact.StrictMode(the default in development for Next.js and Vite templates). StrictMode runs mount effects twice, and the second run took the mount for a slide change: screen readers heard the position of every carousel on page load, andonSlidreceived{ from: 0, to: 0 }. Both now fire only when the active slide changes. -
49d57dd:
readOnlyon aCheckboxorSwitchmakes it read-only: it keeps its state,onChangedoesn't fire, and it carriesaria-readonly="true". The prop went to the input as the nativereadonlyattribute, which browsers ignore on a checkbox or radio, so the control still toggled and nothing said it was read-only. It works inside aCheckboxGroupand for the radio-backedSwitchtoo, and the control can still be focused. -
ab4534c: A
ChipInputwith no chips is one tab stop, its text input. react-aria makes an empty tag group focusable so that a message for the empty state can be reached;ChipInputrenders none, so Tab from the field before it landed on a zero-size element, and a second Tab was needed to reach the input. Removing the last chip, with Backspace or its close button, moved the focus onto the same invisible element. The empty list is now focusable by script only, and the focus react-aria gives it after the last chip goes moves on to the input. -
670cab7: A
CloseButtonwhose icon is swapped throughchildrenkeeps its "Close" name. Any child used to cancel the fallback, anaria-hiddenIconor<svg>included, so every close button with a custom icon and nolabelwas a button with no accessible name. The fallback now gives way only to children that name the button themselves: text, an element that holds text or carries its ownaria-label,aria-labelledbyortitle, or a component other thanIconwhose output it can't read (a<FormattedMessage>). Give an icon component from another libraryaria-hidden. -
91ec10b: On Android, the first tap on a
ContextMenuitem after a long press now chooses it. The click that iOS emits when the finger lifts is swallowed so it doesn't land on the item that has just opened under it; Android emitscontextmenuinstead and no click, so the swallow stayed armed for a second and ate the user's real tap. A long press that the browser reports withcontextmenu, or a cancelled pointer, now leaves nothing to swallow. -
ecc2e88: A
ContextMenuunder a right-to-left locale opens leftwards from the pointer, its start edge at the pointer as the docs describe. It asked forbottom-endunder RTL, which react-aria mirrors again, so the menu extended rightwards as in a left-to-right page. -
3e97d53:
DataGrid'salignaligns the body cells' content (#101):align="middle"on a fixedrowHeightgrid centres it, asTable'saligndoes. The prop set avertical-alignclass on the grid alone, which reached none of the virtualizer's absolutely positioned cells. A cell under a grid withalignrenders its content in one.datagrid-cell-content, which the stylesheet places; the header row keeps its own alignment, as a table's does. -
3e97d53:
<DataGrid hover>highlights body rows alone (#103). The hover rule matched every[role="row"], the header row included, whereTablehoverstbodyrows only. -
526c637:
DataGrid's pinned-column scroll cue follows the content width (#102). It was computed on mount, onscrolland on a resize of the grid, so a column set that changed without either (a column added, adefaultWidthor apinchanged) left thepin="end"shadow in its previous state until the user scrolled. The grid observes its scroll content as well. -
3ed4c88:
DatePickerandDateRangePickernow portal their calendar popover, asCombobox,AutocompleteandPopoverportal their panels: todocument.body, or into the open<dialog>the picker is in. Rendered in place, the calendar was clipped by any ancestor that scrolls (anoverflow: autobox, atable-responsive, a scrolling card or dialog body), and that ancestor scrolled instead of the calendar covering what is under it. The.datepickerpopover is no longer a sibling of the field in the DOM, and a picker rendered open sends no calendar in its server HTML: it appears once the page has hydrated.The segments of
DatePicker,DateRangePickerandTimeFieldsit closer together: each pads its sides by--cx-space-4xsinstead of--cx-space-3xs, so a time reads9:30 PMrather than9 : 30 PM. The dash between the two dates of aDateRangePickeris spaced by--cx-space-xsinstead of--cx-gap. -
9ed518a: A
Modal,AlertorDrawerwithkeyboard={false}stays open however many times Escape is pressed. Chromium honours a preventedcancelonce per user activation, so a second Escape in a row closed the dialog anyway. The Escape keydown of a modal dialog that keeps Escape out is now prevented too, which sends nocancelat all, and the dialog bounces as it does for the first press. An overlay open inside the dialog still takes the key first. -
015bfd3: A
Modal,DrawerorAlertstays open when a press that starts inside it is released over the backdrop (#122): selecting text in a field and letting go outside it, or dragging a crop handle past the dialog's edge. The browser fires theclickof such a press on the nearest common ancestor of its two ends, the<dialog>itself, which was taken for a click on the backdrop and closed the dialog, or bounced it withbackdrop="static". Only a press that goes down and comes up on the backdrop closes or bounces it now, as in@chassis-ui/css's own dialogs. -
7f28789: A
Modal,DrawerorAlertshown again before the browser delivers thecloseevent of its last close stays open (#65). Browsers fire that event from a queued task, after whatever ran in the same task as the close: a wizard that opens its next step fromonHidden, or avisibleset tofalseand back in one event, reopened the dialog first, then took the late event for the browser's own close, firedonCloseandonVisibleChange(false), and closed it. APopoverorTooltipopen in a dialog reopened that way closed too. The event is now left alone when the dialog is open again by the time it arrives; a close the browser makes by itself (a<form method="dialog">, a second Escape) still closes the dialog. -
5214071: The server HTML of a
ModalorDrawerno longer pointsaria-labelledbyat an id it doesn't hold (#67). Before hydration every dialog was labelled by the id a title takes by default, whether a title rendered or not and whatever id it had, so a dialog with no title, or with<ModalTitle id="...">, shipped a dangling reference in every page, closed dialogs included. A dialog rendered withopenis now labelled in its server HTML by the title found among its children, under the id that title renders (its ownid, theasChildelement's, or the generated one), and by none when it has no title; a closed dialog has noaria-labelledbyuntil hydration. After hydration nothing changes: the dialog is labelled by the title that renders. -
af5212e: A
Modal,DrawerorAlertis no longer a stop of its own in the Tab order in Firefox and Safari, which take in a<dialog>without a tabindex and focus it with no ring. ANavbar'sresponsivedrawer shown inline was an invisible stop between the brand and the links, and a non-modalModalorDrawerone before its first control. The dialog now renderstabIndex={-1}. A caller'stabIndexis kept, including when the dialog takes the focus on opening. -
acef387: Of two
Drawers mounted open in the same commit (twodefaultVisibleorvisibledrawers restored from the URL, say), only the last stays open (#68), as the docs promise of a second drawer that opens while one is open. Both stayed open before, two modal dialogs in the top layer at once, each holding a scroll lock: a drawer registered itself for the others to close only after the commit, by when both had already opened. The first now gets a close request (onClose,onVisibleChange(false)), as it does when the second opens later. -
de8fb7c: A
FloatingInputshows itsinvalidFeedbackwhen a submit is held back by the field inside it:<FloatingInput invalidFeedback="Enter your email." label="Email"><TextInput name="email" required /></FloatingInput>marked the empty input as invalid on submit and said nothing. The message now appears with the mark and describes the input until the field has a value or the form is reset, and aFormwithvalidatedshows thevalidFeedbackof a valid field the same way. Aninvalidorvalidset on theFloatingInputstill decides on its own. -
e88ec2c: A form reset puts every field back to its first value, controlled or not, and reports it through
onChange(#86), as the text fields already did. A controlledSelectwent to its first option while its state kept the edited value, which the next render put back; a controlledColorInputorRangeInputstayed as edited; a controlledOtpInputreset to""instead of its first code; andDatePicker,DateRangePicker,AutocompleteandChipInputkept the edited value in what the form submitted. -
a7b0166:
GridandGridItemrender no class for a value@chassis-ui/csshas no rule for, and warn in development (part of #116): aspan,startorendpast twelve columns, arowSpan,rowStartorrowEndpast six rows, a breakpoint'scolumnsorrowspast them, and a count that isn't a whole number from 1 up (rows={2.5}), which set an invalidrepeat()a browser drops.GridItemalso warns forrowsorgapwithoutsubgrid, which set nothing. The basecolumnsof aGridpast twelve still falls back to--cx-grid-columns. -
2f2c71d:
I18nProviderPropsis exported besideI18nProvider(#118). A consumer wrapping the provider in one of their own had to type its props from react-aria's package, the copy theI18nProvidercomment says not to use, since the one this package inlines is whatDatePickerand the other fields read their locale from. -
3b698b8: A text field,
<select>,<textarea>, radio, range or editable element inside aMenu,MenuSubmenuorContextMenulist keeps its own arrow, Home and End keys. The menu's navigation took them from it: Home and End no longer moved the caret, and ArrowDown or ArrowUp left the field for the first or last item, so a<select>in the list could not be operated with the arrows. Escape still closes the menu from inside a field, and a checkbox, which has no use for the arrows, still steps on to the items. -
98dc4e6: A click on the
<label>of a form control inside aMenu's orContextMenu's list keeps the menu open, as one on the control already did. The click lands on the label, or the text inside it, before the browser passes it on to the control, and the check for a form control looked at that element's tag name alone, so the docs' own Forms example closed the menu when "Remember me" was ticked. A click is now used in place when it lands in aninput,select,option,textarea,labelor editable element inside the list, or on aformthere itself: a button in that form still closes the menu. A menu that sits inside a page's form still closes on an item. -
5def6c9: A
MenuorContextMenucloses on a click outside it on a react-aria control too: aTab, aCheckbox, aSelectorComboboxtrigger, a calendar cell, aTableorDataGridrow. react-aria's press handling stops thepointerdownandclickof the element it is on, and the menus heard outside presses onwindowonce they had bubbled there, so the menu stayed open beside the control just used. They now see those events in the capture phase too, and handle one that never bubbles up once its dispatch is over. -
65456a5: Choosing a
Menuitem with the mouse no longer leaves a focus ring on theMenuToggle. Focus still returns to the toggle, so it doesn't fall to the page from the closed list, but without the ring, which comes back with the next key press. A choice made with Enter or Space, and Escape, return focus with the ring as before. The browser decides whether focus moved by script shows a ring from what was focused before, so the ring used to appear only sometimes. -
e943df1: A
Menuno longer callsonHideandonHidden(oronShowandonShownwithdefaultVisible) when it mounts underReact.StrictMode. StrictMode runs mount effects twice, and the second run reported the menu's initial state as a change, so every menu on a page in development sent a spurious hide on load. The four callbacks now fire only when the menu opens or closes. -
cf391e5: A disabled
MenuTogglenever opens its menu.disabledreached only the rendered element, and react-aria's trigger opened on a press and on Enter, Space and the arrow keys all the same. ANavLinkor<a>root (the navbar pattern), or the defaultButtonwithhref, opened on a click as well, and the default<button>on apointerdown. The toggle now passesdisabledto react-aria too. A click on a disabled<a>toggle runs noonClickeither. -
a440a9b:
Select,FileInput,ColorInputandRangeInputkeep their visiblelabelin the accessible name when given anaria-labelledbyof their own, asTextInputdoes:<Select label="Country" aria-labelledby="extra" />is named "Country Extra". The caller'saria-labelledbyoutranked the<label for>, and the name became "Extra". The label now carries an id, and the control is labelled by it and the caller's ids together; without anaria-labelledbyof the caller's, the control is labelled by the<label for>as before. -
579e9b2: An
Alert,ModalorDraweropen inside another dialog closes when that dialog closes (#69), as aPopoverorTooltipin it already did. Left open, it stayed in the top layer under a dialog no longer displayed: invisible, still holding the scroll lock, and, opened as a modal, with the rest of the page inert, where the defaultkeyboard={false}of anAlertleft Firefox and Safari users no way out. It now gets a close request (onClose,onVisibleChange(false)) and hides at once, withonHidden. -
4ed5734:
OtpInput: Backspace on a filled box clears that box in place, as otp-input.mdx says, and a key other than a digit does nothing. Both used to rebuild the boxes from the code as one string with no gaps, so every later digit moved one box left: correcting the third digit of123456gave1 2 4 5 6 _. The boxes now keep what was typed in each, and the digits after a box cleared inside the code can still be focused to correct them.onChangestill reports the digits as one string,12456for1 2 _ 4 5 6, and a code that comes from elsewhere (a controlledvaluethat differs from it, a form reset) fills the boxes from the first. A non-digit key no longer callsonChangewith the unchanged code. -
278a571:
OtpInputtakesform(#91), the id of a form elsewhere on the page: the boxes and the hiddennameinput join it, a submit of it carries the code and a reset of it puts the code back. The other fields already took it; this one typedformas an error and, cast through, put it on the group's<div>. -
22d9307: A
Placeholderwithsrcand noaltrendersalt="", a decorative image. It took the generated graphic's label as alt text, so<Placeholder src="/hero.jpg" width={100} height={100} />was announced as "Placeholder: 100x100", a graphic that isn't shown. Passaltto describe the real image. -
c00402f: A
Popoverwith atitleand anaria-labelof the caller's is named by thearia-label, as aModalorDraweris. The panel kept the title'saria-labelledbybeside it, which outranksaria-label, so the caller's name was ignored. The title still shows. -
5d3d6ee:
PopoverandTooltippanels are as tall as their content (#71). react-aria wrote the room it found beside the trigger as an inlinemax-heighton the panel, which nothing scrolled: a popover taller than the room, a form in a popover on a short viewport, overflowed its own border, and a tooltip placed before it had a size keptmax-height: 0px. The menus keep the room and scroll inside it since 0.5.1; a popover's arrow sits at its edge, which a scrolling panel would clip, so these release it, as chassis-css's own popovers have no cap. -
6762a96:
RadioGrouptakesvalue={null}anddefaultValue={null}(#94):nullclears a controlled selection, as it does forCombobox,Autocompleteand the date and time fields. The props were typedstring, andundefined, the only value the type allowed for "none", makes react-stately treat the group as uncontrolled and keep the previous choice, so a form reset in TypeScript had no legal way to unselect a controlled group. -
839bbb4:
Table,DataGridandTreewithselectionMode="single"now hold one selected key at most. A selection of several, kept fromselectionMode="multiple"or passed with"single", stayed selected in full and was never reported; the first key now stays, andonSelectionChangereports the trimmed selection once. -
106dad1: A
stackedMenuSubmenuis no longer clipped by the list it is in above thesmbreakpoint, a regression of 0.5.1. Since then everyMenulist kept the room react-aria finds beside its trigger as an inlinemax-heightand scrolled inside it; astackedpanel renders inside its parent list, so that list cut it off and scrolled instead of letting it sit beside its trigger. A list that holds astackedsubmenu now keeps the theme's--cx-menu-max-heightonly above that breakpoint, as before 0.5.1. Below it the panel opens in the list's flow, and the list still keeps the room and scrolls. -
1038e04:
<Switch type="radio">renders a checkbox withrole="switch", which ARIA allows, instead of a radio input with that role, which failed axe'saria-allowed-role. It still behaves as one of a radio group: switches of the samenamehave one on at a time, a click leaves the one that is on,requiredasks for one of the group, and the form submits thevalueof the one that is on. -
5c9e548: A
Tablerow listed indisabledKeysis skipped by the arrow keys and carriesaria-disabled="true", asdisabledKeyspromises and asDataGridalready did. Under react-stately's defaultdisabledBehaviorthe row could only not be selected: it still took focus from the keyboard, and nothing told a screen reader it was disabled. -
32943b7:
<Toast animation={false}>appears at once (#108). Withoutfadethere is no transition to run, but the toast still went through theshowingphase for the 50 ms fallback wait, and chassis-css paints.toast.showingat opacity 0 whether or not.fadeis there: the toast was invisible for a tick and then popped in. It hides at once too, andonClosefires then. -
9a34213: Closing one of several toasts or notifications with the mouse no longer keeps the rest on screen. The click focused the close button, and when the toast went the region handed focus on to the next toast, whose autohide paused for as long as it held it: it stayed until the user clicked somewhere else. As in react-aria's own region, a toast closed with the pointer now gives focus back to the element that had it before it entered the region, and the others hide after their
delay. Closed from the keyboard, a toast still hands focus to the next one. -
b865a30: A
ToasterorNotificationStackgiven its ownonFocus,onBlur,onKeyDownor pointer handlers keeps the region's: both run. The caller's props were spread after react-aria's and the region's, and replaced them, so with anonFocusof the caller's, closing a toast from the keyboard dropped focus to<body>instead of handing it to the next toast. -
76ba0b6: A
Treekeeps its tab stop when a controlledexpandedKeyscollapses the row above the focused one (a "Collapse all" button, for example). react-aria-components kept the hidden row as the tree's focused key, so no rendered row could be tabbed to: Tab no longer entered the tree until a row was clicked, and when the hidden row had the focus, it fell to<body>. The collapsed row now takes the focused key, as it does when its own chevron collapses it, and the focus too when the hidden row had it.