Floating Action Button

Floating Action Button

A circular action that can open a menu of related shortcuts.

If you want a fixed floating action button, you can add multiple actions that appear on hover. The live demo is in the bottom-right corner of the page.

Wrap a large extra circle small (or circle extra) and a list of smaller ones in fab. That class pins the control to the corner. The menu opens on hover when the pointer can hover; add click-to-toggle to toggle on click. AutoInit() starts every matching element except those marked no-autoinit. Motion is CSS.

<div class="fab">
  <button type="button" class="button extra circle" aria-label="Edit">
    <span class="material-symbols" aria-hidden="true">mode_edit</span>
  </button>
  <ul>
    <li><a class="button extra circle small" href="#!" aria-label="Attach">…</a></li>
  </ul>
</div>

FAB sizes

A FAB is circle extra (or its alias circle large): 56dp, 16dp corners, primary-container, elevation 3. Three size classes go alongside the extra. The corner grows with the container, so a bigger FAB is not simply a scaled-up one.

small
40dp
default
56dp
medium
80dp
large
96dp
<button type="button" class="circle extra small" aria-label="Add">
  <span class="material-symbols" aria-hidden="true">add</span>
</button>
<button type="button" class="circle extra" aria-label="Add">
  <span class="material-symbols" aria-hidden="true">add</span>
</button>
<button type="button" class="circle extra medium" aria-label="Add">
  <span class="material-symbols" aria-hidden="true">add</span>
</button>
<button type="button" class="circle extra large" aria-label="Add">
  <span class="material-symbols" aria-hidden="true">add</span>
</button>
Size Class Container Corner Icon
Smallcircle extra small40dp12dp24dp
Defaultcircle extra56dp16dp24dp
Mediumcircle extra medium80dp20dp26dp
Largecircle extra large96dp28dp36dp

The large size needs the extra: circle large on its own is the alias for the default 56dp FAB, and keeps that meaning.

On an <a>, add button — the size classes only match button or a.button.

<a class="button circle extra medium" href="#!" aria-label="Add">
  <span class="material-symbols" aria-hidden="true">add</span>
</a>

Each size is the same four custom properties, so a one-off size is a matter of resetting them rather than writing new rules.

Token Default
--md-comp-fab-container-height56px
--md-comp-fab-container-width56px
--md-comp-fab-container-shape16px
--md-comp-fab-icon-size24px

Extended FAB

extend is the extended FAB: an icon plus a label at 56dp, 16dp corners, 8dp between the two. Wrap the label in its own <span> — the order of the two spans is the placement, and the wrapper is what earns the 16dp leading inset.

extend
16dp / 20dp
extend small
56dp
extend medium
80dp
extend large
96dp
extend
trailing icon
<button type="button" class="extend">
  <span class="material-symbols" aria-hidden="true">add</span><span>Create</span>
</button>
<button type="button" class="extend medium">
  <span class="material-symbols" aria-hidden="true">add</span><span>Create</span>
</button>

The size axis is small, medium and large, and the label role grows with the container: title-medium, title-large, headline-small. extend small is 56dp on a symmetric 16dp inset. A sizeless extend is Material 3's base extended FAB — 56dp on a 16dp / 20dp inset, label label-large.

The container colour is a role rather than a value. primary-container is the default; secondary-container and tertiary-container are the other two. Each sets the container and the label colour together, so the hover and focus state layers follow the role with it.

default
primary-container
secondary-container
tertiary-container
<button type="button" class="extend secondary-container">
  <span class="material-symbols" aria-hidden="true">edit</span><span>Compose</span>
</button>
Token Default
--md-comp-extended-fab-container-height56px (80px medium, 96px large)
--md-comp-extended-fab-container-shape16px (20px medium, 28px large)
--md-comp-extended-fab-icon-size24px (28px medium, 36px large)
--md-comp-extended-fab-leading-space16px (26px medium, 28px large)
--md-comp-extended-fab-icon-label-space8px (12px medium, 16px large)
--md-comp-extended-fab-trailing-space20px (16px small, 26px medium, 28px large)
--md-comp-extended-fab-container-color--md-sys-color-primary-container
--md-comp-extended-fab-label-text-color--md-sys-color-on-primary-container

FAB menu

fab-menu is the FAB that expands into a list of labelled actions. The trigger is an ordinary FAB; the actions are a <ul> (or <menu>) of pills, each an icon and a label at 56dp on a full corner. Click the FAB to expand; click it again, click outside, or press Esc to collapse.

Expanded is the framework's, not yours: the constructor stamps aria-expanded on the trigger and open() / close() move it along with the active class. Do not write either into the markup.

<div class="fab-menu">
  <button type="button" class="button extra circle" aria-label="Create">
    <span class="material-symbols" aria-hidden="true">add</span>
    <span class="material-symbols" aria-hidden="true">close</span>
  </button>
  <ul>
    <li><button type="button"><span class="material-symbols" aria-hidden="true">mode_edit</span><span>Compose</span></button></li>
    <li><button type="button"><span class="material-symbols" aria-hidden="true">image</span><span>Add photo</span></button></li>
  </ul>
</div>

Expanded, the FAB is the close button: the same disc on a full corner, the solid role colour rather than its container, and a 20dp glyph. Give the trigger a second icon and it becomes the close glyph — the first shows while collapsed, the second while expanded, and only ever one of them is in the box. With one icon the morph is the whole affordance.

The colour axis is the same three roles as the extended FAB, and one class moves both halves: the actions take primary-container (the default), secondary-container or tertiary-container, and the close button takes the matching solid primary, secondary or tertiary. Material 3 pairs them that way deliberately — the close button is the louder of the two.

<div class="fab-menu secondary-container">…</div>

It is the same FloatingActionButton instance as the fab speed dial, so AutoInit() starts it and open(), close() and isOpen all work. The two never style each other, and neither reaches the FAB to Toolbar transition, which is .fab.toolbar and stays there. direction and hoverEnabled are inert on a FAB menu: it opens upward, on click, both decided in CSS.

Beyond six actions the stagger runs out and the rest arrive together, which is past where Material 3 stops recommending them.

Token Default
--md-comp-fab-menu-close-button-container-height56px
--md-comp-fab-menu-close-button-container-width56px
--md-comp-fab-menu-close-button-icon-size20px
--md-comp-fab-menu-close-button-between-space8px
--md-comp-fab-menu-close-button-container-color--md-sys-color-primary
--md-comp-fab-menu-close-button-icon-color--md-sys-color-on-primary
--md-comp-fab-menu-menu-item-container-height56px
--md-comp-fab-menu-menu-item-between-space4px
--md-comp-fab-menu-menu-item-icon-size24px
--md-comp-fab-menu-menu-item-icon-label-space8px
--md-comp-fab-menu-menu-item-leading-space24px
--md-comp-fab-menu-menu-item-trailing-space24px
--md-comp-fab-menu-menu-item-container-color--md-sys-color-primary-container
--md-comp-fab-menu-menu-item-label-text-color--md-sys-color-on-primary-container

Initialization

The IIFE bundle exposes Expressive.FloatingActionButton. Call init yourself when you need options other than the defaults, or let Expressive.AutoInit() start every .fab with the defaults below.

document.addEventListener('DOMContentLoaded', function() {
  const elems = document.querySelectorAll('.fab');
  const instances = Expressive.FloatingActionButton.init(elems, {
    // specify options here
  });
});

Per-instance options can also be passed through AutoInit:

Expressive.AutoInit(document.body, {
  FloatingActionButton: { direction: 'top' }
});

Options

Name Type Default Description
direction String 'top' Direction the menu opens. One of 'top', 'right', 'bottom', or 'left'. Write direction-left (etc.) on the element, or pass this option — the constructor stamps the class.
hoverEnabled Boolean true When true, the menu opens on hover (CSS). When false, it toggles on click. .click-to-toggle is the markup switch.
toolbarEnabled Boolean false Expand the FAB into a toolbar on click. .toolbar is the markup switch. See FAB to Toolbar.

Methods

All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.FloatingActionButton.getInstance(elem);
.open();

Opens the FAB menu.

instance.open();
.close();

Closes the FAB menu.

instance.close();
.destroy();

Destroy the plugin instance and tear down its event handlers.

instance.destroy();

Properties

Name Type Description
el Element The DOM element the plugin was initialized with.
options Object The options the instance was initialized with.
isOpen Boolean Describes the open/close state of the FAB.

Horizontal FAB

Creating a horizontal FAB is easy. Set the direction option to 'left' or 'right'.

Add direction-left or direction-right on the wrapper. The JS direction option stamps the same class.

<div class="fab direction-left">…</div>

Click-only FAB

To disable hover and toggle the menu when the large button is clicked — useful on touch devices — add click-to-toggle (or pass hoverEnabled: false). On a device with no hover, click is used even without the class.

<div class="fab direction-left click-to-toggle">…</div>

FAB to Toolbar

Add toolbar (or pass toolbarEnabled: true) to expand the button into a full-width bar on click. Prefer a hover or click-only menu unless you specifically need the bar.

<div class="fab toolbar">
  <a class="button extra circle" aria-label="Edit"><span class="material-symbols large" aria-hidden="true">mode_edit</span></a>
  <ul>
    <li><a href="#!" aria-label="Insert chart"><span class="material-symbols" aria-hidden="true">insert_chart</span></a></li>
    <li><a href="#!" aria-label="Quote"><span class="material-symbols" aria-hidden="true">format_quote</span></a></li>
    <li><a href="#!" aria-label="Publish"><span class="material-symbols" aria-hidden="true">publish</span></a></li>
    <li><a href="#!" aria-label="Attach file"><span class="material-symbols" aria-hidden="true">attach_file</span></a></li>
  </ul>
</div>
Expressive.FloatingActionButton.init(
  document.querySelector('.fixed-action-btn.toolbar'),
  { toolbarEnabled: true }
);
  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.