Floating action button

button.btn.fab[type="button"]

Floating action button — one action, pinned to the corner of the viewport.

Examples

One action, pinned

View “One action, pinned” as
<button class="btn btn-primary btn-icon btn-round fab" type="button" aria-label="New message">
  <svg class="icon icon-md" aria-hidden="true"><use href="#icon-check"></use></svg>
</button>

Five classes, each doing a job the library already had a name for: .btn is the control, .btn-primary the colour, .btn-icon squares it, .btn-round rounds it, .fab pins it.

Swap any one of them without new CSS. It is icon-only, so the name goes on the button and never on the <svg>.

With a visible label

View “With a visible label” as
<button class="btn btn-primary btn-round fab fab-extended" type="button">
  <svg class="icon icon-md" aria-hidden="true"><use href="#icon-check"></use></svg>
  New message
</button>

No .btn-icon here: an extended fab is a pill sized by its content, and it needs no aria-label because it has words.

Tokens 5

Level 2, declared on .fab itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by fab
TokenDefault
--fab-z-index60
--fab-inset-block-endvar(--space-4)
--fab-inset-inline-endvar(--space-4)
--fab-shadowvar(--elevation-3)
--fab-size3.5rem

Variants and states

Variants

  • .fab-extended
  • .fab-sm

State it reads

Read from the platform, never mirrored into a class that could disagree with it.

  • :disabled

Before you ship it

What you have to do 1 requirements

  1. Name it. The icon-only fab has no accessible name at all — an aria-hidden <svg> contributes nothing, so the button announces as "button" and a screen reader user is told only that something is there. Put aria-label (or an <span class="sr-only">) on the button, never on the <svg>. Nothing in CSS can supply this and nothing detects its absence at runtime. Keep the corner clear. The fab is position: fixed, so it is out of flow and it covers whatever ends up underneath it — which, at the bottom-end corner of a page, is usually the last row of a table, a "load more" button or a footer link. The reservation is a declaration on your layout, because padding on <body> is wrong the moment the page is a scroll container or a shell. body { padding-block-end: calc(5.5rem + env(safe-area-inset-bottom, 0px)); } Measured: the fab occupies the last 4.5rem of the viewport at the default tokens — 3.5rem of button plus its 1rem inset. 5.5rem is that with 1rem of headroom. No token holds that figure: change --fab-size or the insets and you must change the reservation with it. A fab does not stack itself above anything else fixed to the same edge. A bar of your own pinned to the bottom will sit under it and lose the tap. Raise the fab by the height you already reserved for that bar: .fab { --fab-inset-block-end: calc(4.5rem + var(--space-4)); } Do not add env(safe-area-inset-bottom) to that value. This file already adds it once, so counting it twice leaves a visible gap on a notched phone and none anywhere else — the sort of bug that only appears on hardware nobody testing has. The fab also has the higher z-index of the two (60 against the bar's 50), so once it is raised it still paints above the bar's shadow rather than behind it. Keep it out of a transformed ancestor. position: fixed is relative to the viewport unless an ancestor establishes a containing block — which any transform, filter, backdrop-filter, perspective, will-change or contain: paint does. Put the fab near the end of <body>, as a sibling of your page shell rather than inside it. An animated card that happens to wrap the fab pins it to the card's corner instead of the screen's, and nothing about the CSS looks wrong. (That is also how you preview one inside a box on purpose — the docs demo puts transform: translate(0) on the stage for exactly this reason.) One fab per screen. It is the screen's single most important action. Two of them is a toolbar, and a toolbar belongs in the layout where it can be reached in tab order rather than floating over the content.

src/css/components/fab.css · npx mostlycss add fab