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>

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.

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.

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.