{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tab-bar",
  "title": "Tab bar",
  "description": "custom bottom tab bar: opaque tints, label, active dot under the label",
  "dependencies": [
    "lucide-react-native@^1.21.0",
    "react-native-safe-area-context@~5.7.0"
  ],
  "registryDependencies": [
    "@kit-native/lib-haptics",
    "@kit-native/lib-palette-context",
    "@kit-native/lib-utils",
    "@kit-native/text"
  ],
  "files": [
    {
      "path": "components/kit/tab-bar.tsx",
      "content": "import { Text } from '@/components/ui/text';\nimport { haptic } from '@/lib/haptics';\nimport { useIconStroke, usePalette } from '@/lib/palette-context';\nimport { cn } from '@/lib/utils';\nimport type { LucideIcon } from 'lucide-react-native';\nimport * as React from 'react';\nimport { type ColorValue, Pressable, View } from 'react-native';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\n/**\n * Minimal structural view of what Expo Router's Tabs passes to `tabBar`. Kept local so the\n * kit does not deep-import react-navigation types.\n */\ntype Route = { key: string; name: string; params?: object };\ntype IconRender = (props: { focused: boolean; color: ColorValue; size: number }) => React.ReactNode;\nexport type TabBarProps = {\n  state: { index: number; routes: Route[] };\n  descriptors: Record<string, { options: { title?: string; tabBarIcon?: IconRender; tabBarAccessibilityLabel?: string; href?: string | null } }>;\n  navigation: {\n    emit: (e: { type: 'tabPress' | 'tabLongPress'; target: string; canPreventDefault?: boolean }) => unknown;\n    navigate: (name: string, params?: object) => void;\n  };\n};\n\n/**\n * The kit's bottom tab bar: icon, label, and an active dot UNDER the label so the selected\n * tab reads by shape as well as colour. Opaque tints from the theme (the default tab bar's\n * inactive tint is text at 50% alpha, which makes overlapping strokes look patchy).\n *\n *   <Tabs tabBar={(props) => <TabBar {...props} />}>\n */\nexport function TabBar({ state, descriptors, navigation }: TabBarProps) {\n  const insets = useSafeAreaInsets();\n  const colors = usePalette();\n  return (\n    <View className=\"bg-background border-border flex-row border-t\" style={{ paddingBottom: Math.max(insets.bottom, 8) }} accessibilityRole=\"tablist\">\n      {state.routes.map((route, index) => {\n        const { options } = descriptors[route.key];\n        if (options.href === null) return null;\n        const focused = state.index === index;\n        const label = options.title ?? route.name;\n        const color = focused ? colors.primary : colors.mutedForeground;\n        const onPress = () => {\n          const e = navigation.emit({ type: 'tabPress', target: route.key, canPreventDefault: true }) as { defaultPrevented?: boolean };\n          // The haptic lives in TabBarItem so custom bars get it too.\n          if (!focused && !e.defaultPrevented) navigation.navigate(route.name, route.params);\n        };\n        return (\n          <TabBarItem\n            key={route.key}\n            label={label}\n            focused={focused}\n            color={color}\n            accessibilityLabel={options.tabBarAccessibilityLabel}\n            onPress={onPress}\n            onLongPress={() => navigation.emit({ type: 'tabLongPress', target: route.key })}\n            icon={options.tabBarIcon?.({ focused, color, size: 24 })}\n          />\n        );\n      })}\n    </View>\n  );\n}\n\ntype ItemProps = {\n  label: string;\n  focused: boolean;\n  color: string;\n  icon: React.ReactNode;\n  accessibilityLabel?: string;\n  onPress?: () => void;\n  onLongPress?: () => void;\n};\n\n/** One tab: icon, label, dot. Exported for previews and for custom tab bars. */\nexport function TabBarItem({ label, focused, color, icon, accessibilityLabel, onPress, onLongPress }: ItemProps) {\n  return (\n    <Pressable\n      accessibilityRole=\"tab\"\n      aria-selected={focused}\n      accessibilityLabel={accessibilityLabel ?? label}\n      onPress={\n        onPress\n          ? () => {\n              haptic('selection');\n              onPress();\n            }\n          : undefined\n      }\n      onLongPress={onLongPress}\n      className=\"min-h-14 flex-1 items-center justify-center gap-1 pb-2 pt-2 active:opacity-70\">\n      {icon}\n      <Text className=\"text-[11px] font-medium\" style={{ color }} numberOfLines={1}>\n        {label}\n      </Text>\n      <View className={cn('size-1 rounded-full', focused ? 'bg-primary' : 'bg-transparent')} accessibilityElementsHidden />\n    </Pressable>\n  );\n}\n\n/** Default icon renderer for `tabBarIcon`: a lucide icon whose stroke thickens when focused (and follows the theme's stroke). */\nexport function tabIcon(icon: LucideIcon): IconRender {\n  // Called as a function by the tab bar, not rendered as a component, so the hook lives in TabIcon.\n  function TabIconRender({ focused, color, size }: { focused: boolean; color: ColorValue; size: number }) {\n    return <TabIcon icon={icon} focused={focused} color={color} size={size} />;\n  }\n  return TabIconRender;\n}\n\nfunction TabIcon({ icon: IconCmp, focused, color, size }: { icon: LucideIcon; focused: boolean; color: ColorValue; size: number }) {\n  const stroke = useIconStroke();\n  return <IconCmp color={String(color)} size={size} strokeWidth={((focused ? 2.25 : 1.75) * stroke) / 2} />;\n}\n",
      "type": "registry:component",
      "target": "components/kit/tab-bar.tsx"
    }
  ],
  "meta": {
    "exports": [
      "TabBar",
      "TabBarItem",
      "tabIcon"
    ],
    "api": "<Tabs tabBar={(p) => <TabBar {...p} />}><Tabs.Screen options={{ title, tabBarIcon: tabIcon(HouseIcon) }} /></Tabs>"
  },
  "docs": "Run `npx expo install --fix` afterwards so Expo SDK packages match your SDK. Needs the kit theme (`@kit-native/theme`) and `KitThemeProvider` from `@kit-native/lib-theme-context` at the root.",
  "categories": [
    "navigation"
  ],
  "type": "registry:component"
}