Minor Changes
-
ab9b05e: feat(DST-1491):
<AppShell>renders a skip-link to the page content<AppShell>now renders a localized "Skip to main content" link as its first focusable element. It stays visually hidden until it receives focus, and activating it moves focus to the<main>landmark of the<Page>, so keyboard and screen-reader users can bypass the sidebar and top navigation (WCAG 2.4.1). No setup is needed. The landmark is focusable (tabindex="-1") only while the link has moved focus there, so pages keep their current click behavior. -
d6b5d77: feat(DST-1668):
usePageFocusfalls back to the<main>landmark when a page has no<h1>On an
aria-label-only<Page>, a route change now moves focus to the<main>landmark instead of leaving it on the clicked link. The hook makes the landmark focusable (tabindex="-1") at that point, so afterwards clicking non-interactive page content also moves focus there. Pages with a<Title>still move focus to the heading and never make the landmark focusable. Neither focus move draws a focus ring.<Page>now accepts atabIndexfor the landmark, for example-1for a skip-link target, and the hook keeps it as it is. -
947b7a9: feat(DST-1741): wire
<FileField>into the form validation system<FileField>was the only field-shaped component that could not validate. A
required upload could not block submission, and a file rejected byaccept
disappeared with no message and nothing announced.It now takes
required,error,errorMessage,description,form,
validateandvalidationBehaviorlike every other field, plusmaxSizeto
cap a single file in bytes. Errors render through the sameHelpTextpath as a
<TextField>, so they look identical, and they are associated with the upload
button so a screen reader reads them.A file rejected by
acceptor bymaxSizenow produces a localised error
instead of vanishing. Only drag and drop can deliver such a file, because the
file browser already filters byaccept.Server errors arrive through
<Form validationErrors>, keyed by the field's
name. Resetting the form clears the selection together with any error.One behaviour change worth knowing: the hidden input now renders even when no
nameis given, because constraint validation needs the element inside the
form.namestill decides whether the file is submitted.<FileField>also used to spread its remaining props onto both the field
wrapper and the drop zone. Those areDropZoneprops, so they now reach the
drop zone only, and the component applies its own wiring after them. An
onDroppassed by a consumer no longer replaces the internal handler, which
used to stop files being added at all. Anaria-describedbypassed by a
consumer is merged with the id of the description or error instead of
replacing it.idandslotnow land on the drop zone rather than on the
wrapper. Withsize="small"there is no drop zone, so they stay on the
wrapper as before. -
2319875: feat(DST-1747): add an
addonslot to every form field labelEvery form field now takes an
addonat the end of its label, such as a<Badge>or a<ContextualHelp>. A badge there is sized automatically, so it no longer needssize="inline", and the label row keeps the height of a bare label.<Select label="Associated Team" addon={<Badge variant="master">Master</Badge>} />
Checkbox,RadioandSwitchrename theirbadgeslot toaddon. Replacebadge={…}withaddon={…}.The addon is no longer part of the field's accessible name. A badge is read as the field's description instead.
FileFielddoesn't announce its addon yet. The required indicator now sits before the addon. -
e9df73e: feat(DST-1791): add a
loadingprop to<Table>aria-busypassed to a<Table>never reached the page. React Aria filters it
out, and nothing warned about it, so tables that tried to announce their loading
state announced nothing.<Table loading>now setsaria-busy="true"on the grid itself and announces
the loading state through a polite live region. Nothing is announced when
loading finishes.Replace
aria-busyon a<Table>, or on a wrapper around it, with
loading={isLoading}.
Patch Changes
-
b67903a: Fix form fields overflowing the 320px minimum supported width.
LabelandHelpTextnow break text that has no break opportunity, such as a long compound word, a URL or a file name. Before, that text set its own minimum width and pushed the whole field past the screen. A wrapped error message also keeps its icon on the first line instead of centering it against the block. -
d1b1f15: fix(Tray): keep
Tray.Actionsreachable when the viewport shrinksTray.Contentpinned itsmin-heightto the height it measured at mount and never let go of it. When the viewport shrank afterwards, the tray box shrank but that grid row could not, so the grid overflowed andTray.Actionswalked off the bottom of the screen: at a 390px viewport the close/confirm buttons ended up 432px below the fold, with no way to reach them. Rotation, the on-screen keyboard opening in a ComboBox or Autocomplete tray, and mobile browser UI appearing all trigger it.The pin is still measured once, since it is what stops the tray resizing while a list filters down, but it is now capped at the height the viewport actually leaves for it. The content area absorbs the difference by scrolling, which is what it is for. The cap is a CSS
min()against the tray's own maximum height, so the browser re-resolves it on every viewport change and a filtered list is never re-measured.The tray container also capped itself at
95vh. On mobilevhis the large viewport, so the tray could be laid out taller than what is actually visible while browser UI is showing. It now uses the visual viewport height thatDraweralready used, applied in the component rather than the theme, so no theme has to know about it. -
ec15520: refactor(DST-1752): draw the checkbox and radio box edge from one token
The thin edge around a checkbox, a radio and the selection mark of a list row is
the only thing announcing the control before you click it, and it was written
four times: twice in the theme, once inline inCheckbox.tsx, and once in the
shared grid indicator. The indicator reached for--color-border, the divider
token, so a single-selectSelectListoption and aListViewrow drew their
mark about 1.6 times fainter than the checkbox beside it.There are now three tokens, all derived from
--color-control-borderso they
track any change to it:--color-control-edgeat +0.06 alpha for the resting
edge,--color-control-edge-hoverat +0.18, and
--color-control-edge-disabledat -0.06. Three files read them: theCheckbox
andRadiotheme slots, and the shared grid indicator. The fourth copy, the one
inline inCheckbox.tsx, is deleted rather than repointed, because it never
shipped: the theme slot's edge already beat it in the merged class list. A
resting checkbox therefore looks exactly as it did.The radio box also gains
shrink-0, which the checkbox and the grid indicator
both already carried. It is the only one of the three that sits in a flex row,
so it was the only one that could be squeezed by a long label, and the slot now
matches the mark a list row draws exactly.The disabled step is a token rather than a palette rung because a disabled mark
has three grounds to survive, and both of the opaque values this family reached
for before vanish on one of them:Disabled edge on surfaceon the page ground on a selectedrowdisabled-surface(charcoal-100)1.11:1 1.00:1 1.38:1 disabled-border(charcoal-300)1.53:1 1.38:1 1.00:1 control-edge-disabled(-0.06)1.57:1 1.57:1 1.54:1 disabled-borderis the same palette step asselected, so on a picked row an
opaque edge is not dim but gone. The translucent step holds 1.54:1 to 1.57:1
everywhere, which is the weightdisabled-borderwas chosen for in the first
place.--color-disabled-borderitself is unchanged.Three visible fixes come with it. The selection mark now dims whenever its row
is disabled, instead of waiting for the row to also be selected, so a disabled
unselected row no longer keeps an edge that reads as live. The disabled edge
across the whole family holds its weight on every ground rather than on white
alone. And a disabled control that is checked now shows what it is set to: the
check glyph and the radio dot are drawn incurrentColor,group-selected
sorts aftergroup-disabled, so the unforcedtext-disabledlost the cascade
and the mark paintedselected-bold-foregroundondisabled-surfaceat 1.06:1.The selection mark deliberately has no hover step. Inside a row the row is the
click target and already carries its own hover, so a second hover on a
decorative mark would answer a gesture nobody made.A disabled checkbox also dims its box when it is indeterminate, not only when
it is checked. Indeterminate never setsdata-selected, so it needed its own
fill rule, and without it the newly forcedtext-disabledink landed on a box
still painted the liveselected-bold.No API changes. Expect visual diffs on single-select
SelectListandListView
rows, and on disabled checkboxes and radios, including every disabled checked
one and every disabled indeterminate one. -
25b45b3: fix(DST-1836):
<Table>columns follow their container when it narrows againIn a parent that sizes itself to its content, such as
Columnsor aPanel
withmin-width: fit-content, a<Table>only ever grew. React Aria sizes the
columns to the table's container, and the container took its width from the
table, so after the window widened, the columns kept their widest width.The container no longer takes its width from the table. It keeps the sum of the
column minimums as its own minimum width, so a parent that sizes to its content
still makes room for the columns. -
e9324b2: Fix the keyboard focus ring of
BreadcrumbsandTabs.Breadcrumb links, tabs and the tab panel now show the theme's full-contrast inset focus ring. Before, breadcrumb links fell back to the browser's default outline, and tabs showed a faint halo below the 3:1 contrast a focus indicator needs. Both were clipped by their scroll or overflow container.
The tab panel gains 4px of inline padding, offset by a matching negative margin, so the ring clears its content without moving it. Breadcrumbs' auto-collapse now measures the link padding too, so it collapses at the right width.
-
f4e2236: fix(DSTSUP-280): align the Table selection and drag-handle cells with
alignYThe checkbox cell that
selectionModeadds, and the drag-handle cell that drag and drop adds, now follow the table'salignYlike every other cell. WithalignY="top", the checkbox used to stay vertically centered on tall rows. -
c860eb4: fix(DSTSUP-281): stop tooltips from getting stuck in the top left corner
A tooltip that another tooltip had replaced could come back after a click on its trigger, pinned to the top left corner of the screen and never closing. This showed up most often on the icon-only
Sidebar.Rail. Closed tooltips now unmount, so they can no longer return unpositioned.Tooltipno longer acceptsdefaultOpenandonOpenChange. They never worked onTooltipitself. Set them onTooltip.Triggerinstead, which is whereopenmoved in v18. -
ce5eaec: fix(DSTSUP-283): truncate the current breadcrumb when it does not fit
When auto-collapsed Breadcrumbs are down to the ellipsis and the current item, and the current item still does not fit, it now truncates with an ellipsis. Before, it was clipped mid-word at the container edge. The full label stays available to assistive technology.
-
@marigold/system@18.3.0