Floating FAB Gagan

Single floating action button at bottom-center of screen. Ink background with cream plus glyph. Press state scale(0.97). Shadow-float for elevation. The primary entry point to add a new expense — always visible, never ambiguous about what it does.

Default

screen content

The FAB sits at bottom-center, vertically offset above the tab bar. The mock shows the positional relationship — the FAB overlaps the tab bar's center slot, which should be left empty or act as a visual placeholder.

Bottom offset: 64px (tab bar height) + 16px (gap) = 80px from screen bottom. On devices with a home indicator (iPhone X+), add safe area inset: calc(80px + env(safe-area-inset-bottom)).

States

Default
Hover
Pressed
Disabled

Usage

Do Use exactly one FAB per screen. The FAB is the primary action — "add expense." It should appear on every screen where adding an expense is relevant (home, group detail, expense list). Hide it on screens where the action doesn't apply (settings, profile).
Don't Don't use FAB for secondary or contextual actions. If the action changes based on context (e.g. "add member" on a group screen vs "add expense" on home), that's a signal to rethink the IA, not a reason to change the FAB's icon dynamically.
Do Account for the tab bar in vertical positioning. Bottom offset must always be tab-bar-height + gap, plus safe area inset on notched devices. Test on iPhone SE (smallest) and iPhone 15 Pro Max (largest + dynamic island).
Don't Don't animate from scale(0) on mount. The entrance uses translateY(12px) + scale(0.92) with ease-spring. Don't use transition: all — enumerate transform and box-shadow separately.

Spec

PropertyValueNotes
Size56 × 56pxCircle, border-radius 999px
Background--ink#171513
Glyph+ (fullwidth plus)26px, font-weight 500
Glyph color--s-paper#FBF8F0
Shadow--shadow-floatResting elevation
Hover shadow--shadow-lgPointer devices only
Press shadow--shadow-mdActive state
Hover scale1.04Pointer devices only via @media (hover: hover)
Press scale0.97:active, 140ms ease-out
Positionfixed, bottom-centerAbove tab bar + safe area inset
Z-index--z-nav (50)Above content, below modals/sheets
Entrance duration240msease-spring, from translateY(12px) + scale(0.92)

Accessibility

Label The FAB shows only a plus glyph. Always include aria-label="Add expense" on the button element. Never leave it unlabeled — screen reader users must know this is the primary action.
Touch target 56px meets the 44×44pt minimum touch target. On dense UIs where surrounding content might be tappable, add padding: 8px to the wrapping element to extend the tap zone without changing visual size.
Focus ring Add a visible focus ring for keyboard users: :focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; }. Never suppress focus with just outline: none.

Code

HTML

<div class="fab">
  <button class="fab-btn" aria-label="Add expense"></button>
</div>

CSS — safe area positioning

.fab {
  position: fixed;
  bottom: calc(64px + 16px + env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-nav);
}

Design tokens used

TokenValueRole
--ink#171513Button background
--s-paper#FBF8F0Plus glyph color
--shadow-floatResting elevation
--shadow-lgHover elevation
--shadow-mdPressed elevation
--d-fast140msPress transition duration
--d-med240msEntrance animation duration
--ease-outcubic-bezier(0.23,1,0.32,1)Press easing
--ease-springcubic-bezier(0.34,1.56,0.64,1)Entrance easing
--z-nav50Z-index layer

See it in context

Interactive prototype — this component is live below. Tap, swipe, and drag to explore.

The floating FAB appears in buttons action mode (the default). It is the teal + button at the bottom-centre of the group screen. Tap to see the press state.