FAB
A floating action button (FAB) carries the one action a screen is for: compose, add, start. It floats above the content, holds an icon and no label, and there is at most one in view. When the action needs a word, use the extended FAB. See the M3 FAB guidelines.
Usage
ariaLabel is required: a FAB has no visible text.
import { createFab } from 'mtrl';
const fab = createFab({ icon: addIcon, ariaLabel: 'Create new item' });
fab.on('click', () => createItem());
document.body.append(fab.element);
<m-fab aria-label="Create new item"></m-fab>
<script type="module">
const fab = document.querySelector('m-fab');
fab.setAttribute('icon', addIcon);
fab.addEventListener('click', () => createItem());
</script>
import { Fab } from 'mtrl/react';
import { createItem, addIcon } from './app';
export function Example() {
return (
<Fab onClick={() => createItem()} icon={addIcon} ariaLabel="Create new item" />
);
}
<script setup lang="ts">
import { MFab } from 'mtrl/vue';
import { createItem, addIcon } from './app';
</script>
<template>
<MFab @click="createItem()" :icon="addIcon" aria-label="Create new item" />
</template>
<script lang="ts">
import { Fab } from 'mtrl/svelte';
import { createItem, addIcon } from './app';
</script>
<Fab onclick={() => createItem()} icon={addIcon} ariaLabel="Create new item" />
import { Fab } from 'mtrl/solid';
import { createItem, addIcon } from './app';
export function Example() {
return (
<Fab onClick={() => createItem()} icon={addIcon} ariaLabel="Create new item" />
);
}
Examples
Color and size
variant is one of the container styles primary-container (the default),
secondary-container and tertiary-container, or the tone styles primary, secondary and
tertiary; each takes its color and the matching on- color from the theme. size is
default, medium or large, and the icon grows with it, so one SVG serves every size.
small and surface are deprecated.
import { createFab } from 'mtrl';
const fab = createFab({
icon: editIcon,
ariaLabel: 'Edit',
variant: 'tertiary',
size: 'large',
});
document.body.append(fab.element);
<m-fab variant="tertiary" size="large" aria-label="Edit"></m-fab>
<script type="module">
const fab = document.querySelector('m-fab');
fab.setAttribute('icon', editIcon);
</script>
import { Fab } from 'mtrl/react';
import { editIcon } from './app';
export function Example() {
return (
<Fab variant="tertiary" size="large" icon={editIcon} ariaLabel="Edit" />
);
}
<script setup lang="ts">
import { MFab } from 'mtrl/vue';
import { editIcon } from './app';
</script>
<template>
<MFab variant="tertiary" size="large" :icon="editIcon" aria-label="Edit" />
</template>
<script lang="ts">
import { Fab } from 'mtrl/svelte';
import { editIcon } from './app';
</script>
<Fab variant="tertiary" size="large" icon={editIcon} ariaLabel="Edit" />
import { Fab } from 'mtrl/solid';
import { editIcon } from './app';
export function Example() {
return (
<Fab variant="tertiary" size="large" icon={editIcon} ariaLabel="Edit" />
);
}
In a corner
position fixes the FAB to a corner of the viewport, 16dp from both edges. Leave it unset to
place the FAB yourself, as in a card, a sheet or a
bottom app bar.
import { createFab } from 'mtrl';
const fab = createFab({
icon: addIcon,
ariaLabel: 'Create new item',
position: 'bottom-right',
});
document.body.append(fab.element);
<m-fab position="bottom-right" aria-label="Create new item"></m-fab>
<script type="module">
const fab = document.querySelector('m-fab');
fab.setAttribute('icon', addIcon);
</script>
import { Fab } from 'mtrl/react';
import { addIcon } from './app';
export function Example() {
return (
<Fab icon={addIcon} position="bottom-right" ariaLabel="Create new item" />
);
}
<script setup lang="ts">
import { MFab } from 'mtrl/vue';
import { addIcon } from './app';
</script>
<template>
<MFab :icon="addIcon" position="bottom-right" aria-label="Create new item" />
</template>
<script lang="ts">
import { Fab } from 'mtrl/svelte';
import { addIcon } from './app';
</script>
<Fab icon={addIcon} position="bottom-right" ariaLabel="Create new item" />
import { Fab } from 'mtrl/solid';
import { addIcon } from './app';
export function Example() {
return (
<Fab icon={addIcon} position="bottom-right" ariaLabel="Create new item" />
);
}
A FAB rests at elevation level 3 and rises to 4 on hover; lower() moves it to the lowered
levels, 1 and 2, and raise() restores them. animate: true scales it in when it is added to
the page.
API
Options
| Option | Type | Default | Description |
|---|---|---|---|
icon |
string |
undefined |
Icon as an HTML string, usually an SVG |
ariaLabel |
string |
required | Accessible name |
variant |
'primary-container' | 'secondary-container' | 'tertiary-container' | 'primary' | 'secondary' | 'tertiary' | 'surface' |
'primary-container' |
Color style; surface is deprecated |
size |
'small' | 'default' | 'medium' | 'large' |
'default' |
Container, icon and corner; small is deprecated |
position |
'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' |
undefined |
Fixes the FAB to a corner of the viewport |
disabled |
boolean |
false |
Creates it disabled |
iconSize |
string |
undefined |
Adds an mtrl-icon--<value> class to the icon, for your own CSS; the stylesheet has no rules for it |
animate |
boolean |
false |
Scales the FAB in when it is added to the page |
ripple |
boolean |
true |
Whether a press shows the ripple |
rippleConfig |
{ duration?, timing?, opacity? } |
undefined |
Only duration applies: how long, in ms, a released wave lingers before it is removed. timing and opacity are accepted and not applied |
type |
'button' | 'submit' | 'reset' |
'button' |
The button's type attribute |
value |
string |
undefined |
The button's value attribute, for forms |
class |
string |
undefined |
Extra classes on the element |
prefix |
string |
'mtrl' |
Prefix for CSS class names |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
setIcon(icon) / getIcon() |
icon: string |
FabComponent / string |
The icon HTML |
setPosition(position) |
position: string |
FabComponent |
Moves it to another corner |
getPosition() |
none | string | null |
The current corner, or null when unpositioned |
lower() / raise() |
none | FabComponent |
The lowered or the normal elevation levels |
enable() / disable() |
none | FabComponent |
The disabled state |
setValue(value) / getValue() |
value: string |
FabComponent / string |
The value attribute |
addClass(...classes) |
...classes: string[] |
FabComponent |
Adds classes to the element |
getClass(name) |
name: string |
string |
Prefixes a name: 'fab' gives 'mtrl-fab' |
on(event, handler) / off(event, handler) |
event: string, handler: Function |
FabComponent |
Adds or removes a listener |
destroy() |
none | void |
Removes the element and its listeners |
| Property | Type | Description |
|---|---|---|
element |
HTMLButtonElement |
The button |
icon |
{ setIcon, getIcon, getElement } |
The icon manager |
disabled |
{ enable, disable, isDisabled } |
The disabled-state manager |
lifecycle |
{ destroy } |
The lifecycle manager |
Events
| Event | Description | Data |
|---|---|---|
click |
Pressed; not fired while disabled | { event, element, originalEvent } |
focus / blur |
Took or lost focus | { event, element, originalEvent } |
event and originalEvent are the same DOM event; element is the button.
Accessibility
- A native
<button>, named only byariaLabel. When the icon changes, change the label with it. Tabfocuses it;SpaceandEnteractivate it. Disabled, it leaves the tab order.- Focus shows as a 2dp
outlinering, 2dp from the edge. - A fixed FAB covers the content under it: leave room at the end of a scrolling view.
Styling
Colors are the theme's roles: each style is the role it is named after, on its on- role, and
the state layer takes the icon color.
.mtrl-fab { }
.mtrl-fab--primary-container, .mtrl-fab--secondary-container, .mtrl-fab--tertiary-container { }
.mtrl-fab--primary, .mtrl-fab--secondary, .mtrl-fab--tertiary, .mtrl-fab--surface { }
.mtrl-fab--small, .mtrl-fab--default, .mtrl-fab--medium, .mtrl-fab--large { }
.mtrl-fab--lowered, .mtrl-fab--disabled, .mtrl-fab--animate-enter { }
.mtrl-fab--top-left, .mtrl-fab--top-right, .mtrl-fab--bottom-left, .mtrl-fab--bottom-right { }
.mtrl-fab__icon { }
Measurements
From the Compose M3 token files named in the last column.
| Attribute | Value | Source |
|---|---|---|
| Container, default | 56dp | FabBaselineTokens |
| Container, medium | 80dp | FabMediumTokens |
| Container, large | 96dp | FabLargeTokens |
| Container, small (deprecated) | 40dp | FabSmallTokens |
| Corner, default | 16dp (large) |
FabBaselineTokens |
| Corner, medium | 20dp (large-increased) |
Android fab_tokens.xml |
| Corner, large | 28dp (extra-large) |
FabLargeTokens |
| Corner, small | 12dp (medium) |
FabSmallTokens |
| Icon, default and small | 24dp | FabBaselineTokens, FabSmallTokens |
| Icon, medium | 28dp | FabMediumTokens |
| Icon, large | 32dp | FabLargeTokens; Android says 36dp |
| Elevation, rest / hover / focus / pressed | levels 3 / 4 / 3 / 3 | FabPrimaryContainerTokens |
| Lowered, rest / hover / focus / pressed | levels 1 / 2 / 1 / 1 | ExtendedFabPrimaryTokens |
| Disabled | on-surface at 12% container and 38% icon, level 0 |
library policy; the FAB tokens define no disabled state |
| Offset from the corner | 16dp | the position rules in _fab.scss |
| Entrance | 0.3s, emphasized decelerate | the fab-enter keyframes in _fab.scss |