FixNeeds Refinement
Tab Item Component link

A single tab inside the Tabs component — label, optional leading icon, and active/inactive/disabled states.

Prop and asset cleanup needed
Rename isActive?selected (true/false). Unify the leading-icon slot across orientations. Replace the hardcoded counter (and its raw hex colors) with an instance of the canonical Badge. Replace the placeholder circle with a swappable Icon slot. C2 C3 C6
In Context

Tab Items appear inside the Tabs container. See the Tabs in-context preview for the full screen layout.

Live Preview
Label
Properties
isActive?
orientation
size
hasLeadingIcon
hasCounter
hasRedDot
DS Health
Reusable
Pass
Used by Tabs for all tab surfaces. Four orientation/size combinations cover 360px and 414px screens.
Self-contained
Warn
Carries layout, border, typography. But the counter pill uses hardcoded colors (#ECF1FA, #0F3390) instead of tokens. C3
Consistent
Warn
Property isActive? has a trailing ? and uses Yes/No. The leading-icon slot behaves differently across orientations: vertical always renders one, horizontal exposes hasLeadingIcon boolean. C2
Composable
Warn
Counter pill is drawn locally rather than instancing the canonical Badge component — changes to Badge won't propagate. Icon is a hardcoded gray circle icon-placeholder instead of a swappable Icon slot. C6
Behavior
State iOS Android Figma Property Notes
Active Yes Yes isActive?=Yes Blue label, blue bottom border
Inactive Yes Yes isActive?=No Gray label, light gray bottom border
Pressed / Disabled N/A N/A Not defined. C5
Has counter Yes Yes hasCounter=true (horizontal only) Counter pill. Should instance Badge, not duplicate colors. C6
Has red dot Yes Yes hasRedDot=true 6px red dot in top-right corner
Open Issues
  • Property isActive? has a ? in its name. And uses Yes/No values. Rename to selected with true/false to match Checkbox/Radio conventions. C2 · Variant & Property Naming
  • Leading-icon slot is inconsistent across orientations. Vertical always renders an icon; horizontal gates it on hasLeadingIcon. Unify to a single leading = none | icon prop that behaves identically in both orientations. C2 · Variant & Property Naming
  • Counter pill uses hardcoded hex values. #ECF1FA bg, #0F3390 label — not bound to tokens, so theme changes won't propagate. C3 · Token Coverage
  • Counter is drawn locally instead of instancing Badge. Breaks compositional inheritance — future Badge updates (color, sizing, overflow) won't reach Tab Item. C6 · Asset & Icon Quality
  • Icon is a hardcoded placeholder circle. 32px (vertical) / 24px (horizontal) gray icon-placeholder — should be a swappable Icon slot via instance swap. C6 · Asset & Icon Quality
  • No pressed / disabled states documented. Tap feedback and non-interactive state are critical for a tab atom — engineers are improvising them today. C5 · Interaction State Coverage
  • Code Connect mappings not registered. Blocked until property rename, slot adoption, and state coverage land. C7 · Code Connect Linkability
Design Recommendations
  • Rename isActive?selected with true/false values. Matches Swift Bool / Kotlin Boolean for Code Connect. Rename
  • Unify the leading-icon slot — replace the always-on vertical icon and the hasLeadingIcon boolean with a single leading=none/icon slot that behaves identically across orientations. Property
  • Replace the local counter pill with a Badge instance Badge already ships tokenized colors and will inherit any future token changes. Same pattern used when Avatar Group was repointed to the canonical Avatar. Composition
  • Replace icon-placeholder with a swappable Icon slot (instance-swap). Lets product teams drop in any icon without editing the master. Slot
  • Add pressed and disabled states as explicit variants so engineers don't have to improvise tap feedback. State
  • Add tokens for the counter under main/tab/counter/{bg,label} if keeping it standalone, or adopt Badge's existing tokens. Token
Styles
Vertical · small — 360px screen
DES DEV

Icon above label. 32px icon, 16/16 label (Primary/Label/Base). Active + inactive shown side-by-side.

LabelLabel
Properties
selected
hasRedDot
Properties
Variant Vertical · small — 360px screen
State Active
Red dot false
Colors
Label #6780A9
Border #E5EBF4
Layout
Tab height 44px
Padding H 16px
Indicator height 3px below tab
Typography
Label style Primary/Label/Base
Label font Proxima Soft Bold · 16 / 16 · +0.25
Colors by State

Two states: active and inactive. Counter colors are currently hardcoded (flagged as C3).

Role Token ActiveInactive
Label main/tab/color/{active|inactive}/label #005CE5 #6780A9
Bottom border main/tab/color/{active|inactive}/border #005CE5 #E5EBF4
Counter bg — (hardcoded) #ECF1FA #ECF1FA
Counter label — (hardcoded) #0F3390 #0F3390
Red dot — (raster) Red 6×6px indicator
Vertical · large — 414px screen
DES DEV

Icon above label. 32px icon, 18/18 label (Primary/Label/Large). Optimized for 414px screens.

LabelLabel
Properties
selected
hasRedDot
Properties
Variant Vertical · large — 414px screen
State Active
Red dot false
Colors
Label #6780A9
Border #E5EBF4
Layout
Tab height 44px
Padding H 16px
Indicator height 3px below tab
Typography
Label style Primary/Label/Base
Label font Proxima Soft Bold · 16 / 16 · +0.25
Layout
Role Token
Vertical padding (top) 12px
Vertical padding (bottom) 16px
Horizontal padding (left/right) 12px
Icon size (vertical) 32 × 32
Icon size (horizontal leading) 24 × 24
Icon → label gap (vertical) 12px
Counter size 18px h × padding 6h / 4v
Counter radius 99999px (pill)
Red dot size 6 × 6 (top-right)
Bottom border 2px solid
Horizontal small width 105px
Horizontal large width 112px
Horizontal · small — label only + optional slots
DES DEV

Label-first row. Optional leading icon (24px) and trailing counter (18px pill). Red dot anchored to top-right.

Label0Label0
Properties
selected
hasLeadingIcon
hasCounter
hasRedDot
Properties
Variant Horizontal · small — label only + optional slots
State Active
Leading icon true
Counter true
Red dot false
Colors
Label #6780A9
Border #E5EBF4
Layout
Tab height 44px
Padding H 16px
Indicator height 3px below tab
Typography
Label style Primary/Label/Base
Label font Proxima Soft Bold · 16 / 16 · +0.25
Typography
Role Token Spec
Small Primary/Label/Base Proxima Soft Bold · 16 / 16 · +0.25
Large Primary/Label/Large Proxima Soft Bold · 18 / 18 · +0.25
Counter — (hardcoded) Proxima Soft Bold · 12 / 12 · +0.5
Horizontal · large
DES DEV

Same anatomy as horizontal small but with 18/18 label and 112px cell width.

Label0Label0
Properties
selected
hasLeadingIcon
hasCounter
hasRedDot
Properties
Variant Horizontal · large
State Active
Leading icon true
Counter true
Red dot false
Colors
Label #6780A9
Border #E5EBF4
Layout
Tab height 44px
Padding H 16px
Indicator height 3px below tab
Typography
Label style Primary/Label/Base
Label font Proxima Soft Bold · 16 / 16 · +0.25
Installation Planned API

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")
}
Property Mapping
Figma PropertySwiftUICompose
isActive?=Yes/No selected: Bool selected: Bool
orientation=vertical/horizontal orientation: EBTabOrientation .orientation(.vertical)
size=small/large size: EBTabSize .size(.small)
hasLeadingIcon + vertical always-on icon leading: Icon? leading: Image?
hasCounter=true/false counter: Int? counter: Int?
hasRedDot=true/false showBadge: Bool showBadge: Bool
label: String title: String
SwiftUI
ios/Components/Tabs/EBTabItem.swift
Jetpack Compose
android/components/tabs/EBTabItem.kt
Usage Snippets Planned API
Usage
// Vertical tab with icon
EBTabItem(label: "Overview", leading: Image("overview"))
    .selected(true)

// Horizontal tab with counter
EBTabItem(label: "Inbox", counter: 12)
    .orientation(.horizontal)
    .selected(false)

// Horizontal tab with red-dot indicator
EBTabItem(label: "Alerts", showBadge: true)
    .orientation(.horizontal)
// Vertical tab with icon
EBTabItem(
    label = "Overview",
    leading = painterResource(R.drawable.overview),
    selected = true
)

// Horizontal tab with counter
EBTabItem(
    label = "Inbox",
    orientation = EBTabOrientation.Horizontal,
    counter = 12,
    selected = false
)

// Horizontal tab with red-dot indicator
EBTabItem(
    label = "Alerts",
    orientation = EBTabOrientation.Horizontal,
    showBadge = true
)
Accessibility
RequirementiOSAndroid
Selected state .accessibilityAddTraits(.isSelected) selected = true in semantics
Counter value .accessibilityValue("\(count) unread") contentDescription includes the count
Red dot .accessibilityLabel("New") or append to label Append to contentDescription
Tap target Icon-only cells need 44pt hit area Cells meet 48dp with padding
Usage Guidelines

Do

Use vertical orientation when tabs have icons + short labels (iconic nav). Use horizontal when labels are longer or icons aren't semantically needed.

Don't

Mix orientations within the same Tabs container.

Do

Use counter for numeric indicators (unread counts, items). Use showBadge for "new / unseen" indicators where the count is unimportant.

Don't

Stack counter + red-dot on the same tab — the counter already communicates "there's something here."

Criteria Scorecard
ID Criterion Status Notes
C1 Layer Structure & Naming Ready Semantic: container, icon-label, icon, label, counter-container, red-dot.
C2 Variant & Property Naming Requires Rework isActive? Yes/No + inconsistent leading-icon slot across orientations.
C3 Token Coverage Requires Rework Counter bg #ECF1FA and label #0F3390 are hardcoded.
C4 Native Mappability Ready Custom cell on iOS, Material Tab content on Android.
C5 Interaction State Coverage Requires Rework Pressed and disabled states not defined.
C6 Asset & Icon Quality Requires Rework Placeholder circle instead of Icon slot; counter is duplicated, not a Badge instance.
C7 Code Connect Linkability Needs Refinement Not mapped.
Variants Inventory (8 total)

isActive?orientationsizeNode ID
Yesverticalsmall18482:33263
Noverticalsmall18482:33270
Yesverticallarge18482:33277
Noverticallarge18482:33284
Yeshorizontalsmall18482:33291
Nohorizontalsmall18482:33300
Yeshorizontallarge18482:33309
Nohorizontallarge18482:33318
1.0.0 — April 2026Major
Initial Assessment · node 18482:33262
Component assessed — 8 variants across isActive × orientation × size. Horizontal variants expose optional leading icon, counter, and red-dot slots. Documented
Initial
Property naming issuesisActive? has a ? and uses Yes/No. Leading-icon slot behaves differently across orientations. Open
C2 Open
Counter colors hardcoded#ECF1FA bg and #0F3390 label are raw hex. Should use tokens or instance the Badge component. Open
C3 Open
No pressed/disabled states — State coverage limited to active/inactive. Open
C5 Open
Icon placeholder + duplicated counter — Icon should be a swappable slot; counter should instance the canonical Badge. Open
C6 Open
Code Connect mappings — Not registered. Open
C7 Open