# Floating Action Button

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

## Introduction

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 |
| --- | --- | --- | --- | --- |
| Small | `circle extra small` | 40dp | 12dp | 24dp |
| Default | `circle extra` | 56dp | 16dp | 24dp |
| Medium | `circle extra medium` | 80dp | 20dp | 26dp |
| Large | `circle extra large` | 96dp | 28dp | 36dp |

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-height` | 56px |
| `--md-comp-fab-container-width` | 56px |
| `--md-comp-fab-container-shape` | 16px |
| `--md-comp-fab-icon-size` | 24px |

## 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.

Create `extend`  
16dp / 20dp

Create `extend small`  
56dp

Create `extend medium`  
80dp

Create `extend large`  
96dp

Create `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.

Compose default  
`primary-container`

Compose `secondary-container`

Compose `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-height` | 56px (80px `medium`, 96px `large`) |
| `--md-comp-extended-fab-container-shape` | 16px (20px `medium`, 28px `large`) |
| `--md-comp-extended-fab-icon-size` | 24px (28px `medium`, 36px `large`) |
| `--md-comp-extended-fab-leading-space` | 16px (26px `medium`, 28px `large`) |
| `--md-comp-extended-fab-icon-label-space` | 8px (12px `medium`, 16px `large`) |
| `--md-comp-extended-fab-trailing-space` | 20px (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.

-   Compose
-   Add photo
-   Record

-   Compose
-   Add photo

-   Compose
-   Add photo

```
<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](#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-height` | 56px |
| `--md-comp-fab-menu-close-button-container-width` | 56px |
| `--md-comp-fab-menu-close-button-icon-size` | 20px |
| `--md-comp-fab-menu-close-button-between-space` | 8px |
| `--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-height` | 56px |
| `--md-comp-fab-menu-menu-item-between-space` | 4px |
| `--md-comp-fab-menu-menu-item-icon-size` | 24px |
| `--md-comp-fab-menu-menu-item-icon-label-space` | 8px |
| `--md-comp-fab-menu-menu-item-leading-space` | 24px |
| `--md-comp-fab-menu-menu-item-trailing-space` | 24px |
| `--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](#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 }
);
```
