Search topics
Resizable panels you can only resize by dragging
The divider between a sidebar and the main panel moves only by dragging, so it takes no focus, says no width, and no key or click changes the width.
<div class="divider"></div> The problem
People who can't drag can't make the sidebar wider or narrower, and screen reader users aren't told it can be resized or how wide it is.
A notes app has two panels: a sidebar listing the notes, and the open note beside it. Note titles that are too long for the sidebar are cut short, so the app lets people widen it: a thin line runs between the panels, the pointer turns into a left and right arrow over it, and a drag moves it anywhere from a fifth to half of the window. Press Tab and focus goes through the notes in the sidebar, then jumps to the note's Share button. The line is never a stop, and nothing on the page changes the width any other way.
Some layouts mark the line up as a separator, with role="separator" and aria-orientation="vertical". Nothing changes for the keyboard: Tab still passes it, and the arrow keys do nothing. A screen reader reading down the page finds, at most, a separator line, with no width and nothing to change.
The divider is an empty div that a script moves on pointer events. It isn't a link, a button or a field, and it has no tabindex, so the browser gives it no Tab stop and no keys. Changing the sidebar's width is something the app lets people do, and none of it works from the keyboard, which fails 2.1.1.
The only way to change the width is to drag, which fails 2.5.7. Resizing a panel doesn't need a drag: nothing about the result depends on the path the pointer takes. And keyboard support doesn't settle it either. The Understanding page for 2.5.7 says that "achieving keyboard equivalence for a dragging operation does not automatically meet this success criterion": someone on a touch screen, with a head pointer or with a mouse they can't hold down may have no arrow keys to fall back on, and needs something they can click or tap.
The divider is also a control the script makes work, with no role, name or value, which fails 4.1.2. Adding role="separator" doesn't help on its own. ARIA defines two kinds of separator: a static one, which is a visible boundary like hr, and a focusable one, which is a widget whose aria-valuenow gives its position. A separator that can't take focus is the static kind, so the role now tells assistive technology there is nothing here to operate, and no value says how wide the sidebar is. Its aria-orientation="vertical" is right, since a separator is horizontal unless it says otherwise, but it describes a line, not a control.
People resize a sidebar when the main panel is too cramped to read or write in, or when the sidebar cuts their notes' names short. Both happen most to people who zoom, because zoom makes everything in each panel larger in relation to the window. In an app they use every day, a width they can't change is a cost they pay all day.
5 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (affected)
- Cognitive (affected)
- Seizures (not affected)
- Limited manipulation Keyboard and switch users never land on the divider, and voice control users have nothing to name, so the sidebar stays the width it was given.
- Limited vision People who zoom need to change the width most, since each panel fits less of a magnified page, and the line they have to grab is a pixel or two wide.
- Limited reach and strength People with tremors struggle to grab a thin line and hold it still, and the width changes again as they let go.
- Limited language, cognitive and learning abilities Only the pointer's shape, on hover, says the line can be dragged, and a touch screen has no hover, so many people never find out the sidebar can change.
- Without vision Screen reader users find nothing between the panels, or a separator line with nothing to change, so they aren't told the layout can be adjusted at all.
A scanner sees an empty div, which needs no name, or a separator that can't take focus, which is the static kind and needs nothing either. So neither Before breaks a rule it knows. axe's aria-required-attr does report a separator that takes focus but has no aria-valuenow, so a half-finished fix is found, but the failure itself isn't. EaseWeb's audit sends a review lead for a thin strip with a resize pointer that takes no focus, because only a person can try whether the width can be changed some other way on the page. Asked to make the divider accessible, a code assistant may add tabindex="0" and role="separator" and stop: the divider is then a Tab stop that does nothing, with no value, and there is still no way to resize without a drag.
Try it
Make the sidebar wider with the mouse, then put the mouse aside and try again with Tab.
Broken 1: drag the line between the panes. Then press Tab: focus goes from the last note to Share, never to the divider, and the panel below never mentions it.
Broken 2: the same divider, with role="separator" added. Tab still passes it, and the arrow keys do nothing. A screen reader reading down the page finds at most a separator line, with no width and nothing to change.
Apply a fix. Try it with Tab and the arrow keys, Home and End, then with single clicks only. Can you set the width without dragging?
Broken Can you fix this?
Packing list
- Passport and tickets
- Charger and adapter
- Rain jacket
Screen reader says:
(Tab through the app)
The fix
Let people change the panel width without dragging.
A divider that only a drag moves
<div class="notes-app"> <nav class="sidebar" id="sidebar" aria-labelledby="notes-title" style="width: 30%"> <h2 id="notes-title">Notes</h2> <ul> <li> <a href="/notes/packing-list" aria-current="page">Packing list</a> </li> … </ul> </nav> <div class="divider"></div> <main class="note"> <h1>Packing list</h1> <button type="button">Share</button> … </main></div>What is wrong3 points
- The divider can't take focus, so the keyboard never reaches it.
- A drag is the only way to move it.
- It has no role or value, so assistive technology isn't told there is a control here, or how wide the sidebar is.
A splitter that takes the arrow keys, with Narrower and Wider buttons
<div class="notes-app"> <nav class="sidebar" id="sidebar" aria-labelledby="notes-title" style="width: 30%"> <h2 id="notes-title">Notes</h2> <ul> <li> <a href="/notes/packing-list" aria-current="page">Packing list</a> </li> … </ul> <div class="sidebar-width" role="group" aria-label="Sidebar width"> <button type="button" data-step="-5">Narrower</button> <button type="button" data-step="5">Wider</button> </div> </nav> <div class="divider" role="separator" tabindex="0" aria-orientation="vertical" aria-labelledby="notes-title" aria-controls="sidebar" aria-valuenow="30" aria-valuemin="20" aria-valuemax="50" aria-valuetext="30%"></div> <main class="note"> <h1>Packing list</h1> <button type="button">Share</button> … </main></div>How it is fixed6 steps
- Put the divider in the Tab order with
tabindex="0". A separator that takes focus is the focusable kind, a widget, which must havearia-valuenow. - Name it after the pane it resizes, "Notes", through
aria-labelledby, and pointaria-controlsat that pane. - Give the sidebar's width as a share of the app with
aria-valuenow,aria-valueminandaria-valuemax, and say witharia-valuetextthat the number is a percentage. - Move it by 5% with Left and Right Arrow, and to the narrowest and widest the sidebar can be with Home and End.
preventDefault()stops the arrow keys from scrolling the page too. - Add Narrower and Wider buttons. They take single clicks and taps, so the width can be changed without a drag, which the splitter alone doesn't give. They are native buttons, so they are in the Tab order and voice control users can say "Click Wider". They step by the same 5% as the keys, so they reach every width the keys can.
- Send every way of resizing through
setWidth(), so the value a screen reader reads is always the width on screen. The drag stays, as a shortcut.
This fixes both Befores and passes 2.1.1, 2.5.7 and 4.1.2. The divider now follows the window splitter pattern in the ARIA Authoring Practices Guide, apart from Enter to collapse the pane (see Implementation decisions).
Why this fix: Make a gesture a shortcut, never the only way
Use the splitter with the buttons: it is the only fix that leaves nothing out. It lets keyboard users resize from the line they can see, in the way the ARIA pattern describes, gives screen reader users the width, and takes single clicks and taps. It is the most code, and how a screen reader handles the arrow keys on a separator varies, so the buttons are its fallback as well as its pointer alternative. The buttons alone pass all three criteria and are the smallest change, needing no ARIA beyond hiding the divider, but no width is read and the buttons sit away from the line: they are a first step for a sidebar people resize now and then, until the divider can be made a splitter. The splitter on its own is not enough either: add the buttons to it.
The value can be in any unit, as long as aria-valuenow, aria-valuemin and aria-valuemax use the same one and aria-valuenow changes with every drag, key and button. A share of the app, as here, keeps its meaning at any window size; a width in pixels works too. ARIA asks for aria-valuetext only when the number alone can't carry the value. A bare "30" doesn't say whether it is percent or pixels, so the fixes here add the unit ("30%"); leave it off where the number says it all.
A few things decide how the splitter behaves in a real layout.
- Orientation: a separator is horizontal unless it says otherwise. Panels side by side need
aria-orientation="vertical"and Left and Right Arrow; panels one above the other, such as a list over a preview, keep the default and use Up and Down Arrow. - Right-to-left layouts: APG's Left Arrow moves the divider to the left. With the sidebar on the right, that makes it wider, so read the direction from the page (
getComputedStyle(divider).direction) and flip the keys to match. - Collapsing: APG's pattern also has Enter collapse the pane and restore it, which the fixes here leave out. If you add it, add a visible collapse button too, for the same reason as the Narrower and Wider buttons, and while the pane is closed set
aria-valuetextto say so ("Collapsed"), since its smallest value would read as a width it doesn't have. - Focus: the divider is thin, so draw its focus ring around the whole strip that takes the drag, at 3:1 against both panels; see low-contrast form fields, icons and focus rings.
- The drag: keep the strip that takes the drag wider than the line drawn, and set
touch-action: noneon it, so a drag on it doesn't scroll the page. - Narrow windows: at 400% zoom or on a phone the panels should stack, or the sidebar become a drawer, as pages that scroll sideways on a narrow screen explains. Hide the divider and the buttons then (
display: none), so no Tab stop is left that resizes nothing. - Remembering the width: if the app keeps it between visits, set
aria-valuenowandaria-valuetextfrom the saved width as the page loads, not only after the first change.
Panel libraries vary. Some give the handle role="separator" and tabindex="0" but no aria-valuenow, which axe reports; some give it role="slider", which also takes the arrow keys and has a value, but is read as a slider, not as the line between two panels; some give it nothing at all. Check what the focused handle exposes and whether a click alone can change the width, not what the library's documentation promises.
Design ideas
A divider can stay a hairline and still change width without a drag.
Three ways to draw the line between the notes and the open note. Drag each, then try single clicks, then put the mouse aside and use Tab: two change the width with single presses, and one moves only under a held pointer.
In the design file
- Draw the divider at rest with something to press beside it, not only a line that appears on hover.
- Write down the narrowest and widest the sidebar can be, and the step each press makes, such as 5%.
- Draw the divider and each button with its focus ring, at 3:1 against both panels.
- Draw the phone layout, where the panels stack and no divider is shown.
Passes
- Packing listArrows in a pill on the line A small white pill sits on the middle of the line with a narrower and a wider arrow, each 24 by 24 pixels, so the control is where the eye already looks for it. Each press moves the line by 5%, and the line itself still takes Tab and the arrow keys and still drags. Draw the arrows dimmed at the narrowest and widest, not gone.
Rule of thumb: Make a gesture a shortcut, never the only way
- Packing listSidebar sizes in the toolbar Three sizes, Narrow, Medium and Wide, in a small switch above the panels, with the current one filled in blue (6.83:1 on white), so everyone can see which size is on. The line between the panels can still be dragged to any width in between, and then none of the three is filled.
Rule of thumb: Make a gesture a shortcut, never the only way
Fails
- Packing listA line that appears only on hoverWCAG 2.5.7 The calmest layout: no line at all between the panels until the pointer comes close, when a 2-pixel blue line fades in to be dragged. Dragging it is the only way to change the width, so Tab finds nothing, a click does nothing, and a touch screen, which has no hover, never shows the line.
Rule of thumb: Make a gesture a shortcut, never the only way
Verify the fix
5 checks.
Test with: Keyboard, Screen reader, Mouse, Touch, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the layout or its panel library changes.
How screen readers handle role="separator" tabindex="0"
Limits
This is a starting pattern, not a guarantee for every application. Screen readers differ in how they read a separator and whether they pass it the arrow keys, so test the ones your users have. A single slider for a price or a volume is covered in range sliders that only move by dragging, moving cards and items in drag-and-drop boards that only a mouse can use, and gestures in general in pinch, drag and tilt gestures with no button. Resizable table columns, panels that can be docked or floated, and more than two panes in a row have needs of their own, and are not covered here. Test the complete layout with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- ARIA APG: windowsplitter (opens in a new tab)
- w3.org: wai-aria-1.2 (opens in a new tab)
- WCAG Understanding: dragging-movements (opens in a new tab)
- WCAG Technique F108 (opens in a new tab)
- WCAG Understanding: keyboard (opens in a new tab)
- WCAG Understanding: name-role-value (opens in a new tab)
- developer.mozilla.org: separator_role (opens in a new tab)
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.