How Do You Meet WCAG 2.5.7 Dragging Movements Requirements?
WCAG 2.5.7 Dragging Movements is a new Level AA success criterion introduced in WCAG 2.2 that requires all functionality using dragging to have a single-pointer alternative. This criterion addresses a significant accessibility gap: approximately 15 million Americans have conditions affecting upper limb mobility, and many cannot perform the precise motor control required for drag-and-drop interactions. This guide covers everything you need to know about implementing accessible alternatives to dragging movements.
Key Takeaways
WCAG 2.5.7 fundamentally changes how developers must approach drag-and-drop interfaces. Here are the essential points:
- Every dragging operation must have an alternative that works with single-pointer actions (click, tap, or keyboard)
- This criterion is new in WCAG 2.2, released in October 2023, making it a compliance requirement for AA conformance
- Common drag interactions needing alternatives include sortable lists, sliders, file uploads, and kanban boards
- The path of the dragging motion doesn't need to be replicated—only the end result must be achievable
- Touch gestures like swiping are covered under WCAG 2.5.1 Pointer Gestures, not this criterion
Understanding WCAG 2.5.7 Dragging Movements
What Constitutes a Dragging Movement?
A dragging movement is any action that requires the user to:
- Press and hold at a starting point
- Move to an endpoint while maintaining the press
- Release at the endpoint
This includes mouse-based drag-and-drop, touch-based dragging, and stylus dragging. The criterion specifically targets operations where the path traveled matters less than the start and end points.
Common examples of dragging movements include:
- Reordering items in a list
- Moving cards between columns in a project board
- Adjusting slider values
- Resizing elements by dragging edges
- Drawing tools that create shapes
- Map panning and selection
Why This Criterion Matters
Many users cannot perform dragging movements due to:
- Motor impairments: Conditions like Parkinson's disease, arthritis, or cerebral palsy affect fine motor control
- Assistive technology usage: Screen readers, switch devices, and eye-tracking systems don't support drag operations
- Situational limitations: Users with one hand occupied, broken arms, or using non-traditional pointing devices
- Tremors and involuntary movements: Conditions causing difficulty in maintaining continuous pressure
Before WCAG 2.2, there was no specific requirement addressing drag-and-drop accessibility, creating a compliance gap that left many users unable to complete essential tasks.
The Technical Requirement
The official WCAG 2.5.7 requirement states:
> "All functionality that uses a dragging movement for operation can be achieved by a single pointer without dragging, unless dragging is essential or the functionality is determined by the user agent and not modified by the author."
Key terms explained:
- Single pointer: A finger, mouse cursor, or stylus making contact at a single point
- Without dragging: Using discrete actions like clicks, taps, or keyboard commands
- Essential: Cases where dragging is the only possible method (rare—primarily drawing or signature capture)
Common Interfaces Requiring Alternatives
Sortable Lists and Reorderable Content
Drag-to-reorder is ubiquitous in modern interfaces:
<!-- Drag-only implementation (fails WCAG 2.5.7) -->
<ul class="sortable-list">
<li draggable="true">Item 1</li>
<li draggable="true">Item 2</li>
<li draggable="true">Item 3</li>
</ul><!-- Accessible implementation with button controls -->
<ul class="sortable-list" role="list">
<li>
<span class="item-content">Item 1</span>
<div class="reorder-controls">
<button aria-label="Move Item 1 up" disabled>Move Up</button>
<button aria-label="Move Item 1 down">Move Down</button>
</div>
</li>
<li>
<span class="item-content">Item 2</span>
<div class="reorder-controls">
<button aria-label="Move Item 2 up">Move Up</button>
<button aria-label="Move Item 2 down">Move Down</button>
</div>
</li>
<li>
<span class="item-content">Item 3</span>
<div class="reorder-controls">
<button aria-label="Move Item 3 up">Move Up</button>
<button aria-label="Move Item 3 down" disabled>Move Down</button>
</div>
</li>
</ul>Kanban and Project Boards
Task management interfaces often rely heavily on drag-and-drop:
// Accessible kanban implementation
class AccessibleKanban {
constructor(container) {
this.container = container;
this.initDragAndDrop();
this.initClickAlternative();
this.initKeyboardNavigation();
}
initClickAlternative() {
// Add move button to each card
this.container.querySelectorAll('.kanban-card').forEach(card => {
const moveButton = document.createElement('button');
moveButton.className = 'move-card-btn';
moveButton.setAttribute('aria-haspopup', 'menu');
moveButton.textContent = 'Move to...';
moveButton.addEventListener('click', () => {
this.showMoveMenu(card);
});
card.appendChild(moveButton);
});
}
showMoveMenu(card) {
const columns = this.container.querySelectorAll('.kanban-column');
const menu = document.createElement('div');
menu.setAttribute('role', 'menu');
menu.className = 'move-menu';
columns.forEach(column => {
const option = document.createElement('button');
option.setAttribute('role', 'menuitem');
option.textContent = column.querySelector('.column-title').textContent;
option.addEventListener('click', () => {
this.moveCard(card, column);
menu.remove();
});
menu.appendChild(option);
});
card.appendChild(menu);
menu.querySelector('button').focus();
}
moveCard(card, targetColumn) {
targetColumn.querySelector('.card-container').appendChild(card);
this.announceMove(card, targetColumn);
}
announceMove(card, column) {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.className = 'sr-only';
announcement.textContent = `Card moved to ${column.querySelector('.column-title').textContent}`;
document.body.appendChild(announcement);
setTimeout(() => announcement.remove(), 1000);
}
}Range Sliders
Custom sliders must provide alternatives to dragging the thumb:
<!-- Accessible range slider -->
<div class="accessible-slider" role="group" aria-labelledby="volume-label">
<label id="volume-label">Volume</label>
<button
class="slider-decrement"
aria-label="Decrease volume"
onclick="adjustSlider('volume', -10)">
-
</button>
<input
type="range"
id="volume"
min="0"
max="100"
value="50"
aria-valuenow="50"
aria-valuemin="0"
aria-valuemax="100">
<button
class="slider-increment"
aria-label="Increase volume"
onclick="adjustSlider('volume', 10)">
+
</button>
<span class="slider-value" aria-live="polite">50%</span>
</div>.accessible-slider {
display: flex;
align-items: center;
gap: 0.5rem;
}
.slider-decrement,
.slider-increment {
width: 2rem;
height: 2rem;
border-radius: 50%;
border: 2px solid #333;
background: #fff;
cursor: pointer;
font-size: 1.25rem;
font-weight: bold;
}
.slider-decrement:hover,
.slider-increment:hover {
background: #eee;
}File Upload Drop Zones
Drag-and-drop file uploads need click-to-browse alternatives:
<div class="file-upload-zone"
ondrop="handleDrop(event)"
ondragover="handleDragOver(event)">
<p>Drag files here or</p>
<label for="file-input" class="upload-button">
Browse files
<input
type="file"
id="file-input"
multiple
accept="image/*,.pdf"
class="visually-hidden"
onchange="handleFileSelect(event)">
</label>
</div>Implementation Strategies
Strategy 1: Click-Based Alternatives
Provide discrete click targets that achieve the same result:
// Click-to-select, click-to-place pattern
class ClickToMove {
constructor() {
this.selectedItem = null;
}
selectItem(item) {
if (this.selectedItem) {
this.selectedItem.classList.remove('selected');
}
this.selectedItem = item;
item.classList.add('selected');
this.enableDropTargets();
}
enableDropTargets() {
document.querySelectorAll('.drop-target').forEach(target => {
target.classList.add('accepting');
target.setAttribute('aria-dropeffect', 'move');
});
}
placeItem(target) {
if (!this.selectedItem) return;
target.appendChild(this.selectedItem);
this.selectedItem.classList.remove('selected');
this.selectedItem = null;
this.disableDropTargets();
}
}Strategy 2: Context Menus
Right-click or long-press menus with movement options:
function showContextMenu(element, event) {
event.preventDefault();
const menu = document.createElement('div');
menu.className = 'context-menu';
menu.setAttribute('role', 'menu');
const actions = [
{ label: 'Move up', action: () => moveUp(element) },
{ label: 'Move down', action: () => moveDown(element) },
{ label: 'Move to top', action: () => moveToTop(element) },
{ label: 'Move to bottom', action: () => moveToBottom(element) }
];
actions.forEach(({ label, action }) => {
const button = document.createElement('button');
button.setAttribute('role', 'menuitem');
button.textContent = label;
button.addEventListener('click', () => {
action();
menu.remove();
});
menu.appendChild(button);
});
menu.style.left = `${event.clientX}px`;
menu.style.top = `${event.clientY}px`;
document.body.appendChild(menu);
}Strategy 3: Keyboard Navigation
Full keyboard support for all draggable operations:
function initKeyboardDrag(list) {
list.addEventListener('keydown', (event) => {
const item = event.target.closest('[role="listitem"]');
if (!item) return;
switch (event.key) {
case ' ':
case 'Enter':
event.preventDefault();
toggleDragMode(item);
break;
case 'ArrowUp':
if (item.classList.contains('dragging')) {
event.preventDefault();
moveItemUp(item);
}
break;
case 'ArrowDown':
if (item.classList.contains('dragging')) {
event.preventDefault();
moveItemDown(item);
}
break;
case 'Escape':
cancelDrag(item);
break;
}
});
}
function toggleDragMode(item) {
if (item.classList.contains('dragging')) {
item.classList.remove('dragging');
item.setAttribute('aria-grabbed', 'false');
announceToScreenReader('Item dropped');
} else {
item.classList.add('dragging');
item.setAttribute('aria-grabbed', 'true');
announceToScreenReader('Item grabbed. Use arrow keys to move, Enter to drop, Escape to cancel.');
}
}How to Test for WCAG 2.5.7 Compliance
Manual Testing Checklist
- Identify all drag operations: Catalog every interface element that uses dragging
- Attempt mouse-free completion: Try completing each operation using only clicks and keyboard
- Test with assistive technology: Verify screen reader users can complete all operations
- Check mobile alternatives: Ensure tap-based alternatives exist for touch drag
Automated Testing Limitations
Currently, no automated tools reliably detect WCAG 2.5.7 violations because:
- Tools cannot determine if a drag alternative exists
- The semantic connection between drag and alternative isn't machine-detectable
- Custom implementations vary too widely for pattern matching
Testing Script Template
// Manual testing helper
function auditDraggableElements() {
const draggables = document.querySelectorAll(
'[draggable="true"], [data-draggable], .ui-draggable, .sortable-item'
);
const results = [];
draggables.forEach((element, index) => {
results.push({
index,
element: element.outerHTML.substring(0, 100),
hasAriaGrabbed: element.hasAttribute('aria-grabbed'),
hasMoveButtons: !!element.querySelector('[class*="move"], [aria-label*="move"]'),
hasKeyboardHandler: typeof element.onkeydown === 'function',
location: getElementPath(element)
});
});
console.table(results);
return results;
}How to Fix WCAG 2.5.7 Dragging Issues
Step 1: Audit Existing Drag Operations
Document all dragging functionality in your application:
// Discovery script
const dragOperations = [
{
location: 'Dashboard > Task List',
operation: 'Reorder tasks',
currentMethod: 'Drag only',
proposedFix: 'Add up/down buttons'
},
{
location: 'Settings > Widget Layout',
operation: 'Rearrange widgets',
currentMethod: 'Drag only',
proposedFix: 'Add position dropdown'
}
];Step 2: Implement Accessible Alternatives
For each operation, implement at least one alternative:
<!-- Before: Drag-only reorder -->
<li class="task" draggable="true">
Complete accessibility audit
</li>
<!-- After: Multiple input methods -->
<li class="task" draggable="true" role="listitem" aria-grabbed="false">
<span class="task-content">Complete accessibility audit</span>
<div class="task-controls">
<button
class="reorder-btn"
aria-label="Reorder task: Complete accessibility audit"
onclick="showReorderMenu(this.parentElement.parentElement)">
<svg aria-hidden="true"><!-- drag handle icon --></svg>
</button>
<button onclick="moveUp(this)" aria-label="Move up">Up</button>
<button onclick="moveDown(this)" aria-label="Move down">Down</button>
</div>
</li>Step 3: Ensure Equivalent Functionality
The alternative must provide the same capabilities as dragging:
+------------------------------+--------------------------------+
| Drag Capability | Alternative Equivalent |
+------------------------------+--------------------------------+
| Move to any position | Position selector dropdown |
+------------------------------+--------------------------------+
| Continuous feedback | Live region announcements |
+------------------------------+--------------------------------+
| Cancel mid-drag | Escape key handler |
+------------------------------+--------------------------------+
| Visual placement preview | Focus styling on target |
+------------------------------+--------------------------------+Frequently Asked Questions
Does WCAG 2.5.7 mean I can't use drag-and-drop anymore?
No. You can absolutely continue using drag-and-drop—it's intuitive for many users. The requirement is that you must also provide an alternative method that doesn't require dragging. Both methods can coexist.
What about drawing applications where dragging is essential?
WCAG 2.5.7 includes an exception for cases where dragging is "essential"—meaning the dragging path itself is the operation. Freehand drawing, signature capture, and certain games fall under this exception. However, this exception is narrow; most drag-to-move operations have viable alternatives.
How is this different from WCAG 2.5.1 Pointer Gestures?
WCAG 2.5.1 covers path-based gestures (like swiping) and multipoint gestures (like pinch-to-zoom). WCAG 2.5.7 specifically addresses dragging movements where content is moved from point A to point B. An interface could pass 2.5.1 but fail 2.5.7 if it has inaccessible drag-and-drop.
Do sliders automatically fail this criterion?
Native HTML range inputs (`<input type="range">`) are handled by user agents (browsers) and are therefore exempt. Custom sliders that rely solely on dragging the thumb would fail. The fix is simple: ensure keyboard increment/decrement works and consider adding +/- buttons.
Is this criterion required for WCAG AA compliance?
Yes. WCAG 2.5.7 is a Level AA requirement in WCAG 2.2. Any organization claiming AA conformance to WCAG 2.2 must meet this criterion. Note that older WCAG 2.1 conformance claims don't include this criterion.
Related Resources
- WCAG 2.2 Compliance Guide: Everything You Need to Know
- Complete Accessibility Testing Guide for Web Developers
- Best Shopify Accessibility Tool 2025: A Complete Review
This article was crafted using a cyborg approach—human expertise enhanced by AI to deliver comprehensive, accurate, and actionable accessibility guidance.
Stay informed
Accessibility insights delivered
straight to your inbox.


Automate the software work for accessibility compliance, end-to-end.
Empowering businesses with seamless digital accessibility solutions—simple, inclusive, effective.
Book a Demo