Add liquid glass bottom tab bar to iOS, Android and mWeb - #101339
sumo-slonik wants to merge 40 commits into
Conversation
Single floating capsule, native glass on iOS 26, translucent fallback elsewhere.
UITabBar gives iOS 26 glass, Material bar on Android, rounded JS bar on web.
bottom-tabs 7.16.2, native 7.2.6, elements 2.9.19. rns 4.25.0 needs screenKey.
An interactive swipe back left the bar out until the gesture ended.
Adds an RNScreens patch tinting unselected items on iOS 26.
|
|
# Conflicts: # src/components/Navigation/NavigationTabBar/index.tsx # src/pages/Insights/InsightsPage.tsx # src/pages/home/HomePage.tsx # src/pages/inbox/sidebar/NavigationTabBarAvatar.tsx
Untint icons, label every tab, move visibility to screen options, tear down the color scheme coordinator.
The tab fragment is removed and never re-added on the pop. Also reset the color scheme coordinator.
The page skeleton stops at the first cached data and the list rendered a zero-sized view in the gap.
Android leaves the Material bar for the JS one; iOS paints its labels into the icon bitmaps.
# Conflicts: # src/pages/Insights/InsightsPage.tsx # src/pages/inbox/sidebar/SidebarLinks.tsx
Restore Android tab state, gate Insights on registered tabs, sum LHN inset.
Icons go to 20x20 with a tighter label gap; Android and mWeb blur the backdrop.
Adds a milky glass tint over a light blur.
… web Drops the blur layer and expo-blur, and removes dead reveal and lazy-split code.
|
I think iOS is already looking good. The content now takes up the full screan, and I also added the padding for the button selection. I think iOS looks good now. I’m checking Android now. Screen.Recording.2026-10-01.at.14.01.37.mov |
|
Awesome, let us know when you want another set of test builds. |
|
I've checked Android and mWeb as well, and it seems to me that we can try building another ad hoc build. Screen.Recording.2026-10-01.at.15.13.26.movScreen.Recording.2026-10-01.at.15.12.39.mov |
|
🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here. |
|
Okay cool - one discussion I was having with @dubielzyk-expensify is I wonder if Android would look better if the tab ba used appBG as the background color. Then maybe we can just use a subtle shadow on the top of the bar instead of a border (or use .5px border), like so: |
This comment has been minimized.
This comment has been minimized.
Sounds good.
Go with what you want 👍 |
|
Haha okay! @sumo-slonik can we at least try those Android changes and see how it looks? |
I'm on it |
|
@shawnborton I tried the Android changes: the tab bar now uses appBG as its background, with a subtle shadow along the top edge instead of the tonal edge. I also noticed your mock doesn't have the pill behind the selected tab, so I put together two versions, one with the pill and one without it. Without the pill, the selected tab is marked only by the green icon and the bold label. Without pill: Screen.Recording.2026-10-02.at.11.18.19.movWith pill: Screen.Recording.2026-10-02.at.11.15.34.movLet me know what you think and which direction we want to go! |
|
Hoping we can open this PR up for review today! I'd like to get it in app next week. |
|
That feels quite good to me 👍 I will run another set of test builds so @dubielzyk-expensify can test Android, but otherwise let's get this ready for review? |
|
🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here. |
|
@shawnborton I don't think another build will give us the answer yet, because we still haven't decided whether we want to go with the pill or without it. I'll push the code once we decide which direction we want to take. |
|
By pill, do you mean the light green BG behind the icon on Android? |
This comment has been minimized.
This comment has been minimized.
Yep, exactly |
|
Got it, I think we want that so it feels more native material/Android. Will let Danny and Jon confirm though, but I say push that change. |
|
I would go with that for now. Jon won't be back until next week. |
|
mWeb is adjusted as well: Screen.Recording.2026-10-02.at.12.38.52.movWouldn’t we want the color of the pill on mWeb to be the same as on Android? |
|
I don't think so, no. I think on mWeb we just want it to feel on brand and as close to the floating idea as possible. Again, let's wait until @dubielzyk-expensify chimes in as he might have stronger feelings about making this feel more native on Android. |
|
In that case, I’ve pushed the changes. We can kick off an ad hoc build and wait for @dubielzyk-expensify feedback. |
|
🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
I think I'm aligned with everything above. I'd reckon we want to keep the little icon pill background on Android because that's a standard pattern for the Material navigation bar (docs).
Agree with this as well 👍 I'm also down with the background change to Android you suggested Shawn. I think it looks nice. Seems like typically Android uses a separate color for that bottom tab bar, but the colors and text of the tabs read better on appBG and there's still very clear separation between the tab bar and the content so IMO it's all good. |


Explanation of Change
Replaces the JS bottom tab bar on narrow layouts, changing its look on every platform:
UITabBarController, rendered with liquid glass on iOS 26.The native bars come from
createNativeBottomTabNavigator(@react-navigation/bottom-tabs/unstable) on top ofTabsfromreact-native-screens. Both are experimental APIs, so upgrading either package may break this.iOS 26 ignores most tab item colors, so icons are recolored with Skia and the status dot is drawn into the icon (native badges all take the selected tab's color). The unselected label color can't be changed.
Tab screens are no longer lazy, so a tab shows its skeleton as soon as it's picked.
Patches (details in each
details.md):react-native-screens+4.28.0+001+animate-hiding-the-native-tab-bar: animates hiding and showing the bar instead of a blink. The floating buttons fade with it.react-native-screens+4.28.0+002+bold-selected-tab-label: bold label on the selected Android tab.react-native-screens+4.28.0+003+no-android-tab-icon-tint: no icon tint on Android, so the avatar keeps its colors.@react-navigation+bottom-tabs+7.16.2+002+active-indicator-color-precedence: fixes a bug that ignoredtabBarActiveIndicatorColoron Android.@react-navigation+bottom-tabs+7.16.2+003+hidden-tab-items: addstabBarItemHidden, because native bars fit at most 5 of our 6 tabs (Insights or Account, depending on the beta).@react-navigation/bottom-tabsis bumped to 7.16.2 and@react-navigation/nativeto 7.2.6, with their existing patches carried over.The branch history keeps a reverted iOS color patch on purpose, to document a dead end.
Performance
Tab switching on iOS vs
main(iPhone 17 simulator, iOS 27, dev build, 5 rounds). Average of median changes across all source tabs:Android tab switching is also noticeably smoother with the native Material bar.
Fixed Issues
$ #101169
PROPOSAL: N/A
Tests
Offline tests
Behavior is identical offline. None of these changes depend on network state, so the same steps as in the
Testssection apply with the network connection turned off.QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionWaiting for copylabel to the issue)Avatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari