UI y estilos
En BullStock conviven dos kits de UI. Saber cuál usar es lo primero que hay que resolver antes de escribir una pantalla.
| Carpeta | Qué es | Cuándo usarlo |
|---|---|---|
components/ui-native/ | Primitivas sobre HeroUI Native | Lo nuevo va acá |
components/ui/ | Primitivas generadas de Gluestack | Solo si tocás una pantalla que ya las usa. No las edites a mano: son generadas |
Hoy la app importa @/components/ui/ en ~85 archivos y @/components/ui-native/ en ~10: la
migración está en curso, no terminada.
La decisión
BullStock nació con NativeWind 4 (Tailwind v3) + Gluestack UI. Tailwind v4 y la new architecture de React Native dejaron atrás la config que Gluestack asume, y el kit generado es incómodo de mantener (no se puede editar sin perder los cambios en la próxima generación).
Se decidió migrar en dos ejes:
- Estilos → Uniwind (Tailwind v4).
nativewindya no es dependencia y no haytailwind.config.js. - Componentes → HeroUI Native, reemplazando gradualmente a Gluestack.
Migración gradual, no big-bang: son ~85 archivos de pantallas críticas sin tests automatizados; la validación es manual en dispositivo. Al tocar una pantalla, migrala.
Estilos con Uniwind
El entrypoint es global.css:
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
@source './app';
@source './components';
/* … */
@theme {
--color-primary-400: #6BB0ED; /* azul Oxitec */
--color-primary-500: #4A9AE8;
--color-secondary-500: #0A7FC9;
/* … */
}
Eso expone la paleta como utilities (bg-primary-500, text-secondary-700). No hardcodees hex.
El tema claro/oscuro se conmuta con Uniwind.setTheme() desde contexts/ThemeContext.tsx.
Las primitivas nuevas
Todo se exporta desde components/ui-native/index.ts:
Button · Chip · Dialog + AlertDialog · Input · SearchField · Skeleton
(+ LegacySkeleton de compatibilidad) · Tabs + SimpleTabs · Toast + useShowToast ·
BottomSheet + ControlledBottomSheet.
Sueltos en la misma carpeta: AnimatedBlurView, DialogBlurBackdrop, HapticPressable,
SafeAreaView, ScrollShadow.
El puente de la migración
components/shared/form/customButton.tsx envuelve el Button de ui-native y traduce la API
vieja de Gluestack (action / variant / size) a la de HeroUI.
Seguí usándolo en vez de reescribir cada call-site: es lo que permite migrar sin tocar 85 archivos de una.
Componentes compartidos de dominio
components/shared/, organizado por categoría:
Formularios — SheetSelect (el selector estándar: trigger + modal a pantalla completa) ·
customSelect (el viejo, sobre Gluestack; preferí SheetSelect) · ProductSelector (selector de
producto por elemento, con imágenes, cantidades y depósito — el compuesto más grande del kit).
Feedback — ConfirmDialog · networkBanner · ErrorBoundary · loading / loading2 /
customLoading · skeleton · carouselModal · updateRequiredModal.
Cámara — Camera, ScanMask, ScanConfirmation, inventareableCameraScan, torchButton.
Layout — ContentSheet · ResponsiveContainer · PressableScale (sus props scale* están
@deprecated e ignoradas).
Otros — NativeHeader · drawer + logout-alert · fade-in-box / slide-in-box ·
externalImage · countdown / full-text · MapAppPicker · ModeSwitch · app-info ·
viewDatabase.
Lo específico de una feature va en components/roadmap/, components/inventory/,
components/patient/, components/home/. Si sirve para más de una, subilo a
shared/<categoría>/.
Gotchas de la convivencia
metro.config.js tiene dos redirecciones que existen solo por la convivencia de kits:
idb→lib/idb-shim.js— Firebase pide IndexedDB, que no existe en RN.tailwindcss/resolveConfigy*/tailwind.config→lib/tailwind-shim.js—useBreakPointValuede Gluestack importa APIs de Tailwind v3 que ya no existen.
Si los sacás sin terminar la migración, Metro no resuelve y la app no compila.
SheetSelect usa el TouchableOpacity de gesture-handlerNo es un descuido: dentro de un @gorhom/bottom-sheet en Android, el Pressable de RNGH a veces no
dispara onPress. Los Pressable internos del modal sí son los de React Native, porque el Modal
vive en una ventana nativa propia, fuera del sheet.
components/ui/gluestack-ui-provider/ está marcado @deprecated (el tema ya no se aplica ahí) pero
el provider sigue montado, porque los componentes generados lo necesitan.
CLAUDE.md del repo está desactualizado en estoDice "NativeWind 4", "Gluestack UI" y "theme extensions en tailwind.config.js". Ninguna de las tres
es cierta hoy.
Bottom sheets con teclado en Android
Receta que funciona, por si la necesitás: usar el scrollable de @gorhom/bottom-sheet +
contentContainerClassName='h-full' + los handlers de bottom-sheet en el input. El ejemplo real está
en el body de agregar suministro de la pantalla de movimiento de stock del vehículo.