What FloatButton does for you, and what it leaves to the page around it.
What it renders
A FloatButton is a native <button>. It is in the tab order, takes Enter and Space, shows the focus ring, and disabled sets the real attribute.
With href it is an <a>. A disabled link drops its href and leaves the tab order. Use href for navigation and onClick for everything else, not both.
The group is a plain div, with no role and no aria-expanded. That is fine for a static stack, and the part you fill in for a menu.
BackTop is added and removed, not hidden. Below visibilityHeight it is not on the page, so it cannot be tabbed to or found by a screen reader.
Naming an icon-only button
A button with only an icon has no accessible name, and nothing warns you. The default file icon is not a name either.
Give every circle FloatButton an aria-label. A circle has no room for content, so the label is its only name.
tooltip is not a name. It helps a mouse user and is not read by screen readers or shown on touch. Use both, saying the same thing.
A square button with content names itself. An aria-label on top replaces that name, so the button announces something other than what it shows.
Name the action, not the icon. “Compose message”, not “pencil”. If a badge count matters, put it in the name: “Notifications, 3 unread”.
The group trigger and its expanded state
In menu mode the trigger gets the group's aria-label but no aria-expanded, so a screen reader hears a button and never learns that it opens anything.
Add aria-expanded yourself. Set it on FloatButton.Group, which passes it to the trigger. It needs the real state, so control the group with open and onOpenChange.
Keep the trigger's name the same when it opens. The icon already changes, and aria-expanded says the rest.
The menu's buttons exist only while open, so a closed menu adds no tab stops. Opening does not move focus into it, and closing does not bring focus back. If a button in the menu closes it, move focus back to the trigger.
trigger="hover" works only with a mouse. A keyboard or touch user can reach the trigger and press it, and nothing opens. Use trigger="click", which works with keyboard, touch and mouse.
Where it lands in the tab order
A FloatButton floats over the page but keeps its place in the tab order, wherever it sits in your markup.
Render it near the end of the page, after the main content.
Do not use tabIndex to fix the order. A positive value jumps it ahead of every other control. Move the element instead.
Keep it after the main landmark, so it does not sit between a skip link and the content.
Check it against modals. A button painted over an open Modal or Drawer can be clicked from outside the dialog.
Covering content, small viewports and zoom
The button covers the page instead of pushing it, and it covers more as the screen gets smaller or the reader zooms in.
Add bottom padding to the scrolling container, about the button plus its inset. Otherwise the last row, line or submit button stays underneath it.
Test at 200% and 400% zoom. At 320 pixels wide the button can cover a fifth of a line.
Check the touch target. The default button is 40 pixels, under the 44 a touch target wants. Raise the controlHeightLG token rather than padding around it, and keep it clear of the system gesture area.
One fixed thing per corner. A cookie banner or chat launcher on top of the button hides it.
Contrast and motion
The icon sits on the button's own fill, but the button scrolls over whatever the page shows, so its edge has to stand out from all of it.
Check the button against what is behind it. A type="default" button relies on its shadow for 3:1, and it can vanish over a card of the same color. Give it a border.
Check it over the hardest background it crosses: a hero image, a chart, a dark section.
Check the badge against the button, and do not rely on a dot alone. Put its meaning in the name.
The group's menu animates in. Turn antd motion off with theme={{ token: { motion: false } }} for readers who prefer reduced motion.
BackTop handles reduced motion itself, jumping instead of gliding. Do not replace it with a smooth scroll that skips the check.