UX Design

Accessible Rich Text Editor Design: Formatting Content Inclusively

By EZUD Published · Updated

Accessible Rich Text Editor Design: Formatting Content Inclusively

Rich text editors — WYSIWYG editors, content management system editors, email composers — let users create formatted content with headings, lists, links, images, and more. These editors combine the complexity of a text processor with the accessibility challenges of a web application. Building or choosing an accessible rich text editor is one of the hardest accessibility problems in web development, but the W3C WAI-ARIA provides patterns for toolbar, menubar, and editing interaction that make it achievable.

The Fundamental Challenge

Rich text editors use contenteditable — an HTML attribute that makes a <div> or other element editable. The contenteditable attribute provides basic text entry and cursor movement, but every formatting action (bold, italic, headings, lists, links) must be implemented as custom functionality. Each of these formatting actions needs:

  • A keyboard shortcut
  • A toolbar button
  • Screen reader announcement of the action and result
  • A way to determine the current formatting state

Multiply this across dozens of formatting options, and the accessibility surface area is enormous.

Toolbar Accessibility

The editor toolbar should follow the W3C ARIA toolbar pattern:

<div role="toolbar" aria-label="Text formatting" aria-controls="editor">
  <div role="group" aria-label="Text style">
    <button aria-pressed="false" aria-label="Bold (Ctrl+B)">B</button>
    <button aria-pressed="false" aria-label="Italic (Ctrl+I)">I</button>
    <button aria-pressed="false" aria-label="Underline (Ctrl+U)">U</button>
  </div>
  <div role="group" aria-label="Block format">
    <button aria-label="Heading level" aria-expanded="false" aria-haspopup="listbox">
      Paragraph ▾
    </button>
  </div>
  <div role="group" aria-label="Lists">
    <button aria-pressed="false" aria-label="Bulleted list">
      <svg aria-hidden="true"><!-- list icon --></svg>
    </button>
    <button aria-pressed="false" aria-label="Numbered list">
      <svg aria-hidden="true"><!-- numbered list icon --></svg>
    </button>
  </div>
  <div role="group" aria-label="Insert">
    <button aria-label="Insert link (Ctrl+K)">
      <svg aria-hidden="true"><!-- link icon --></svg>
    </button>
    <button aria-label="Insert image">
      <svg aria-hidden="true"><!-- image icon --></svg>
    </button>
  </div>
</div>

Toolbar Keyboard Interaction

  • Tab: Moves focus from the editing area to the toolbar (and back)
  • Left/Right Arrow: Moves between toolbar buttons (roving tabindex)
  • Enter/Space: Activates the focused button
  • Escape: Returns focus from the toolbar to the editing area

Grouping related buttons with role="group" and aria-label helps screen reader users understand the toolbar’s organization.

Toggle Button States

Formatting buttons that toggle (bold, italic, list) must use aria-pressed to communicate state:

<!-- Text is currently bold -->
<button aria-pressed="true" aria-label="Bold (Ctrl+B)">B</button>

When the cursor moves within the editor, update aria-pressed states to reflect the formatting at the current position. If the cursor is on bold text, the Bold button shows aria-pressed="true".

Editing Area

The editing area uses contenteditable:

<div
  id="editor"
  contenteditable="true"
  role="textbox"
  aria-multiline="true"
  aria-label="Email body"
  aria-describedby="editor-help"
>
  <p>Start typing...</p>
</div>
<p id="editor-help" class="visually-hidden">
  Use Ctrl+B for bold, Ctrl+I for italic, Ctrl+K to insert a link. Press Tab to access the formatting toolbar.
</p>

Key Attributes

  • role="textbox": Identifies the editable area as a text input to screen readers
  • aria-multiline="true": Communicates that the editor accepts multiple lines
  • aria-label: Names the editor for screen reader users
  • aria-describedby: Points to keyboard shortcut hints

Keyboard Shortcuts

Standard keyboard shortcuts should work consistently:

ShortcutAction
Ctrl+BBold
Ctrl+IItalic
Ctrl+UUnderline
Ctrl+KInsert/edit link
Ctrl+ZUndo
Ctrl+Y / Ctrl+Shift+ZRedo
TabMove to toolbar / indent list item
Shift+TabMove from toolbar / outdent list item
EnterNew paragraph
Shift+EnterLine break

Document these shortcuts in the aria-describedby text and in a help dialog accessible from the toolbar (a “Keyboard shortcuts” button).

The Tab Key Problem

Tab in a rich text editor has a dual purpose — inserting a tab or indent in the content, and moving focus to the toolbar or out of the editor. This conflict requires a design decision:

  • Option A: Tab moves focus (standard web behavior). Indent/outdent uses Ctrl+] and Ctrl+[.
  • Option B: Tab inserts indentation (desktop application behavior). Escape then Tab moves focus out.

Document whichever approach you choose and ensure it is communicated via aria-describedby.

When the user presses Ctrl+K or activates the link button, open a dialog with:

<dialog aria-label="Insert link">
  <h2>Insert Link</h2>
  <label for="link-url">URL</label>
  <input type="url" id="link-url" required />
  <label for="link-text">Link text</label>
  <input type="text" id="link-text" />
  <button type="submit">Insert</button>
  <button type="button">Cancel</button>
</dialog>

Pre-populate the link text with any selected text from the editor. Return focus to the editor after dialog dismissal.

Image Insertion

Image insertion should prompt for:

  • The image file or URL
  • Alt text — this is critical. The editor should require or strongly encourage alt text for every inserted image, following image alt text best practices
<dialog aria-label="Insert image">
  <h2>Insert Image</h2>
  <label for="img-url">Image URL</label>
  <input type="url" id="img-url" required />
  <label for="img-alt">Image description (alt text)</label>
  <input type="text" id="img-alt" required />
  <span class="help-text">Describe the image for people who cannot see it.</span>
  <button type="submit">Insert</button>
  <button type="button">Cancel</button>
</dialog>

Accessible Output

The content produced by the rich text editor must be accessible. The editor should generate clean, semantic HTML:

  • Headings use <h2>, <h3>, etc. (not <p><strong><font size="5">)
  • Lists use <ul> and <ol> (not styled paragraphs)
  • Links use <a href> with descriptive text
  • Images include alt attributes
  • Tables use proper <th> and <caption>

An editor that produces inaccessible HTML creates accessibility problems downstream for every reader of that content.

Choosing an Accessible Editor Library

When selecting a third-party editor:

  • TinyMCE: Comprehensive ARIA support, keyboard shortcuts, configurable toolbar. Actively maintained accessibility features.
  • CKEditor 5: WCAG 2.1 AA compliant by design. Keyboard navigation, screen reader support, and accessible dialogs.
  • ProseMirror/TipTap: Lower-level frameworks that require more accessibility work but offer flexibility.
  • Draft.js: Requires significant custom accessibility implementation.

Evaluate by testing with NVDA and VoiceOver. Create formatted content, navigate with keyboard, and verify screen reader announcements.

Testing Rich Text Editors

  1. Type and format text: Apply bold, italic, headings, and lists using only keyboard shortcuts. Verify formatting is applied.
  2. Toolbar navigation: Tab to the toolbar, navigate with arrow keys, activate buttons, return to the editor.
  3. Insert a link and image: Complete the dialog workflow with keyboard only. Verify the editor receives the content.
  4. Screen reader: Navigate through formatted content in the editor. Verify headings, lists, and links are announced correctly.
  5. Output audit: Inspect the generated HTML. Confirm it uses semantic elements.
  6. Apply testing methodology for comprehensive validation.

Key Takeaways

Accessible rich text editors require an ARIA toolbar pattern with toggle states, a properly identified editing area, consistent keyboard shortcuts, and dialogs for complex operations like link and image insertion. The Tab key behavior must be defined and documented. Inserted images should require alt text. The HTML output must be semantic. When choosing a library, test with real screen readers rather than relying on accessibility claims. The most accessible editor is one that produces accessible content — the accessibility chain extends from the author’s tool to every reader of the published content.

Sources