Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-eels-wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'react-native-bottom-tabs': minor
---

Add `experimental_bakedTintColors` prop to opt into the iOS 26 Liquid Glass active and inactive tint color workaround.
46 changes: 35 additions & 11 deletions apps/example/src/Examples/TintColors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Article } from '../Screens/Article';
import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
import { Button, Platform, StyleSheet, View } from 'react-native';

const renderScene = SceneMap({
article: Article,
Expand All @@ -12,8 +13,11 @@ const renderScene = SceneMap({
chat: Chat,
});

const isAndroid = Platform.OS === 'android';

export default function TintColorsExample() {
const [index, setIndex] = useState(0);
const [bakedTintColors, setBakedTintColors] = useState(false);
const [routes] = useState([
{
key: 'article',
Expand All @@ -31,9 +35,11 @@ export default function TintColorsExample() {
},
{
key: 'contacts',
focusedIcon: require('../../assets/icons/person_dark.png'),
focusedIcon: isAndroid
? require('../../assets/icons/person_dark.png')
: { sfSymbol: 'person.fill' },
title: 'Contacts',
activeTintColor: 'yellow',
activeTintColor: 'blue',
},
{
key: 'chat',
Expand All @@ -45,14 +51,32 @@ export default function TintColorsExample() {
]);

return (
<TabView
sidebarAdaptable
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
tabBarActiveTintColor="red"
tabBarInactiveTintColor="orange"
scrollEdgeAppearance="default"
/>
<View style={styles.container}>
<View style={styles.controls}>
<Button
title={`${bakedTintColors ? 'Disable' : 'Enable'} Experimental Baked Tint Colors`}
onPress={() => setBakedTintColors((value) => !value)}
/>
</View>
<TabView
sidebarAdaptable
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
tabBarActiveTintColor="red"
tabBarInactiveTintColor="orange"
experimental_bakedTintColors={bakedTintColors}
scrollEdgeAppearance="default"
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
controls: {
padding: 12,
},
});
14 changes: 14 additions & 0 deletions docs/docs/docs/guides/standalone-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,20 @@ Color for inactive tabs.
On iOS >= 26 (Liquid Glass), this prop is ignored.
:::

#### `experimental_bakedTintColors` <Badge text="iOS" type="info" /> <Badge text="experimental" type="danger"/>

Enable an experimental mode which bakes (rasterizes) the label into the icon image, allowing for better control of the tint colors on iOS 26+.

- Type: `boolean`

:::warning
This feature is experimental, and might be removed in the future.

It has many drawbacks, such as SFSymbol icon sizes being different on label width, badges being positioned far away from the icon depending on the label width, and possibly breaking accessibility since the label will be baked inside the icon image.

Use with caution, only if you prioritize tint color consistency between platforms.
:::

#### `tabBarStyle`

Object containing styles for the tab bar.
Expand Down
14 changes: 14 additions & 0 deletions docs/docs/docs/guides/usage-with-react-navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ Color for the inactive tabs.
On iOS >= 26 (Liquid Glass), this prop is ignored.
:::

#### `experimental_bakedTintColors` <Badge text="iOS" type="info" /> <Badge text="experimental" type="danger"/>

Enable an experimental mode which bakes (rasterizes) the label into the icon image, allowing for better control of the tint colors on iOS 26+.

- Type: `boolean`

:::warning
This feature is experimental, and might be removed in the future.

It has many drawbacks, such as SFSymbol icon sizes being different on label width, badges being positioned far away from the icon depending on the label width, and possibly breaking accessibility since the label will be baked inside the icon image.

Use with caution, only if you prioritize tint color consistency between platforms.
:::

#### `tabBarStyle`

Object containing styles for the tab bar.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ class RCTTabViewManager(context: ReactApplicationContext) :
view.setInactiveTintColor(value)
}

override fun setExperimentalBakedTintColors(view: ReactBottomNavigationView?, value: Boolean) {
}

override fun setActiveIndicatorColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null) {
val color = ColorStateList.valueOf(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
_tabViewProvider.inactiveTintColor = RCTUIColorFromSharedColor(newViewProps.inactiveTintColor);
}

if (oldViewProps.experimentalBakedTintColors != newViewProps.experimentalBakedTintColors) {
_tabViewProvider.experimentalBakedTintColors = newViewProps.experimentalBakedTintColors;
}

if (oldViewProps.hapticFeedbackEnabled != newViewProps.hapticFeedbackEnabled) {
_tabViewProvider.hapticFeedbackEnabled = newViewProps.hapticFeedbackEnabled;
}
Expand Down Expand Up @@ -262,4 +266,3 @@ - (void)onLayoutWithSize:(CGSize)size reactTag:(NSNumber *)reactTag {

#endif // RCT_NEW_ARCH_ENABLED


Loading
Loading