/
The second principle of WCAG accessibility is "Operable" - ensuring that all users can interact with and navigate your website's interface. An operable interface works regardless of how someone interacts with it, whether through a mouse, keyboard, voice commands, or assistive technologies.
Consider the frustration of trying to use a website when your mouse stops working, or attempting to navigate a site using only the Tab key. These scenarios highlight why operable interfaces matter for everyone - from users with motor disabilities to power users who prefer keyboard shortcuts.
Operable interface requirements focus on making sure every interactive element on your website can be accessed and used by all users. This means providing multiple ways to navigate, ensuring sufficient time for interactions, and avoiding content that could cause seizures or other physical reactions.
An operable interface provides multiple ways for users to interact with content and complete tasks. The key aspects include:
The goal is ensuring that no matter how someone interacts with your website, they can access all the same functionality and information.
Every interactive element must be accessible through keyboard navigation, as many users rely on keyboards instead of pointing devices.
<!-- Native focusable elements work automatically -->
<button>Submit Form</button>
<a href="page.html">Go to Page</a>
<input type="text" placeholder="Enter text">
<select>
<option>Choose option</option>
</select>
<textarea placeholder="Enter message"></textarea>
<!-- Make custom interactive elements focusable -->
<div tabindex="0" role="button" onclick="handleClick()">Custom Button</div><!-- Good: Natural tab order follows logical sequence -->
<form>
<label for="firstname">First Name:</label>
<input type="text" id="firstname" name="firstname">
<label for="lastname">Last Name:</label>
<input type="text" id="lastname" name="lastname">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Submit</button>
</form>
<!-- Avoid disrupting natural tab order -->
<!-- <input tabindex="3"> <input tabindex="1"> <input tabindex="2"> --><!-- Provide skip links for keyboard users -->
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<a href="#navigation" class="skip-link">Skip to navigation</a>
<header>
<nav id="navigation">
<ul>
<li><a href="home.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<h1>Page Content</h1>
<!-- Main content here -->
</main>
</body>
<style>
/* Hide skip links until focused */
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 1000;
}
.skip-link:focus {
top: 6px;
}
</style><!-- Support both mouse and keyboard interactions -->
<div class="interactive-card"
tabindex="0"
role="button"
onclick="activateCard()"
onkeydown="handleKeyDown(event)">
<h3>Interactive Card</h3>
<p>Press Enter or Space to activate</p>
</div>
<script>
function handleKeyDown(event) {
// Activate on Enter (13) or Space (32)
if (event.keyCode === 13 || event.keyCode === 32) {
event.preventDefault();
activateCard();
}
}
function activateCard() {
// Handle activation logic
console.log('Card activated');
}
</script>Users must always know where they are on the page and be able to navigate predictably.
<style>
/* Ensure all interactive elements have clear focus indicators */
button:focus,
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* Custom focus styles for better visibility */
.custom-focus:focus {
outline: 3px solid #ff6b35;
outline-offset: 2px;
background-color: #fff8f0;
}
/* Never remove focus indicators entirely */
/* *:focus { outline: none; } ← DON'T DO THIS */
</style>
<button class="custom-focus">Button with Custom Focus</button>
<a href="#" class="custom-focus">Link with Custom Focus</a><!-- Modal dialog with proper focus management -->
<div id="modal" class="modal" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-content">
<h2 id="modal-title">Confirm Action</h2>
<p>Are you sure you want to delete this item?</p>
<div class="modal-actions">
<button id="cancel-btn">Cancel</button>
<button id="confirm-btn" class="danger">Delete</button>
</div>
<button class="modal-close" aria-label="Close dialog">×</button>
</div>
</div>
<script>
// Simple focus trap implementation
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
e.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
e.preventDefault();
}
}
}
});
}
</script>Users need sufficient time to read and interact with content, with options to extend or disable time limits.
<!-- Warn users before sessions expire -->
<div id="timeout-warning" class="warning" aria-live="assertive" style="display: none;">
<h3>Session Expiring Soon</h3>
<p>Your session will expire in <span id="countdown">5:00</span> minutes.</p>
<button onclick="extendSession()">Extend Session</button>
<button onclick="saveAndLogout()">Save and Logout</button>
</div>
<script>
function showTimeoutWarning() {
document.getElementById('timeout-warning').style.display = 'block';
document.getElementById('timeout-warning').focus();
}
function extendSession() {
// Extend user session
document.getElementById('timeout-warning').style.display = 'none';
}
</script><!-- Provide controls for auto-updating content -->
<section class="live-updates">
<div class="update-controls">
<h2>Live Feed</h2>
<button id="pause-updates">Pause Updates</button>
<button id="resume-updates" style="display: none;">Resume Updates</button>
</div>
<div id="live-content" aria-live="polite">
<!-- Live content appears here -->
</div>
</section>
<script>
let updatesRunning = true;
function pauseUpdates() {
updatesRunning = false;
document.getElementById('pause-updates').style.display = 'none';
document.getElementById('resume-updates').style.display = 'inline-block';
}
function resumeUpdates() {
updatesRunning = true;
document.getElementById('resume-updates').style.display = 'none';
document.getElementById('pause-updates').style.display = 'inline-block';
}
</script><!-- Warn about time limits and provide extensions -->
<form>
<div class="time-limit-notice">
<p>This form will timeout in <span id="form-timer">10:00</span> for security.</p>
<button type="button" onclick="extendFormTime()">Extend Time</button>
</div>
<label for="sensitive-data">Sensitive Information:</label>
<input type="text" id="sensitive-data" name="sensitive-data">
<button type="submit">Submit</button>
</form>Content must not contain elements that flash or move in ways that could trigger seizures or vestibular disorders.
<!-- Provide controls for motion -->
<div class="animation-controls">
<button onclick="toggleAnimations()">Toggle Animations</button>
<p>Animations: <span id="animation-status">On</span></p>
</div>
<div class="animated-content">
<div class="moving-element">Content with animation</div>
</div>
<style>
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
.moving-element {
animation: none;
}
}
/* Safe animation that doesn't flash rapidly */
.moving-element {
animation: gentle-slide 3s ease-in-out infinite;
}
@keyframes gentle-slide {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(20px); }
}
</style>
<script>
function toggleAnimations() {
const elements = document.querySelectorAll('.moving-element');
elements.forEach(el => {
el.style.animationPlayState =
el.style.animationPlayState === 'paused' ? 'running' : 'paused';
});
}
</script><!-- Don't auto-play media, provide user controls -->
<video controls preload="metadata">
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English">
Your browser does not support the video element.
</video>
<!-- If auto-play is necessary, provide pause control -->
<div class="media-container">
<video id="hero-video" muted loop>
<source src="background-video.mp4" type="video/mp4">
</video>
<button class="pause-video" onclick="toggleVideo()">Pause Video</button>
</div>Users need multiple ways to find content and understand their current location.
<!-- Provide multiple ways to find content -->
<header>
<!-- Primary navigation -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<!-- Search functionality -->
<form class="search-form" role="search">
<label for="search">Search site:</label>
<input type="search" id="search" name="search" placeholder="Enter keywords">
<button type="submit">Search</button>
</form>
</header>
<!-- Breadcrumb navigation -->
<nav aria-label="Breadcrumb">
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/products/laptops">Laptops</a></li>
<li aria-current="page">Gaming Laptops</li>
</ol>
</nav>
<!-- Site map link -->
<footer>
<nav aria-label="Footer navigation">
<ul>
<li><a href="/sitemap">Site Map</a></li>
<li><a href="/help">Help</a></li>
</ul>
</nav>
</footer><head>
<!-- Descriptive page titles -->
<title>Gaming Laptops - Products - TechStore</title>
</head>
<body>
<!-- Clear heading hierarchy -->
<main>
<h1>Gaming Laptops</h1>
<section>
<h2>Featured Gaming Laptops</h2>
<article>
<h3>UltraGame Pro 15</h3>
<p>Product description...</p>
</article>
</section>
<section>
<h2>Compare Gaming Laptops</h2>
<h3>By Price Range</h3>
<h3>By Performance</h3>
</section>
</main>
</body><!-- Keep navigation consistent across pages -->
<nav class="main-nav" aria-label="Main navigation">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/support">Support</a></li>
</ul>
</nav>
<!-- Indicate current page/section -->
<style>
.main-nav a[aria-current="page"] {
background-color: #e6f3ff;
font-weight: bold;
}
</style>Support various input methods and provide help for user interactions.
<!-- Provide clear instructions and help -->
<form>
<fieldset>
<legend>Contact Information</legend>
<div class="form-group">
<label for="phone">Phone Number:</label>
<input type="tel"
id="phone"
name="phone"
aria-describedby="phone-format"
placeholder="(555) 123-4567">
<div id="phone-format" class="help-text">
Format: (555) 123-4567
</div>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password"
id="password"
name="password"
aria-describedby="password-help"
required>
<div id="password-help" class="help-text">
Must be at least 8 characters with one number and one special character.
</div>
</div>
</fieldset>
</form><!-- Help prevent and recover from errors -->
<form novalidate>
<div class="form-group">
<label for="email">Email Address:</label>
<input type="email"
id="email"
name="email"
aria-describedby="email-error"
required>
<div id="email-error" class="error-message" aria-live="polite">
<!-- Error messages appear here -->
</div>
</div>
<div class="form-group">
<label>
<input type="checkbox" required aria-describedby="terms-error">
I agree to the terms and conditions
</label>
<div id="terms-error" class="error-message" aria-live="polite">
<!-- Error messages appear here -->
</div>
</div>
<button type="submit">Submit</button>
</form>Interfaces that work with keyboards and assistive technologies serve users with various motor and visual disabilities.
Keyboard shortcuts and clear navigation benefit all users, especially power users and those in situations where mouse use is difficult.
Efficient keyboard navigation often results in faster task completion for experienced users.
Operable interfaces help meet accessibility requirements in many jurisdictions.
Creating operable interfaces ensures that all users can successfully interact with your HTML content, regardless of their input method or abilities. By focusing on keyboard accessibility, proper focus management, safe content practices, and clear navigation, you build websites that truly work for everyone.
Start with keyboard accessibility - make sure every interactive element can be reached and used with a keyboard. Then add proper focus indicators, implement timing considerations, and provide multiple navigation methods. Remember that operable design often improves the experience for all users, not just those with disabilities.
The goal is creating interfaces that respond predictably to user input, provide clear feedback, and offer multiple ways to accomplish tasks. When you design with operability in mind, you create websites that are more reliable, more usable, and more inclusive for your entire audience.