Skip to content

Commit 947e483

Browse files
authored
Merge pull request #10 from pmndrs/mobile-screen-readers-improvment
Mobile screen readers improvement
2 parents a8d50c9 + 240cc1a commit 947e483

File tree

8 files changed

+344
-361
lines changed

8 files changed

+344
-361
lines changed

example/package.json

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
{
2-
"name": "main_a11y_features_test",
2+
"name": "mr-main-a11y-features-test",
33
"version": "1.0.0",
44
"license": "MIT",
5-
"description": "",
5+
"description": "This sandbox has been generated!",
66
"keywords": [],
77
"main": "src/index.js",
88
"dependencies": {
9-
"@juggle/resize-observer": "^3.2.0",
9+
"@juggle/resize-observer": "3.2.0",
1010
"@pmndrs/branding": "0.0.4",
11-
"@react-spring/three": "^9.0.0-rc.3",
12-
"@react-spring/web": "^9.0.0-rc.3",
13-
"@react-three/drei": "2.2.13",
14-
"drei": "^2.2.13",
15-
"react": ">=16.13",
16-
"react-dom": ">=16.13",
11+
"@react-three/drei": "2.2.15",
12+
"@react-three/postprocessing": "1.5.1",
1713
"react-scripts": "4.0.1",
18-
"react-three-fiber": ">=5.0"
14+
"valtio": "0.6.0"
1915
},
2016
"alias": {
2117
"react": "../node_modules/react",

example/public/diamond.glb

-16.4 KB
Binary file not shown.

example/public/rupee.glb

-8.92 KB
Binary file not shown.

example/src/App.js

Lines changed: 141 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,176 @@
1-
import "./styles.css"
2-
import { Canvas, useFrame } from "react-three-fiber"
3-
import React, { Suspense, useRef, useEffect, useState } from "react"
4-
import { useGLTF, OrbitControls, ContactShadows } from "drei"
1+
import * as THREE from "three"
2+
import { Canvas, useFrame, useThree } from "react-three-fiber"
3+
import React, { Suspense, useRef } from "react"
4+
import { ContactShadows } from "@react-three/drei"
55
import { A11y, useA11y, A11yAnnouncer } from "../../"
66
import { ResizeObserver } from "@juggle/resize-observer"
7-
import { a as animated } from "@react-spring/web"
8-
import { useTransition, useSpring } from "@react-spring/core"
9-
import diamond from "../public/diamond.glb"
10-
import rupee from "../public/rupee.glb"
11-
import { a } from "@react-spring/three"
12-
13-
const SimpleToggleButton = props => {
14-
const mesh = useRef()
15-
const a11yContext = useA11y()
16-
const [prevA11yContextValue, saveA11yContextValue] = useState(a11yContext)
17-
18-
const { color } = useSpring({ color: a11yContext.focus ? "#5dc8dc" : a11yContext.hover ? "#239db4" : "lightblue" })
19-
const [scale, setScale] = useSpring(() => ({
20-
scale: [1, 1, 1],
21-
}))
22-
useEffect(() => {
23-
if (!prevA11yContextValue.focus && a11yContext.focus) {
24-
setScale({
25-
to: [{ scale: [1.2, 1.2, 1.2] }, { scale: [1, 1, 1] }],
26-
config: { duration: 200 },
27-
})
28-
}
29-
saveA11yContextValue(a11yContext)
30-
}, [a11yContext])
31-
7+
import { proxy, useProxy } from "valtio"
8+
import { EffectComposer, SSAO, SMAA } from "@react-three/postprocessing"
9+
import { Badge } from "@pmndrs/branding"
10+
11+
const state = proxy({ dark: false, active: 0, rotation: 0, disabled: true })
12+
const geometries = [
13+
new THREE.SphereBufferGeometry(1, 32, 32),
14+
new THREE.TetrahedronBufferGeometry(1.5),
15+
new THREE.TorusBufferGeometry(1, 0.35, 16, 32),
16+
new THREE.OctahedronGeometry(1.5),
17+
new THREE.IcosahedronBufferGeometry(1.5),
18+
]
19+
20+
function ToggleButton(props) {
21+
const a11y = useA11y()
3222
return (
33-
<a.mesh {...props} ref={mesh} scale={scale.scale}>
34-
<a.torusGeometry args={a11yContext.pressed ? [0.5, 1, 10, 20] : [1, 0.5, 10, 20]} />
35-
<a.meshStandardMaterial color={color} />
36-
</a.mesh>
23+
<mesh {...props}>
24+
<torusGeometry args={[0.5, a11y.pressed ? 0.28 : 0.25, 16, 32]} />
25+
<meshStandardMaterial color={a11y.focus ? "lightsalmon" : a11y.hover ? "lightpink" : "lightblue"} />
26+
</mesh>
3727
)
3828
}
3929

40-
function Diamonds({ targetRotation, setTargetRotation, setActiveRupee, activeRupee }) {
41-
const group = useRef()
42-
const { nodes, materials } = useGLTF(diamond)
43-
const a11yContext = useA11y()
44-
30+
function SwitchButton(props) {
31+
const a11y = useA11y()
4532
return (
46-
<group ref={group} dispose={null}>
47-
<A11y
48-
role="button"
49-
description="Press to show the left rupee"
50-
activationMsg="rupee showing"
51-
actionCall={() => {
52-
setActiveRupee(activeRupee === 1 ? 5 : activeRupee - 1)
53-
setTargetRotation(targetRotation - Math.PI / 2)
54-
}}>
55-
<Diamond geometry={nodes.Cylinder.geometry} position={[-10, 0, 0]} rotation={[0, Math.PI / 4, -Math.PI / 2]} />
56-
</A11y>
57-
<A11y
58-
role="button"
59-
description="Press to show the right rupee"
60-
activationMsg="rupee showing"
61-
actionCall={() => {
62-
setActiveRupee(activeRupee === 5 ? 1 : activeRupee + 1)
63-
setTargetRotation(targetRotation + Math.PI / 2)
64-
}}>
65-
<Diamond geometry={nodes.Cylinder.geometry} position={[10, 0, 0]} rotation={[0, -Math.PI / 4, Math.PI / 2]} />
66-
</A11y>
67-
</group>
33+
<>
34+
<mesh {...props} rotation={[0, 0, a11y.pressed ? Math.PI / 4 : -Math.PI / 4]}>
35+
<boxBufferGeometry args={[0.3, 2, 0.3]} />
36+
<meshStandardMaterial color={a11y.focus ? "lightsalmon" : a11y.hover ? "lightpink" : "lightblue"} />
37+
</mesh>
38+
<mesh {...props}>
39+
<torusGeometry args={[0.3, 0.2, 16, 20]} />
40+
<meshStandardMaterial color={a11y.focus ? "lightsalmon" : a11y.hover ? "lightpink" : "lightblue"} />
41+
</mesh>
42+
</>
6843
)
6944
}
7045

71-
const Diamond = props => {
72-
const a11yContext = useA11y()
73-
46+
function Floor(props) {
7447
return (
75-
<mesh {...props}>
76-
<meshStandardMaterial color={a11yContext.focus ? "#0fffcc" : a11yContext.hover ? "#71f8db" : "#87d4f7"} />
77-
</mesh>
48+
<>
49+
<ContactShadows rotation-x={Math.PI / 2} position={[0, -5, 0]} opacity={0.4} width={30} height={30} blur={1} far={15} />
50+
<mesh {...props} position={[0, -5.1, 0]} rotation={[-Math.PI / 2, 0, 0]}>
51+
<planeBufferGeometry args={[30, 30, 1]} />
52+
<meshStandardMaterial color={"#eef5f7"} />
53+
</mesh>
54+
</>
7855
)
7956
}
8057

81-
const Rupee = props => {
82-
const rupeeRef = useRef()
83-
const a11yContext = useA11y()
84-
85-
useFrame(() => {
86-
if (rupeeRef.current) {
87-
rupeeRef.current.rotation.y += 0.01
88-
}
89-
})
58+
function Nav({ left }) {
59+
const snap = useProxy(state)
60+
const { viewport } = useThree()
61+
const radius = Math.min(12, viewport.width / 2.5)
62+
return (
63+
<A11y
64+
role="button"
65+
description={`Press to show the ${left ? "left" : "right"} shape`}
66+
activationMsg="shape showing"
67+
actionCall={() => {
68+
state.rotation = snap.rotation + ((Math.PI * 2) / 5) * (left ? -1 : 1)
69+
state.active = left ? (snap.active === 0 ? 4 : snap.active - 1) : snap.active === 4 ? 0 : snap.active + 1
70+
}}
71+
disabled={snap.disabled}>
72+
<Diamond position={[left ? -radius : radius, 0, 0]} rotation={[0, 0, -Math.PI / 4]} />
73+
</A11y>
74+
)
75+
}
9076

77+
function Diamond({ position, rotation }) {
78+
const a11y = useA11y()
9179
return (
92-
<mesh ref={rupeeRef} {...props} scale={a11yContext.focus ? [1.2, 1.2, 1.2] : [1, 1, 1]}>
93-
<meshStandardMaterial color={props.color} />
80+
<mesh position={position} rotation={rotation}>
81+
<tetrahedronBufferGeometry />
82+
<meshPhongMaterial color={a11y.focus ? "lightsalmon" : a11y.hover ? "lightpink" : "lightblue"} />
9483
</mesh>
9584
)
9685
}
9786

98-
function Rupees({ targetRotation, activeRupee }) {
99-
const group = useRef()
100-
const { nodes, materials } = useGLTF(rupee)
101-
102-
useFrame(() => {
103-
if (group.current) {
104-
group.current.rotation.y = (1 - 0.1) * group.current.rotation.y + 0.1 * targetRotation
87+
function Shape({ index, active, ...props }) {
88+
const snap = useProxy(state)
89+
const vec = new THREE.Vector3()
90+
const ref = useRef()
91+
useFrame((state, delta) => {
92+
if (snap.disabled) {
93+
return
10594
}
95+
const s = active ? 2 : 1
96+
ref.current.scale.lerp(vec.set(s, s, s), 0.1)
97+
ref.current.rotation.y = ref.current.rotation.x += delta / (active ? 1.5 : 4)
98+
ref.current.position.y = active ? Math.sin(state.clock.elapsedTime) / 2 : 0
10699
})
107-
108100
return (
109-
<group ref={group} dispose={null}>
110-
<A11y role="content" description="a red rupee" tabIndex={activeRupee === 1 ? 0 : -1}>
111-
<Rupee
112-
position={[Math.round(5 * Math.cos(0)), 0, Math.round(5 * Math.sin(0))]}
113-
material={materials.Material}
114-
color="red"
115-
geometry={nodes.Cube.geometry}
116-
/>
117-
</A11y>
118-
<A11y role="content" description="a green rupee" tabIndex={activeRupee === 2 ? 0 : -1}>
119-
<Rupee
120-
position={[Math.round(5 * Math.cos((1 * 2 * Math.PI) / 5)), 0, Math.round(5 * Math.sin((1 * 2 * Math.PI) / 5))]}
121-
material={materials.Material}
122-
color="green"
123-
geometry={nodes.Cube.geometry}
124-
/>
125-
</A11y>
126-
<A11y role="content" description="a silver rupee" tabIndex={activeRupee === 3 ? 0 : -1}>
127-
<Rupee
128-
position={[Math.round(5 * Math.cos((2 * 2 * Math.PI) / 5)), 0, Math.round(5 * Math.sin((2 * 2 * Math.PI) / 5))]}
129-
material={materials.Material}
130-
color="silver"
131-
geometry={nodes.Cube.geometry}
132-
/>
133-
</A11y>
134-
<A11y role="content" description="a purple rupee" tabIndex={activeRupee === 4 ? 0 : -1}>
135-
<Rupee
136-
position={[Math.round(5 * Math.cos((3 * 2 * Math.PI) / 5)), 0, Math.round(5 * Math.sin((3 * 2 * Math.PI) / 5))]}
137-
material={materials.Material}
138-
color="purple"
139-
geometry={nodes.Cube.geometry}
140-
/>
141-
</A11y>
142-
<A11y role="content" description="a yellow rupee" tabIndex={activeRupee === 5 ? 0 : -1}>
143-
<Rupee
144-
position={[Math.round(5 * Math.cos((4 * 2 * Math.PI) / 5)), 0, Math.round(5 * Math.sin((4 * 2 * Math.PI) / 5))]}
145-
material={materials.Material}
146-
color="yellow"
147-
geometry={nodes.Cube.geometry}
148-
/>
149-
</A11y>
150-
</group>
101+
<mesh rotation-y={index * 2000} ref={ref} {...props} geometry={geometries[index]}>
102+
<meshPhongMaterial />
103+
</mesh>
151104
)
152105
}
153106

154-
const Carroussel = () => {
155-
const [targetRotation, setTargetRotation] = useState(0)
156-
const [activeRupee, setActiveRupee] = useState(1)
157-
107+
function Carroussel() {
108+
const { viewport } = useThree()
109+
const snap = useProxy(state)
110+
const group = useRef()
111+
const radius = Math.min(6, viewport.width / 5)
112+
useFrame(() => (group.current.rotation.y = THREE.MathUtils.lerp(group.current.rotation.y, snap.rotation - Math.PI / 2, 0.1)))
158113
return (
159-
<Suspense fallback={null}>
160-
<group position={[0, 0, 0]}>
161-
<Diamonds
162-
targetRotation={targetRotation}
163-
setTargetRotation={setTargetRotation}
164-
activeRupee={activeRupee}
165-
setActiveRupee={setActiveRupee}
166-
/>
167-
<Rupees targetRotation={targetRotation} activeRupee={activeRupee} />
168-
169-
<ContactShadows rotation-x={Math.PI / 2} position={[0, -10, 0]} opacity={0.25} width={100} height={100} blur={2} far={50} />
170-
</group>
171-
</Suspense>
114+
<group ref={group}>
115+
{["sphere", "pyramid", "donut", "octahedron", "icosahedron"].map((name, i) => (
116+
<A11y key={name} role="content" description={`a ${name}`} tabIndex={-1}>
117+
<Shape
118+
index={i}
119+
position={[radius * Math.cos(i * ((Math.PI * 2) / 5)), 0, radius * Math.sin(i * ((Math.PI * 2) / 5))]}
120+
active={snap.active === i}
121+
color={name}
122+
/>
123+
</A11y>
124+
))}
125+
</group>
172126
)
173127
}
174128

175-
useGLTF.preload(diamond)
176-
useGLTF.preload(rupee)
177-
178-
const App = () => {
179-
const [darktheme, setDarktheme] = useState(false)
180-
const mainStyle = useSpring({
181-
background: darktheme ? "#1c1c1c" : "#f4f4f4",
182-
})
183-
129+
export default function App() {
130+
const snap = useProxy(state)
184131
return (
185-
<animated.main style={mainStyle}>
186-
<Canvas resize={{ polyfill: ResizeObserver }} camera={{ position: [0, 0, 15] }} pixelRatio={[1, 2]}>
187-
<pointLight position={[100, 100, 100]} intensity={0.5} />
188-
<hemisphereLight color="#ffffff" groundColor="#b9b9b9" position={[2, -25, 10]} intensity={0.85} />
189-
<Carroussel />
190-
<A11y
191-
role="button"
192-
description="Dark mode button theme"
193-
pressedDescription="Dark mode button theme, activated"
194-
actionCall={() => {
195-
setDarktheme(!darktheme)
196-
}}
197-
activationMsg="Theme Dark enabled"
198-
deactivationMsg="Theme Dark disabled">
199-
<SimpleToggleButton position={[0, -8, 0]} />
200-
</A11y>
132+
<main className={snap.dark ? "dark" : "bright"}>
133+
<Canvas resize={{ polyfill: ResizeObserver }} camera={{ position: [0, 0, 15], near: 4, far: 30 }} pixelRatio={[1, 1.5]}>
134+
<pointLight position={[100, 100, 100]} intensity={snap.disabled ? 0.2 : 0.5} />
135+
<pointLight position={[-100, -100, -100]} intensity={1.5} color="red" />
136+
<ambientLight intensity={snap.disabled ? 0.2 : 0.8} />
137+
<group position-y={2}>
138+
<Nav left />
139+
<Nav />
140+
<Carroussel />
141+
<Floor />
142+
<A11y
143+
role="button"
144+
description="Light lowering button"
145+
pressedDescription="Light lowering button, activated"
146+
actionCall={() => (state.dark = !snap.dark)}
147+
activationMsg="Lower light enabled"
148+
deactivationMsg="Lower light disabled"
149+
disabled={snap.disabled}
150+
debug={true}
151+
a11yElStyle={{ marginLeft: "-40px" }}>
152+
<ToggleButton position={[0, -3, 9]} />
153+
</A11y>
154+
<A11y
155+
role="button"
156+
pressed={true}
157+
description="Power button, click to disable the scene"
158+
pressedDescription="Power button, click to turn on the scene"
159+
actionCall={() => (state.disabled = !snap.disabled)}
160+
activationMsg="Scene activated"
161+
deactivationMsg="Scene disabled">
162+
<SwitchButton position={[-3, -5, 7]} />
163+
</A11y>
164+
</group>
165+
{/* <Suspense fallback={null}>
166+
<EffectComposer multisampling={0}>
167+
<SSAO radius={20} intensity={50} luminanceInfluence={0.1} color="#154073" />
168+
<SMAA />
169+
</EffectComposer>
170+
</Suspense> */}
201171
</Canvas>
172+
<Badge />
202173
<A11yAnnouncer />
203-
</animated.main>
174+
</main>
204175
)
205176
}
206-
207-
export default App

example/src/index.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
import React from "react";
2-
import ReactDOM from "react-dom";
1+
import React from "react"
2+
import ReactDOM from "react-dom"
3+
import "./styles.css"
4+
import App from "./App"
35

4-
import App from "./App";
5-
6-
const rootElement = document.getElementById("root");
7-
ReactDOM.render(
8-
<React.StrictMode>
9-
<App />
10-
</React.StrictMode>,
11-
rootElement
12-
);
6+
const rootElement = document.getElementById("root")
7+
ReactDOM.render(<App />, rootElement)

0 commit comments

Comments
 (0)