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, the same three an ExtendedFab takes. s is M3's baseline FAB and the default; reach for a bigger one when the FAB is competing
with a large hero image. There is no 40 pixel FAB, because M3 Expressive no longer recommends one.
<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 the stronger pair and the three container styles the softer one; pick whichever stands out against the surface behind it. There is no surface style, because M3 Expressive no longer recommends one.
<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
round is fully rounded and square is a tighter radius, reading the same
way as on Button. Either way, a press morphs the
corner towards the other shape.
<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 drops the shadow a level, for a FAB sitting on a surface that is already raised,
such as inside 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 the icon alone and animates the width. Drive it
from whatever you already track, a scroll position or a media query. The label moves to aria-label while it is collapsed, 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 turns the FAB into a set of related actions. The trigger becomes square and its icon
turns into a close affordance while the menu is open, and the items rise into place one after
another. placement decides which side they fan out to. Picking an action closes the
menu; pass closeOnSelect=false to keep it open for actions that toggle something.
variant colours the trigger. The items are your own buttons, so pair them with it
yourself: set the tonal Button tokens on the FabMenu and every item inherits
them.
<script lang="ts">
import { Button, FabMenu } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<div style="display:flex;justify-content:center;padding-block-start:9rem;width:16rem">
<FabMenu label="Create">
{#snippet icon()}<Icon>add</Icon>{/snippet}
<Button variant="tonal">New document</Button>
<Button variant="tonal">New folder</Button>
<Button variant="tonal">Upload</Button>
</FabMenu>
</div>
<div style="display:flex;justify-content:center;padding-block-start:9rem;width:16rem">
<FabMenu
label="Create, tertiary"
variant="tertiary"
style="--np-tonal-button-container-color: var(--np-color-tertiary-container); --np-tonal-button-label-text-color: var(--np-color-on-tertiary-container)"
>
{#snippet icon()}<Icon>add</Icon>{/snippet}
<Button variant="tonal">New document</Button>
<Button variant="tonal">New folder</Button>
<Button variant="tonal">Upload</Button>
</FabMenu>
</div>
As link
Give a FAB an href and it renders an <a>. A disabled FAB stays a <button>, because a disabled link is still followable.
<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-pressed-shape | Corner radius while pressed. |
--np-fab-elevation | Shadow, overriding lowered. |
Accessibility
The FAB renders a native <button>, or an <a> with href. It usually shows an icon alone, so the label is what gives it a name,
and the same text becomes its tooltip.
An extended FAB shows the label as text and is named by it. Keep the FAB late in the DOM, where it sits visually, so the tab order matches the screen, and remember that one screen has one FAB: two of them leave people guessing which action the screen is about.
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' | 'round' | Corner radius at rest. |
lowered | boolean | false | Drops the shadow a level. |
disabled | boolean | null | undefined | false | Makes the FAB non-interactive and forces a <button>. |
ExtendedFab attributes
Everything Fab takes, with label shown as text rather than used as a tooltip,
plus:
| Attribute | Type | Default | Description |
|---|---|---|---|
collapsed | boolean | false | Shrinks to the icon alone, animating the width. The label moves to aria-label. |