[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93759":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":14,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":15,"rankGlobal":9,"rankLanguage":9,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":17,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":27,"readmeContent":28,"aiSummary":29,"trendingCount":14,"starSnapshotCount":14,"syncStatus":30,"lastSyncTime":31,"discoverSource":32},93759,"expo-glass-tabs","davidmokos\u002Fexpo-glass-tabs","davidmokos","Floating liquid-glass tab bar for Expo Router — minimize-on-scroll, sliding highlight, finger scrubbing with haptics, progressive edge blur",null,"TypeScript",237,8,125,0,42.86,"MIT License",false,"main",true,[21,22,23,24,25,26],"expo","expo-router","liquid-glass","react-native","reanimated","tab-bar","2026-09-21 04:01:26","\u003Cdiv align=\"center\">\n\n# expo-glass-tabs\n\n**A floating liquid-glass tab bar for Expo Router — the Revolut-style bottom bar.**\n\n[![npm version](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002Fexpo-glass-tabs?logo=npm&color=CB3837)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fexpo-glass-tabs)\n[![npm downloads](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fdm\u002Fexpo-glass-tabs?color=blue)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fexpo-glass-tabs)\n[![TypeScript](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTypeScript-first-3178C6?logo=typescript&logoColor=white)](#api)\n[![license](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fl\u002Fexpo-glass-tabs?color=green)](.\u002FLICENSE)\n\n\u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdavidmokos\u002Fexpo-glass-tabs\u002Fmain\u002Fassets\u002Fdemo.gif\" alt=\"expo-glass-tabs demo — liquid glass tab bar minimizing on scroll\" width=\"640\" \u002F>\n\n\u003C\u002Fdiv>\n\n---\n\n## Why\n\niOS 26's native tab bar can minimize on scroll — but it collapses to a **single icon**. Revolut's bar keeps *all* tabs visible and just drops the labels, shrinking the pill in both dimensions. That behavior isn't reachable from the native `UITabBar`, so this package rebuilds it on top of Expo Router's headless tabs — with real native materials.\n\n## Features\n\n- 🪟 **Real liquid glass** — iOS 26 `UIGlassEffect` via `expo-glass-effect`: true squircle corners, rim refraction, content lensing. Solid fallback on older iOS and Android.\n- 📉 **Minimize on scroll** — scroll down and the pill shrinks in both dimensions while labels collapse; every icon stays visible. Scroll up to expand. Critically-damped springs, no flicker (rubber-band overscroll is filtered out).\n- 🛝 **Sliding highlight** — the active-tab pill physically travels between tabs on an interruptible, transform-only spring.\n- 👆 **Finger scrubbing** — drag along the bar: the highlight tracks your finger 1:1, icons light up as you pass them, haptic ticks fire at each boundary, and navigation happens on release — screens never jump mid-drag.\n- 🌫️ **Progressive edge blur** — content dissolves gradually behind the bar with no hard blur line. The same component works for top bars.\n- 🎞️ **Subtle screen transitions** — fade + micro-scale between tabs via a `TabSlot` `renderFn`.\n- ⚡ **UI-thread everything** — all animation runs in Reanimated worklets with native gesture recognizers; a blocked JS thread doesn't drop a frame.\n- 🧩 **Pure TypeScript** — no custom native code. Works in Expo Go and any dev build.\n\n## Installation\n\n```sh\nnpx expo install expo-glass-tabs expo-blur expo-glass-effect expo-haptics expo-symbols react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-screens\n```\n\nWrap your app root in `GestureHandlerRootView` (Expo Router does not do this for you):\n\n```tsx\n\u002F\u002F app\u002F_layout.tsx\nimport { GestureHandlerRootView } from 'react-native-gesture-handler';\n\nexport default function RootLayout() {\n  return \u003CGestureHandlerRootView style={{ flex: 1 }}>{\u002F* ... *\u002F}\u003C\u002FGestureHandlerRootView>;\n}\n```\n\n## Quick start\n\n```tsx\nimport { useRouter } from 'expo-router';\nimport { Tabs, TabList, TabSlot, TabTrigger } from 'expo-router\u002Fui';\nimport {\n  GlassTabBar,\n  GlassTabButton,\n  TabBarMinimizeProvider,\n  renderFadingTabScreen,\n  type GlassTabItem,\n} from 'expo-glass-tabs';\n\nconst ITEMS: (GlassTabItem & { href: string })[] = [\n  { name: 'index', href: '\u002F', label: 'Home', icon: 'house.fill' },\n  { name: 'invest', href: '\u002Finvest', label: 'Invest', icon: 'chart.line.uptrend.xyaxis' },\n  { name: 'payments', href: '\u002Fpayments', label: 'Payments', icon: 'arrow.left.arrow.right' },\n  { name: 'crypto', href: '\u002Fcrypto', label: 'Crypto', icon: 'bitcoinsign' },\n];\n\nexport default function AppTabs() {\n  const router = useRouter();\n  return (\n    \u003CTabBarMinimizeProvider>\n      \u003CTabs>\n        \u003CTabSlot style={{ height: '100%' }} renderFn={renderFadingTabScreen} \u002F>\n        \u003CTabList asChild>\n          \u003CGlassTabBar onIndexSelected={(i) => router.navigate(ITEMS[i].href as never)}>\n            {ITEMS.map(({ href, ...item }, index) => (\n              \u003CTabTrigger key={item.name} name={item.name} href={href as never} asChild>\n                \u003CGlassTabButton item={item} index={index} \u002F>\n              \u003C\u002FTabTrigger>\n            ))}\n          \u003C\u002FGlassTabBar>\n        \u003C\u002FTabList>\n      \u003C\u002FTabs>\n    \u003C\u002FTabBarMinimizeProvider>\n  );\n}\n```\n\nThen attach the scroll hook in every screen that should minimize the bar:\n\n```tsx\nimport Animated from 'react-native-reanimated';\nimport { useMinimizeOnScroll } from 'expo-glass-tabs';\n\nexport default function HomeScreen() {\n  const onScroll = useMinimizeOnScroll();\n  return (\n    \u003CAnimated.ScrollView onScroll={onScroll} scrollEventThrottle={16}>\n      {\u002F* content *\u002F}\n    \u003C\u002FAnimated.ScrollView>\n  );\n}\n```\n\n## Custom icons\n\nUse `renderIcon` when an SF Symbol won't do — a brand logo, for example. It's called once per tint layer, so the active\u002Finactive crossfade stays on the UI thread:\n\n```tsx\nimport { Image } from 'expo-image';\n\nconst home: GlassTabItem = {\n  name: 'index',\n  label: 'Home',\n  renderIcon: ({ tint }) => (\n    \u003CImage source={require('.\u002Flogo.png')} tintColor={tint} style={{ width: 18, height: 17 }} \u002F>\n  ),\n};\n```\n\n## Theming\n\n```tsx\n\u003CGlassTabBar\n  theme={{\n    activeTint: '#FFFFFF',\n    inactiveTint: '#9E9EA6',\n    highlight: 'rgba(255,255,255,0.14)',   \u002F\u002F sliding pill\n    glassTint: 'rgba(10,10,12,0.55)',      \u002F\u002F tint over the liquid glass\n    solidFallback: 'rgba(18,18,20,0.94)',  \u002F\u002F pre-iOS 26 \u002F Android background\n  }}\n  haptics \u002F\u002F scrub tick (iOS), default true\n\u002F>\n```\n\n## Progressive blur for your own edges\n\n`ProgressiveBlur` is exported on its own — drop it behind a transparent header:\n\n```tsx\nimport { ProgressiveBlur } from 'expo-glass-tabs';\n\n\u003CProgressiveBlur\n  direction=\"top\"\n  style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 160 }}\n\u002F>;\n```\n\n## API\n\n| Export | Kind | Purpose |\n| --- | --- | --- |\n| `GlassTabBar` | component | The floating pill — use via `TabList asChild` |\n| `GlassTabButton` | component | One trigger — use via `TabTrigger asChild` |\n| `TabBarMinimizeProvider` | component | Wrap the `Tabs` tree once |\n| `useMinimizeOnScroll()` | hook | Scroll handler for `Animated.ScrollView` |\n| `useTabBarMinimized()` | hook | Raw 0..1 minimize progress, for custom UI |\n| `renderFadingTabScreen` | function | `TabSlot` `renderFn` with fade + micro-scale |\n| `ProgressiveBlur` | component | Gradient blur anchored to a screen edge |\n| `MINIMIZE_SPRING` | constant | The spring config, if you want to match it |\n\n## How it works\n\nThe bar's *structure* is declared in JS (Expo Router headless tabs), but everything you see and feel at runtime is native:\n\n- **Materials** — `UIGlassEffect`, `UIVisualEffectView`, SF Symbols, `UIFeedbackGenerator`.\n- **Motion** — Reanimated worklets on the UI thread; the sliding highlight and scrub are transform-only (GPU-composited, no layout work per frame).\n- **Gestures** — native recognizers via `react-native-gesture-handler`; a `Pan` (scrub) races a `Tap`, so taps stay forgiving while drags feel attached to the finger.\n\nThe corner geometry is rendered by the glass view itself (its native corner configuration), not an RN clipping mask — that's what preserves the true squircle and the rim lighting through the whole minimize animation.\n\n## License\n\nMIT © [David Mokos](https:\u002F\u002Fgithub.com\u002Fdavidmokos)\n","这是一个为 Expo Router 设计的悬浮式液态玻璃风格底部标签栏组件，实现 Revolut 风格的动态交互体验。核心功能包括：滚动时双维度收缩（图标保留、文字隐藏）、滑动高亮指示器、手指拖拽 scrub 导航（带触觉反馈）、渐进式边缘模糊及纯 Reanimated 驱动的 UI 线程动画。基于 expo-glass-effect 实现真实 iOS 26 UIGlassEffect 效果，并提供跨平台降级支持。适用于需要高端视觉质感与流畅手势交互的 React Native 移动应用，尤其适合金融类、设计驱动型或追求原生级动效的 Expo 项目。",2,"2026-07-25 02:30:02","CREATED_QUERY"]