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…
Live specimen
Interact with the demo. Every part is real and numbered.
الرسائل
Anatomy: every part, named
Hover a row to locate it in the demo above.
Build prompt
How it behaves right-to-left
This section is ours alone.
The corner moves to the opposite side
mirrorsA 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 mirrorsA 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
mirrorsInside 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 mirrorsThe 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
mirrorsWhen 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.
See also
Updated · 2026-08-19