Search topics
Floating buttons and panels that keyboard focus reaches last
A floating Book now bar and the sheet it opens are written after the footer, so keyboard and screen reader users reach the page's main action, and then its dates, only after the whole page.
The problem
Keyboard and screen reader users reach the page's Book now button only after the whole page, and once it opens the dates, reach those only after the whole page again.
A tour company's booking page is drawn for a phone: "Old town walking tour", "2 hours, from $25 a person", a photo, the meeting point with an Open in maps link, three questions that open with a tap (Is the route step-free?, What if it rains?, Can I cancel?), and a Read all 128 reviews link, then the footer's Help centre, Terms and Contact us. A white bar floats at the bottom of the screen from the moment the page loads, with "From $25" and a Book now button, so booking is always a thumb away. Book now opens a sheet just above the bar to choose a date, with Continue.
A keyboard user loads the page and presses Tab to reach Book now, which they can see. Focus goes to Open in maps instead, then to each question, the reviews link and the three footer links. Book now is the ninth Tab. A screen reader user reading from the top hears the whole page, footer included, before "Book now, button, collapsed".
The developer sees the problem and moves the bar up the markup, after the price, so Book now is the first Tab. A second problem is left behind. Press Enter and the sheet opens; press Tab to reach the first date, and focus goes back to Open in maps. The dates come after the whole page again, nine presses later.
Both the bar and the sheet are written at the end of the page, after the footer, where booking widgets and component libraries often put whatever floats, so no parent can clip it. CSS draws them at the bottom of the screen, but Tab and a screen reader don't follow the drawing: they move through the page in the order of the markup, and in the markup the bar and the sheet come last. That is the gap technique C27 closes, making the order in the markup match the order on screen.
Success criterion 2.4.3 Focus Order asks that focus moves in an order that keeps the meaning and the task intact. The bar is drawn in front of the page from the first moment, as the page's main action, and on a phone it is the only Book now button there is. Yet it comes after everything, so keyboard and screen reader users work through the whole page to reach what everyone else reaches with one tap. Focus order doesn't have to follow the layout, and the criterion's Understanding page passes navigation written after the main content, so whether a floating control may come last is a judgement about the page. A back to top button or a chat launcher at the end of the page is a different case: they aren't what the page is for, and people expect them there.
The sheet fails the same criterion in the way failure F85 describes: a dialog or menu that a button opens, where focus doesn't move into it and the next Tab doesn't reach it. Moving the button fixes the first failure and leaves this one, because the sheet is still at the end. F85's test has a second half, which this sheet passes: Escape closes it and puts focus back on Book now. On the way to the sheet, focus also passes behind it, so the person loses sight of it too.
The longer the page, the further away the bar and the sheet are: on a real booking page, with a gallery of photos, a map, a dozen reviews and a full footer, Book now can be dozens of presses away, and the dates as many again.
3 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 (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Limited manipulation Keyboard and switch users see Book now from the moment the page loads, and reach it with the ninth Tab, after the footer. With the bar moved up, once it opens the dates, the next Tab goes back to the top of the page, and the dates are nine presses away again: on a real page with photos and reviews, dozens.
- Without vision Screen reader users hear the whole page, footer included, before Book now, with nothing to say a booking button was on screen all along; and when it opens the dates, they are read only at the end of the page.
- Limited vision Magnifier users zoomed in on the bar see Book now, but focus is elsewhere on the page until the ninth Tab, and with the bar moved up, the magnifier follows focus away from the sheet once it opens.
Every part is right on its own. Book now is a button with a name and aria-expanded, the sheet has a heading, a legend and labelled options, and every control can be reached, so axe has nothing to report. The failure is in the order, and only a person can say what order the page means. EaseWeb's audit now raises a lead for a control fixed to the window that Tab reaches only after the page's footer, leaving out back to top buttons, which belong at the end, and chat launchers and cookie banners, which have their own checks. And it reports the second failure as a finding: it opens the buttons that have aria-expanded and aria-controls, up to twelve on a page, and reports a panel when focus neither moves into it nor reaches it with the next Tab. A panel that a button opens without saying so, or one inside a third-party frame, is still found only by hand. AI assistants asked for a bar or a sheet that floats over the page can reach for a portal or document.body.append() to get it out from under overflow and z-index, without saying what that does to the order.
Try it
Book the tour with the keyboard. How many Tab presses does it take?
- Press Tab from the top of the page until you reach Book now.
- Press Enter to open the dates, then Tab. Does focus go to the first date, or back into the page?
- From the dates, press Shift + Tab, then open the sheet again and close it with Escape.
Broken Can you fix this?
Old town walking tour
2 hours, from $25 a person
Meeting point: the fountain in Market Square. Open in maps
Is the route step-free?
Yes. The route keeps to the flat streets of the old town.
What if it rains?
The tour goes ahead, with a covered stop at the market hall.
Can I cancel?
Yes, free of charge up to 24 hours before the start.
Choose a date
Focus: –
The fix
Write the floating button with the content it acts on, and its panel straight after it, in the markup or with popovertarget.
- 1A floating button that is the page's main action comes in the markup with its content, not after the footer.
- 2The next Tab after a button that opens a panel goes into the panel, or the panel takes focus as it opens; closing it puts focus back on the button.
- 3Check the order both ways, with Tab and Shift + Tab, in every browser you support.
The bar and its sheet after the footer
/* Room for the bar, so it never covers the end of the page or focus. */html { scroll-padding-bottom: 5rem;}body { padding-bottom: 5rem;}.book-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; align-items: center; justify-content: space-between; /* 4rem tall and 1rem above the bottom edge: the 5rem kept for it. */ height: 4rem; padding: 0 1rem; border-radius: 1rem; background: #fff;}.sheet { position: fixed; inset-inline: 1rem; bottom: 5.5rem; padding: 1rem; border-radius: 1rem; background: #fff;}The bar with its content, the sheet straight after it
/* Room for the bar, so it never covers the end of the page or focus. */html { scroll-padding-bottom: 5rem;}body { padding-bottom: 5rem;}.book-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; align-items: center; justify-content: space-between; /* 4rem tall and 1rem above the bottom edge: the 5rem kept for it. */ height: 4rem; padding: 0 1rem; border-radius: 1rem; background: #fff;}.sheet { position: fixed; inset-inline: 1rem; bottom: 5.5rem; padding: 1rem; border-radius: 1rem; background: #fff;}The button, its state, the closing and the room kept for the bar are all written well. Only the place of the bar is wrong: it comes after the footer, so Book now is the ninth Tab, after Open in maps, the three questions, the reviews link and the footer's three links. The sheet comes straight after it, so once Book now is reached, the dates are the next Tab.
The bar sits in the markup right after the price, which it repeats, and the sheet right after the bar; nothing else changes, and position: fixed draws both in the same places on screen. Book now is the first Tab in the page. With the sheet open, Tab from Book now goes to the first date, Tab past Continue goes on to Open in maps and closes the sheet, so the link with focus is never under it, and Shift + Tab from the dates goes back to Book now. While the sheet is closed, hidden keeps it out of the order. A screen reader reading from the top meets the bar after the price, and the sheet next. This is technique C27 for the bar, and technique SCR26, putting content that a control reveals straight after it, for the sheet. It depends on position: fixed still meaning the window: a parent with transform, filter or contain makes the bar and the sheet fixed to that parent instead, and a parent's z-index can draw later content over them, so check their parents.
All three put Book now with the price, and all three pass 2.4.3. Moving the sheet straight after the bar is the smallest change, with nothing else to add, and the only one where reading on from Book now in a screen reader also meets the dates next, in every browser. It needs the parents of the bar and the sheet to leave position: fixed and their stacking alone. The popover needs no script to open, to close on Escape, or to put focus back, tells a screen reader that Book now is expanded, and draws the sheet over everything wherever its markup sits, so it is the fix when a booking widget, a theme or a component library puts its panels at the end of the page; browse mode still reads the sheet there. Moving focus into the sheet is for a sheet you can neither move nor turn into a popover, such as one a third-party script adds. It is not enough on its own, because Tab and Shift + Tab out of the sheet still go to the end of the page.
Write the floating button where its content is: a Book now bar after the tour's name and price, which it repeats; a Filter button before the products it filters; an Add to bag bar after the product's name and price. Its corner on screen decides nothing for a keyboard or a screen reader. When the page already has its own Book now button near the top, the floating bar is a second way to the same action, and can stay at the end, as long as the page's own button is reached early.
The sheet here is non-modal, because people can still read the tour behind it. If a sheet covers the page so that nothing behind it can be used, make it a modal dialog instead: focus goes into it and stays there until it closes, as in modals that leave keyboard focus behind them.
Every version here closes the sheet when focus moves from inside it to anything on the page other than Book now, as search panels that close on Tab and drop keyboard focus does once focus has left the whole component. Left open, the sheet would cover what Tab moves on to, and could hide the control with focus entirely, which 2.4.11 Focus Not Obscured (Minimum) allows for content the person opened only while they can uncover it without moving focus. The listener reads relatedTarget, the element focus is moving to, so a click on the sheet's own padding, which moves focus to no other control, leaves it open, as does Tab out of the page. The bar itself keeps its 5rem clear with scroll-padding-bottom and padding at the end of the page, so it never covers focus or the end of the page, as in floating buy bars that cover the page when zoomed in.
Booking widgets and component libraries often render bars, popovers, menus and sheets through a portal, such as React's createPortal or Vue's Teleport, into the end of body, so that no parent clips them. Look for the option to render in place, or to use the native popover. Where neither exists, move focus into the panel, and check that Escape puts focus back on the button.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Verify the fix
6 checks, no mouse.
Test with: Keyboard, Screen reader
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the bar, its sheet or the page layout changes, and after a booking widget, component library or third-party script is updated.
Limits
This is a starting pattern, not a guarantee for every application. The bar, its sheet and the tour are this example's; test every floating button on the page, and every panel one opens, including chat, help and feedback panels, with the longest page it appears on. Whether a floating button belongs at the end of the order is a judgement about the page: a back to top button does, and a page's main action doesn't. A chat panel also has to read its messages out, as in chat widgets that never announce their messages. Popover focus order depends on the browser, so test it in each one you support. Class names 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.