DhPillTabs
Segmented pill control for switching between sibling views, built on Material3 TabRow — the segments share one rounded track and a variant-filled thumb floats to the selected segment as selection changes. Same selectedIndex / variant contract as DhTabs, plus a pageContent overload that bundles a swipeable HorizontalPager whose drag the thumb follows.
Basic — primary variant
The defaults: the segments share one rounded track, and a --color-primary thumb floats to the selected segment, sliding between segments as selection changes.


var current by remember { mutableStateOf(0) }
DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = listOf(
DhTabs.Item("Upcoming"),
DhTabs.Item("Completed"),
DhTabs.Item("Overdue")
)
)Variant — secondary
The floating thumb fills with --color-secondary.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = items,
variant = DhTabs.Variant.Secondary
)Variant — tertiary
The floating thumb fills with --color-tertiary.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = items,
variant = DhTabs.Variant.Tertiary
)Variant — error
The floating thumb fills with --color-error. Useful for highlighting destructive-flow filters.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = items,
variant = DhTabs.Variant.Error
)Icon-only configuration
Pass only the icon slot on each Item. Provide contentDescription for screen readers.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = listOf(
DhTabs.Item(icon = { DhIcon(DhIcons.Star, null, decorative = true) }, contentDescription = "Highlights"),
DhTabs.Item(icon = { DhIcon(DhIcons.Heart, null, decorative = true) }, contentDescription = "Favourites"),
DhTabs.Item(icon = { DhIcon(DhIcons.Search, null, decorative = true) }, contentDescription = "Browse")
)
)Label + icon configuration
Pass both label and icon on each Item; the chip lays them out inline.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = listOf(
DhTabs.Item("Highlights", icon = { DhIcon(DhIcons.Star, null, decorative = true) }),
DhTabs.Item("Favourites", icon = { DhIcon(DhIcons.Heart, null, decorative = true) }),
DhTabs.Item("Browse", icon = { DhIcon(DhIcons.Search, null, decorative = true) })
)
)With badges
Pass a badge on any Item; it rides the segment label via BadgedBox and stays inside the capsule on the floating thumb.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = listOf(
DhTabs.Item("Inbox", badge = "12"),
DhTabs.Item("Drafts"),
DhTabs.Item("Sent", badge = "99+")
)
)Scrollable overflow
When the segments are wider than the track, scrollable=true lays them out at their intrinsic width in a 90.dp-min horizontal scroller.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = manyTabs,
scrollable = true
)Swipeable (HorizontalPager)
The content-hosting overload drops selectedIndex / onTabSelected and takes a pageContent lambda. It bundles a HorizontalPager: tapping a segment animates to that page, swiping moves the selection, and the floating thumb follows the swipe gesture frame by frame. (No preview — the swipe animation and the hosted page content can't be shown in a static snapshot; run the demo to see it.)
// Content-hosting overload: no selectedIndex / onTabSelected —
// the bundled HorizontalPager owns the selection, and a swipe
// moves both the page and the floating thumb.
DhPillTabs(
tabs = listOf(
DhTabs.Item("Day"),
DhTabs.Item("Week"),
DhTabs.Item("Month")
)
) { page ->
when (page) {
0 -> DayContent()
1 -> WeekContent()
else -> MonthContent()
}
}Disabled chip
Tabs gated behind a permission or feature flag.


DhPillTabs(
selectedIndex = current,
onTabSelected = { current = it },
tabs = listOf(
DhTabs.Item("Active"),
DhTabs.Item("Archive"),
DhTabs.Item("Premium", enabled = false)
)
)