FAB
A floating action button carries the primary action of a screen. Use one per screen, for the action people come to that screen to do.
Usage
A FAB shows an icon and no text, so give it a label. It becomes the accessible name
and a tooltip, the same way title works on a Button.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab label="Add">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
Sizes
Three sizes: 56, 80 and 96 pixels. An ExtendedFab takes the same three. s is the default. Use a bigger one when the FAB competes with a large hero image.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab size="s" label="Add, small">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab size="m" label="Add, medium">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab size="l" label="Add, large">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
Colors
Six color styles, each named after the tokens it maps to. The three tone styles are stronger, the three container styles softer. Pick the one that stands out against the surface behind it.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab variant="primary" label="Primary">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab variant="secondary" label="Secondary">
{#snippet icon()}<Icon>mail</Icon>{/snippet}
</Fab>
<Fab variant="tertiary" label="Tertiary">
{#snippet icon()}<Icon>favorite</Icon>{/snippet}
</Fab>
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab variant="primary-container" label="Primary container">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab variant="secondary-container" label="Secondary container">
{#snippet icon()}<Icon>mail</Icon>{/snippet}
</Fab>
<Fab variant="tertiary-container" label="Tertiary container">
{#snippet icon()}<Icon>favorite</Icon>{/snippet}
</Fab>
Shape
square, the default, has a corner radius that grows with the size. round is fully rounded, as on Button. The shape does not change on
press.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab shape="round" label="Round">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab shape="square" label="Square">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
Lowered
lowered lowers the shadow by one level. Use it for a FAB on a raised surface, such as a
card or a sheet.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab label="Default elevation">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
<Fab lowered label="Lowered">
{#snippet icon()}<Icon>add</Icon>{/snippet}
</Fab>
Extended FAB
An extended FAB shows its label as text, so it names itself and needs no tooltip. Use it when the action is not obvious from an icon alone.
<script lang="ts">
import { ExtendedFab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<ExtendedFab label="Compose">
{#snippet icon()}<Icon>mail</Icon>{/snippet}
</ExtendedFab>
<ExtendedFab variant="tertiary" label="Search">
{#snippet icon()}<Icon>search</Icon>{/snippet}
</ExtendedFab>
Collapsing
collapsed shrinks an extended FAB to its icon and animates the width. Set it from a
scroll position or a media query. While collapsed, the label moves to aria-label, so
the button keeps its name.
<script lang="ts">
import { ExtendedFab, Switch } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
let collapsed = $state(false)
</script>
<label style="display:inline-flex;align-items:center;gap:0.5rem">
<Switch bind:selected={collapsed} />
Collapsed
</label>
<ExtendedFab {collapsed} label="Compose">
{#snippet icon()}<Icon>mail</Icon>{/snippet}
</ExtendedFab>
FAB menu
A FAB menu opens two to six related actions from the FAB, as on a phone. When it opens, the FAB
turns into a round 56px close button in its top trailing corner, at any FAB size. The items rise
into place next to it, aligned with its trailing edge. When it closes, they fade out. placement sets which side they open to. In a short window the items scroll.
Use FabMenuItem for the actions. It has the size of a medium button and always shows
its label, ideally with an icon. The menu takes its colors from the FAB's variant, so
a tertiary FAB opens a tertiary close button with tertiary container items.
<script lang="ts">
import { FabMenu, FabMenuItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<div style="display:flex;justify-content:center;padding-block-start:14rem;width:16rem">
<FabMenu label="Create">
{#snippet icon()}<Icon>add</Icon>{/snippet}
<FabMenuItem>
{#snippet icon()}<Icon>mail</Icon>{/snippet}
Message
</FabMenuItem>
<FabMenuItem>
{#snippet icon()}<Icon>calendar_today</Icon>{/snippet}
Event
</FabMenuItem>
<FabMenuItem>
{#snippet icon()}<Icon>videocam</Icon>{/snippet}
Video
</FabMenuItem>
</FabMenu>
</div>
<div style="display:flex;justify-content:center;padding-block-start:14rem;width:16rem">
<FabMenu label="Create, tertiary" variant="tertiary-container">
{#snippet icon()}<Icon>add</Icon>{/snippet}
<FabMenuItem>
{#snippet icon()}<Icon>mail</Icon>{/snippet}
Message
</FabMenuItem>
<FabMenuItem>
{#snippet icon()}<Icon>calendar_today</Icon>{/snippet}
Event
</FabMenuItem>
<FabMenuItem>
{#snippet icon()}<Icon>videocam</Icon>{/snippet}
Video
</FabMenuItem>
</FabMenu>
</div>
On the web
On larger windows and desktop web, M3 opens a regular menu from the FAB instead, 4px away from it. To build it, anchor a Menu to the FAB.
<script lang="ts">
import { ExtendedFab, Menu, MenuItem } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
let fab: HTMLButtonElement | HTMLAnchorElement | undefined = $state()
</script>
<ExtendedFab
label="Create"
style="anchor-name:--create-fab"
command="toggle-popover"
commandfor="create-menu"
aria-haspopup="menu"
bind:element={fab}
>
{#snippet icon()}<Icon>add</Icon>{/snippet}
</ExtendedFab>
<Menu
anchor={fab}
id="create-menu"
style="position-anchor:--create-fab"
--np-menu-margin="4px"
--np-menu-position-area="bottom span-right"
--np-menu-justify-self="none"
>
{#each ['Event', 'Task', 'Out of office', 'Working location'] as entry (entry)}
<MenuItem command="hide-popover" commandfor="create-menu">{entry}</MenuItem>
{/each}
</Menu>
As link
Give a FAB an href and it renders an <a>.
<script lang="ts">
import { Fab } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Fab href="/components/button" label="Go to buttons">
{#snippet icon()}<Icon>arrow_forward</Icon>{/snippet}
</Fab>
Theming
| Custom property | Description |
|---|---|
--np-fab-container-color | Background, overriding the variant. |
--np-fab-icon-color | Icon and label color, overriding the variant. |
--np-fab-shape | Corner radius at rest, overriding the shape. |
--np-fab-elevation | Shadow, overriding lowered. |
--np-fab-motion-spatial | How an extended FAB collapses and expands, by default --np-motion-expressive-default-spatial. |
Accessibility
The FAB renders a native <button>, or an <a> with href. It usually shows only an icon, so the label gives it its name. The same
text becomes its tooltip.
An extended FAB is named by its label text. Keep the FAB late in the DOM, where it sits visually, so the tab order matches the screen. Use one FAB per screen. With two, people have to guess which action the screen is about.
A FAB menu has role="menu", and a menu may only hold menu items. FabMenuItem has role="menuitem". When the menu opens, focus stays on the
close button, and the items follow it top to bottom. They share one tab stop. The arrow keys move
between them, and typing a letter jumps to the action that starts with it. On the FAB, ↓ and ↑ open the menu on its first and last action.
API
Fab attributes
A FAB renders a <button>, or an <a> with an href, and takes that element's attributes. bind:element gives you whichever
it rendered.
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | undefined | undefined | Accessible name and tooltip text. A FAB shows no text, so set it. |
icon | Snippet | undefined | undefined | The icon. |
variant | 'primary' | 'secondary' | 'tertiary' | 'primary-container' | 'secondary-container' |
'tertiary-container' | 'primary-container' | Color style, named after the tokens it maps to. |
size | 's' | 'm' | 'l' | 's' | 56, 80 or 96 pixels. |
shape | 'round' | 'square' | 'square' | Corner radius. |
lowered | boolean | false | Drops the shadow a level. |
ExtendedFab attributes
Everything Fab takes, plus the attribute below. The label shows as text instead
of a tooltip.
| Attribute | Type | Default | Description |
|---|---|---|---|
collapsed | boolean | false | Shrinks to the icon alone, animating the width. The label moves to aria-label. |
FabMenuItem attributes
A medium tonal Button with role="menuitem", taking its attributes, plus:
| Attribute | Type | Default | Description |
|---|---|---|---|
icon | Snippet | undefined | undefined | Leading icon. It helps tell the actions apart. |