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>

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 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-elevationShadow, overriding lowered.
--np-fab-motion-spatialHow 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.

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''square'Corner radius.
loweredbooleanfalseDrops the shadow a level.

ExtendedFab attributes

Everything Fab takes, plus the attribute below. The label shows as text instead of a tooltip.

AttributeTypeDefaultDescription
collapsedbooleanfalseShrinks 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:

AttributeTypeDefaultDescription
iconSnippet | undefinedundefinedLeading icon. It helps tell the actions apart.