From 4a3d6df1f418e990ebd848674f21b0100937da16 Mon Sep 17 00:00:00 2001 From: taffroi Date: Tue, 3 Mar 2026 03:44:10 +0100 Subject: [PATCH 1/6] (UI) Navigation bar with filled icons when focused and closer to the web UI The older Feather icons were directly taken from Pixelfed and didn't have filled versions. I didn't know to make webUI icons work in expo, so I used the material community icons since it has filled icons & look similar to the icons of loops.video. I made the colors softer, following the same theme as loops webUI so it should look cleaner. I replaced the profile tab icon by the avatar of the user, and added an outline when focused. The create tab icon has been replaced by a plus, but i'm not sure if it's a good icon since while you can directly upload a video from the gallery, it takes you directly in camera mode. I added an outline and border radius to the bar, I thought it looks cool and matches with the rest of the app. Feel free to remove it if you think it's out of place. I would have increase the height of the bar because the tabs are really close to the navigation bar of android (with the 3 buttons) but it would overlaps with the video player. Let me know what do you think about this ! --- src/app/(tabs)/_layout.tsx | 77 ++++++++++++++++++++++++++++++++------ src/components/Avatar.tsx | 1 + 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 58db01f..45bf045 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -1,11 +1,14 @@ +import Avatar from '@/components/Avatar'; import { useTheme } from '@/contexts/ThemeContext'; import { useNotificationPolling } from '@/hooks/useNotificationPolling'; import { useAuthStore } from '@/utils/authStore'; import { useNotificationStore } from '@/utils/notificationStore'; -import Feather from '@expo/vector-icons/Feather'; +import { Feather, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; import { Tabs } from 'expo-router'; import { useMemo } from 'react'; -import { Platform } from 'react-native'; +import { Platform, Image } from 'react-native'; +import props from '@/components/profile/AccountHeader'; +import tw from 'twrnc'; export default function TabsLayout() { const { user } = useAuthStore(); @@ -25,12 +28,12 @@ export default function TabsLayout() { initialRouteName="index" screenOptions={{ backBehavior: 'order', - tabBarActiveTintColor: colorScheme === 'dark' ? '#fff' : '#000', - tabBarInactiveTintColor: colorScheme === 'dark' ? '#555' : '#999', + tabBarActiveTintColor: colorScheme === 'dark' ? '#FFFFFF' : '#101828', + tabBarInactiveTintColor: colorScheme === 'dark' ? '#99A1AF' : '#6A7282', tabBarStyle: { backgroundColor: colorScheme === 'dark' ? '#000' : '#fff', - borderTopWidth: 1, - borderTopColor: colorScheme === 'dark' ? '#1e2939' : '#eee', + outlineWidth: 1, + outlineColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', height: Platform.OS === 'ios' ? 94 : 94, paddingTop: Platform.OS === 'ios' ? 11 : 5, paddingBottom: Platform.OS === 'ios' ? 8 : 5, @@ -41,6 +44,8 @@ export default function TabsLayout() { height: 0, }, shadowRadius: 0, + borderRadius: 24, + position: Platform.OS === 'ios' ? 'static' : 'absolute', }, }}> , + tabBarIcon: ({ focused, color }) => { + let iconName; + let size = 28; + + iconName = focused ? 'home' : 'home-outline'; + // size = focused ? 32 : 28; + + return ; + }, + tabBarLabelStyle: { + fontWeight: 100, + }, }} /> , + tabBarIcon: ({ focused, color }) => { + let iconName; + let size = 28; + + iconName = focused ? 'compass' : 'compass-outline'; + // size = focused ? 32 : 28; + + return ; + }, }} /> , + tabBarIcon: ({ focused, color }) => { + let iconName; + let size = 28; + + iconName = focused ? 'plus-box' : 'plus-box-outline'; + // size = focused ? 32 : 28; + + return ; + }, }} /> , + tabBarIcon: ({ focused, color }) => { + let iconName; + let size = 28; + + iconName = focused ? 'bell' : 'bell-outline'; + // size = focused ? 32 : 28; + + return ; + }, }} /> , + tabBarIcon: ({ focused, color }) => { + let iconName; + let size = 28; + let style; + + iconName = focused ? 'account' : 'account-outline'; + // size = focused ? 32 : 28; + style = focused + ? { + outlineWidth: 2.5, + outlineColor: colorScheme === 'dark' ? '#FFFFFF' : '#101828', + } + : null; + + return ; + }, }} /> diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index a631d7b..c283181 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -16,6 +16,7 @@ const DEFAULT_FALLBACK_URL = 'https://loopsusercontent.com/avatars/default.jpg?v // Built-in theme presets. const THEMES = { + tab: { size: 24, radius: 9999, borderWidth: 0, borderColor: '#101828' }, sm: { size: 32, radius: 9999, borderWidth: 0, borderColor: 'transparent' }, md: { size: 40, radius: 9999, borderWidth: 0, borderColor: 'transparent' }, lg: { size: 56, radius: 9999, borderWidth: 1, borderColor: 'rgba(0,0,0,0.08)' }, From 75bdc6923f8e5d81407770cbe2c66956208175fc Mon Sep 17 00:00:00 2001 From: Alexandre Tachau Date: Sat, 7 Mar 2026 16:24:53 +0100 Subject: [PATCH 2/6] No border radius, slightly bolder outline & slightly more padding --- src/app/(tabs)/_layout.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 45bf045..560515c 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -32,11 +32,11 @@ export default function TabsLayout() { tabBarInactiveTintColor: colorScheme === 'dark' ? '#99A1AF' : '#6A7282', tabBarStyle: { backgroundColor: colorScheme === 'dark' ? '#000' : '#fff', - outlineWidth: 1, + outlineWidth: 2, outlineColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', - height: Platform.OS === 'ios' ? 94 : 94, - paddingTop: Platform.OS === 'ios' ? 11 : 5, - paddingBottom: Platform.OS === 'ios' ? 8 : 5, + height: Platform.OS === 'ios' ? 94 : 96, + paddingTop: Platform.OS === 'ios' ? 11 : 6, + paddingBottom: Platform.OS === 'ios' ? 8 : 6, elevation: 0, shadowColor: '#666', shadowOpacity: 0, @@ -44,7 +44,7 @@ export default function TabsLayout() { height: 0, }, shadowRadius: 0, - borderRadius: 24, + borderRadius: 0, position: Platform.OS === 'ios' ? 'static' : 'absolute', }, }}> From 2087d3f92386e9c301696dc2a05069a4e0d3da35 Mon Sep 17 00:00:00 2001 From: Alexandre Tachau Date: Sat, 7 Mar 2026 16:28:58 +0100 Subject: [PATCH 3/6] adapting padding for iOS --- src/app/(tabs)/_layout.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 560515c..17db1e7 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -34,9 +34,9 @@ export default function TabsLayout() { backgroundColor: colorScheme === 'dark' ? '#000' : '#fff', outlineWidth: 2, outlineColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', - height: Platform.OS === 'ios' ? 94 : 96, - paddingTop: Platform.OS === 'ios' ? 11 : 6, - paddingBottom: Platform.OS === 'ios' ? 8 : 6, + height: Platform.OS === 'ios' ? 96 : 96, + paddingTop: Platform.OS === 'ios' ? 12 : 6, + paddingBottom: Platform.OS === 'ios' ? 9 : 6, elevation: 0, shadowColor: '#666', shadowOpacity: 0, From 2d3952751258422c465c36bc374b5bbaca3260db Mon Sep 17 00:00:00 2001 From: Alexandre Tachau Date: Mon, 9 Mar 2026 00:59:45 +0100 Subject: [PATCH 4/6] Tab bar: New style for create button --- src/app/(tabs)/_layout.tsx | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 17db1e7..272d10d 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -6,7 +6,7 @@ import { useNotificationStore } from '@/utils/notificationStore'; import { Feather, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; import { Tabs } from 'expo-router'; import { useMemo } from 'react'; -import { Platform, Image } from 'react-native'; +import { Platform, Image, StyleSheet } from 'react-native'; import props from '@/components/profile/AccountHeader'; import tw from 'twrnc'; @@ -96,12 +96,20 @@ export default function TabsLayout() { headerShown: false, tabBarIcon: ({ focused, color }) => { let iconName; - let size = 28; + let size = 32; + let background = [{backgroundColor: 'red'}]; - iconName = focused ? 'plus-box' : 'plus-box-outline'; + iconName = focused ? 'plus' : 'plus'; + if (colorScheme === 'dark') { + color = focused ? '#000' : '#E5E7EB' + background = focused ? [styles.createButton, {backgroundColor: '#E5E7EB'}] : [styles.createButton, {backgroundColor: '#1e2939'}] + } else { + color = focused ? '#fff' : '#1e2939' + background = focused ? [styles.createButton, {backgroundColor: '#1e2939'}] : [styles.createButton, {backgroundColor: '#E5E7EB'}] + }; // size = focused ? 32 : 28; - return ; + return ; }, }} /> @@ -152,3 +160,15 @@ export default function TabsLayout() { ); } + +const styles = StyleSheet.create({ + createButton: { + display: 'flex', + textAlign: 'center', + height: 38, + paddingTop: 3, + width: 44, + borderRadius: 8, + marginTop: 2, + }, +}); From 539c4f02c951935c259e1fe7e4a849aa795a38f1 Mon Sep 17 00:00:00 2001 From: Alexandre Tachau Date: Mon, 9 Mar 2026 01:50:54 +0100 Subject: [PATCH 5/6] Tabbar: Labels on, added safe areas & code cleanup --- src/app/(tabs)/_layout.tsx | 64 +++++++++++++------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 272d10d..86dca75 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -8,12 +8,14 @@ import { Tabs } from 'expo-router'; import { useMemo } from 'react'; import { Platform, Image, StyleSheet } from 'react-native'; import props from '@/components/profile/AccountHeader'; +import { useSafeAreaInsets } from 'react-native-safe-area-context' import tw from 'twrnc'; export default function TabsLayout() { const { user } = useAuthStore(); const { badgeCount } = useNotificationStore(); const { colorScheme } = useTheme(); + const insets = useSafeAreaInsets() const displayBadgeCount = useMemo(() => { if (badgeCount == 0) return undefined; @@ -28,45 +30,35 @@ export default function TabsLayout() { initialRouteName="index" screenOptions={{ backBehavior: 'order', + tabBarShowLabel: true, + headerShown: false, tabBarActiveTintColor: colorScheme === 'dark' ? '#FFFFFF' : '#101828', tabBarInactiveTintColor: colorScheme === 'dark' ? '#99A1AF' : '#6A7282', tabBarStyle: { backgroundColor: colorScheme === 'dark' ? '#000' : '#fff', - outlineWidth: 2, - outlineColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', - height: Platform.OS === 'ios' ? 96 : 96, + borderTopWidth: 2, + borderColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', + height: (Platform.OS === 'ios' ? 76 : 76) + insets.bottom, paddingTop: Platform.OS === 'ios' ? 12 : 6, paddingBottom: Platform.OS === 'ios' ? 9 : 6, - elevation: 0, - shadowColor: '#666', - shadowOpacity: 0, - shadowOffset: { - height: 0, - }, - shadowRadius: 0, - borderRadius: 0, - position: Platform.OS === 'ios' ? 'static' : 'absolute', + position: 'static', }, + tabBarLabelStyle: { + marginTop: 1} }}> { let iconName; let size = 28; iconName = focused ? 'home' : 'home-outline'; - // size = focused ? 32 : 28; return ; }, - tabBarLabelStyle: { - fontWeight: 100, - }, }} /> { let iconName; let size = 28; iconName = focused ? 'compass' : 'compass-outline'; - // size = focused ? 32 : 28; return ; }, @@ -90,10 +79,8 @@ export default function TabsLayout() { { let iconName; let size = 32; @@ -107,7 +94,6 @@ export default function TabsLayout() { color = focused ? '#fff' : '#1e2939' background = focused ? [styles.createButton, {backgroundColor: '#1e2939'}] : [styles.createButton, {backgroundColor: '#E5E7EB'}] }; - // size = focused ? 32 : 28; return ; }, @@ -116,9 +102,8 @@ export default function TabsLayout() { { @@ -126,7 +111,6 @@ export default function TabsLayout() { let size = 28; iconName = focused ? 'bell' : 'bell-outline'; - // size = focused ? 32 : 28; return ; }, @@ -137,22 +121,16 @@ export default function TabsLayout() { options={{ title: 'Profile', tabBarAccessibilityLabel: 'Profile', - tabBarShowLabel: false, - headerShown: false, - tabBarIcon: ({ focused, color }) => { - let iconName; - let size = 28; + tabBarIcon: ({ focused }) => { let style; - - iconName = focused ? 'account' : 'account-outline'; - // size = focused ? 32 : 28; style = focused ? { - outlineWidth: 2.5, - outlineColor: colorScheme === 'dark' ? '#FFFFFF' : '#101828', + outlineWidth: 2.5, + outlineColor: colorScheme === 'dark' ? '#FFFFFF' : '#101828', + borderWidth: 1.5, + borderColor: colorScheme === 'dark' ? '#000' : '#FFFFFF', } : null; - return ; }, }} @@ -165,10 +143,10 @@ const styles = StyleSheet.create({ createButton: { display: 'flex', textAlign: 'center', - height: 38, - paddingTop: 3, + height: 40, + paddingTop: 4, width: 44, borderRadius: 8, - marginTop: 2, + marginTop: 13, }, }); From 0424cfc044170ade33976b7ece2fbc0a75300ecd Mon Sep 17 00:00:00 2001 From: Alexandre Tachau Date: Mon, 9 Mar 2026 01:57:07 +0100 Subject: [PATCH 6/6] Tabbar: adjusting height --- src/app/(tabs)/_layout.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 86dca75..884f754 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -3,13 +3,12 @@ import { useTheme } from '@/contexts/ThemeContext'; import { useNotificationPolling } from '@/hooks/useNotificationPolling'; import { useAuthStore } from '@/utils/authStore'; import { useNotificationStore } from '@/utils/notificationStore'; -import { Feather, Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; +import { MaterialCommunityIcons } from '@expo/vector-icons'; import { Tabs } from 'expo-router'; import { useMemo } from 'react'; -import { Platform, Image, StyleSheet } from 'react-native'; +import { Platform, StyleSheet } from 'react-native'; import props from '@/components/profile/AccountHeader'; import { useSafeAreaInsets } from 'react-native-safe-area-context' -import tw from 'twrnc'; export default function TabsLayout() { const { user } = useAuthStore(); @@ -38,7 +37,7 @@ export default function TabsLayout() { backgroundColor: colorScheme === 'dark' ? '#000' : '#fff', borderTopWidth: 2, borderColor: colorScheme === 'dark' ? '#1e2939' : '#E5E7EB', - height: (Platform.OS === 'ios' ? 76 : 76) + insets.bottom, + height: (Platform.OS === 'ios' ? 72 : 72) + insets.bottom, paddingTop: Platform.OS === 'ios' ? 12 : 6, paddingBottom: Platform.OS === 'ios' ? 9 : 6, position: 'static',