UIDex

Floating Action Button

زرّ الإجراء العائم

FloatingActionButton

also calledFAB، floating button، extended FAB، mini FAB، speed dial button، compose button، الزرّ العائم، فاب، زرّ الإضافة العائم، الزرّ الدائري العائم، زرّ الإجراء الأساسي

A floating action button is a disc that sits on a layer above the screen content, stays put while the content passes underneath, and carries the one action a user is most likely to want on that screen. It differs from an ordinary button by being outside the layout flow: a normal button lives in the page, gets pushed around by content, and scrolls away with it, while this one is pinned to a corner and covers whatever passes beneath. It differs from an app-bar or toolbar action, because those are several actions of equal weight that operate on what is displayed right now (sort, search, share), while this is one action ranked above everything around it. And it differs from a speed dial, the cluster of three or four mini buttons that fans out on press: that is an expanded state of this same control rather than a separate element, and reaching for it usually admits you could not pick one action. One rule settles most reviews: one FAB per screen, and it carries an action that creates or adds. A delete, anything else you cannot take back, and any merely secondary action stay out of a spot whose size and position promise the user this is the most important thing on the screen. A screen with no action worth that promise should carry no FAB at all.

If you called it…

"the round plus button in the corner""the button that stays while the list scrolls""the circle floating over the feed""the compose button in the mail app""the button that fans out into smaller buttons"

Live specimen

Interact with the demo. Every part is real and numbered.

٩:٤١

الرسائل

فريق التصميمالمراجعة النهائية اليوم
سارة النعيميبعتت لك الملفّ
الدعم الفنّيتمّ إغلاق التذكرة
محمود عادلنشوفك بكرة الساعة ٩
أرشيف قديملا جديد هنا
حيّز محجوز للزرّ
RTLالزرّ يقفز إلى الركن المقابل، والزائد لا ينعكس. جرّب الفتح والتمرير

Anatomy: every part, named

Hover a row to locate it in the demo above.

Build prompt

Build a floating action button for a mobile screen. Exactly one per screen, holding the action a user is most likely to want there (create or add). Keep a delete or anything else irreversible off it, and keep secondary actions off it too. Draw it as a 56dp disc with a 24dp icon, pin it with inset-inline-end and inset-block-end above env(safe-area-inset-bottom) with viewport-fit=cover in the viewport meta tag, and write no right, left, or bottom in any rule that positions it. The icon carries no text beside it, so give the button an accessible name describing the action through aria-label on the web, contentDescription on Android, and accessibilityLabel on iOS, and never name it after the shape. Reserve a run at the end of the list underneath equal to the button height plus both margins (about 88dp) through contentPadding so the last row stays tappable, and lift the button over a snackbar by changing inset-block-end instead of letting the snackbar cover it. Add an extended variant: a pill with the icon at the logical start and a one or two word label that neither truncates nor wraps, collapsing to a circle on scroll down and expanding again at the top, with gap between icon and label rather than margin-right, and no row-reverse under [dir="rtl"]. If you need a speed dial, make the parent a toggle carrying aria-expanded, give every mini button (40dp drawn, 48dp touchable) a visible label beside it, open the cluster upward with flex-direction: column and no flip in RTL, and close it on an outside press and on Escape without trapping focus. Set the fan-out motion origin with a transform-origin you flip by hand under [dir="rtl"], because transform has no logical equivalent. Do not mirror the plus or the camera glyph in Arabic: scope any flip to directional icons only.

How it behaves right-to-left

This section is ours alone.

The corner moves to the opposite side

mirrors

A button pinned to the bottom-right corner in English belongs in the bottom-left corner in Arabic, because its anchor is the end of the reading line rather than a fixed side. On the web write inset-inline-end together with inset-block-end instead of right and bottom. In Compose use Modifier.align(Alignment.BottomEnd), or FabPosition.End inside a Scaffold, and stay away from AbsoluteAlignment.BottomRight, which ignores LayoutDirection by design. In Android XML the value is android:layout_gravity="bottom|end" and not "bottom|right", and none of it mirrors on the device until android:supportsRtl="true" is on the <application> tag. On iOS constrain to trailingAnchor rather than rightAnchor, or place it with .overlay(alignment: .bottomTrailing) in SwiftUI.

The button glyph does not mirror

never mirrors

A plus, a camera, a pencil, a microphone are fixed shapes with no direction in their meaning, and mirroring one in Arabic makes it look broken rather than translated. Only directional glyphs flip: the back arrow, the send arrow, the "next" chevron. So never write a blanket [dir="rtl"] svg { transform: scaleX(-1) }; scope the flip to a class you put on arrows. The platforms carry the metadata themselves: android:autoMirrored="true" on a vector drawable, Icons.AutoMirrored.Filled.Send in Compose, Image.flipsForRightToLeftLayoutDirection(true) in SwiftUI, and in SF Symbols arrow.backward, which mirrors, over arrow.left, which never does. The plus that rotates 45 degrees into a close mark needs none of it, since rotation is direction-neutral.

The icon at the start of the extended pill

mirrors

Inside the pill the icon sits at the logical start and the label follows it: to the right of the text in Arabic, to its left in English. Build the pill as a flex row and leave the DOM order alone, since the inline axis mirrors on its own. Do not add flex-direction: row-reverse under [dir="rtl"], because you flip what already flipped, the icon returns to the wrong side, and visual order splits from reading order and from focus order. Write the space between icon and label as gap or margin-inline-end, never margin-right. The icon slot of ExtendedFloatingActionButton in Compose places it at the start for you, and a SwiftUI Label does the same. Asymmetric pill padding (tighter on the icon side, looser on the text side) belongs in padding-inline-start and padding-inline-end.

The cluster axis does not flip

never mirrors

The cluster opens upward in both directions, because it stacks on the block axis, and direction does not touch that axis at all (only writing-mode does). A flex-direction: column container therefore needs no column-reverse in Arabic, DOM order stays equal to visual order and to screen-reader order, and reversing the array by hand is the thing that breaks that equality. Each mini button label is a separate matter: it sits at the logical start of its own button, to the left in English and to the right in Arabic. Put the label before the button in DOM inside a flex row and it mirrors for free, or pin it with inset-inline-end: calc(100% + 8px) rather than right. Align the whole cluster with align-items: flex-end, which in a column container resolves on the inline axis and follows direction by itself.

The fan-out origin flips by hand

mirrors

When the pill grows or the cluster springs out of the disc, the motion starts at the corner the button occupies, and that corner moves with direction while transform knows nothing about direction. CSS has no logical form of transform, and transform-origin on the horizontal axis takes only left, right, center, a length, or a percentage: there is no inline-end keyword in it. Write transform-origin: bottom right and flip it to bottom left under [dir="rtl"], or compute the percentage from a direction variable. The lift above a snackbar is the opposite case: translateY or inset-block-end holds the same value in both directions and needs no RTL rule at all. The usual defect there is not direction but letting the snackbar cover the button, since both are pinned to the same corner with nothing coordinating them. On Android a CoordinatorLayout performs that offset through FloatingActionButton.Behavior; outside one, you move the button yourself.

In code

Each row is one framework's word for the same thing. Take the row your project speaks.

Jetpack ComposeScaffold(floatingActionButton = { FloatingActionButton(…) })The Scaffold slot handles placement and window insets, and FabPosition.End makes the corner follow LayoutDirection.
Material ComponentsFloatingActionButton.Behavior + CoordinatorLayoutInside a CoordinatorLayout it lifts the button clear of a Snackbar by setting dodgeInsetEdges to bottom; outside one, you lift it yourself.
SwiftUI.overlay(alignment: .bottomTrailing) { … }iOS ships no FAB control, so you compose one, and .bottomTrailing is the part that moves with layoutDirection.
UIKitview.trailingAnchor / safeAreaLayoutGuideA trailing anchor resolves against layout direction where rightAnchor does not, and safeAreaLayoutGuide is what keeps it off the home indicator.
FlutterfloatingActionButtonLocation: FloatingActionButtonLocation.endFloatThe names are logical (endFloat, endDocked, startFloat); there is no rightFloat to pick by mistake.
HTML<button aria-label="…" aria-expanded>The web has no dedicated role for it: an ordinary button with fixed positioning, and aria-expanded only when a speed dial is attached.
CSSinset-inline-end + inset-block-endThe logical pair that moves the button to the opposite corner on its own when the page flips.

See also

Updated · 2026-08-19