Search topics
Floating buy bars that cover the page when zoomed in
A floating Add to bag bar keeps the space drawn for one row, but at 400% zoom it wraps onto two, covers nearly half the window and keeps the page's last link under it for good.
flex-wrap: wrap; The problem
People who zoom in can't read the end of the page, and keyboard users among them lose focus under a floating bar that covers nearly half the window.
A clothing shop's product page is drawn for a phone 390 pixels wide: a photo, "Linen overshirt", $48, the colour, an Add to bag button, a short description, the details, and links to the size guide, delivery and returns, and contact. As the page scrolls, a white bar floats just above the bottom edge, rounded and softly shadowed, with the name, the price and Add to bag again, so the button is always a thumb away. The developer builds it as drawn and, knowing that bars like this can hide focus, keeps the bar's 5rem clear: scroll padding so focus stops above it, and padding at the end of the page so the last link can scroll above it. On the phone and on a laptop, it works.
Someone with low vision zooms the page to 400% in a window 1280 by 1024 pixels, which leaves the page 320 by 256 CSS pixels. At that width the name, the price and the button no longer fit in one row, and Add to bag drops to a second one. The bar is now 6rem tall, and with its offset it covers the bottom 7rem of a window 16rem tall: nearly half of it. The 5rem kept for it is 2rem short. Scroll to the end and Contact us stays under the bar, with no further to scroll. Tab to it, and focus is on a link nobody can see.
The space kept for the bar was measured from the drawing, one row tall, but the bar's height depends on the width it gets. Nothing tells the reserve that the bar has grown. Success criterion 1.4.10 asks that content can be presented at a width of 320 CSS pixels without loss of information or functionality. Contact us is on the page, but at that width no amount of scrolling shows it, so it is lost, and so is the link. Success criterion 2.4.11 fails with it: when the link has focus, it is entirely hidden by the bar. The scroll padding tells the browser to bring focus to 5rem above the bottom edge, which the grown bar still covers, and at the end of the page there is nowhere left to scroll, so the browser does all it was told and the link stays hidden. That is failure F110, a sticky footer or header completely hiding focused elements.
The Understanding page for 1.4.10 warns about pinned content beyond what it hides: it can make reading difficult if not impossible, and the page strongly suggests that at smaller sizes such parts get static positioning, or can be hidden by the person using the page. Here the strip left to read in, above the bar, is 9rem: six lines of text.
Anyone who sees the page at a small size in CSS pixels: zoomed in on a desktop, or with a large display size on a phone. A phone held on its side gives the bar a short window too, wide enough to keep it on one row but leaving little to read above it.
3 of 10 groups affected
- No vision (not 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 (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Limited vision People who zoom the page to 400% get a window 256 CSS pixels tall, and the bar, grown to two rows, covers nearly half of it. They read through the strip left above it, a few lines at a time, and the page's last link never comes out from under it.
- Limited manipulation Keyboard users who zoom in Tab to the last links and focus lands under the bar, out of sight, and there is no further to scroll. They can't tell what Enter will open.
- Limited language, cognitive and learning abilities Reading through a strip a few lines tall breaks up the text, and people who lose their place have to find it again after every scroll.
Scanners load the page at desktop size, where the bar fits on one row and the space kept for it matches. axe has no rule for it, and checks for hidden focus tab through the page at one window size. The failure needs a narrow window and a page scrolled to the end. EaseWeb's audit now looks for it: at 320 by 256 CSS pixels it scrolls to the end of the page and reports text or a control still under a fixed or sticky element. Design reviews look at the phone frame the bar was drawn in, 390 pixels wide, and rarely at 320. A bar written by an AI assistant can get flex-wrap: wrap to make it responsive, with the reserve copied from the height in the design.
Try it
Zoom the page to 400%, then read it to the end.
Drag the Zoom slider to 400%, then scroll the shop’s page to the bottom, or Tab to its last links. Can you read Contact us?
Broken Can you fix this?
Linen overshirt
$48
Colour: Sand
Washed linen, soft from the first wear. Wear it open over a T-shirt, or buttoned up on its own.
- 100% linen
- Machine wash at 30°C
- Relaxed fit
Window: 640 by 512 CSS pixels. The bar sits at the end of the page.
The fix
Keep the bar to one row so the space kept for it stays true, and let it sit in the page when the window is short.
- 1At every width, a floating bar is no taller than the space kept for it, so focus always stops above it.
- 2Scrolled to the end, nothing on the page is still under a floating element.
- 3Check the page at 400% zoom in a 1280 by 1024 window, and on a phone held on its side.
:root { /* The bar as drawn: 4rem tall, 1rem above the bottom edge. */ --bar-space: 5rem;}html { scroll-padding-bottom: var(--bar-space);}body { padding-bottom: var(--bar-space);}.buy-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; padding: 0.75rem 1rem; border-radius: 1rem; background: #fff;}.buy-bar-name { flex-grow: 1;}.buy-bar-add { margin-inline-start: auto; white-space: nowrap;}A bar that keeps to one row
:root { /* The bar as drawn: 4rem tall, 1rem above the bottom edge. */ --bar-space: 5rem;}html { scroll-padding-bottom: var(--bar-space);}body { padding-bottom: var(--bar-space);}.buy-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; align-items: center; gap: 0.5rem 1rem; padding: 0.75rem 1rem; border-radius: 1rem; background: #fff;}.buy-bar-name { flex-grow: 1; /* Cut the name short rather than wrap: it is the page's heading too. */ min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;}.buy-bar-add { margin-inline-start: auto; white-space: nowrap;}The bar comes last in the page, after the footer, and is fixed to the bottom of the window. At 320 pixels the button wraps onto a second row, so the bar grows to 6rem, while the scroll padding and the padding at the end of the page still keep 5rem. The last 2rem of the page are under the bar at every scroll position.
Without flex-wrap: wrap, the bar stays one row at every width, and the name gives up the room: at 320 pixels only its first few letters are left. The bar keeps the 4rem it was drawn at, so the space kept for it stays true, and every line of the page, Contact us included, can be scrolled clear of it. The full name is still the page's heading, so nothing is lost. The look on the phone and the desktop doesn't change. What it doesn't change either is the share of the window: at 400% the bar still covers 5rem of a window 16rem tall, almost a third, and people read through what is left. Keep the bar's other text, and its button's label, short enough to fit one row beside the price at 320 pixels.
Both keep the end of the page clear of the bar at every size, so both pass 1.4.10 and 2.4.11. Keeping the bar to one row is the smallest change, one line of CSS removed and four added, and a shorter name in the bar, but at 400% the bar still covers almost a third of the window, which is why it is marked as not enough on its own. Pinning the bar only when the window is tall enough is recommended: it is the same change plus one media query, and in a short window it gives the person all of the screen to read, while the phone and desktop look stays as drawn.
For the designer, that means two more frames in the design file. Draw the bar at 320 pixels wide, with the name cut short, and write the bar's height beside it, so the developer keeps exactly that space. Draw the window at 320 by 256 pixels, the size 400% zoom leaves, with the bar sitting after the footer. Keep the bar to one row of short things: a price and a button fit at 320 pixels; a photo, a size picker and a name don't.
Bars added by a shop platform's app or a third-party script often can't be restyled. Look for the vendor's settings for small screens, override its position from your own stylesheet if the markup is stable, and test again after each update.
Count everything that floats over the bottom of the window, because the space kept has to cover them all, and each is another thing to check at 400%.
- a chat button above the bar, which needs its own corner kept as in floating chat buttons that cover part of keyboard focus;
- a back to top button, which usually appears only after scrolling;
- a cookie banner, which on a first visit sits over the bar and adds its own height, and comes last in the page as in cookie banners that hide keyboard focus and come last;
- a sticky header, which takes the top of the same short window, as in sticky headers and banners that hide keyboard focus.
When the name has to show in full, keep the wrap and measure the bar instead: set --bar-space from the bar's rendered height with a ResizeObserver, adding its offset, so the reserve grows with it. That keeps the end of the page clear, but the bar takes even more of a short window, so pair it with the media query. position: sticky with bottom: 1rem, on a bar that is the last child of body, is another way to keep the end of the page clear: a sticky bar keeps its own place in the page, and settles there at the end. Focus in the middle of the page still needs scroll-padding-bottom to match its height.
In a short window, hiding a bar that only repeats the page's own button, with display: none, works as well as moving it, as long as everything in it is also on the page. A button that puts the bar away is the other answer the Understanding page names; it needs a name ("Hide"), a place near the start of the bar's own Tab order, and to keep the bar away for the rest of the visit. Both answers want the bar to keep to one row while it floats.
The breakpoint is in em, which in a media query means the browser's default text size, so it follows that setting, not the page's CSS; browser zoom shrinks the window in CSS pixels and so reaches it. Pick the height at which the bar and a useful amount of the page both fit: 30em leaves the bar floating at 200% zoom in a window 1024 pixels tall.
A chat panel that opens over the page is its own floating part, which must also close from the keyboard and read out its messages: see chat widgets that never announce their messages. Where the bar is written in the markup decides when keyboard and screen reader users reach it, and a panel it opens has to come next after it, as in floating buttons and panels that keyboard focus reaches last.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Verify the fix
5 checks, no mouse.
Test with: Zoom, Keyboard
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the bar, its text or the page layout changes.
Limits
This is a starting pattern, not a guarantee for every application. The 30em breakpoint and the bar's sizes are this example's; test your own bar with your longest product name, price and translation. A page that scrolls sideways at 320 pixels is a different failure, covered in pages that scroll sideways on a narrow screen. Passing at 400% doesn't make the bar easy to use at 400%: check that the page's own Add to bag button is easy to find once the bar sits in the page. Class names and tokens in the snippets are placeholders for your own.
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.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.