Repository navigation
@chassis-ui/react v0.5.0
Minor Changes
-
7d76ef8: Breaking:
@chassis-ui/css0.11 is required (the peer range is>=0.11.0 <0.12.0), and a project
that compiles its Sass needs@chassis-ui/tokens0.10.1. No prop, export or class name of this
package changes; css 0.8 to 0.11 change how a page looks with no change of markup. Headings and the
title roles are set in Montserrat, the base colors of the default tokens are new, and buttons,
badges, chips, pagination links, progress bars and the other components that had one lose their
background gradient ($enable-background-gradients: truekeeps it). The font family of
components is namedui:.font-uiand--cx-font-family-ui, where.font-textand
--cx-font-family-textare deprecated..h1to.h6are deprecated for.font-h1to
.font-h6. An SVG icon in aLinkwithiconLinkis drawn at1emof the link text instead of
--cx-icon-size(24 px with the default tokens); a size class such as.icon-lgon the icon makes
it larger. Blockquotes are Regular Italic instead of Light Italic. The changelogs of
@chassis-ui/css0.8.0 to 0.11.0 have the full list.style.cssis built with css 0.11 and reads the custom properties of theuifamily, such as
--cx-font-family-ui,--cx-font-size-ui-smand--cx-font-weight-ui-strong, which css 0.7
doesn't declare. The default of--cx-divider-label-font-sizeisvar(--cx-font-size-ui-sm),
the size it had.
-
36a439b: Fixes to the carousel, the time field, range presets, list items and the scrollspy, and two values that no longer render a class.
Carousel:- A controlled carousel (
activeIndex) is fed fromonSlide. The docs and the development warning also offeredonSlid, which cannot do it: it fires once a slide has arrived, and a controlled carousel doesn't leave for one untilactiveIndexnames it. The warning now fires whenonSlideis missing, with or withoutonSlid. - When the parent of a controlled carousel doesn't follow a swipe, the track scrolls back to the active slide once the swipe has been at rest for half a second, which leaves a parent that follows late the time to. It stayed on the slide the user had swiped to, and the next step that was followed didn't scroll.
onSlidreports the slidesactiveIndexmoved between when the parent chose the slide itself, or answered a request with another one. It reported the last step the carousel had asked for.- Autoplay under
ends="stop"is over when it arrives at the last slide. It waited one more interval there, with the indicator filling andCarouselPlayPausereading Pause.
- A controlled carousel (
- Breaking: an empty
TimeFieldwithrequiredholds its form back from submitting, and its first segment takes focus.requiredwasaria-requiredalone andform.checkValidity()wastrue. A form that should submit anyway needsnoValidate. - Breaking: a preset of
RangeCalendarorDateRangePickerwith an unavailable day in its range is disabled, as the grid can't select a range across one. It was committed as given. - A
ListItemwhosecomponentorasChildelement is a component, such as a router link, stays mounted when itshrefcomes and goes. It was remounted, and lost its state and focus. ScrollspyanduseScrollspymark the last section once the scrolling area is at its end. A last section shorter than the area below the activation line never became the active one.- Breaking:
CardImagerenders no class for anorientationthat@chassis-ui/csshas none for, and warns in development:toporbottomat a breakpoint, and any value at2xl. It rendered a class with no rule, such asmd:card-image-top, which did nothing unless your own CSS defined it. - Breaking:
Skeletonrenders no width class for aspanthat isn't a whole number from 1 to 12,'auto'or a boolean, such as0,13or'50%', and warns in development. It rendered a class with no rule, such asw-13/12.
-
9b2c9d1:
DataGrid'spin="end"columns are pinned inside the layout engine, from the column widths and positions it computes, instead of being measured from the page and moved by atransformafterwards.- An end-pinned column that nothing pushes off the grid stays where the layout put it. In a grid whose columns fit, it was moved by the grid's width and drawn past the last column; after a column narrower than react-stately's 75px minimum, it was drawn past the edge.
- An end-pinned column is in the page at any scroll position. One further right than the grid's width plus about a third wasn't rendered until scrolled to.
- An end-pinned header is drawn over the headers that scroll under it. It was under every header to its left.
pinis read off the column that has it, so columns in a fragment, a wrapper component or thecolumnsrender function ofDataGridHeaderpin as written. A header with a fragment pinned the wrong column, and the render-function form didn't pin to the end or get either scroll cue.- Under RTL, the end-pinned column is pinned to the left edge, and the grid takes
datagrid-scrolled-startonce scrolled. - Breaking: each scroll-cue shadow is on the edge content scrolls under, the trailing edge of the start group and the leading edge of the end group. They were on the outer edges. The defaults of
--datagrid-pin-cue-shadow-startand--datagrid-pin-cue-shadow-endchange to match, with a mirrored pair under[dir="rtl"]. A value of your own for either property is applied as before, on the edge its horizontal offset gives; to match the new default, flip the sign of that offset, and give[dir="rtl"]the opposite sign. - In Firefox, a
pin="start"header stays at the edge however often the grid is scrolled. After a couple of horizontal scrolls it scrolled away with the other columns.
-
7dd8314: Fixes to form inputs, the date pickers and the combobox family.
- Breaking:
requiredis the native attribute where it wasaria-requiredalone, so a form checks it: on aNumberField's input, on the radios of a requiredRadioGroup, and on the checkboxes of a requiredCheckboxGroupwhile none is selected.form.checkValidity()wastruefor each of them when empty. A form that should submit anyway needsnoValidate. - A
Radiopasses itsformto its input. ChipInputinside aFloatingInputis named by the floating label. It kept its fallbackaria-labeland was named "Add value Skills".OtpInputfocuses the next empty box after a partial paste. Focus went to the last filled box, where the next digit typed replaced it.- Breaking:
InputAdornwithcomponent="button"istype="button"unlesstypeis given. A password reveal adorn submitted the form around it. - A
Checkbox,RadioorSwitchwith no label appliesclassName, on the.check-inputelement, wherestylealready went. - Breaking:
Chipwritesaria-pressedonly on a chip with the button role. A staticpressedchip had it on a<span>with no role, and a chip that is a link (href, or an<a>ascomponentor theasChildelement) on its<a>, which axe reports for both; the.activeclass still shows it. Give a link chiprole="button"for it to be announced as pressed, as withButton. Progresskeeps the caller'saria-labelledby.DatePickerandDateRangePickerdescribe their segments and calendar button by the help and feedback, which reached only the group around them, and the group keeps react-aria's description of the selected date.- The server HTML of a
Comboboxwith no options no longer describes its input by the no-results message, which exists only once hydrated. Autocompleteleaves focus on the field the user moved to when its list closes. It always focused the toggle.onClick,onKeyDownandonFocusgiven to it run beside the toggle's own handlers; they were replaced.- Breaking: a preset of
RangeCalendarorDateRangePickeris selected as the grid selects a range: its dates in order, kept insideminValue/maxValue, in the calendar of the current value and with that value's times. A preset with no day inside the limits is disabled.
- Breaking:
-
92e7307:
Form'svalidatedshows every field in the form as valid or invalid.- Breaking:
validatedno longer renders the.was-validatedclass.@chassis-ui/csshas no rule for it, so a validated form showed nothing: no field looked valid, and an invalid one looked invalid only after a submit it had held back. A stylesheet that targetsform.was-validatedneeds another selector. - Every field in a
Formwithvalidatedshows itself as valid or invalid by its own constraints (required,type,patternand the like), and again as its value changes: its class,aria-invalid, and itsvalidFeedbackorinvalidFeedback(errorMessageon a group). Setinvalid, tofalsetoo, orvalidon a field, and that prop decides how the field looks instead. This holds forTextInput,Textarea,SearchField,NumberField,Select,FileInput,ColorInput,RangeInput,Combobox,Autocomplete,OtpInput,ChipInput,Checkbox,Switch,RadioGroup,CheckboxGroup,TimeField,DatePickerandDateRangePicker. A control that isn't one of these, such as a plain<input className="form-input">, isn't affected.
- Breaking:
-
2a52f70: Fixes to form fields,
Navbar,Carouseland a few small components.Autocomplete: the search field is a query only. After a selection it held the option's text when the list opened again, what was typed went after it so nothing matched, and emptying it cleared the selection. It is now empty each time the list opens, and the selection changes only when an option is chosen.Autocomplete:value={null}is a controlled empty selection, so a parent can clear the toggle. It was read as novalue, and the selection went uncontrolled.- Breaking: a single
Selectwith aplaceholderand novalue,defaultValueorselectedoption starts on the placeholder, and its value is''. The browser showed the first enabled option, which was the value, and arequiredselect passed validation with nothing chosen. GivedefaultValuefor a select that should start on an option. Select: an option ofoptionswith alabel, novalueandselected: trueis selected. It renders with its label as its value, and the default was looked for under an empty value.OtpInput: a form reset puts the code back once, todefaultValueor empty, with oneonChange. Each box reset itself against the same code, which left "1234" as "123" and calledonChangefour times.OtpInput: focus on a box past the first empty one moves to that box, where the next digit goes. A digit typed in the third box of1 _ _ _landed in the second.ChipInputandCombobox: a required field that held a submit back stays marked invalid while what is typed isn't a value, and the mark goes with the chip or the option that satisfies it. The first character typed cleared the mark, and the chip that satisfied aChipInputleft it.ChipInput: atmaxChips, Enter and the separator leave the text they can't add in the input, as a paste does. They cleared it.TimeField: a required field takes focus after a submit it held back once alabelorhelpwas given later. Focus went nowhere.RangeCalendarandDateRangePicker: a preset kept insideminValue/maxValue, or given in reverse, shows as the selected preset once chosen.Switchwithtype="radio",isSelectedand noonChangeno longer logs React's warning about acheckedinput with no handler.- Breaking:
Navbar'scontainerrenderscontainerbeside its variant,container fluidorcontainer xl, which@chassis-ui/cssstyles. It renderedcontainer-fluidandcontainer-xl, which have no rule, so the wrapper had no width and wasn't laid out as the navbar's row. CSS of your own written for those class names needs the new ones. Carousel:- With fewer slides than the active index needs, the last slide there is becomes the active one, which a controlled carousel asks for through
onSlide. No slide was active, Next was disabled and Prev did nothing underends="stop". - A controlled carousel looping from its last slide to its first fires
onSlidonce the loop has arrived. It fired as the loop began. - A slide's own
aria-labelis kept. The position ("1 of 3") was written over it. - Unmounting during a loop stops its animation.
- With fewer slides than the active index needs, the last slide there is becomes the active one, which a controlled carousel asks for through
Link: anaria-currentgiven besideactiveis kept.activewrote"page"over it, as over a step's"step".BreadcrumbItemwithhref=""renders a link, to the current document.Paginationwithpages={0}renders its Previous and Next controls, disabled, and no pages. It rendered an empty navigation landmark.ButtonandCloseButtonpass atypethey are given to a component reference ascomponent. It was dropped.
-
cd92542: Fixes to how
Modal,Alert,Drawer,Popover,MenuandContextMenuopen, close and nest.- A
Modal,AlertorDrawerwhosevisiblegoes back totruewhile its exit transition runs stays open. The end of that exit closed it, withvisiblestilltrue. onClosefires once per close. Every Escape or backdrop click during the exit fired it again.- A dialog the browser closes by itself (a second Escape in a row, a
<form method="dialog">) asks to close throughonCloseandonVisibleChange(false), releases the scroll lock and firesonHidden. onShownfires for a dialog mounted withvisibleunderReact.StrictMode.- Breaking: Escape closes the innermost open overlay only. One press closed a menu, the popover it was in and the dialog around both.
- Breaking: a press inside an overlay opened from a
Popover, aMenuor aContextMenu(a nested popover, aMenuwithcontainer, a picker's list) no longer counts as a press outside it.Popovercloses on the press outside, not the click that follows, so a control that removes itself when clicked doesn't close it. - A submenu nested in a
MenuSubmenucloses with it. - Breaking:
Menu'scontainerputs the list in the open dialog the menu is in, and so does aMenuSubmenuwith its panel: both went todocument.body, behind aModal's backdrop, where a submenu could not be used. AMenucloses when the dialog around it does. ModalandDrawerhavearia-labelledbyonly when aModalTitleorDrawerTitleis rendered, under the id the title has, and not at all when given anaria-label. The attribute named an id no element had when there was no title or the title had its ownid.- Breaking: a
responsiveDrawerasks to close when the viewport grows past its breakpoint, where it stayed over a scroll-locked page.
- A
-
513a8e8: Fixes to how dialogs, popovers, menus and toasts open, close, take focus and name themselves.
- Breaking:
<Drawer scroll>with its backdrop leaves the page scrollable, asscrollsays. It locked the page whenever the drawer opened modally. - A
Modal,AlertorDrawertaken out of the tree while open ({open && <Modal visible>}) returns focus to the element that opened it. Focus fell to<body>. - A
ModalTitleorDrawerTitlewithasChildnames its dialog under the child's ownid. The dialog'saria-labelledbynamed an id no element had. - Breaking: a
Popoverheld open byvisiblewith noonVisibleChangetakes no Escape. On top of the open overlays it prevented every Escape, so aModalunder it could not close. - A
Popoverwhose trigger is in a shadow root toggles closed when the trigger is pressed. The press closed it and the click opened it again. - A
Popoverwith notitleand noaria-labelis named by its trigger (aria-labelledby, and anidon the trigger when it has none). It was a dialog with no name. PopoverandTooltipfire noonShoworonHideon mount underReact.StrictMode.- Breaking: a
PopoverorTooltipmounts its panel once, in the dialog it is in. Content in a dialog mounted twice on its first open: its effects ran twice, its state was lost and its fade with it. One mounted open now fades in. - Breaking: the list of a
Menu,MenuSubmenu,ContextMenu,ComboboxandAutocompletetakes its height from chassis-css's--cx-menu-max-height. react-aria wrote an inlinemax-heightover it. The 320px cap of aComboboxorAutocompletelist, which the components set under a name chassis-css doesn't read, now applies. - Overlays mounted open together take Escape innermost first. A
Modalmounted around an openMenutook the key from it. - Breaking: a click on a
MenuTogglecloses another openMenu. Both stayed open. - Breaking: choosing a
Menuitem returns focus to the toggle, unless the item moved it elsewhere. It fell to<body>. - A
ContextMenuinside another opens alone on a right-click or Shift+F10, and a changedidreaches the region and its list's label. - Breaking: a
MenuSubmenuwithactivation="hover"opens on a tap on a device without hover, and a click on its trigger on a device with hover no longer closes the menu. - A
ToastorNotificationhidden while hovered or focused (its close button takes focus) dismisses itself again when shown. Adelaya timer can't hold (Infinity, over 2^31 - 1 ms) runs no timer: it dismissed at once. - Closing a queued toast or notification from the keyboard moves focus to the next one, or back to where it was before the region. It fell to
<body>.
- Breaking:
-
20e97a2:
ProgressandProgressBartakeindeterminate, for work whose extent is not known: a page navigation, an upload before its size is known, a request with no progress events. The bar sweeps across the track, andProgressleaves outaria-valuenow,aria-valueminandaria-valuemax, where it used to report "0%".valueandinlineValueare ignored, andshowValuekeeps its place in the caption row empty. Withstripedthe bar fills the track with its stripes instead, moving whenanimatedis set. The sweep animatestransformonly, so it keeps running while the main thread is busy; underprefers-reduced-motion: reducethe bar shows still stripes.--cx-progress-indeterminate-animationreplaces the animation. The styles are in@chassis-ui/react/style.css. -
8e7d759:
requiredon the fields that had none, fields that show the submit they held back, andformon the groups.DatePicker,DateRangePicker,Combobox,Autocomplete,OtpInputandChipInputtakerequired. An empty one holds its form back from submitting and takes focus; a form that should submit anyway needsnoValidate.DatePickerasks for a date, or withselectionMode="multiple"for one date at least.DateRangePickerasks for both dates.ComboboxandAutocompleteask for a selected option: text typed in aComboboxwithout selecting one is not a value.OtpInputasks for every box.ChipInputasks for one chip at least: text typed and not added as a chip is not a value.
- Breaking: a field that held its form back from submitting shows as invalid, with its
invalidFeedback(errorMessageon a group), until it no longer would or the form is reset. A form with an invalid field isn't submitted, and nothing said why: react-aria cancels the browser's own message for the fields it backs, and a value in an input that isn't displayed has none. Every form field does this:TextInput,Textarea,SearchField,NumberField,Select,FileInput,Combobox,Autocomplete,OtpInput,ChipInput,Checkbox,Switch,RadioGroup,CheckboxGroup,TimeField,DatePickerandDateRangePicker. Setinvalid, tofalsetoo, orvalid, and that prop decides how the field looks instead; a form withnoValidateholds nothing back. - A
CheckboxGroupthat is no longerinvalidtakes the state off its checkboxes. They keptaria-invalidand.is-invalidonce the group had been invalid, untilinvalid={false}was set. - A radio-backed
Switch(type="radio") withinvalid={false}no longer rendersaria-invalid="false". RadioGroupandCheckboxGroupdeclareform, the id of the<form>a group rendered outside it belongs to. It already reached every radio and checkbox through react-aria; TypeScript refused it on the group.
-
e934a10: Fixes to refs and
asChild, the navigation components and the carousel.- A callback ref that returns a cleanup function (React 19) has it run when the element goes away. Every component that forks your ref with its own (
asChild, aTooltiporPopovertrigger,Link,Button) dropped the cleanup and called the ref withnullinstead. Anasynccallback ref is called; it was never called. - Under
asChild, the inline styles a component computes are merged with the child's ownstyle, the child's winning. A child with anystylelost them: aGrid's--cx-grid-gap, aGridItem'sgrid-template-rows. - A component element given to
asChild, such as a router link, stays mounted when itshrefortocomes and goes. It was remounted, and lost its state and focus. - Two copies of the package in one page, or a hot reload of it, no longer leave
outline: noneon a pressed element for good. TabandTabPanelwork with a number asid, as the type allows.<Tab id={1}>showed no panel and ignoreddisabled.selectedKey,defaultSelectedKeyanddisabledKeystake the numbers too, andonSelectionChangereports theidas theTabhas it.- An item of
List'sitemsrenders as aListItemdoes. A disabled linked item is out of the tab order and doesn't navigate, and a linked item is marked by aScrollspyaround the list. NavOverflowkeeps every item of a list that has no more thanthresholdof them. It moved them all into the menu.- An
Accordionitem with noidthat startsopenis the one expanded whenonExpandedChangeis set. A different item was. Carousel:- Autoplay follows
interval,ends,items,transitionandonSlideas they change. It kept those of the render it started in. - A controlled carousel whose parent leaves
activeIndexwhere it is keeps stepping from the slide that is shown. - When autoplay reaches the last slide of
ends="stop",CarouselPlayPauseshows Play. It is disabled on that slide while stopped, asCarouselControlNextis, and focus on it moves to the previous control: there is nothing left to play, and Play there ran one interval with no slide to go to. A cycle that would start on the last slide doesn't. - With
transition="fade",onSlidfires when the incoming slide has faded in. It fired withonSlide. - Breaking: in a right-to-left layout Left Arrow moves to the next slide and Right Arrow to the previous one, as the slides lie.
- Autoplay follows
- Breaking: a
Buttonthat is a link (href, or an<a>ascomponentor theasChildelement) no longer getsaria-pressed, which the link role doesn't take and axe reports.pressedstill applies the.activeclass; give the buttonrole="button"for it to be announced as pressed.
- A callback ref that returns a cleanup function (React 19) has it run when the element goes away. Every component that forks your ref with its own (
-
2b36f91:
gapofStacktakes an object per breakpoint, as itsdirectionand every gap ofFlexdo:gap={{ base: 'sm', md: 'lg' }}rendersgap-sm md:gap-lg. A plain value renders as before.
Patch Changes
-
35c2817:
colorofDataGridcolors the grid. It rendered the color class alone, which no rule of the grid's stylesheet reads, so a colored grid looked like one with no color. The prop now renderscontextbeside the color, on the grid and on itsfooter, andstyle.cssmaps the context colors onto the grid as@chassis-ui/cssdoes for a table. A hovered row takes the header's background, which is opaque, so that a pinned cell never shows the cells scrolled under it. -
0180b18:
DataGridno longer writes a<style>element for the shadow of its pinned columns. The HTML standard allows a<style>in<body>only with its rules in@scope, and an HTML validator reports the grid's markup since it checks that. The cells and the column header of the lastpin="start"column and of the firstpin="end"column now carrydatagrid-pin-cue-startanddatagrid-pin-cue-end, andstyle.cssgives them the shadow while the grid is scrolled, from the same--datagrid-pin-cue-shadow-startand--datagrid-pin-cue-shadow-end. The grid loses the class made fromuseId(datagrid-r…) that scoped the generated rules. -
f4d0eea:
FloatingInputties its label, help and feedback to the field inside it. It generates the ids and aTextInput,SelectorTextareainside takes them:<FloatingInput label="Email"><TextInput placeholder="…" /></FloatingInput>needs noids, noidand noaria-labelledby, and aTextInputorTextareano longer prints react-aria's "If you do not provide a visible label" warning under a floating label.idsstill replaces a generated id, and ids already repeated on the control keep working. A control that is not a field component of this library, such as a native<input>, still takes its ids throughidsby hand;FloatingInputwarns in development when no element has the id its label points at, in place of the warning aboutidsnot being set. -
06e84d0: A pressed link or button no longer gets an inline
outlinestyle. The library's document-level listener setstyle="outline: none"(outline: mediumin Safari) on every focusable element atpointerdownand took it back on blur, on any element of the page, the library's own or not. In Safari, where a clicked button takes no focus and so never blurs, the style stayed for good and the button showed no focus ring when reached by keyboard later; in the other browsers it hid the ring a key press brings back to a clicked element.The outline is now suppressed only where a press would show a keyboard ring: on the pressed element when it takes focus and matches
:focus-visible, which happens where react-aria focuses by script as the press starts (aMenuToggle, a calendar date). It is restored on blur or at the next key press, and an element that had nostyleattribute is left without one. -
8a0f9d1:
colorofSpinnercolors the spinner. It renderedfg-{color}, which sets the text color, and a spinner of@chassis-ui/cssdraws from--cx-spinner-color: every spinner stayed the default color. The prop now rendersspinner-{color}, as a whole class name. -
90455a7:
MenuSubmenuBackgoes back to the list without closing the menu. Its click reached theautoCloselistener ofMenuandContextMenu, which read it as a click on an item and closed the whole menu; the click now stops at the back item, as a click on the submenu's trigger does. Closing astackedsubmenu below thesmbreakpoint, with the back item, ArrowLeft or Escape, returns focus to its trigger: the trigger was focused while@chassis-ui/cssstill hid it, so focus fell to the page. -
cbeecae:
colorofTableandStaticTablecolors the table. It rendered the color class alone (table primary), and@chassis-ui/csscolors a table through.table.context: the table looked like one with no color. The prop now renderscontextbeside the color. -
9fb97ff:
orientation="vertical"ofTabsstacks the tabs. It setaria-orientationand the arrow keys only, so the list was announced as vertical, answered to the up and down keys, and was drawn as a row. TheTabListnow also gets theflex-columnclass. The panels stay below the list: lay out the root withclassName, for exampled-flex align-items-start gap-md, to put them beside it.