feat(BottomTabBar): add optional badge on tab items#250
Conversation
BottomTabBarItem gains an optional `badge: String?` rendered as a small Tag over the icon's top-trailing corner via Material3 BadgedBox, so a tab can flag a "Soon"/"New" state without shifting the icon or label. Null keeps the current icon-only layout. Demo shows it on the "Teya AI" item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an optional text badge to BottomTabBarItem so tabs can display a small “Soon/New” pill over the icon using Material3 BadgedBox, and updates the demo + API dumps accordingly.
Changes:
- Added
BottomTabBarItem.badge: String? = null(public API) with KDoc. - Rendered the badge via
BadgedBoxandLemonadeUi.Tag(voice = TagVoice.Neutral)whenbadge != null. - Updated the composeApp demo to show a
"Soon"badge on the “Teya AI” tab; regenerated API dumps.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| kmp/expressive/src/commonMain/kotlin/com/teya/lemonade/BottomTabBar.kt | Public API adds badge; UI now overlays a Tag badge on the tab icon via BadgedBox. |
| kmp/expressive/api/expressive.klib.api | Regenerated API dump reflecting the new badge property and updated ctor/copy/component signatures. |
| kmp/expressive/api/desktop/expressive.api | Regenerated desktop API dump for the same public API changes. |
| kmp/expressive/api/android/expressive.api | Regenerated android API dump for the same public API changes. |
| kmp/composeApp/src/commonMain/kotlin/com/teya/lemonade/BottomTabBarDisplay.kt | Demo updated to showcase the new badge on one tab item. |
| Box( | ||
| modifier = Modifier.graphicsLayer { | ||
| scaleX = iconScale | ||
| scaleY = iconScale | ||
| }, | ||
| ) { |
| public data class BottomTabBarItem( | ||
| val label: String, | ||
| val icon: LemonadeIcons, | ||
| val selectedIcon: LemonadeIcons? = null, | ||
| val badge: String? = null, | ||
| ) |
Adding `badge` to the BottomTabBarItem data class changed the primary constructor, copy and copy$default signatures — an ABI break that tripped the API Stability Review gate. Per the binary-compatibility skill, append the property (done) and restore the old `<init>` and `copy` symbols as @deprecated(HIDDEN) shims. The retained symbols dump as `synthetic` (identical descriptor), so the classifier now reads ADDITIONS_ONLY and CI auto-passes — no maintainer gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
API Dump (updated after binary-compat fix)Verdict:
No property renames/removals, no abstract interface members, no enum changes. |
Use the dedicated Badge component (brand-coloured pill) instead of a neutral Tag, matching the "Soon" treatment in the design. Public API is unchanged (still `badge: String?`), so the BCV baseline is untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
BottomTabBarItemgains an optionalbadge: String?. When set, a smallLemonadeUi.Tagrenders over the icon's top-trailing corner via Material3BadgedBox— so a tab can flag a "Soon" / "New" state without shifting the icon or label.nullkeeps the current icon-only layout (no behavior change for existing call sites — the param is defaulted).Driven by the app needing a "Soon" badge on the Teya AI tab while the feature is behind a flag.
Changes
BottomTabBarItem.badge: String? = null(+ KDoc).BottomTabBarItemContentwraps the icon inBadgedBox, renderingLemonadeUi.Tag(label = badge, voice = .Neutral)when present..apidumps regenerated (apiDump): android / desktop / klib.BottomTabBarDisplay) shows the badge on the "Teya AI" item.Design notes / for review
TagVoice.Neutral(gray pill). The app's reference design (web) shows a lime/brand "Soon" pill — there's no brandTagVoicetoday, so the exact color needs a design/token call. Happy to switch to whichever voice (or a new brand voice) you prefer.BadgedBoxdefault top-end placement; a text Tag is wider than a dot badge, so on very narrow slots it can sit close to the edge. Open to a tighter/size-capped badge variant if preferred.Verify
:expressive:compileDebugKotlinAndroid✅:expressive:apiDump✅ (committed)🤖 Generated with Claude Code