Skip to content

Accessibility

The OmniBots chat widget is built to meet WCAG 2.1 AA compliance requirements. This page documents the specific accessibility features, keyboard interactions, ARIA patterns, internationalization support, and responsive behavior that ensure the widget is usable by everyone.

Keyboard Navigation

The widget is fully operable via keyboard without requiring a mouse or touchscreen.

Key Bindings

KeyAction
TabMoves focus to the next focusable element within the widget
Shift + TabMoves focus to the previous focusable element
EscapeCloses the chat window (returns focus to the launcher)
EnterSends a message (in the text input), activates buttons and links
Shift + EnterInserts a newline in the text input
SpaceActivates buttons and toggles

Focus Management

Focus behavior follows a predictable pattern to avoid disorientation:

  1. Opening the widget -- Focus moves to the chat window. If the pre-chat form is shown, focus moves to the first form field. If the chat is active, focus moves to the message input
  2. Focus trapping -- While the chat window is open, Tab cycles through focusable elements within the widget only. Focus does not leak to elements behind the widget
  3. Closing the widget -- Focus returns to the launcher button
  4. Dialog modals -- When the feedback form dialog opens, focus is trapped within it. Pressing Escape closes the dialog and returns focus to the trigger element

Visible Focus Indicators

All interactive elements display a visible focus ring when focused via keyboard:

  • Inputs and buttons: 2px solid outline in the theme's primary color with 2px offset
  • Focus-visible only: Focus indicators appear only for keyboard navigation (:focus-visible), not for mouse clicks (:focus:not(:focus-visible))
  • High contrast: Focus rings are designed to be visible against both light and dark backgrounds
imageWidget showing visible keyboard focus indicators: 2px primary-color outline on the send button, text input, and a quick reply button, with tab order numbers annotated
Keyboard focus indicators and tab order

Live Regions

The widget uses ARIA live regions to announce dynamic content changes to screen readers.

Message List

The message list container uses role="log" with aria-live="polite". New messages are announced as they arrive without interrupting the user's current action.

Typing Indicator

The typing indicator has role="status" with aria-live="polite", announcing when the bot or agent is typing.

Error Messages

  • Connection errors use role="status" with aria-live="polite"
  • Form validation errors use role="alert" for immediate announcement
  • Upload errors are announced via role="alert"

Escalation Banner

The escalation banner uses role="status" with aria-live="polite" and aria-atomic="true", ensuring the entire banner content is re-announced when the state changes (connecting, waiting, connected).

Unread Count

The unread message badge on the launcher button is announced to screen readers via appropriate ARIA attributes.

Text Alternatives

All non-text content has text alternatives:

ElementTechnique
Bot/agent avatararia-hidden="true" (decorative; sender info is in the message label)
Icon buttonsaria-label describing the action (e.g., "Attach file", "Send message")
SVG iconsaria-hidden="true" on the SVG, descriptive aria-label on the parent button
Image attachmentsalt attribute with filename (e.g., "Image: photo.jpg")
File type iconsaria-hidden="true" (decorative; file info is in adjacent text)
Rich content imagesalt attribute from image_alt property
Quick reply iconsDecorative; the button text serves as the label

Message Labels

Each message bubble has a computed aria-label that includes:

  • Sender identity ("You", "Bot", "Agent", or "System")
  • Timestamp
  • Message content (Markdown stripped, truncated to 120 characters)
  • Attachment count (if any)

Example: "Bot at 2:34 PM: Here are three options for your account. 1 file attached"

Color Contrast

The widget enforces minimum contrast ratios throughout:

Text Contrast (4.5:1 minimum)

ElementForegroundBackgroundRatio
Body text#111827#FFFFFF15.4:1
Muted text#6B7280#FFFFFF4.6:1
Recording duration#DC2626#FFFFFF4.6:1
Connection status#B45309#FFFFFF4.6:1
Error messages#EF4444#FFFFFF4.5:1

UI Component Contrast (3:1 minimum)

ComponentTechniqueRatio
Input border#767F8D on white3.2:1
Icon buttons1px solid #767F8D border3.2:1
Send button1px solid rgba(0,0,0,0.2) for light primary colors3:1+
Escalation disconnect buttonWhite border on primary background3:1+

Luminance Detection

The widget includes a luminance detection algorithm for bot bubbles. When a dark bot_bubble_color is configured, the widget automatically switches to light-on-dark color schemes for links, code blocks, blockquotes, and other inline elements.

Resizable Text

The widget supports text resizing up to 200% without loss of functionality:

  • Line heights use relative values (e.g., 1.5) rather than fixed pixel values
  • Container overflow is set to auto to allow scrolling when text-spacing increases
  • The textarea max-height is set to 120px with overflow-y: auto
  • Long source titles use wrapping instead of nowrap to accommodate increased text size
  • Touch targets maintain a minimum of 24px height (per WCAG 2.5.8)

RTL Support

The widget supports right-to-left (RTL) languages with automatic layout mirroring.

RTL Languages

The following languages trigger RTL layout:

LanguageCode
Arabicar
Hebrewhe
Persianfa
Urduur

When an RTL language is detected (via user selection, browser language, or auto-detection), the widget sets dir="rtl" on its container element. This causes:

  • Text alignment to flip to right-to-left
  • Flex layouts to reverse direction
  • The input area icons to mirror positions
  • Quick replies and chips to flow from right to left

RTL detection is performed by the isRTL() function, which checks the base language code (first two characters) against the known RTL language set.

imageSide-by-side comparison of widget in LTR English layout (left-aligned bot bubbles, right-aligned user bubbles, left send button) and RTL Arabic layout (mirrored alignment, right-to-left text flow)
LTR vs RTL layout comparison

Bundled UI Translations

The widget includes bundled translations for 24 languages, covering all UI labels, button text, status messages, and accessibility strings. These translations load instantly without a network request.

LanguageCodeLanguageCode
EnglishenThaith
SpanishesVietnamesevi
FrenchfrTurkishtr
Haitian CreolehtSwedishsv
GermandeIndonesianid
PortugueseptUkrainianuk
ItalianitHebrewhe
DutchnlDanishda
PolishplFinnishfi
RussianruMalayms
JapanesejaHindihi
KoreankoArabicar
Chinesezh

Each translation covers the following UI strings:

  • Header status (Online, Connecting, Offline)
  • Input area (placeholder, send, attach, voice controls)
  • Messages (welcome, empty state, typing indicator)
  • Escalation (waiting, connected, disconnect)
  • Errors (connection, upload)
  • Accessibility (window labels, descriptions, button labels)
  • Hub (select department, switching to agent)

Server-Side Translation

For languages beyond the 24 bundled translations, the widget supports server-side translation via the backend's translation service. This enables support for 100+ languages. The widget sends a translate_request message over WebSocket and receives translated strings in response.

Language Detection Modes

The widget supports four language detection modes, configured per-bot:

ModeBehavior
autoBackend detects the visitor's language from their first message
browserUses the browser's navigator.language setting
user_selectVisitor manually selects their language from a selector
auto_with_selectAuto-detection with the option for the visitor to override via selector

When widget_language_selector is true in the language configuration, a globe/language icon appears in the input area. Clicking it opens a language selection panel where the visitor can choose from the supported languages.

The current language priority is: user selection > auto-detected > browser language > configured fallback.

Responsive Design

The widget adapts its layout and behavior based on screen size and device capabilities.

Mobile (< 480px)

  • Widget expands to fullscreen overlay
  • Font sizes increase to 16px in inputs (prevents iOS Safari zoom)
  • Touch targets increase to minimum 44px for easy tapping
  • Remove buttons on file previews are always visible (no hover required)
  • Avatar sizes reduce from 32px to 28px to save space
  • Input card border radius reduces from 16px to 12px
  • Safe area insets are applied for notched devices

Safe Area Insets

The widget respects safe-area-inset-* environment variables on notched devices (e.g., iPhone with Dynamic Island):

css
@supports (padding-bottom: env(safe-area-inset-bottom)) {
  @media screen and (max-width: 480px) {
    .input-area {
      padding-bottom: calc(12px + env(safe-area-inset-bottom));
    }
  }
}

This ensures the input area is not obscured by the device's home indicator or notch.

Tablet

On tablet-sized screens, the widget uses its default floating window layout with configurable dimensions. The max_height_percent setting (default 90%) prevents the widget from exceeding the viewport height.

Landscape Mode

In landscape orientation on mobile devices, the widget adjusts its layout to use the available horizontal space while respecting safe area insets on both sides.

Reduced Motion

The widget respects the prefers-reduced-motion media query. When enabled:

ElementNormal BehaviorReduced Motion
Recording mic buttonPulsing scale animationStatic (no animation)
TTS stop button ringsPulsing expansion animationStatic
Spinner (typing, loading)Rotating animationStatic or no animation
Dropdown transitionsSlide + fade animationInstant appearance
Content expand/collapseSmooth height transitionInstant
Submit button hoverTranslateY liftNo movement
Chevron rotation (sources)Smooth rotationInstant
css
@media (prefers-reduced-motion: reduce) {
  .icon-btn.recording {
    animation: none;
  }
  .dropdown-enter-active,
  .dropdown-leave-active {
    transition: none;
  }
}

High Contrast Mode

The widget responds to prefers-contrast: high:

  • Form input borders increase from 1px to 2px width
  • Error messages use bold (font-weight: 600) text
  • Focus indicators remain the standard 2px with offset for clarity

Screen Reader Testing

The widget has been tested with assistive technologies to ensure a consistent experience. Key interaction patterns include:

  • Launcher button announces as a button with the chat window's expanded/collapsed state
  • Message list announces as a log region; new messages are read as they arrive
  • Pre-chat form announces field labels, required status, and validation errors
  • Quick replies announce as buttons with their action labels
  • Rich content elements include appropriate ARIA roles and labels
  • Escalation state changes are announced via the banner's live region
  • File upload progress is announced via role="progressbar" with aria-valuenow

Widget Container

The widget container uses role="none" so assistive technology looks through to the actual interactive elements inside. The container mirrors aria-expanded to indicate whether the chat window is open or closed.

OmniBots AI Bot Platform