OTP Input Accessibility and Keyboard Support
Accessible OTP input with ARIA, keyboard navigation, and screen reader support.
OTP Input Accessibility and Keyboard Support
An accessible OTP input works with keyboard and screen readers. Here is how to make it accessible.
ARIA Labels
<div class="otp-container" role="group" aria-label="Enter OTP"> <input type="text" maxlength="1" aria-label="Digit 1" inputmode="numeric" /> <input type="text" maxlength="1" aria-label="Digit 2" inputmode="numeric" /> <input type="text" maxlength="1" aria-label="Digit 3" inputmode="numeric" /> <input type="text" maxlength="1" aria-label="Digit 4" inputmode="numeric" /> </div> `` `role="group"` and `aria-label` on the container. `aria-label` on each input for screen readers. ### Keyboard Navigation - **Tab**: default browser tab navigation (left to right). - **Shift+Tab**: reverse tab navigation (right to left). - **Arrow Left/Right**: custom navigation between inputs. - **Backspace**: clear current or go to previous. - **Enter**: submit when complete. ```js input.addEventListener("keydown", (e) => { if (e.key === "Enter" && isComplete()) verifyOTP(); if (e.key === "Backspace" && !e.target.value && index > 0) { inputs[index - 1].focus(); } }); input.addEventListener("keyup", (e) => { if (e.key === "ArrowLeft" && index > 0) inputs[index - 1].focus(); if (e.key === "ArrowRight" && index < inputs.length - 1) inputs[index + 1].focus(); }); `` ### Screen Reader Announcements ```js function announce(message) { const live = document.createElement("div"); live.setAttribute("aria-live", "polite"); live.className = "sr-only"; live.textContent = message; document.body.appendChild(live); setTimeout(() => live.remove(), 1000); } // on completion announce("OTP entered. Press Enter to verify."); // on error announce("Invalid OTP. Please try again."); `` ### Focus Management - Focus the first input on page load. - Focus the next input on digit entry. - Focus the previous input on backspace (if empty). - Focus the first input on error (after clearing). ```js window.addEventListener("load", () => inputs[0].focus()); `` ### Mobile Accessibility - `inputmode="numeric"`: shows numeric keyboard. - `pattern="[0-9]*"`: numeric pattern for older browsers. - `autocomplete="one-time-code"`: lets iOS auto-fill from SMS. ```html <input type="text" autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" /> `` ### The Takeaway Accessible OTP: ARIA labels (role, aria-label), keyboard navigation (Tab, arrows, Backspace, Enter), screen reader announcements (aria-live), focus management (first on load, next on input, previous on backspace), and mobile accessibility (inputmode, autocomplete). Accessibility is not optional.
Add ARIA labels (role='group', aria-label on each input). Support keyboard navigation (Tab, arrows, Backspace, Enter). Use aria-live for screen reader announcements. Manage focus (first on load, next on input, previous on backspace). Set inputmode='numeric' for mobile.
role='group' and aria-label on the container. aria-label='Digit N' on each input. Optionally, aria-live='polite' for announcements (e.g., 'OTP entered' or 'Invalid OTP').
Use aria-label on each input so screen readers announce 'Digit 1', 'Digit 2', etc. Use aria-live='polite' for announcements like 'OTP complete, press Enter to verify' or 'Invalid OTP'.
Set autocomplete='one-time-code' on the input. iOS detects SMS OTP codes and offers to auto-fill. Also set inputmode='numeric' and pattern='[0-9]*' for the numeric keyboard.
Tab: default browser navigation. Arrow Left/Right: custom navigation between inputs. Backspace: clear current or go to previous (if empty). Enter: submit when complete. Do not preventDefault on Tab.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

