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

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>

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 propertyDescription
--np-fab-container-colorBackground, overriding the variant.
--np-fab-icon-colorIcon and label color, overriding the variant.
--np-fab-shapeCorner radius at rest, overriding the shape.
--np-fab-pressed-shapeCorner radius while pressed.
--np-fab-elevationShadow, overriding lowered.

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.

AttributeTypeDefaultDescription
labelstring | undefinedundefinedAccessible name and tooltip text. A FAB shows no text, so set it.
iconSnippet | undefinedundefinedThe 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.
loweredbooleanfalseDrops the shadow a level.
disabledboolean | null | undefinedfalseMakes 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:

AttributeTypeDefaultDescription
collapsedbooleanfalseShrinks to the icon alone, animating the width. The label moves to aria-label.