Floating Action Button
Promote one primary contextual action while preserving reading space, focus and navigation.
Choose one promoted action
Use a floating action for a frequent creation or other primary contextual task. Keep ordinary navigation, destructive operations and a collection of unrelated actions in their appropriate controls. Prefer an extended visible label when the icon alone is ambiguous.
This is a MobTranslate web equivalent with its own dimensions and tokens. It does not claim to implement Material Expressive motion physics, morphing or every native-platform FAB behavior.
Anatomy and sizes
| Mode | Rendered specification | Decision |
|---|---|---|
| Small icon | 44×44px | Compact target retains the product touch minimum. |
| Medium icon | 56×56px | Default promoted action. |
| Large icon | 72×72px | Use only when space and action hierarchy justify it. |
| Extended | Auto width; min-height follows size; 20px inline / 12px block padding | Visible label can wrap; do not force translated text into an icon square. |
| Surface | --radius-lg / --shadow-lg / primary roles | Use shared theme/state tokens. |
| Position | Owned by application layout | Component itself does not fix to viewport or hide content. |
Working creation lab
Local example workspace. The action sits in flow here; application placement must preserve content and focus clearance.
0 example drafts created locally. Nothing was saved or published.
'use client';
import { useState, useRef } from 'react';
import { FloatingActionButton, TooltipProvider, Tooltip, TooltipTrigger, TooltipPortal, TooltipPositioner, TooltipPopup } from '@mobtranslate/ui';
export function FloatingActionsExample() {
const portalContainer=useRef<HTMLDivElement>(null);
const [count, setCount] = useState(0);
const [extended, setExtended] = useState(true);
const [size, setSize] = useState<'sm' | 'md' | 'lg'>('md');
const action=<FloatingActionButton size={size} extended={extended} aria-label="Add example draft" onClick={() => setCount(count + 1)}>
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12h14M12 5v14" stroke="currentColor" strokeWidth="2" fill="none" /></svg>{extended && 'Add example draft'}
</FloatingActionButton>;
return <div ref={portalContainer} data-fab-lab className="space-y-4">
<label className="block">Floating action size<select className="mt-input mt-2" value={size} onChange={event => setSize(event.target.value as typeof size)}>{(['sm', 'md', 'lg'] as const).map(value => <option key={value}>{value}</option>)}</select></label>
<label className="flex items-center gap-3 min-h-11"><input type="checkbox" checked={extended} onChange={event => setExtended(event.target.checked)} />Show extended action label</label>
<div className="relative border border-[var(--color-border)] rounded-lg p-4 space-y-6">
<p>Local example workspace. The action sits in flow here; application placement must preserve content and focus clearance.</p>
<div className="flex justify-end"><TooltipProvider><Tooltip disabled={extended}><TooltipTrigger render={action} /><TooltipPortal container={portalContainer}><TooltipPositioner sideOffset={8}><TooltipPopup>Create an example draft locally.</TooltipPopup></TooltipPositioner></TooltipPortal></Tooltip></TooltipProvider></div>
</div>
<p role="status">{count} example {count === 1 ? 'draft' : 'drafts'} created locally. Nothing was saved or published.</p>
</div>;
}
Placement and adaptation
- Prefer in-flow or a reserved action region when reading content needs unobstructed access. A viewport-fixed action requires a deliberate content-clearance contract.
- Use logical positioning and account for safe-area insets and on-screen keyboards. Test both directions rather than assuming bottom-right is universal.
- Reserve space for bottom navigation, consent banners, toasts and focused controls. Do not place all overlays in the same corner.
- When space is limited, choose a visible in-flow action rather than hiding its label or obscuring source citations.
- Keep DOM/focus order logical; visual floating position does not justify moving the action ahead of essential instructions.
API reference
| Prop | Contract |
|---|---|
| size | sm | md | lg; md default |
| extended | boolean; false. Extended mode supplies visible label content. |
| aria-label | Required for icon-only mode; supply an accurate action name. |
| children / leftIcon | Icon and optional visible extended label |
| variant | Button variants; primary default |
| type | button default; submit/reset explicit |
| disabled / loading / loadingText | Same behavior as Button; task status remains application-owned |
| className / style / native props | Passed to the native action; layout can add placement rules |
| asChild / fullWidth | Excluded; application placement and navigation are separate concerns |
| ref | Ref<HTMLElement> to the native button |
Behavior and accessibility
- Use a real button for an action and a link for navigation. Visible labels and accessible names must identify the same task.
- Native buttons activate with Enter and Space; ordinary disabled buttons leave the tab order. Do not rely on a spinner as the only pending announcement.
- Report pending, completion, cancellation and failure with task-specific status text. A local animation is not proof that a request completed.
- Retain a meaningful focus destination when a pending button becomes disabled or disappears. The lab returns focus after completion or cancellation.
- Focus, meaningful boundaries and icons need their own contrast checks. Text-pair measurements alone do not certify a complete component.
- Respect reduced motion and forced colors. Do not obscure focus with a floating control or sticky header.
Failure cases
- Avoid a floating delete/publish action without clear consequence and workflow safeguards.
- Do not add several competing floating actions or use a plus symbol for an unrelated task.
- Do not hide permission, attribution or recovery content beneath the action.
- A local draft count is not a persisted draft or a published entry. Verify the real outcome when connecting a backend.
Verification
- Toggle extended/icon-only mode, select all sizes and activate using keyboard/pointer. The local count and status must agree.
- Measure 44/56/72px icon targets and check the extended label at narrow width.
- Test fixed placement in the real consuming layout with zoom, RTL, safe areas, on-screen keyboard and bottom overlays.
- Inspect focus, disabled and pending behavior in both themes and reduced-motion/forced-colors modes.