Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/dev/s2-docs/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default Layout;

export const section = 'Getting started';
export const hideNav = true;
export const hideFromSearch = true;

<WelcomeHeader />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Accessibility is at the core of all of our work on the React Spectrum team, and

Our initial implementation followed the typical ARIA patterns such as [slider](https://www.w3.org/WAI/ARIA/apg/patterns/slider/) to implement ColorArea, ColorSlider, and ColorWheel, and [listbox](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/) to implement ColorSwatchPicker. This provided good support for mouse, touch, and keyboard input, but the screen reader experience left something to be desired. Out of the box, screen readers would only announce raw channel values like “Red: 182, Green: 96, Blue: 38”. I don’t know about you, but I can’t imagine what color that is just by hearing those numbers!

<video src={initialVideoUrl} style={{width: '100%', display: 'block', margin: '20px auto'}} controls preload="metadata" />
<video src={initialVideoUrl + '#t=0.1'} style={{maxWidth: 'min(100%, 700px)', display: 'block', margin: '20px auto'}} controls preload="metadata" aria-label="Demo of VoiceOver announcement in ColorArea before improvements" />

## Improving screen reader announcements

Expand Down Expand Up @@ -167,6 +167,6 @@ After developing this algorithm to generate color descriptions, we integrated it

The video below shows interacting with a ColorArea with color descriptions. You can also try it yourself with a screen reader in the example above.

<video src={finalVideoUrl} style={{width: '100%', display: 'block', margin: '20px auto'}} controls preload="metadata" />
<video src={finalVideoUrl + '#t=0.1'} style={{maxWidth: 'min(100%, 700px)', display: 'block', margin: '20px auto'}} controls preload="metadata" aria-label="Demo of VoiceOver announcement in ColorArea after improvements" />

Check out our [ColorPicker](../ColorPicker.html) components in React Aria to build accessible, customizable, and styleable color pickers in your own applications.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Mobile browsers often introduce delays before emulated mouse events like onClick

The CSS `:active` and `:hover` pseudo-classes are also affected by mouse event emulation. For example, when tapping down on a button and dragging your finger off, the active state persists even when your finger is not over it. This makes it appear like lifting your finger will activate the button when it will not. This is not how native buttons behave, so it can feel inconsistent with user expectations.

<video src={draggingVideoUrl} style={{width: '100%', display: 'block', margin: '20px auto'}} controls preload="metadata" />
<video src={draggingVideoUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />

## Pointer events

Expand All @@ -84,7 +84,7 @@ Touch events can also be canceled by scrolling. If you touch a button and then s

Text selection gestures are another case where we need to determine the user's intent. On iOS, for example, a long press begins text selection. However, when pressing a button, you wouldn't usually expect text selection to start.

<video src={textSelectionVideoUrl} style={{width: '100%', display: 'block', margin: '20px auto'}} controls preload="metadata" />
<video src={textSelectionVideoUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />

It is possible to add the `user-select: none` CSS property to the button to make it non-selectable, but even with that enabled, Safari still tries to select elements nearby. The only way to avoid this is to add `user-select: none` to the entire page. We wouldn't want to do this all the time though, because some elements should allow text selection to occur. React Aria automatically handles adding `user-select: none` to the page on touch start on a pressable element, and removes it after a short delay on press up. The delay is necessary because iOS may begin selecting even after touch up within some threshold.

Expand All @@ -108,7 +108,7 @@ Each of these events receive a unified `PressEvent` object rather than the under

With the [usePress](../usePress.html) hook, our buttons handle interactions consistently. Dragging your pointer off the button correctly removes the active state, text selection is canceled, and issues with emulated mouse events are avoided.

<video src={buttonVideoUrl} style={{width: '100%', display: 'block', margin: '20px auto'}} controls preload="metadata" />
<video src={buttonVideoUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />

Try a live example for yourself in our [Button](../../s2/Button.html) docs!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ There are many aspects of focus management, and perhaps we will cover more in fu

An important feature for keyboard users is a **focus ring**. This is a visual affordance for the currently focused element, which allows a keyboard user to know which element they are currently on. It may only be visible when navigating with a keyboard, however, so as not to distract mouse and touchscreen users.

<video src={focusRingVideoUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />
<video src={focusRingVideoUrl} loop autoPlay muted style={{maxWidth: 'min(100%, 640px)', display: 'block', margin: '20px auto'}} />

As you can see in the above video, the focus ring appears around each button when it receives keyboard focus, but when the user interacts with a mouse it does not appear. To implement this, we attach global event listeners for pointer, keyboard, and focus events at the document level and keep track of the most recent input modality that the user was interacting with. If the user most recently interacted with a keyboard or assistive technology, we show the focus ring, otherwise we do not show it.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ After many months of research, development, and extensive testing across browser
- **Asynchronous loading** — Autocomplete suggestions can be loaded asynchronously, and large lists can be loaded on demand through infinite scrolling.
- **Customizability** — React Aria hooks allow full control over the rendering and styling of your ComboBox component, while letting us take care of the behavioral complexities for you. Use our default filter or you can provide custom filtering for complete control.

<video src={comboboxVideoUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '40px auto'}} />
<video src={comboboxVideoUrl} loop autoPlay muted style={{maxWidth: 'min(100%, 1920px)', display: 'block', margin: '40px auto'}} />

## Building a ComboBox

Expand Down Expand Up @@ -72,7 +72,7 @@ Luckily for us, iOS 13 added support for the [VisualViewport](https://developer.
when the onscreen keyboard was opened or dismissed by listening to the VisualViewport's `resize` event. Leveraging these two allowed us to create a tray that properly adjusts to the presence of iOS onscreen keyboard. Check out the video below
to see how the ComboBox tray worked before and after we switched to the VisualViewport API. If you'd like to track the visual viewport size in your own app, you can use the [useViewportSize](https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/utils/src/useViewportSize.ts) hook available in the `@react-aria/utils` package.

<video src={comboboxVisualViewportUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />
<video src={comboboxVisualViewportUrl} loop autoPlay muted style={{maxWidth: 'min(80%, 350px)', display: 'block', margin: '20px auto'}} />

### Page Scrolling

Expand All @@ -81,7 +81,7 @@ now that our ComboBox tray sizes itself to fit in the visual viewport, users cou
or root element of the document. This preserves the user's ability to scroll through the options in the tray but blocks any attempt to scroll the page itself until the tray is closed. The video below
illustrates the difference in scrolling behavior before and after our fix. If you are building your own overlays and would like to prevent this kind of document scrolling behavior, check out the [usePreventScroll](https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/usePreventScroll.ts) hook in the `react-aria/overlays` package.

<video src={comboboxScrollingUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />
<video src={comboboxScrollingUrl} loop autoPlay muted style={{maxWidth: 'min(80%, 350px)', display: 'block', margin: '20px auto'}} />

## Accessibility

Expand Down Expand Up @@ -142,7 +142,7 @@ we only trigger the `LiveAnnouncer` on Apple devices to avoid announcement overl

If you are interested in using this `LiveAnnouncer` yourself, check out [LiveAnnouncer](https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/live-announcer/src/LiveAnnouncer.tsx) in `@react-aria/live-announcer`. Otherwise, the [useComboBox](https://react-spectrum.adobe.com/react-aria/useComboBox.html) hook provides you with all of the custom messaging out of the box. See the video below for a sneak peek!

<video src={comboboxAccessibilityUrl} loop autoPlay muted style={{width: '100%', display: 'block', margin: '20px auto'}} />
<video src={comboboxAccessibilityUrl} controls aria-label="Demo of VoiceOver announcement in ComboBox" style={{maxWidth: 'min(100%, 700px)', display: 'block', margin: '20px auto'}} />

## Conclusion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ We took a different approach, and followed the lead of native date picker UIs on

This approach has benefits for internationalization and accessibility, as well as usability on mobile. For internationalization, individual segments avoid the problem of parsing dates in various formats entirely. The date format is automatically determined based on the locale, and the user only needs to fill in the values and not worry about messing up the separators or getting the order wrong. Each segment is also individually labeled for accessibility, so users always know which field they are on (e.g. "year", "month", "day", etc.). This is much easier to use for screen reader users than a plain text field where the expected format is unknown. Finally, on mobile, we can take advantage of the numeric software keyboard, which is nicer to use than a full QWERTY keyboard.

<video src={datepickerScreenReader} loop autoPlay muted style={{width: '100%', display: 'block', margin: '40px auto'}} />
<video src={datepickerScreenReader + '#t=0.1'} preload="metadata" controls aria-label="Demo of the DateField component using VoiceOver, showing keyboard controls of incrementing and decrementing date and time segments." style={{width: '100%', display: 'block', margin: '40px auto'}} />

The [useDateField](https://react-spectrum.adobe.com/react-aria/useDateField.html) and [useTimeField](https://react-spectrum.adobe.com/react-aria/useTimeField.html) hooks (or the [DateField](../../s2/DateField.html) and [TimeField](../../s2/TimeField.html) React Spectrum components) may be used standalone in cases where the user is likely to already know the date they need to enter, or the date is far in the past or future, e.g. a birthday or passport expiration date. In these cases, browsing through a calendar UI to find a date is tedious, and entering the date with a keyboard is much more efficient.

Expand All @@ -85,7 +85,7 @@ For range calendars, we also ensure that the selected date range is clearly comm

Another important area we considered with our calendar components was mobile. With touch screen readers, users access each control on screen using swipe gestures to move a virtual cursor forward and backward. Because of this, calendars can be quite tedious to navigate because they contain so many elements, especially when multiple months are displayed at once. We made sure to provide context when a user enters a calendar of the whole range of visible dates, and included an extra visually hidden "next" button at the end of the dates so a user doesn't need to swipe all the way back to the start to navigate to the next month. The column headers are also skipped to improve ease of navigation, since the day names are already included in the label of each cell.

<video src={calendarMobileScreenReader + '#t=0.1'} preload="metadata" controls aria-label="Demo of the Calendar component using VoiceOver on iOS" style={{maxHeight: '600px', width: '100%', display: 'block', margin: '40px auto'}}>
<video src={calendarMobileScreenReader + '#t=0.1'} preload="metadata" controls aria-label="Demo of the Calendar component using VoiceOver on iOS" style={{maxHeight: '600px', maxWidth: 'min(100%, 1920px)', width: '100%', display: 'block', margin: '40px auto'}}>
<track src={calendarMobileScreenReaderVTT} default kind="captions" label="English Captions" srcLang="en-us" type="text/vtt" />
</video>

Expand Down
Loading