Skip to content

v1.0.3

Choose a tag to compare

@github-actions github-actions released this 04 Aug 23:03
ea09502

Overview

🔧 Bug Fixes

  • Slider,Rating: omit aria-label when ariaLabelledby is provided (#784)
  • a11y: complete non-button host polyfill for default-button controls (#751)
  • AlertDialog: focus the Cancel element on open per the APG alertdialog pattern (#630)
  • useBreakpoints: resolve initial state through matchMedia like update() (#730)
  • Input: make the type prop reactive (#757)
  • NumberField: default accessible name on the spinbutton (#772)
  • NumberField: add ariaLabelledby prop for aria-labelledby on the spinbutton (#640)
  • Progress: only emit aria-labelledby when Progress.Label is mounted (#638)
  • useResizeObserver: report the content box from immediate entries (#729)
  • Slider: default accessible name via locale when unlabeled (#771)
  • Snackbar: stop inline position:relative from overriding portal positioning (#781)
  • Group,Selection,Single,Step: complete the provider Item ARIA contract (#613)
  • Rating: name the slider and drop focusable stars from examples (#773)
  • Snackbar: announce toasts via persistent portal live regions (#774)

Other Commits

  • Select: fix double role=option in Item examples (#775)

Patch Changes

  • #784 e697bab Thanks @johnleider! - fix(Slider,Rating): omit aria-label when ariaLabelledby is provided

    When both ariaLabel and ariaLabelledby are provided, aria-labelledby now consistently wins across Slider, Rating, and NumberField — aria-label is omitted from the DOM. Assistive technology output is unchanged (the ARIA accessible-name algorithm already prefers aria-labelledby); only the emitted attributes are now consistent.

  • #751 f6b8698 Thanks @johnleider! - fix(a11y): complete non-button host polyfill for default-button controls

    Controls that default to as="button" now apply a consistent host contract when the element is not a native button: role="button", tabindex 0/−1, and Enter/Space activation (gated so native buttons keep browser handling only). Covers Carousel Next/Previous, Dialog and AlertDialog activators/actions/close, Pagination First/Prev/Next/Last, Popover Activator, Snackbar Close, plus gating on Collapsible/ExpansionPanel activators and Toggle.

  • #630 9bd0517 Thanks @sridhar-3009! - fix(AlertDialog): focus the Cancel element on open per the APG alertdialog pattern (#630)

    The WAI-ARIA Authoring Practices alertdialog pattern calls for initial focus to land on the least-destructive action, and the docs already stated this — but AlertDialogContent never actually called .focus() on the Cancel element, so focus landed wherever the browser defaulted. AlertDialogCancel now registers its DOM element on the shared context, and AlertDialogContent focuses it immediately after opening.

  • #770 b9f69ed Thanks @johnleider! - fix(useBreakpoints): resolve initial state through matchMedia like update() (#730)

    createBreakpoints() previously derived its initial breakpoint name, band flags, and isMobile from an innerWidth comparison, while update() used matchMedia. At fractional zoom or with classic scrollbars the two can disagree, so bare createBreakpoints() consumers could get a wrong first paint that silently flipped band on the first resize. Initial state now resolves through the same matchMedia-based logic as update(). SSR and no-matchMedia environments keep the width-comparison fallback.

  • #766 2a91437 Thanks @sridhar-3009! - fix(Input): make the type prop reactive (#757)

    Input.Root's type prop was destructured once at setup and assigned as a plain
    value onto InputRootContext — every sibling field on the context is a ref or
    getter, but type was not. Input.Control read that frozen value, so changing
    :type on Input.Root after mount (the classic password reveal-toggle pattern)
    never reached the rendered <input>'s DOM type attribute.

    type is now placed on the context as toRef(() => type), and Input.Control
    reads root.type.value, matching how every other reactive context field is
    consumed.

  • #772 000304c Thanks @johnleider! - fix(NumberField): default accessible name on the spinbutton (#772)

    NumberField.Control rendered no accessible name unless a label or ariaLabelledby prop was set, failing the axe label rule in the documented default shape. The spinbutton now falls back to the locale-driven NumberField.label message (default: "Number"), matching the increment/decrement buttons. Providing label or ariaLabelledby overrides the default as before.

  • #640 a75e37f Thanks @sridhar-3009! - fix(NumberField): add ariaLabelledby prop for aria-labelledby on the spinbutton (#640)

    NumberField.Control previously only supported accessible naming via the label string prop, rendered as aria-label. Consumers who render a visible <label> element outside the component had no way to wire it to the spinbutton. ariaLabelledby now flows through NumberField.Root's context and renders as aria-labelledby on the spinbutton, suppressing aria-label when both are set to avoid a conflicting accessible name.

  • #638 7ead8a0 Thanks @sridhar-3009! - fix(Progress): only emit aria-labelledby when Progress.Label is mounted (#638)

    Progress.Root unconditionally emitted aria-labelledby pointing to a label id even when no Progress.Label child was mounted, creating a dangling IDREF that assistive technology could misreport. aria-labelledby is now conditional on a label actually being present, and a new ariaLabel prop covers the case where no visible label exists but an accessible name is still needed.

  • #769 5457353 Thanks @johnleider! - fix(useResizeObserver): report the content box from immediate entries (#729)

    With immediate: true, the first synthesized entry's contentRect carried border-box dimensions in viewport coordinates, scaled by CSS transforms — every later entry from the observer reports the content box with padding offsets. The immediate entry now matches native semantics: width/height are the content box and top/left are the computed padding offsets, so useElementSize reports content-box dimensions from mount instead of jumping on the first resize.

  • #771 a9b7da7 Thanks @johnleider! - fix(Slider): default accessible name via locale when unlabeled (#771)

    Slider.Thumb rendered role="slider" with no accessible name unless ariaLabel or ariaLabelledby was passed, failing axe's aria-input-field-name rule (serious) out of the box. The thumb now defaults its aria-label to the localized Slider.label message, falling back to "Slider", and skips the default when ariaLabelledby is provided so the referenced label wins.

  • #781 9bf5b4c Thanks @johnleider! - fix(Snackbar): stop inline position:relative from overriding portal positioning (#781)

    Your positioning classes on Snackbar.Portal (absolute, fixed, …) work again — the slot style now carries only zIndex. A wrapper that is still position: static after mount gets position: relative applied automatically, so the stacking-context guarantee from #602 is preserved. In renderless mode, make sure the wrapper you render is positioned for the z-index to take effect.

  • #633 cd59970 Thanks @sridhar-3009! - fix(Group,Selection,Single,Step): complete the provider Item ARIA contract (#613)

    aria-selected is only valid on roles like option and tab, so the state the provider Items emitted was ignored by assistive technology. SelectionItem and SingleItem now emit role="option", StepItem emits role="tab", and all four Item families (including GroupItem's existing role="checkbox") ship tabindex plus Enter/Space (Space for checkbox) keyboard activation so the bound element is operable without consumer completion.

  • #773 47342cf Thanks @johnleider! - fix(Rating): name the slider and drop focusable stars from examples (#773)

    Rating.Root now exposes ariaLabel and ariaLabelledby props and always emits an accessible name on the role="slider" element — a locale-driven "Rating" default applies when neither is set. Documented examples no longer render Rating.Item as <button>, so the slider contains no focusable descendants; items stay non-focusable spans and click-to-select is unchanged.

  • #775 8191795 Thanks @johnleider! - docs(Select): fix double role=option in Item examples (#775)

    The documented Select.Item and Combobox.Item usage spread the slot attrs onto an inner element inside a non-renderless Item, so following it produced a nested duplicate role="option" (axe aria-required-parent, critical) and click handlers that fired twice. The Treeview Cue/Checkbox/Indicator/SelectAll and Radio Root/Group examples had the same shape. If you copied any of these, remove the inner v-bind="attrs" spread and put your content directly in the slot — or add renderless so your element is the only one rendered.

  • #774 77f1f59 Thanks @johnleider! - fix(Snackbar): announce toasts via persistent portal live regions

    Snackbar.Portal now auto-renders a new Snackbar.Announcer — a visually-hidden polite + assertive live-region pair, empty from app start — and each Snackbar.Root mirrors its rendered text into the matching region on mount (urgent routes to the assertive/alert region), so screen readers reliably announce the first toast, including on NVDA where JS-injected role="status" regions are not announced. Toast content renders immediately with no delay; identical consecutive messages re-announce. Pass :announcer="false" on the Portal to omit the pair or place <Snackbar.Announcer> yourself; a bare Snackbar.Root without a Portal keeps its role attributes as best-effort.