The Tabs container composes a row of Tab Item atoms and manages the active index. Currently exposes 3 variants split by tabsCount (2 / 3 / 4). Figma component is currently named singular "Tab" — should be renamed "Tabs".
tabsCount — native tabs accept a list of items, not a fixed count variant. The container becomes one flexible component instead of 3 rigid variants.Contexts are illustrative. Final screens will reference actual GCash patterns. Tabs sit below a Title Bar to switch between screen sections.
Depth/D4), and flex layout. Composes Tab Item children cleanly.tabsCount uses string values ("2"/"3"/"4") instead of integer or dropping the variant. C227:89110). Changes to Tab Item propagate here.| State | iOS | Android | Figma Property | Notes |
|---|---|---|---|---|
| 2 tabs | Yes | Yes | tabsCount="2" | Width 124px |
| 3 tabs | Yes | Yes | tabsCount="3" | Width 186px |
| 4 tabs | Yes | Yes | tabsCount="4" | Width 248px |
| 5+ tabs / scrollable | N/A | N/A | — | Not documented. Native needs a scrollable variant for overflow. C5 |
- Component name is singular ("Tab"). Should be plural ("Tabs") to match the container semantics and disambiguate from the Tab Item atom. C2 · Variant & Property Naming
-
tabsCountis a variant with string values."2"/"3"/"4"— native tabs take a list; the count should not be a discrete Figma variant. Drop the property and let the container accept a collection. C2 · Variant & Property Naming - No scrollable / overflow variant documented. For 5+ tabs, native uses
ScrollableTabRowon Android and horizontal scroll on iOS — DS has no pattern, so engineers improvise. C5 · Interaction State Coverage - Code Connect mappings not registered. Blocked until the plural rename and
tabsCountdrop land. C7 · Code Connect Linkability
- Rename "Tab" → "Tabs" (plural). Matches the atom/container pattern already used by Avatar + Avatar Group and Menu Grid + Service Item. Rename
- Drop the
tabsCountvariant. The container should be a single flexible component that accepts an array of Tab Items. Collapses 3 variants → 1. Property - Add a scrollable variant (or document that 5+ tabs should switch to a ScrollableTabRow pattern). Prevents engineers from improvising overflow behavior. State
4 Tab Items in an equal-width flex row. 248px total width.
| Role | Token |
|---|---|
| Total width (2 tabs) | 124px |
| Total width (3 tabs) | 186px |
| Total width (4 tabs) | 248px |
| Per-tab width | 62px (flex 1 0 0) |
| Gap between tabs | 0 (shared border-bottom) |
| Shadow | Depth/D4 — 0 0 8px #73819A1A |
3 Tab Items in an equal-width flex row. 186px total width.
2 Tab Items in an equal-width flex row. 124px total width.
iOS — Swift Package Manager
// In Xcode: File → Add Package Dependencies "https://github.com/AY-Org/eb-ds-ios"
Android — Gradle (Kotlin DSL)
dependencies { implementation("com.eastblue.ds:tabs:1.0.0") }
| Figma Property | SwiftUI | Compose |
|---|---|---|
| items | items: [EBTabItem] | items: List<EBTabItem> |
| selectedIndex | @Binding selection: Int | selectedIndex: Int |
| onSelect | onSelect: (Int) -> Void | onSelect: (Int) -> Unit |
| scrollable | .scrollable(true) | scrollable: Boolean |
EBTabs( items: [ EBTabItem(label: "Overview", icon: .image(Image("overview"))), EBTabItem(label: "Details"), EBTabItem(label: "History") ], selection: $selectedIndex )
EBTabs( items = listOf( EBTabItem(label = "Overview", icon = painterResource(R.drawable.overview)), EBTabItem(label = "Details"), EBTabItem(label = "History") ), selectedIndex = state, onSelect = { state = it } )
| Requirement | iOS | Android |
|---|---|---|
| Tab list role | Automatic via TabView | Automatic via TabRow (Material semantics) |
| Selected state announced | .accessibilityAddTraits(.isSelected) on active tab | selected = true in semantics |
| Keyboard / focus navigation | iOS handles via focus traits | Compose handles via focus semantics |
Do
Use 2–4 tabs for primary navigation within a screen. Tabs should represent peer sections of equal importance.
Don't
Use tabs for sequential flows (Step 1 / Step 2 / Step 3) — use a Stepper component instead.
Do
Keep tab labels short (one or two words). If labels exceed 12 characters, use icons only or switch to vertical orientation.
Don't
Nest Tabs inside Tabs — creates navigation ambiguity and accessibility issues.
| ID | Criterion | Status | Notes |
|---|---|---|---|
| C1 | Layer Structure & Naming | Ready | Tab 1, Tab 2, ... container, icon-label. Semantic. |
| C2 | Variant & Property Naming | Requires Rework | Component named singular "Tab"; tabsCount uses string values and shouldn't be a variant at all. |
| C3 | Token Coverage | Ready | Shadow and spacing bound to tokens. Colors live on the Tab Item atom. |
| C4 | Native Mappability | Ready | Maps cleanly to TabView / TabRow. |
| C5 | Interaction State Coverage | Needs Refinement | Active/inactive covered via Tab Item. No scrollable pattern for 5+ tabs. |
| C6 | Asset & Icon Quality | Ready | Children are Tab Item instances. |
| C7 | Code Connect Linkability | Needs Refinement | Not mapped. |
After the recommended restructure these 3 variants collapse to 1 flexible container accepting a list.
| tabsCount | Width | Node ID |
|---|---|---|
| 2 | 124px | 18482:33259 |
| 3 | 186px | 18482:33255 |
| 4 | 248px | 18482:33250 |
tabsCount is a variant property — Should be removed; the container should accept a list of Tab Items instead of exposing a fixed count enum.
Open