A Fabric.js canvas is a bitmap, so a screen reader cannot see its objects. An accessible Fabric.js editor needs good controls around the canvas. The engine gives you the state; your app renders the buttons, dialogs and status text.
Undo and redo buttons
<button
onClick={() => engine.undo()}
disabled={!state.canUndo}
aria-keyshortcuts="Control+Z Meta+Z"
>
Undo
{state.undoLabel ? (
<span className="sr-only">: {state.undoLabel}</span>
) : null}
</button>- Use real
<button>elements, so they get focus and keyboard activation. - Put the step label, such as "Undo: Move rect", in the accessible name. Screen reader support for
titleis unreliable. bindKeyboardShortcutsleaves inputs and Fabric.js text editing alone, so keyboard users keep native text undo there.
Save status
Announce save changes politely, without moving focus:
<p aria-live="polite" aria-atomic="true">
{state.saveStatus === "saving" && "Saving…"}
{state.saveStatus === "saved" && "All changes saved"}
{state.saveStatus === "unsaved" && "Unsaved changes"}
{state.saveStatus === "error" && "Could not save. Retrying."}
{state.saveStatus === "conflict" && "Someone else changed this document."}
</p>Keep the aria-live region on the page all the time and change only its text. A region added at the moment of the change is often not announced.
Choices in a dialog
Conflicts, recovery offers and missing images need the user to decide. Show them in a <dialog>, move focus into it, and return focus when it closes. Name each choice clearly, such as "Keep my version", rather than "OK".
The canvas itself
- Give the canvas an accessible name:
<canvas aria-label="Floor plan editor">. - Offer a text list of important objects, built from
engine.toDocument().objects. - Offer keyboard ways to move the selected object, such as arrow keys that call
engine.transaction("Move", ...).
An accessible Fabric.js editor also avoids time limits. Autosave and recovery run in the background and never need a quick response from the user.