|
1 |
| -# TSDX React User Guide |
| 1 | +<h1>react-three-a11y👩🦯</h1> |
2 | 2 |
|
3 |
| -Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it. |
| 3 | +An easy to use package designed to bring accessibility features to [react-three-fiber](https://github.com/pmndrs/react-three-fiber) such as focus indication, keyboard tab navigation, and screen reader support. |
4 | 4 |
|
5 |
| -> This TSDX setup is meant for developing React component libraries (not apps!) that can be published to NPM. If you’re looking to build a React-based app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`. |
| 5 | +# How to use |
6 | 6 |
|
7 |
| -> If you’re new to TypeScript and React, checkout [this handy cheatsheet](https://github.com/sw-yx/react-typescript-cheatsheet/) |
| 7 | +## Initial setup |
8 | 8 |
|
9 |
| -## Commands |
10 |
| - |
11 |
| -TSDX scaffolds your new library inside `/src`, and also sets up a [Parcel-based](https://parceljs.org) playground for it inside `/example`. |
12 |
| - |
13 |
| -The recommended workflow is to run TSDX in one terminal: |
| 9 | +Install the @react-three/a11y package |
14 | 10 |
|
| 11 | +with npm : |
15 | 12 | ```bash
|
16 |
| -npm start # or yarn start |
| 13 | +npm install @react-three/a11y |
17 | 14 | ```
|
18 |
| - |
19 |
| -This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`. |
20 |
| - |
21 |
| -Then run the example inside another: |
22 |
| - |
| 15 | +with yarn |
23 | 16 | ```bash
|
24 |
| -cd example |
25 |
| -npm i # or yarn to install dependencies |
26 |
| -npm start # or yarn start |
| 17 | +yarn add @react-three/a11y |
27 | 18 | ```
|
28 | 19 |
|
29 |
| -The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, we use [Parcel's aliasing](https://parceljs.org/module_resolution.html#aliases). |
30 |
| - |
31 |
| -To do a one-off build, use `npm run build` or `yarn build`. |
32 |
| - |
33 |
| -To run tests, use `npm test` or `yarn test`. |
34 |
| - |
35 |
| -## Configuration |
36 |
| - |
37 |
| -Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly. |
| 20 | +Now, you'll have to import the A11yAnnouncer component. We usually place it next to the R3F Canvas component. |
38 | 21 |
|
39 |
| -### Jest |
40 |
| - |
41 |
| -Jest tests are set up to run with `npm test` or `yarn test`. |
42 |
| - |
43 |
| -### Bundle analysis |
44 |
| - |
45 |
| -Calculates the real cost of your library using [size-limit](https://github.com/ai/size-limit) with `npm run size` and visulize it with `npm run analyze`. |
46 |
| - |
47 |
| -#### Setup Files |
48 |
| - |
49 |
| -This is the folder structure we set up for you: |
50 |
| - |
51 |
| -```txt |
52 |
| -/example |
53 |
| - index.html |
54 |
| - index.tsx # test your component here in a demo app |
55 |
| - package.json |
56 |
| - tsconfig.json |
57 |
| -/src |
58 |
| - index.tsx # EDIT THIS |
59 |
| -/test |
60 |
| - blah.test.tsx # EDIT THIS |
61 |
| -.gitignore |
62 |
| -package.json |
63 |
| -README.md # EDIT THIS |
64 |
| -tsconfig.json |
| 22 | +```jsx |
| 23 | + import { A11yAnnouncer } from "@react-three/a11y" |
| 24 | + {...} |
| 25 | + <Canvas> |
| 26 | + {...} |
| 27 | + </Canvas> |
65 | 28 | ```
|
66 | 29 |
|
67 |
| -#### React Testing Library |
68 |
| - |
69 |
| -We do not set up `react-testing-library` for you yet, we welcome contributions and documentation on this. |
70 |
| - |
71 |
| -### Rollup |
72 |
| - |
73 |
| -TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details. |
74 |
| - |
75 |
| -### TypeScript |
76 |
| - |
77 |
| -`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs. |
| 30 | +This will both help us emulate focus inside the canvas and provide some text to screen readers when nescessary. |
| 31 | + |
| 32 | +Then to add some accessibility features to your 3D Objects / Groups of 3D object you'll have to import the A11y component too. |
| 33 | +Then you wrap the 3D objects you want to make focusable like so |
| 34 | + |
| 35 | +```jsx |
| 36 | + import { A11yAnnouncer, A11y } from "@react-three/a11y" |
| 37 | + |
| 38 | + <A11yDom > |
| 39 | + <Canvas> |
| 40 | + {...} |
| 41 | + <A11y> |
| 42 | + <My3DComponent /> |
| 43 | + </A11y> |
| 44 | + {...} |
| 45 | + <A11y> |
| 46 | + <AGroupOf3DComponent /> |
| 47 | + </A11y> |
| 48 | + {...} |
| 49 | + </Canvas> |
| 50 | + </A11yDom> |
| 51 | +``` |
78 | 52 |
|
79 |
| -## Continuous Integration |
| 53 | +At this point both My3DComponent and AGroupOf3DComponent can receive focus. |
| 54 | +More presciesly, the emulated "focus" will be on the parent A11y that will act as a provider and give the possibility to it's children to access the state. |
| 55 | +But even if they're focusable, nothing will be displayed / read etc without a few more attributes. |
80 | 56 |
|
81 |
| -### GitHub Actions |
| 57 | +## accessing the hover / focused / pressed state |
82 | 58 |
|
83 |
| -Two actions are added by default: |
| 59 | +For each child wrapped in a A11y component, you can access the focus / hover / pressed state like so |
84 | 60 |
|
85 |
| -- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix |
86 |
| -- `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit) |
| 61 | +import useA11y from '@react-three/a11y' then |
87 | 62 |
|
88 |
| -## Optimizations |
| 63 | +```jsx |
| 64 | + import { A11yAnnouncer, A11y, useA11y } from "@react-three/a11y" |
| 65 | + |
| 66 | + {...} |
89 | 67 |
|
90 |
| -Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations: |
| 68 | + const My3DComponent = (props) { |
91 | 69 |
|
92 |
| -```js |
93 |
| -// ./types/index.d.ts |
94 |
| -declare var __DEV__: boolean; |
| 70 | + //call useA11yContext to get the A11yContext from the provider |
| 71 | + const a11yContext = useA11yContext(); |
| 72 | + //now you have access to a11yContext.hover, a11yContext.focus and a11yContext.pressed |
95 | 73 |
|
96 |
| -// inside your code... |
97 |
| -if (__DEV__) { |
98 |
| - console.log('foo'); |
| 74 | + return ( |
| 75 | + <mesh |
| 76 | + {...props} |
| 77 | + <boxBufferGeometry args={[1, 1, 1]} /> |
| 78 | + //here we'll change the material color depending on the a11yContext state |
| 79 | + <meshStandardMaterial color={a11yContext.hover || a11yContext.focus ? 'hotpink' : 'orange'} /> |
| 80 | + </mesh> |
| 81 | + ) |
99 | 82 | }
|
100 | 83 | ```
|
101 | 84 |
|
102 |
| -You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions. |
103 |
| - |
104 |
| -## Module Formats |
105 |
| - |
106 |
| -CJS, ESModules, and UMD module formats are supported. |
107 |
| - |
108 |
| -The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found. |
109 |
| - |
110 |
| -## Deploying the Example Playground |
111 |
| - |
112 |
| -The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. Here are some guidelines for **manually** deploying with the Netlify CLI (`npm i -g netlify-cli`): |
113 |
| - |
114 |
| -```bash |
115 |
| -cd example # if not already in the example folder |
116 |
| -npm run build # builds to dist |
117 |
| -netlify deploy # deploy the dist folder |
| 85 | +In this example, the meshStandardMaterial of the component My3DComponent will change color if he is either focused or hovered. |
| 86 | +How you display the focus / hover information to the user is up to you ! Just make sure it's intuitive for your user ! |
| 87 | + |
| 88 | +## The role attribute |
| 89 | + |
| 90 | +Like in HTML, you can focus different kind of element and expect different things depending on what you're focusing. |
| 91 | +That's why the A11y component is divided in 3 categories. |
| 92 | + |
| 93 | +- role="content" ( default ) |
| 94 | +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. |
| 95 | +It's not meant to trigger anything on click or to be activable with the Keyboard. |
| 96 | +More on this role <a href="/#content"> below </a> |
| 97 | +- role="button" |
| 98 | +This role is meant to emulate the behaviour of a button or a togglable button. |
| 99 | +It will display a cursor pointer when your cursor is over the linked 3D object. |
| 100 | +It will call a function on click but also on any kind of action that would trigger a focused button ( Enter, Double-Tap .. ) |
| 101 | +It is also actionnable by user using a screen reader. |
| 102 | +More on this role <a href="/#button"> below </a> |
| 103 | +- role="link" |
| 104 | +This role is meant to emulate the behaviour of a regular html link. |
| 105 | +It should be used in combination with something that will trigger navigation on click. |
| 106 | +Just like the button one, it is accessible to all kind of user. |
| 107 | +More on this role <a href="/#link"> below </a> |
| 108 | + |
| 109 | +## Call function on focus |
| 110 | + |
| 111 | +The A11y attribute focusCall will be called each time this component receive focus ( Usually through tab navigation ) |
| 112 | +You can for instance use it in order to make sure the currently focused element is in view by adjusting it position or the camera position. |
| 113 | + |
| 114 | +```jsx |
| 115 | + import { A11yAnnouncer, A11y } from "@react-three/a11y" |
| 116 | + |
| 117 | + <A11yDom > |
| 118 | + <Canvas> |
| 119 | + {...} |
| 120 | + <A11y role="content" focusCall={()=>{ |
| 121 | + //rotate camera to show the focused element |
| 122 | + }}> |
| 123 | + <My3DComponent /> |
| 124 | + </A11y> |
| 125 | + {...} |
| 126 | + </Canvas> |
| 127 | + </A11yDom> |
118 | 128 | ```
|
119 | 129 |
|
120 |
| -Alternatively, if you already have a git repo connected, you can set up continuous deployment with Netlify: |
121 |
| - |
122 |
| -```bash |
123 |
| -netlify init |
124 |
| -# build command: yarn build && cd example && yarn && yarn build |
125 |
| -# directory to deploy: example/dist |
126 |
| -# pick yes for netlify.toml |
| 130 | +## Call function on click / keyboard Click |
| 131 | + |
| 132 | +The A11y attribute actionCall will call the associated function each time this component gets clicked / focused then keyboard activated etc.. |
| 133 | + |
| 134 | +```jsx |
| 135 | + import { A11yAnnouncer, A11y } from "@react-three/a11y" |
| 136 | + |
| 137 | + <A11yDom > |
| 138 | + <Canvas> |
| 139 | + {...} |
| 140 | + <A11y role="button" actionCall={()=>{ |
| 141 | + alert('This button have been clicked') |
| 142 | + }}> |
| 143 | + <My3DComponent /> |
| 144 | + </A11y> |
| 145 | + {...} |
| 146 | + </Canvas> |
| 147 | + </A11yDom> |
127 | 148 | ```
|
128 | 149 |
|
129 |
| -## Named Exports |
130 |
| - |
131 |
| -Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library. |
132 |
| - |
133 |
| -## Including Styles |
134 |
| - |
135 |
| -There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like. |
| 150 | +## Provide a description of the currenlty focused / hovered element |
| 151 | + |
| 152 | +Your A11y component will provide a description to the screen reader users on focus / hover as long as you provide it to the description attribute. |
| 153 | +Optionnaly, you can also show the description to the user when he hover it by setting showAltText={true}. |
| 154 | + |
| 155 | +```jsx |
| 156 | + import { A11yAnnouncer, A11y } from "@react-three/a11y" |
| 157 | + |
| 158 | + <A11yDom > |
| 159 | + <Canvas> |
| 160 | + {...} |
| 161 | + <A11y role="content" description="A rotating red square"> |
| 162 | + //will read "A rotating red square" to screen readers on focus / hover |
| 163 | + <My3DSquare /> |
| 164 | + </A11y> |
| 165 | + {...} |
| 166 | + <A11y role="content" description="A bouncing blue sphere" showAltText={true}> |
| 167 | + //will read "A bouncing blue sphere" to screen readers on focus / hover while also showing it on mouseover |
| 168 | + <My3DSphere /> |
| 169 | + </A11y> |
| 170 | + {...} |
| 171 | + </Canvas> |
| 172 | + </A11yDom> |
| 173 | +``` |
136 | 174 |
|
137 |
| -For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader. |
| 175 | +If your A11y component have the role="button", you can use two more attributes : |
| 176 | +- activationMsg : When the user will click / activate the "button" the screen reader will read what you wrote in activationMsg |
| 177 | +- desactivationMsg : When set, it turns your button in a togglable button. Which means he now has a on/off state. Screen readers will read the state of the button as well as the desactivation message / activation message that you set when toggling it. |
| 178 | + |
| 179 | +```jsx |
| 180 | + import { A11yAnnouncer, A11y } from "@react-three/a11y" |
| 181 | + |
| 182 | + <A11yDom > |
| 183 | + <Canvas> |
| 184 | + {...} |
| 185 | + <A11y role="button" description="This button will send a thank you email to the team" activationMsg="Email is sending"> |
| 186 | + //will read the description on hover / focus then will read activationMsg if clicked / pressed |
| 187 | + <My3DSquare /> |
| 188 | + </A11y> |
| 189 | + {...} |
| 190 | + <A11y |
| 191 | + role="button" |
| 192 | + description="This button can enable and disable dark theme" |
| 193 | + activationMsg="Dark theme enabled" |
| 194 | + desactivationMsg="Dark theme disabled" |
| 195 | + > |
| 196 | + //will read the description on hover / focus then will read activationMsg if turned on or desactivationMsg if tuned off |
| 197 | + <My3DSphere /> |
| 198 | + </A11y> |
| 199 | + {...} |
| 200 | + </Canvas> |
| 201 | + </A11yDom> |
| 202 | +``` |
138 | 203 |
|
139 |
| -## Publishing to NPM |
| 204 | +####content |
140 | 205 |
|
141 |
| -We recommend using [np](https://github.com/sindresorhus/np). |
| 206 | +####button |
142 | 207 |
|
143 |
| -## Usage with Lerna |
| 208 | +####link |
144 | 209 |
|
145 |
| -When creating a new package with TSDX within a project set up with Lerna, you might encounter a `Cannot resolve dependency` error when trying to run the `example` project. To fix that you will need to make changes to the `package.json` file _inside the `example` directory_. |
| 210 | +## Screen reader support |
146 | 211 |
|
147 |
| -The problem is that due to the nature of how dependencies are installed in Lerna projects, the aliases in the example project's `package.json` might not point to the right place, as those dependencies might have been installed in the root of your Lerna project. |
| 212 | +## Next steps |
148 | 213 |
|
149 |
| -Change the `alias` to point to where those packages are actually installed. This depends on the directory structure of your Lerna project, so the actual path might be different from the diff below. |
| 214 | +- [ ] Improve the accessibility for mobile screen readers such as Voice Over and Talk Back |
150 | 215 |
|
151 |
| -```diff |
152 |
| - "alias": { |
153 |
| -- "react": "../node_modules/react", |
154 |
| -- "react-dom": "../node_modules/react-dom" |
155 |
| -+ "react": "../../../node_modules/react", |
156 |
| -+ "react-dom": "../../../node_modules/react-dom" |
157 |
| - }, |
158 |
| -``` |
| 216 | +### Maintainers : |
159 | 217 |
|
160 |
| -An alternative to fixing this problem would be to remove aliases altogether and define the dependencies referenced as aliases as dev dependencies instead. [However, that might cause other problems.](https://github.com/palmerhq/tsdx/issues/64) |
| 218 | +- [`twitter 👋 @AlaricBaraou`](https://twitter.com/AlaricBaraou) |
0 commit comments