RichTextEditor accessibility
WCAG 2.1 AA requirements
Semantics
- The editor area is a standard
contentEditableelement, which screen readers handle natively. - The toolbar uses
aria-labelon each button for screen reader announcement. - Toggle buttons (Bold, Italic, etc.) use
aria-pressedto indicate active formatting state.
Focus management
- A 2px focus ring (
focus-within:ring-2 ring-ring) appears around the entire editor container when any part receives focus. - The border becomes transparent on focus to avoid visual doubling.
- Toolbar buttons are focusable and show
focus-visible:ring-2.
Keyboard interaction
| Key | Action |
|---|---|
Tab | Indent content (semi, full) or move focus. |
Shift+Tab | Outdent content (semi, full). |
Ctrl+B | Toggle bold. |
Ctrl+I | Toggle italic. |
Ctrl+U | Toggle underline (default, semi, full). |
Ctrl+Z | Undo (semi, full). |
Ctrl+Shift+Z | Redo (semi, full). |
Markdown shortcuts (semi, full)
| Input | Result |
|---|---|
# | Heading 1 |
## | Heading 2 |
### | Heading 3 |
**text** | Bold |
*text* | Italic |
`text` | Inline code |
- or * | Bullet list |
1. | Numbered list |
> | Block quote (full only) |
Toolbar
- Each toolbar button has a descriptive
titleattribute that doubles as a tooltip showing the keyboard shortcut. - Buttons are grouped logically with visual separators (
aria-hidden). - The toolbar wraps (
flex-wrap) at narrow widths so all buttons remain visible and accessible.
Read-only mode
- When
readOnlyistrue, the toolbar is completely removed from the DOM (not just hidden). - The editor container gets
opacity-70as a visual indicator. - The content area is not editable — the Lexical editor's
editableconfig is set tofalse.
Error boundary
- The built-in Lexical error boundary catches rendering errors and prevents the entire page from crashing.
- Errors are logged to the console for debugging.
Tables
- Table cells are navigable via arrow keys within the Lexical table plugin.
- Table structure uses semantic
<table>,<tr>,<td>elements.
Check lists
- Check list items render as interactive checkboxes within the editor.
- Items use Lexical's built-in
CheckListPluginwith proper checked/unchecked CSS classes.
Audit checklist
- Toolbar buttons announce their purpose via
aria-label - Active formatting states announced via
aria-pressed - Focus ring visible on keyboard navigation
- Keyboard shortcuts work (Ctrl+B, Ctrl+I, etc.)
- Tab/Shift+Tab indent/outdent works (semi, full)
- Markdown shortcuts convert on space (semi, full)
- Read-only mode removes toolbar from DOM
- Content is accessible to screen readers via
contentEditable - Table cells are navigable via keyboard
- 4.5:1 contrast on text and placeholder in both themes