Eunomia v1.0.0-beta.2
AllAngularReactNext.jsJavaScriptVue
EunomiaSalmonForestVioletOceanGoldFireCustom…
🇬🇧 English🇫🇷 Français

Fab

Fab is a circular button that calls attention to a screen's single most important action, such as composing a new message or adding an item to a list. It shares its visual styling and behavior with the regular button, so it can be extended into a wider pill that reveals a text label on hover or focus, and it can dock itself to a corner of the viewport with no extra layout work.

Dependencies

eun-icon · if set eun-loader · if loading
Overview API Examples Accessibility

When to use

Use a FAB for the single most important, most frequent action on a screen, such as composing a new message or adding an item, not for a list of equally-weighted actions. A page should have at most one. See Alternatives below for several related actions instead of just one.

Install & usage

Pick a framework in the toolbar above and these snippets adapt.

npm install @eunomia/elements
import "@eunomia/elements/fab.js";
import "@eunomia/elements/icon.js";
<eun-fab icon="add" aria-label="Create"></eun-fab>

Importing the file registers <eun-fab> as a custom element, with no further setup needed. It works with any framework, or none, since it's a standard web component.

Alternatives

You want .. Prefers Several related, equally-weighted actions rather than one standout action eun-button

Guidance

  • Reserve eun-fab for one single, primary, frequently-used action per screen: "compose", "create", "add"
  • Wire it to a target with command/commandfor instead of a click handler whenever the FAB doesn't need to run other logic first, preferred over the Popover API's own popovertarget: see Invoker Commands API below
  • Always set aria-label when label isn't set, since an icon alone has no accessible name
  • Set position when the FAB should stay docked to a viewport corner while the page scrolls, and leave it unset to place the FAB yourself inside normal page flow (e.g. a toolbar)
  • Use label when the icon alone might be ambiguous, so hovering/focusing confirms the action in words
  • Using more than one eun-fab on the same screen, see Alternatives above for several related actions instead
  • Leaving both label and aria-label unset: the button would have no accessible name at all

Live testing

Properties

Fab <eun-fab>

Attributes

NameTypeDefaultDescription
aria-labelstringThe accessible label for the button. Required when label isn't set, since an icon alone has no accessible name, and recommended even when it is if the label text alone doesn't describe the action
iconEunomiaIconNameThe icon displayed at all times
labelstringAn optional text label, revealed on hover or focus by the slide animation. Omit it for an icon only button
position'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'Fixes the button to a corner of the viewport. Left unset, the button follows the normal document flow
loading-labelstring'Loading…'The accessible label announced while the button is loading
activebooleanfalseIndicates whether the component is on disabled state or not. By default, set to false.

Import the exact TypeScript type behind any property above, see Types.

Properties

JS-only — no matching HTML attribute, set these from a script or a template binding.

NameTypeDefaultDescription
variant'primary' | 'secondary' | 'critical' | 'success' | 'warning''primary'The color theme applied to the button
sizestring'm'The size of the button
hrefstringThe URL the button navigates to. When set, the button renders as a link instead of a button
target'_blank' | '_self' | '_parent' | '_top'Where to open the linked URL. Only used when href is set. Opening in a new tab automatically announces it to assistive technology, and adds an external link icon when label is also set
downloadstringThe filename used when the linked URL is downloaded instead of navigated to. Only used when href is set
loadingbooleanfalseWhether the button shows a loading indicator. Like disabled, it blocks interaction, but keeps the icon and label in place so the button doesn't change size
commandstringThe command sent to the element targeted by commandFor when the button is clicked, following the Invoker Commands API
commandForstringThe id of the element that command is sent to
typestring"button"The button type: "submit", "reset", or "button". Determines behavior when clicked inside a form.
roundedbooleanfalseIndicates whether the button should take a round shape or not.
slidebooleanfalseIndicates whether the button should handle a slide mechanism.

Slots

NameDescription
iconReplaces the icon entirely with any content, not just an icon element. Falls back to icon when empty

Events

NameTypeDescription
clickEventFired when the button is activated by mouse, Enter, or Space
eunnavigateNavigationEventFired before following href, so a client-side router can intercept the navigation. Not fired for downloads, and cancelable

Every event above follows the same naming convention, covered in Events.

CSS custom properties

NameDescription
--fab-backgroundSets the background color
--fab-background-hoverSets the background color on hover
--fab-textSets the color of the text and icon
--fab-focus-outlineSets the color of the focus outline
--fab-transitionSets the duration of the slide animation and the hover and focus color changes
--fab-z-indexSets the stack order of a positioned button
--fab-offsetSets the distance from the viewport edge for a positioned button

Variants

Same variants as eun-button: primary (default), secondary, critical, success, warning.

<eun-fab icon="add" aria-label="Create"></eun-fab>
<eun-fab icon="add" variant="secondary" aria-label="Create"></eun-fab>
<eun-fab icon="delete" variant="critical" aria-label="Delete"></eun-fab>
<eun-fab icon="favorite" variant="success" aria-label="Save"></eun-fab>
<eun-fab icon="warning" variant="warning" aria-label="Warn"></eun-fab>

Part of the shared vocabulary covered in Variants, alongside every other component that reuses it.

Custom icon

Need something other than a eun-icon, or a different icon library entirely? Slot your own content into icon instead. It replaces the prop-driven icon entirely.

<eun-fab aria-label="Create">
  <svg
    slot="icon"
    width="20"
    height="20"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
  >
    <path d="M12 5v14M5 12h14"></path>
  </svg>
</eun-fab>

Invoker Commands API

Setting command and commandfor turns the FAB into an invoker: no click handler needed, the target picks up the command itself. Prefer this over wiring a click handler yourself, and over the Popover API's own popovertarget, which only ever covers popover-attributed targets. Modal and Drawer both understand show-modal, close, and request-close out of the box.

Write a new message.

<eun-modal id="compose-modal" heading="Compose message">
  <p>Write a new message.</p>
</eun-modal>
<eun-fab
  icon="edit"
  aria-label="Compose"
  command="show-modal"
  commandfor="compose-modal"
></eun-fab>

See the Invoker Commands API guide for custom commands and current browser support.

Extended

Set label to make the FAB extendable: it stays a plain circle until hovered/focused, then slides open to reveal the label, the same slide behavior eun-button exposes directly, driven here automatically by whether label is set. Hover or focus the button below to see it expand.

<eun-fab icon="add" label="Create"></eun-fab>

Without label, the FAB is always icon-only: set aria-label in that case, since an icon alone has no accessible name.

Sizes

Five sizes are available, matching eun-button's own scale: xs, s, m (default), l, xl.

<eun-fab icon="add" size="xs" aria-label="Create"></eun-fab>
<eun-fab icon="add" size="s" aria-label="Create"></eun-fab>
<eun-fab icon="add" aria-label="Create"></eun-fab>
<eun-fab icon="add" size="l" aria-label="Create"></eun-fab>
<eun-fab icon="add" size="xl" aria-label="Create"></eun-fab>

Fixed positioning

Set position (bottom-right, bottom-left, top-right, top-left) to dock the FAB to a corner of the viewport, with no extra CSS needed.

<eun-fab icon="add" position="bottom-right" aria-label="Create"></eun-fab>

Distance from the edge defaults to --eun-space-l (24px) and is customizable through --fab-offset, while the stacking order defaults to 40 and is customizable through --fab-z-index.

Leave position unset to place the FAB yourself: it then behaves like a normal inline element wherever you put it in the page flow (e.g. inside a toolbar, as in every other demo on this page).

Disabled

<eun-fab icon="add" disabled aria-label="Create"></eun-fab>

Loading

Setting loading blocks interaction like disabled, but keeps the icon/label in place (invisible, so the button's size doesn't change) with a eun-loader layered on top.

<eun-fab icon="add" loading aria-label="Create"></eun-fab>
import "@eunomia/elements/loader.js";

Links

Setting href renders the FAB as a native <a> internally instead of a <button>, exactly like eun-button.

<eun-fab
  icon="open_in_new"
  label="Documentation"
  href="https://example.com"
></eun-fab>

Opening in a new tab

Setting target="_blank" automatically adds rel="noopener noreferrer" and appends "(opens in a new tab)" to the accessible name, exactly like eun-button. When label is also set, a small external-link icon is appended to it too. An icon-only FAB (no label) only gets the accessible-name change, since there's no natural spot for a second icon inside its small circular footprint without crowding the primary one.

<eun-fab
  icon="open_in_new"
  label="Documentation"
  href="https://example.com"
  target="_blank"
  aria-label="Documentation"
></eun-fab>

Keyboard interactions

Key Action
Tab / Shift+Tab Moves focus to/from the FAB, like any other button
Enter Activates the button (native <button> behavior, or native link navigation when href is set)
Space Activates the button. Ignored when href is set, matching native <a> behavior

Accessible name

An icon alone has no accessible name: set aria-label whenever label isn't set, and consider it even when label is set if the label text alone isn't a sufficient description of the action. Refer to eun-button's own Accessibility tab for the full reasoning behind this convention, shared identically here.

For a link FAB opening a new tab (href + target="_blank"), "(opens in a new tab)" is announced automatically, appended directly to aria-label when one is set, or as a visually-hidden addition to the accessible name otherwise, so no manual wording is needed either way.

Disabled and loading state

A disabled FAB carries the native disabled attribute (button mode) or aria-disabled="true" with href omitted (link mode). Either way, it's removed from the tab order and emits no events, exactly like eun-button's own disabled state.

While loading, aria-busy="true" is set on the rendered <button>/<a>, the FAB is removed from the tab order, and the loading indicator exposes role="status" (via eun-loader's own aria-label) plus a redundant visually-hidden aria-live="polite" announcement, so assistive technology reliably picks up the state change even if it missed the initial aria-busy update.

Fixed positioning doesn't trap focus or content

position only ever changes CSS (position: fixed plus an offset). It adds no ARIA role, no focus trap, and doesn't remove any other page content from the accessibility tree. A positioned FAB sits in the same tab order position its DOM placement already gives it, so place it thoughtfully in markup (typically near the end of the document) so Tab doesn't jump to it unexpectedly in the middle of an unrelated flow.

Reference links

HTML Specification: Button element
WAI-ARIA Authoring Practices: Button Pattern