@react-three/a11y Documentation

repository·main·Indexed 20 days ago

https://github.com/pmndrs/react-three-a11y

Accessibility support for React Three Fiber (R3F) that emulates standard HTML accessibility features in WebGL scenes. Provides components like A11y for focus management, roles (content, button, togglebutton, link), and screen reader support via A11yAnnouncer. Includes tools for managing user preferences such as prefers-reduced-motion and prefers-color-scheme through the A11yUserPreferences provider and useUserPreferences hook.

Tokens
8.8K
Snippets
34
Records
43
Agent score
68%

What's inside @react-three/a11y

  1. How @react-three/a11y manages accessibility in Canvas

    main

    The library manages accessibility by syncing semantic DOM elements that are absolutely positioned over the visible content in your page. When you use the A11y component with a specific role, the library appends the corresponding HTML tag to your document to ensure screen readers can interact with the 3D scene elements.

    Role to HTML Mapping:

    • role="link" $\rightarrow$ <a>
    • role="button" $\rightarrow$ <button>
    • role="content" $\rightarrow$ <p>
    • role="togglebutton" $\rightarrow$ <button> (with aria-pressed attribute)
  2. Use A11yUserPreferences across the Canvas boundary

    main

    Because React context cannot be readily shared between react-dom and the react-three-fiber renderer, providing A11yUserPreferences outside of the <Canvas /> component will prevent it from being consumed by components inside the <Canvas />.

    To bridge this gap, use the useContextBridge hook from @react-three/drei. This allows you to forward the context provided in the DOM layer into the R3F canvas layer.

  3. How A11yAnnouncer communicates with screen readers

    main

    The A11yAnnouncer component is used to send messages to screen readers via a div that is only visible to assistive technologies. It utilizes a zustand store to update the content of a live region.

    The underlying HTML structure used for announcements is:

    <div aria-atomic="true" aria-live="polite">{message}</div>
  4. Use the 'content' role to make 3D elements accessible

    main

    The role="content" is the simplest role in the @react-three/a11y component. It is intended for non-decorative elements in your canvas, acting similarly to an alt attribute for images.

    When you use this role, the library injects a <p> tag into the DOM containing your description. This allows screen reader users to perceive the text or meaning of the 3D object. It also provides a native focus indicator that syncs with the element's position in the DOM, helping screen reader users understand their location on the page.

    <A11y role="content" description="Welcome to my website">
      <Some3DComponentShowingText />
    </A11y>
  5. Configure A11y roles

    main

    The role prop determines the behavior, cursor, and special attributes of the A11y component:

    • content: Uses the default cursor. Used for providing information or navigation steps. It is not meant to be activated/clicked.
    • button: Uses the pointer cursor. Emulates a button or toggle. Triggers actionCall on click or keyboard activation. Supports activationMsg.
    • togglebutton: Emulates a button with two states (using aria-pressed). Supports activationMsg and deactivationMsg.
    • link: Uses the pointer cursor. Emulates an HTML link. Requires the href prop for screen reader information (it does not handle the actual navigation).

    Special Attributes per Role:

    • button: activationMsg
    • togglebutton: activationMsg, deactivationMsg
    • link: href (required for screen reader context)
    // Button example
    <A11y role="button" description="Send email" activationMsg="Sending email" />
    
    // ToggleButton example
    <A11y 
      role="togglebutton" 
      description="Dark theme" 
      activationMsg="Switched to dark theme" 
      deactivationMsg="Switched to light theme" 
    />
    
    // Link example
    <A11y role="link" href="https://url.com" />
  6. Use A11y roles: content, button, togglebutton, and link

    main

    The role prop determines the behavior, cursor, and special attributes of the A11y component:

    • content: Uses the default cursor. Intended for providing information or serving as a tab stop. It is not meant to be activated by clicks or keyboard.
    • button: Uses the pointer cursor. Emulates a button. It triggers actionCall on click or keyboard activation. Supports the activationMsg attribute.
    • togglebutton: Emulates a button with two states (using aria-pressed). Supports activationMsg and deactivationMsg to describe state changes.
    • link: Uses the pointer cursor. Emulates an HTML link. Requires the href prop so screen readers can announce the destination (this prop does not handle the actual navigation).

    Note: For link, you must manually implement the navigation logic in an actionCall.

    // Button example
    <A11y role="button" description="Send email" activationMsg="Sending email" />
    
    // ToggleButton example
    <A11y 
      role="togglebutton" 
      description="Dark theme" 
      activationMsg="Switched to dark theme" 
      deactivationMsg="Switched to light theme" 
    />
    
    // Link example
    <A11y role="link" href="https://url.com" />
  7. Make components accessible with the A11y component

    main

    To make a 3D object focusable and accessible, wrap it with the A11y component. The A11y component acts as a provider for its children, handling emulated focus state. Note that by default, nothing will be visually displayed or shown unless you configure props like showAltText or specific roles.

    import { A11y } from '@react-three/a11y'
    
    // ...
    <A11y>
      <MyComponent />
    </A11y>
  8. Adapt color schemes based on user preference

    main

    You can adapt the visual appearance of your 3D objects (like material colors) based on the user's preferred color scheme using a11yPrefersState.prefersDarkScheme.

    const My3dObject = () => {
      // access the user preferences
      const { a11yPrefersState } = useUserPreferences()
      const mesh = useRef()
    
      return (
        <mesh ref={mesh}>
          <boxBufferGeometry args={[1, 1, 1]} />
          <meshStandardMaterial color={a11yPrefersState.prefersDarkScheme ? 'darkblue' : 'lightblue'} />
        </mesh>
      )
    }
  9. Emulate a button using the A11y component

    main

    To make a 3D object behave like an accessible button, wrap it in the <A11y /> component and set the role prop to "button".

    When using `role="button":

    • The cursor changes to a pointer when hovering over the object.
    • The actionCall function is triggered on click, as well as on keyboard actions (like Enter) or gestures (like Double-Tap).
    • The element becomes actionable for screen reader users.
    • You can provide a description to explain the button's purpose and an activationMsg to announce a specific message when the button is activated.
    <A11y
      role="button"
      description="Send email"
      activationMsg="Sending email"
      actionCall={() => sendEmail()}
    >
      <Some3DComponent />
    </A11y>
  10. Handle reduced motion preferences

    main

    To respect users who request reduced motion, use the useUserPreferences hook to access a11yPrefersState.prefersReducedMotion. You can use this boolean to conditionally skip animations or frame-based updates inside useFrame.

    const My3dObject = () => {
      // access the user preferences
      const { a11yPrefersState } = useUserPreferences()
      const mesh = useRef()
    
      // Rotate mesh every frame
      useFrame(() => {
        // unless the user prefers reduced motion
        if (!a11yPrefersState.prefersReducedMotion) {
          mesh.current.rotation.x = mesh.current.rotation.y += 0.01
        }
      })
    
      return (
        <mesh ref={mesh}>
          <boxBufferGeometry args={[1, 1, 1]} />
          <meshStandardMaterial />
        </mesh>
      )
    }