Skip to content

Commit d800716

Browse files
committed
Merge branch 'main' into mobile-screen-readers-improvment
2 parents 766aa74 + 6a0164d commit d800716

File tree

2 files changed

+80
-149
lines changed

2 files changed

+80
-149
lines changed

.codesandbox/ci.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"sandboxes": ["randomstring", "/example/"]
2+
"sandboxes": ["vecdv"]
33
}

README.md

Lines changed: 79 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -4,154 +4,87 @@
44
[![Downloads](https://img.shields.io/npm/dt/@react-three/a11y.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/@react-three/a11y)
55
[![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/ZZjjNvJ)
66

7+
```bash
8+
npm install @react-three/a11y
9+
```
10+
711
`@react-three/a11y` brings accessibility to WebGL, with easy to use components [react-three-fiber](https://github.com/pmndrs/react-three-fiber) components to enable focus indication, keyboard tab navigation, and screen reader support.
812

13+
Live demo: https://n4rzi.csb.app
14+
915
# How to use
1016

11-
## Initial setup
17+
First, place the `A11yAnnouncer` component next to the R3F Canvas component. This is critical, because it will manage the screen-reader and help emulate focus!
1218

13-
Install the @react-three/a11y package
19+
```jsx
20+
import { A11yAnnouncer } from '@react-three/a11y'
1421

15-
```bash
16-
npm install @react-three/a11y
22+
function App() {
23+
return (
24+
<>
25+
<Canvas />
26+
<A11yAnnouncer />
27+
</>
28+
)
29+
}
1730
```
1831

19-
Now, you'll have to import the `A11yAnnouncer` component. We usually place it next to the R3F Canvas component.
32+
To add accessibility features to your scene you'll have to wrap components you want to make focusable with the `A11y` component:
2033

2134
```jsx
22-
import { A11yAnnouncer } from "@react-three/a11y"
23-
{...}
24-
<Canvas>
25-
{...}
26-
</Canvas>
27-
<A11yAnnouncer />
28-
```
29-
30-
This will both help us emulate focus inside the canvas and provide some text to screen readers when necessary.
31-
32-
To add some accessibility features to your 3D Objects/Groups you'll have to wrap the 3D objects you want to make focusable with the `A11y` component:
35+
import { A11y } from '@react-three/a11y'
3336

34-
```jsx
35-
import { A11yAnnouncer, A11y } from "@react-three/a11y"
36-
37-
<Canvas>
38-
{...}
39-
<A11y>
40-
<My3DComponent />
41-
</A11y>
42-
{...}
43-
<A11y>
44-
<AGroupOf3DComponent />
45-
</A11y>
46-
{...}
47-
</Canvas>
48-
<A11yAnnouncer />
37+
<A11y>
38+
<MyComponent />
39+
</A11y>
4940
```
5041

51-
At this point both _My3DComponent_ and _AGroupOf3DComponent_ can receive focus.
52-
More accurately, the emulated "focus" will be on the parent `A11y` that will act as a provider and let its children access the state.
53-
But even if they're focusable, nothing will be displayed / read etc without a few more attributes.
42+
`MyComponent` can now receive focus. More accurately, the emulated "focus" will handled at the `A11y` components which acts as a provider for children to access its state. But even if objects are focusable, nothing will be displayed or shown by default.
5443

5544
## Accessing the hover, focused & pressed state
5645

57-
For each child wrapped in a `A11y` component, you can access the `focus` / `hover` / `pressed` state like so:
46+
For each child wrapped in the `A11y` component, you can access the `focus`, `hover` and `pressed` state like so:
5847

5948
```jsx
60-
import { A11yAnnouncer, A11y, useA11y } from "@react-three/a11y"
61-
62-
{...}
63-
64-
const My3DComponent = (props) {
65-
66-
//call useA11y to get the A11yContext from the provider
67-
const a11yContext = useA11y();
68-
//now you have access to a11yContext.hover, a11yContext.focus and a11yContext.pressed
49+
import { useA11y } from '@react-three/a11y'
6950

51+
function Box(props) {
52+
const a11y = useA11y()
7053
return (
7154
<mesh {...props}>
72-
<boxBufferGeometry args={[1, 1, 1]} />
73-
{/* here we'll change the material color depending on the a11yContext state */}
74-
<meshStandardMaterial color={a11yContext.hover || a11yContext.focus ? 'hotpink' : 'orange'} />
55+
<boxBufferGeometry />
56+
<meshStandardMaterial color={a11y.hover || a11y.focus ? 'hotpink' : 'orange'} />
7557
</mesh>
7658
)
7759
}
7860
```
7961

80-
In this example, the _meshStandardMaterial_ of the component _My3DComponent_ will change color if he is either focused or hovered.
81-
How you display the focus / hover information to the user is up to you! Just make sure it's intuitive for your user!
82-
83-
## The role attribute
84-
85-
Like in HTML, you can focus different kind of elements and expect different things depending on what you're focusing.
86-
That's why the `A11y` component has 3 different use cases:
87-
88-
- `role="content"` ( default ) <a href="/#content">More below </a>
89-
90-
- `role="button"` <a href="/#button">More below </a>
91-
92-
- `role="link"` <a href="/#link">More below </a>
93-
9462
## Call function on focus
9563

96-
The `focusCall` prop of `A11y` will be called each time this component receive focus ( Usually through tab navigation ).
97-
You can for instance use it in order to make sure the currently focused element is in view by adjusting its position or the moving the camera.
64+
The `focusCall` prop of `A11y` will be called each time this component receives focus (usually through tab navigation).
9865

9966
```jsx
100-
import { A11yAnnouncer, A11y } from "@react-three/a11y"
101-
102-
<Canvas>
103-
{...}
104-
<A11y role="content" focusCall={()=>{
105-
//rotate camera to show the focused element
106-
}}>
107-
<My3DComponent />
108-
</A11y>
109-
{...}
110-
</Canvas>
111-
<A11yAnnouncer />
67+
<A11y role="content" focusCall={()=> console.log("in focus")} ... />
11268
```
11369

11470
## Call function on click / keyboard Click
11571

116-
The `actionCall` prop of `A11y` will be called each time this component gets clicked, focused, keyboard activated etc..
72+
The `actionCall` prop of `A11y` will be called each time this component gets clicked, focused, keyboard activated etc.
11773

11874
```jsx
119-
import { A11yAnnouncer, A11y } from "@react-three/a11y"
120-
121-
<Canvas>
122-
{...}
123-
<A11y role="button" actionCall={()=>{
124-
alert('This button have been clicked')
125-
}}>
126-
<My3DComponent />
127-
</A11y>
128-
{...}
129-
</Canvas>
130-
<A11yAnnouncer />
75+
<A11y role="button" actionCall={()=> console.log("clicked")} ... />
13176
```
13277

133-
## Provide a description of the currenlty focused / hovered element
78+
## Provide a description of the currently focused / hovered element
13479

13580
When using the `description` prop, the `A11y` component will provide a description to the screen reader users on focus/hover.
13681
Optionally, you can also show the description to the user on hover by setting `showAltText={true}`.
13782

13883
```jsx
139-
import { A11yAnnouncer, A11y } from "@react-three/a11y"
140-
141-
<Canvas>
142-
{...}
143-
<A11y role="content" description="A rotating red square">
144-
//will read "A rotating red square" to screen readers on focus / hover
145-
<My3DSquare />
146-
</A11y>
147-
{...}
148-
<A11y role="content" description="A bouncing blue sphere" showAltText={true}>
149-
//will read "A bouncing blue sphere" to screen readers on focus / hover while also showing it on mouseover
150-
<My3DSphere />
151-
</A11y>
152-
{...}
153-
</Canvas>
154-
<A11yAnnouncer />
84+
// Reads "A rotating red square" to screen readers on focus / hover
85+
<A11y role="content" description="A rotating red square" ... />
86+
// Reads "A bouncing blue sphere" to screen readers on focus / hover while also showing it on mouseover
87+
<A11y role="content" description="A bouncing blue sphere" showAltText ... />
15588
```
15689

15790
If your `A11y` component has the `role="button"`, you can use three more props:
@@ -161,56 +94,56 @@ If your `A11y` component has the `role="button"`, you can use three more props:
16194
- `pressedDescription`: When set, it turns your button in a togglable button. Which means it now has a on/off state. This description will replace the one passed via `description` when the toggle is active.
16295

16396
```jsx
164-
import { A11yAnnouncer, A11y } from "@react-three/a11y"
165-
166-
<Canvas>
167-
{...}
168-
<A11y role="button" description="This button will send a thank you email to the team" activationMsg="Email is sending">
169-
//will read the description on hover / focus then will read activationMsg if clicked / pressed
170-
<My3DSquare />
171-
</A11y>
172-
{...}
173-
<A11y
174-
role="button"
175-
description="This button can enable dark theme. Dark theme is off"
176-
pressedDescription="This button can disable dark theme. Dark theme is on"
177-
activationMsg="Dark theme enabled"
178-
deactivationMsg="Dark theme disabled"
179-
>
180-
//will read the description on hover / focus then will read activationMsg if turned on or deactivationMsg if tuned off
181-
<My3DSphere />
182-
</A11y>
183-
{...}
184-
</Canvas>
185-
<A11yAnnouncer />
97+
// Reads the description on hover/focus then will read activationMsg if clicked/pressed
98+
<A11y role="button" description="Sends a thank you email to the team" activationMsg="Email is sending" ... />
99+
// Reads the description on hover/focus then will read activationMsg if turned on or deactivationMsg if tuned off
100+
<A11y
101+
role="button"
102+
description="This button can enable dark theme. Dark theme is off"
103+
pressedDescription="This button can disable dark theme. Dark theme is on"
104+
activationMsg="Dark theme enabled"
105+
deactivationMsg="Dark theme disabled" ... />
186106
```
187107

188108
## The three roles of the `A11y` component
189109

190-
#### `content`
110+
Like in HTML, you can focus different kind of elements and expect different things depending on what you're focusing.
191111

192-
`cursor: default`
193-
This role is meant to provide information to screen readers or to serve as a step for a user to navigate your site using Tab for instance.
194-
It's not meant to trigger anything on click or to be activable with the Keyboard.
195-
Therefore it won't show a pointer cursor on hover.
112+
#### Content
196113

197-
#### `button`
114+
```jsx
115+
<A11y role="content" ... />
116+
```
117+
118+
Uses the `default` cursor. This role is meant to provide information to screen readers or to serve as a step for a user to navigate your site using Tab for instance. It's not meant to trigger anything on click or to be activable with the Keyboard. Therefore it won't show a pointer cursor on hover.
119+
120+
#### Buttons
121+
122+
123+
```jsx
124+
<A11y
125+
role="button"
126+
activationMsg="Button activated"
127+
deactivationMsg="Button deactivated"
128+
pressedDescription="Button pressed" ... />
129+
```
130+
131+
Uses the `pointer` cursor. Special attributes: `activationMsg`, `deactivationMsg` and `pressedDescription`.
132+
133+
This role is meant to emulate the behaviour of a button or a togglable button. It will display a cursor pointer when your cursor is over the linked 3D object. It will call a function on click but also on any kind of action that would trigger a focused button (Enter, Double-Tap, ...). It is also actionnable by user using a screen reader.
198134

199-
`cursor: pointer`
200-
Special attributes : activationMsg, deactivationMsg, pressedDescription
201-
This role is meant to emulate the behaviour of a button or a togglable button.
202-
It will display a cursor pointer when your cursor is over the linked 3D object.
203-
It will call a function on click but also on any kind of action that would trigger a focused button ( Enter, Double-Tap .. )
204-
It is also actionnable by user using a screen reader.
205135
You can turn it into a button with aria-pressed by providing the following properties deactivationMsg, pressedDescription in addition to the usual description and activationMsg properties.
206136

207-
#### `link`
137+
#### Links
138+
208139

209-
`cursor: pointer`
210-
`special attributes : href`
211-
This role is meant to emulate the behaviour of a regular html link.
212-
It should be used in combination with something that will trigger navigation on click.
213-
Just like the button one, it is accessible to all kind of user.
140+
```jsx
141+
<A11y role="link" href="https://url.com" ... />
142+
```
143+
144+
Uses the `pointer` cursor. Special attributes: `href`.
145+
146+
This role is meant to emulate the behaviour of a regular html link. It should be used in combination with something that will trigger navigation on click.
214147

215148
```diff
216149
- Don't forget to provide the href attribute as he is required for screen readers to read it correctly !
@@ -226,9 +159,7 @@ In order to provide informations to screen reader users and use this package at
226159
Use a custom tabindex with for your A11y components by providing a number to the tabIndex attribute
227160

228161
```jsx
229-
<A11y tabIndex={2}>
230-
<My3DSquare />
231-
</A11y>
162+
<A11y tabIndex={2} ... />
232163
```
233164

234165
More about the use of tabIndex on <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex">developer.mozilla.org</a>

0 commit comments

Comments
 (0)