Skip to content
SproutAceve · v3.22.0
Get started
Overview What is your role? Connect to Sprout Adopting Sprout
Foundations
Brand & logos Color Typography Spacing & layout Elevation Iconography Accessibility
Components
Overview Buttons Inputs & forms Numeric input Data grid Ribbon toolbar Navigation Cards Alert Avatar Badge Dialog Tabs Floating tabs Menu
Practice
Interaction & motion Voice & content Product patterns
Artificial Intelligence
AI patterns AI transparency & disclosure AI disclosure label
People & ethics
Design principles Personas Trust and ethics Meet the makers
Playground
Build the systemplay Tower of tokensplay
Behind the system
How it works Changelogv3.22 Roadmap Contribute
Done
Components

Floating tabs.

A pill-shaped track holding two to five mutually exclusive tabs. It switches the view of content already on screen — it never navigates away and never submits anything. One tab is always active.

Anatomy

Four parts. The track is the only element with a fill at rest — every tab is transparent until it becomes active.

3
4
2
1
1
Track

The container. Fully rounded, #152F1A, hugs its contents in both directions. 52px tall.

2
Tab

One option. Transparent when inactive, a filled pill at #E1FFAD 12% when active.

3
Icon

20 × 20, filled. Active tab only — it is the second signal for the active state.

4
Label

Required. Label medium, 14 / 20, weight 600. One or two words, no truncation.

Spacing

Every measurement the track and its tabs use. Active = Overview, so both the 12px active edge and the 16px inactive edge are visible at once.

Hover or focus a row to see that measurement on the component above.

8px --sprout-spacing-xsTrack padding, verticalFixed in every state. 8 + 36 tab + 8 = the 52px track height.
12px --sprout-spacing-sTrack padding, side — active edgeWhen the tab on that edge is active. The pill carries the last 4px of its own padding, so the two together read as one consistent margin.
16px --sprout-spacing-mTrack padding, side — inactive edgeWhen the tab on that edge is inactive.
16px --sprout-spacing-mBetween tabsAuto-layout gap on the track. Every pair, active or not.
8px --sprout-spacing-xsTab padding8px all round. Identical on active and inactive tabs.
4px --sprout-spacing-2xsIcon to labelInside the active tab.

Variants

Three content variants. Pick one per control and keep it consistent across every tab. Width behaviour is hug only.

Icon + label — default

The icon appears on the active tab only. This is the pattern that ships in the library.

Label only

Use when the options are not easily iconified — date ranges, statuses, numbers.

Icon only

Two or three tabs maximum, and only for universally understood pairs (grid/list, map/table). Every tab needs an accessible name and a tooltip.

Width — hug only in v1. Each tab sizes to its label and the track hugs the tabs, so the control is always as wide as its content and no wider. There is no fill or stretch behaviour yet.

Styles

One treatment. brandSurface tokens carry the same value in light and dark mode, so the component never inverts — the two panels below are the same component, unchanged.

Light background

The app surface. The pill reads as a control against it, not as content.

Dark background

Same fills, same labels, same icon. Nothing inverts — this is the point of using brandSurface.

Floating tabs use brandSurface colour tokens, and those tokens hold the same value in light and dark mode. The component does not invert or flip — it looks identical in both. Do not build a light-mode variant.

These three values are not variables yet — there is no brandSurface collection in this file, and nothing on the canvas is bound to one. Until it exists, the component carries raw hex.

States

Active and inactive tabs each get their own hover and pressed treatment, so a tab is never ambiguous about whether it is already the current view.

INACTIVE TAB
Rows
Default
no fill
Rows
Hover
label brightens, no fill
Rows
Pressed
white 6% overlay
Rows
Focus visible
2px ring, outside
Rows
Disabled
label at 40%
ACTIVE TAB
Rows
Active
#E1FFAD 12%
Rows
Active · hover
fill one step lighter
Rows
Active · focus
ring outside the pill

Size

One size in v1: 52px tall. The active state changes colour and adds a filled icon — it never changes the type size or weight.

52px — the only size

label medium 14 / 20 · icon 20 filled · tab padding 8 · track padding 8 / 16 (12 on the active edge) · gap 16

On the question of text height and padding: leave the type as it is. 14 / 20 is Sprout's label medium, and raising the line height only makes the pill taller without helping legibility. The height comes from padding instead: 20px label + 8px tab padding = a 36px tab, + 8px track padding = the 52px you want.

The thing that does need solving is the touch target. The track is 52px, but each individual tab is only 36px tall — still under the 44px minimum for touch. Fix it in code with a transparent hit area that fills the full 52px track height, not with more padding. Adding padding to reach 44px would push the track to 60px and break the size you have chosen.

Icons

The icon appears on the active tab only, and is always filled. It is the second signal for the active state, alongside colour.

ACTIVE-ONLY ICON — THE LIBRARY PATTERN

Because the icon only appears when active, the track changes width as the user moves through it. Give the track a min-width equal to its widest state, or use Fill, so the layout around it does not shift.

StyleAlways filled. Use the solid/ set — never the outline set, in any state.
WhenActive tab only. Inactive tabs are label-only, so the icon is what marks the active view.
PositionLeading, before the label. Never trailing, never above.
Size20 × 20, matched to the 14 / 20 label.
Colour#E1FFAD, the same as the active label. Never independently tinted.
SourceSprout icon set only. No product-specific or third-party glyphs.
AccessibilityDecorative — the label carries the meaning, so the icon needs no accessible name.
NeverNo badges, counts or dots layered on the icon. Put a count in the label instead.

Do & Don't

The four mistakes this component actually attracts in review.

✓ Do
Keep labels to one or two words.

Tabs sit side by side, so every extra word costs horizontal space a smaller screen does not have.

✕ Don't
Don't write sentences.

Long labels push the track past the content width, and there is no truncation behaviour — it just overflows.

✓ Do
Stay between two and five tabs.

All options visible at once is the whole point of the component.

✕ Don't
Don't use it as a scrolling tab strip.

Past five options, use Tabs or a Select. Floating tabs that scroll have stopped being floating tabs.

✓ Do
Always show exactly one active tab.

The control is single-select and has no empty state — there is always a current view.

✕ Don't
GridListMap
Don't allow multiple selection.

If users need to combine options, that is a set of Chips or checkboxes, not this.

✓ Do
Keep the options parallel.

Same category, same grammar, same level of the hierarchy.

✕ Don't
Don't mix an action into the set.

Every tab must be a view. An action inside a tab list is a keyboard and screen-reader trap — put it beside the control as a Button.

Usage

Reach for floating tabs when the user is choosing how to look at something, not what to do with it. v1 is for the mobile app.

v1 targets the mobile app. Floating tabs sit above the content, over the scroll, on a phone screen. Desktop and back-office placement is out of scope until the pattern has shipped on mobile.

Use floating tabs when
  • Two to five options, all worth showing at once
  • The options are mutually exclusive and one is always active
  • Switching is instant and reversible — no save, no confirm
  • The content being switched is already on screen
  • Typical cases: view switch (grid / list / map), time range (day / week / month), status filter on a work-order list
Use something else when
  • Six or more options → Tabs, or a Select
  • Options are page-level destinations → Navigation
  • Multiple options can be on at once → Chips or checkboxes
  • The choice is part of a form submission → Radio group
  • The tab performs an action → Button or Button group
  • It is a single on/off → Switch

Bottom spacing for floating elements

Floating tabs and the FAB sit on top of the scroll. Every scrollable area — Overview, Time, Rows, Files — must reserve bottom padding for whatever floats over it, or the last row of content hides behind the pill.

Tabs only52 (track) + 16 gap + safe area
Tabs + FAB52 (track) + 16 gap + FAB height + 16 gap + safe area

FAB height is not confirmed yet — fill it in before this ships to a product.

Placement
PositionFloating above the content, horizontally centred, clear of the safe area at the bottom of the screen.
WidthHug. The track is as wide as its tabs and no wider — it does not span the screen.
With a FABThe FAB keeps its own corner. Leave at least 16px between the track and the FAB so neither blocks the other.
ScrollThe track stays put while content scrolls underneath. The drop shadow is what separates them.
NeverTwo sets of floating tabs on one screen, or floating tabs inside a sheet or modal.
Bottom paddingThe scroll area must reserve room for whatever floats over it — see the caution above. Required on every tab, not just the default one.
Content
LengthOne or two words. Nouns, not verbs — "Files", not "View files".
CaseSentence case. Not ALL CAPS, not Title Case.
GrammarParallel across tabs — all plural nouns, or all singular.
CountsAllowed inline when meaningful: "Open (12)". Apply to every tab or none.
TranslationNordic and German labels run 30–40% longer than English. Test the widest locale before locking a hug-width control into a tight header.
In context

The plain case — tabs over scrolling content. Note what this screen shows: "Mark as ready" is cut off behind the pill, because the scroll area has no bottom padding. Exactly the bug the caution above describes.

The tabs share the screen with a floating action button. The FAB keeps its own corner and the track stays centred, so neither blocks the other.

Related — Tabs · Buttons · Chips · Navigation · Ribbon toolbar

← PreviousTabsNext →Context menu
Sprout · Aceve Design System · v3.22.0
Figma library GitLabReleases