@tipmexico/tm_theme
v2.0.0
Published
module that contain design system
Readme
TM Theme - Sistema de Diseño
Información del Proyecto
| Propiedad | Valor | | --------------- | --------------------------------------------------------- | | Nombre | @tipmexico/tm_theme | | Versión | 1.2.34 | | Repositorio | Bitbucket |
Resumen Ejecutivo
TM Theme es un sistema de diseño (Design System) completo y robusto para aplicaciones web y móviles de TipMéxico, construido con React Native y Expo. Implementa la metodología Atomic Design y proporciona una biblioteca completa de componentes reutilizables, tematización multi-marca y tokens de diseño consistentes para garantizar:
- Consistencia visual entre plataformas (Web, iOS, Android)
- Desarrollo acelerado con componentes pre-construidos
- Tematización dinámica multi-marca
- Accesibilidad y mejores prácticas de UX
- Type safety completo con TypeScript
- Documentación interactiva con Storybook
Características Principales
Arquitectura de Diseño
- Atomic Design - Metodología de diseño escalable
- Design Tokens - Sistema de tokens para colores, tipografía, espaciado
- Multi-Brand Theming - Soporte para múltiples marcas (Bitcar, TIP, OFS)
- Component-Driven Development - Componentes aislados y reutilizables
Plataformas Soportadas
- Web - React 19+
- iOS - iOS 13+
- Android - API 21+
Tecnologías Core
- TypeScript 5.x - Type safety completo
- React Native 0.79.5 - Framework multi-plataforma
- Expo 53 - Toolchain y SDK
- Storybook 9.x - Documentación interactiva
- React Native SVG - Iconografía vectorial
- Poppins & Roboto - Tipografía personalizada
Arquitectura del Sistema
Metodología Atomic Design
┌─────────────────────────────────────────────────────────────┐
│ TEMPLATES │
│ (Estructuras de página completa) │
│ - LoginTemplate │
│ - Layouts completos │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ORGANISMS │
│ (Secciones complejas de UI) │
│ - Header │
│ - BottomNavigation │
│ - CardStatusDetails │
│ - CardTitleDetails │
│ - ModalSearchMaintenance │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ MOLECULES │
│ (Grupos de componentes) │
│ - Accordion │
│ - Modal │
│ - Pagination │
│ - FileUploader │
│ - CardActions, CardMetrics, CardTimeline │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ATOMS │
│ (Componentes fundamentales) │
│ - Button, TextField, Checkbox, Dropdown │
│ - Avatar, Badge, Alert, Spinner │
│ - Icon, Card, Label, Switch │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DESIGN TOKENS │
│ (Valores fundamentales) │
│ - Colors (Paletas de marca) │
│ - Typography (Heading, Paragraph, Button) │
│ - Spacing (none, xxs, xs, sm, md, lg, xl, 2xl-6xl) │
│ - Radius (Bordes redondeados) │
└─────────────────────────────────────────────────────────────┘Inventario de Componentes
Atoms (27 Componentes Fundamentales)
Componentes básicos e indivisibles que forman la base del sistema:
| Componente | Descripción | Casos de Uso | | --------------- | ----------------------------------- | --------------------------------- | | Alert | Mensajes de alerta y notificaciones | Errores, advertencias, info | | Avatar | Imagen de perfil circular | Perfiles de usuario | | Badge | Etiquetas y contadores | Notificaciones, estados | | BreadCrumbs | Navegación de ruta | Navegación jerárquica | | Button | Botones de acción | CTAs, formularios | | Card | Contenedor de información | Tarjetas de contenido | | Checkbox | Selección múltiple | Formularios, filtros | | DatePicker | Selector de fecha | Formularios de fecha | | Divisor | Separador visual | Secciones, grupos | | Dropdown | Selector desplegable | Menús, selección de opciones | | Icon | Iconos vectoriales | Decoración, navegación | | IconButton | Botón solo con icono | Acciones rápidas | | InfoDetail | Detalle de información | Datos estructurados | | Label | Etiquetas de texto | Formularios, identificadores | | ProgressBar | Barra de progreso | Carga, completitud | | RadioButton | Selección única | Formularios, opciones excluyentes | | RatingStars | Calificación con estrellas | Reviews, valoraciones | | Skeleton | Placeholder de carga | Loading states | | Spinner | Indicador de carga circular | Loading, procesamiento | | Steper | Indicador de pasos | Wizards, procesos multi-paso | | Switch | Interruptor on/off | Configuraciones, toggles | | Tab | Pestañas de navegación | Vistas alternativas | | TextArea | Entrada de texto multilínea | Comentarios, descripciones | | TextField | Entrada de texto | Formularios, búsqueda | | TextLink | Enlace de texto | Navegación, enlaces | | TimePicker | Selector de hora | Agendamiento | | Tooltip | Ayuda contextual | Información adicional |
Molecules (14 Componentes Compuestos)
Grupos de átomos que forman componentes funcionales más complejos:
| Componente | Descripción | Componentes Utilizados | | --------------------- | ------------------------------------- | ------------------------- | | Accordion | Panel expandible/colapsable | Icon, Label, Card | | CardActions | Tarjeta con acciones | Card, Button, Icon | | CardMetrics | Tarjeta de métricas/estadísticas | Card, Label, Icon | | CardStatus | Tarjeta con indicador de estado | Card, Badge, Label | | CardTimeline | Tarjeta de línea de tiempo | Card, Timeline | | CardTitle | Tarjeta con título y subtítulo | Card, Label | | EdgeCases | Componentes para estados vacíos/error | Icon, Label, Button | | FileUploader | Cargador de archivos | Button, Icon, ProgressBar | | Modal | Ventana modal | Card, Button, IconButton | | ModalConfirmation | Modal de confirmación | Modal, Button | | Pagination | Paginación de contenido | Button, Label, Icon | | SnackBar | Notificación temporal | Alert, Icon | | TableStriped | Tabla con filas alternadas | Card, Label | | Timeline | Línea de tiempo | Icon, Label, Divisor |
Organisms (5 Componentes Complejos)
Secciones complejas de UI que combinan múltiples moléculas:
| Componente | Descripción | Casos de Uso | | -------------------------- | ---------------------------------- | ------------------------ | | BottomNavigation | Navegación inferior móvil | Apps móviles, navegación | | CardStatusDetails | Tarjeta con detalles de estado | Dashboard, seguimiento | | CardTitleDetails | Tarjeta con título y detalles | Información de entidades | | Header | Encabezado de aplicación | Navegación, branding | | ModalSearchMaintenance | Modal de búsqueda de mantenimiento | Búsqueda especializada |
Templates (1 Plantilla)
Estructuras de página completa:
| Template | Descripción | Casos de Uso | | ----------------- | ----------------------------------- | ------------------------- | | LoginTemplate | Plantilla completa de inicio sesión | Autenticación, onboarding |
Sistema de Iconografía
El módulo incluye 60+ iconos vectoriales SVG personalizados y optimizados:
Categorías de Iconos
Navegación y Acciones (16):
- IconArrowLeft, IconArrowRight, IconUp, IconDown
- IconHome, IconMenu, IconClose, IconBack
- IconAdd, IconEdit, IconTrash, IconSearch
- IconFilter, IconShare, IconSend, IconUpload, IconDownload
Documentos y Archivos (8):
- IconDocument, IconDocumentAdd, IconDocumentNone
- IconFolder, IconFolderAdd, IconImages, IconPhoto
- IconDocumetPersonal
Vehículos y Mantenimiento (6):
- IconCar, IconCarMaintenance, IconMaintenance
- IconTools, IconFix, IconPackage
Comunicación (5):
- IconChat, IconBell, IconPhone, IconMail, IconInfo
Usuario y Seguridad (7):
- IconUsers, IconSecurity, IconSecurityLock
- IconLogout, IconConfiguration, IconEye, IconEyeOff
Estado y Feedback (8):
- IconCheck, IconLike, IconStar, IconStarFilled, IconStarHalfFilled
- IconProblem, IconAccident, IconSlash
Tiempo y Calendario (5):
- IconCalendar, IconCalendarContact, IconClock
- IconClockHistory, IconPoint
Otros (5):
- IconHelp, IconMoney, IconReport, IconPin, IconLess
Características de los Iconos
- Formato: SVG vectorial escalable
- Tamaños: Configurables (16px, 20px, 24px, 32px, 48px)
- Colores: Adaptables a tema y contexto
- Optimización: SVG optimizados para rendimiento
Sistema de Tematización
Marcas Soportadas
TM Theme soporta 3 marcas con temas personalizados:
| Marca | Descripción | Color Principal | | ---------- | -------------------------- | --------------- | | Bitcar | Marca principal de gestión | Personalizado | | TIP | TipMéxico brand | Personalizado | | OFS | OFS brand | Personalizado |
Cada marca tiene su propia paleta de colores, tipografía y tokens de diseño.
Design Tokens
1. Sistema de Colores
Colores Globales (compartidos entre marcas):
{
black: '#000000',
white: '#FFFFFF',
transparent: 'transparent',
// Estados
green: '#10B981', // Success
greenLight: '#D1FAE5',
red: '#EF4444', // Error
redLight: '#FEE2E2',
blue: '#3B82F6', // Info
blueLight: '#DBEAFE',
yellow: '#F59E0B', // Warning
yellowLight: '#FEF3C7',
// Escala de grises
gray: {
1: '#F9FAFB',
2: '#F3F4F6',
3: '#E5E7EB',
4: '#D1D5DB',
5: '#9CA3AF',
6: '#6B7280',
7: '#4B5563',
8: '#1F2937',
},
// Social
greenWhatsapp: '#25D366',
}Colores de Marca (específicos por marca):
{
primary: string, // Color principal de marca
accents: {
1: string, // Acento primario
2: string, // Acento secundario
3: string, // Acento terciario
4: string, // Acento cuaternario
}
}2. Sistema Tipográfico
Familias de Fuentes:
- Poppins - Títulos y headings
- Roboto - Texto de párrafo y UI
Escalas Tipográficas:
Heading (Títulos):
{
h1: 32, // Display
h2: 28, // Page title
h3: 24, // Section title
h4: 20, // Card title
h5: 18, // Small title
h6: 16, // Subtitle
}Paragraph (Párrafos):
{
xlarge: 18, // Destacado
large: 16, // Cuerpo principal
regular: 14, // Cuerpo estándar
small: 12, // Texto secundario
xsmall: 10, // Captions, notas
}Button (Botones):
{
large: 16,
regular: 14,
small: 12,
}3. Sistema de Espaciado
Escala de espaciado consistente basada en múltiplos de 4px:
{
none: 0, // 0px
xxs: 4, // 4px
xs: 8, // 8px
sm: 12, // 12px
md: 16, // 16px (base)
lg: 20, // 20px
xl: 24, // 24px
'2xl': 32, // 32px
'3xl': 40, // 40px
'4xl': 48, // 48px
'5xl': 64, // 64px
'6xl': 80, // 80px
}4. Sistema de Bordes (Radius)
Escala de redondeo de bordes:
{
none: 0, // Cuadrado
xxs: 2, // Sutil
xs: 4, // Pequeño
sm: 6, // Mediano-pequeño
md: 8, // Mediano (estándar)
lg: 12, // Grande
xl: 16, // Extra grande
'2xl': 20, // Muy grande
'3xl': 24, // Pill
'4xl': 32, // Circular
'5xl': 40, // Circular grande
'6xl': 9999, // Completamente circular
}Hooks Personalizados
TM Theme proporciona 4 hooks especializados para trabajar con el sistema de diseño:
1. useTheme
Propósito: Acceder al tema actual y sus funciones
Retorna:
{
theme: ITheme, // Tema completo actual
brand: Brand, // Marca actual ('bitcar' | 'tip' | 'ofs')
mode: Mode, // Modo ('light' | 'dark')
setBrand: (brand) => void, // Cambiar marca
setMode: (mode) => void, // Cambiar modo
}Ejemplo de Uso:
import { useTheme } from '@tipmexico/tm_theme';
function MyComponent() {
const { theme, brand, setBrand } = useTheme();
return (
<View style={{ backgroundColor: theme.colors.primary }}>
<Text>Marca actual: {brand}</Text>
<Button onPress={() => setBrand('tip')}>
Cambiar a TIP
</Button>
</View>
);
}2. useColor
Propósito: Obtener colores del tema de forma type-safe
Parámetros:
color: ColorType- Tipo de color a obtener
Retorna: string - Valor hexadecimal del color
Colores Disponibles:
- Básicos:
'primary','black','white','transparent' - Estados:
'green','greenLight','red','redLight','blue','blueLight','yellow','yellowLight' - Grises:
'gray1'a'gray8' - Acentos:
'accents1'a'accents4'
Ejemplo de Uso:
import { useColor } from '@tipmexico/tm_theme';
function MyComponent() {
const primaryColor = useColor('primary');
const errorColor = useColor('red');
const grayColor = useColor('gray5');
return (
<View>
<Text style={{ color: primaryColor }}>Primary Text</Text>
<Text style={{ color: errorColor }}>Error Message</Text>
<View style={{ backgroundColor: grayColor }} />
</View>
);
}3. useTypography
Propósito: Obtener tamaños de fuente del tema
Parámetros:
type: TypographyType- Tipo de tipografía
Retorna: number - Tamaño de fuente en puntos
Tipos Disponibles:
- Heading:
'h1','h2','h3','h4','h5','h6' - Paragraph:
'xlarge','large','regular','small','xsmall' - Button:
'large','regular','small'(con prefijo button-)
Ejemplo de Uso:
import { useTypography } from '@tipmexico/tm_theme';
function MyComponent() {
const h1Size = useTypography('h1');
const bodySize = useTypography('regular');
const captionSize = useTypography('small');
return (
<View>
<Text style={{ fontSize: h1Size }}>Título</Text>
<Text style={{ fontSize: bodySize }}>Contenido</Text>
<Text style={{ fontSize: captionSize }}>Caption</Text>
</View>
);
}4. useSearch
Propósito: Hook de búsqueda y filtrado reutilizable
Características:
- Búsqueda en tiempo real
- Debouncing integrado
- Filtrado flexible
- Type-safe
Ejemplo de Uso:
import { useSearch } from '@tipmexico/tm_theme';
function SearchableList({ items }) {
const { searchTerm, setSearchTerm, filteredResults } = useSearch(
items,
(item, term) => item.name.toLowerCase().includes(term.toLowerCase())
);
return (
<View>
<TextField
value={searchTerm}
onChangeText={setSearchTerm}
placeholder="Buscar..."
/>
{filteredResults.map(item => (
<Card key={item.id}>{item.name}</Card>
))}
</View>
);
}5. useAppFonts
Propósito: Cargar fuentes específicas de la marca
Parámetros:
brand: Brand- Marca para cargar fuentes
Retorna: [boolean] - Array con estado de carga
Uso Interno: Este hook es usado automáticamente por el ThemeProvider, no necesitas usarlo directamente.
Providers
1. ThemeProvider
Propósito: Provider principal que envuelve la aplicación para habilitar tematización
Props:
{
initialBrand?: 'bitcar' | 'tip' | 'ofs', // Default: 'bitcar'
initialMode?: 'light' | 'dark', // Default: 'light'
children: ReactNode
}Ejemplo de Uso:
import { ThemeProvider } from '@tipmexico/tm_theme';
export default function App() {
return (
<ThemeProvider initialBrand="tip" initialMode="light">
<YourApplication />
</ThemeProvider>
);
}2. LoaderProvider
Propósito: Gestión global de estados de carga
Funcionalidades:
- Mostrar/ocultar loader global
- Loading states centralizados
- Overlay de carga
Ejemplo de Uso:
import { LoaderProvider, useLoader } from '@tipmexico/tm_theme';
// En App.tsx
function App() {
return (
<ThemeProvider>
<LoaderProvider>
<YourApp />
</LoaderProvider>
</ThemeProvider>
);
}
// En componente
function MyComponent() {
const { showLoader, hideLoader } = useLoader();
const handleAction = async () => {
showLoader();
await someAsyncAction();
hideLoader();
};
}3. ModalProvider
Propósito: Gestión global de modales
Funcionalidades:
- Abrir/cerrar modales desde cualquier parte
- Stack de modales
- Animaciones de entrada/salida
Ejemplo de Uso:
import { ModalProvider, useModal } from '@tipmexico/tm_theme';
// En App.tsx
function App() {
return (
<ThemeProvider>
<ModalProvider>
<YourApp />
</ModalProvider>
</ThemeProvider>
);
}
// En componente
function MyComponent() {
const { openModal, closeModal } = useModal();
const handleOpenModal = () => {
openModal({
title: 'Confirmación',
content: <Text>¿Estás seguro?</Text>,
onConfirm: () => console.log('Confirmed'),
});
};
}Instalación y Configuración
Instalación
Para Proyectos Expo Managed
# Con yarn
yarn add @tipmexico/tm_theme
# Para iOS (después de instalar)
npx pod-installDependencias Peer
Asegúrate de tener instaladas las siguientes dependencias:
# Dependencias requeridas
expo install react-native-svg
expo install react-native-gesture-handler
expo install expo-fontConfiguración Básica
1. Envolver la Aplicación con ThemeProvider
// App.tsx
import React from 'react';
import { ThemeProvider } from '@tipmexico/tm_theme';
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
return (
<ThemeProvider initialBrand="bitcar" initialMode="light">
<NavigationContainer>
<YourAppNavigation />
</NavigationContainer>
</ThemeProvider>
);
}2. Configuración con Múltiples Providers
// App.tsx
import React from 'react';
import {
ThemeProvider,
LoaderProvider,
ModalProvider
} from '@tipmexico/tm_theme';
export default function App() {
return (
<ThemeProvider initialBrand="tip">
<LoaderProvider>
<ModalProvider>
<NavigationContainer>
<YourAppNavigation />
</NavigationContainer>
</ModalProvider>
</LoaderProvider>
</ThemeProvider>
);
}3. Configuración TypeScript
Asegúrate de que tu tsconfig.json incluya:
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"jsx": "react-native",
"moduleResolution": "node"
}
}4. Configuración de SVG (React Native)
Si usas React Native sin Expo, configura el transformer de SVG:
metro.config.js:
const { getDefaultConfig } = require('expo/metro-config');
module.exports = (() => {
const config = getDefaultConfig(__dirname);
const { transformer, resolver } = config;
config.transformer = {
...transformer,
babelTransformerPath: require.resolve('react-native-svg-transformer'),
};
config.resolver = {
...resolver,
assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...resolver.sourceExts, 'svg'],
};
return config;
})();Verificación de Instalación
Para verificar que todo funciona correctamente:
// TestComponent.tsx
import React from 'react';
import { View } from 'react-native';
import {
Button,
TextField,
Card,
useTheme,
useColor
} from '@tipmexico/tm_theme';
export function TestComponent() {
const { theme, brand } = useTheme();
const primaryColor = useColor('primary');
return (
<View style={{ flex: 1, padding: 16 }}>
<Card>
<TextField
label="Test Input"
placeholder="Escribe algo..."
/>
<Button
title="Test Button"
onPress={() => console.log('Funciona!')}
/>
</Card>
</View>
);
}Ejemplos de Uso
Ejemplo 1: Formulario de Login
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import {
LoginTemplate,
TextField,
Button,
Alert,
useTheme,
useColor,
} from '@tipmexico/tm_theme';
export function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const { theme } = useTheme();
const primaryColor = useColor('primary');
const handleLogin = async () => {
if (!email || !password) {
setError('Por favor completa todos los campos');
return;
}
// Lógica de login
};
return (
<LoginTemplate logo={require('./assets/logo.png')}>
<View style={styles.container}>
{error && (
<Alert
type="error"
message={error}
onClose={() => setError('')}
/>
)}
<TextField
label="Email"
placeholder="[email protected]"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<TextField
label="Contraseña"
placeholder="••••••••"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button
title="Iniciar Sesión"
onPress={handleLogin}
fullWidth
style={{ marginTop: theme.spacing.lg }}
/>
</View>
</LoginTemplate>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
gap: 16,
},
});Ejemplo 2: Lista con Búsqueda y Filtros
import React from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import {
TextField,
Card,
Badge,
IconButton,
Skeleton,
useSearch,
useTheme,
IconSearch,
IconFilter,
} from '@tipmexico/tm_theme';
interface Vehicle {
id: string;
name: string;
plate: string;
status: 'active' | 'maintenance' | 'inactive';
}
export function VehicleListScreen({ vehicles, loading }: Props) {
const { theme } = useTheme();
const { searchTerm, setSearchTerm, filteredResults } = useSearch(
vehicles,
(vehicle, term) =>
vehicle.name.toLowerCase().includes(term.toLowerCase()) ||
vehicle.plate.toLowerCase().includes(term.toLowerCase())
);
if (loading) {
return (
<View style={styles.container}>
<Skeleton count={5} height={80} />
</View>
);
}
return (
<View style={styles.container}>
<View style={styles.searchBar}>
<TextField
placeholder="Buscar vehículo..."
value={searchTerm}
onChangeText={setSearchTerm}
leftIcon={<IconSearch size={20} />}
style={{ flex: 1 }}
/>
<IconButton
icon={<IconFilter size={24} />}
onPress={() => {/* Abrir filtros */}}
/>
</View>
<FlatList
data={filteredResults}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Card style={styles.card}>
<View style={styles.cardContent}>
<View>
<Text style={styles.vehicleName}>{item.name}</Text>
<Text style={styles.plate}>{item.plate}</Text>
</View>
<Badge
label={item.status}
variant={
item.status === 'active'
? 'success'
: item.status === 'maintenance'
? 'warning'
: 'error'
}
/>
</View>
</Card>
)}
contentContainerStyle={{ gap: theme.spacing.md }}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
searchBar: {
flexDirection: 'row',
gap: 8,
marginBottom: 16,
},
card: {
padding: 16,
},
cardContent: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
vehicleName: {
fontSize: 16,
fontWeight: '600',
},
plate: {
fontSize: 14,
color: '#6B7280',
},
});Ejemplo 3: Dashboard con Métricas
import React from 'react';
import { View, ScrollView, StyleSheet } from 'react-native';
import {
Header,
CardMetrics,
CardTimeline,
CardActions,
useTheme,
useColor,
IconCar,
IconMaintenance,
IconMoney,
IconUsers,
} from '@tipmexico/tm_theme';
export function DashboardScreen() {
const { theme } = useTheme();
const primaryColor = useColor('primary');
const metrics = [
{
id: '1',
title: 'Vehículos',
value: '248',
icon: <IconCar size={32} color={primaryColor} />,
trend: '+12%'
},
{
id: '2',
title: 'Mantenimientos',
value: '43',
icon: <IconMaintenance size={32} color={primaryColor} />,
trend: '+5%'
},
{
id: '3',
title: 'Gasto Mensual',
value: '$45.2K',
icon: <IconMoney size={32} color={primaryColor} />,
trend: '-3%'
},
{
id: '4',
title: 'Operadores',
value: '156',
icon: <IconUsers size={32} color={primaryColor} />,
trend: '+8%'
},
];
const recentActivities = [
{
id: '1',
title: 'Mantenimiento programado',
subtitle: 'Vehículo ABC-123',
time: 'Hace 2 horas'
},
{
id: '2',
title: 'Nueva multa registrada',
subtitle: 'Vehículo XYZ-789',
time: 'Hace 4 horas'
},
];
return (
<View style={styles.container}>
<Header
title="Dashboard"
subtitle="Resumen de tu flota"
showBack={false}
/>
<ScrollView
style={styles.content}
contentContainerStyle={{ gap: theme.spacing.lg }}
>
{/* Métricas */}
<View style={styles.metricsGrid}>
{metrics.map((metric) => (
<CardMetrics
key={metric.id}
title={metric.title}
value={metric.value}
icon={metric.icon}
trend={metric.trend}
style={styles.metricCard}
/>
))}
</View>
{/* Actividad Reciente */}
<CardTimeline
title="Actividad Reciente"
items={recentActivities}
/>
{/* Acciones Rápidas */}
<CardActions
title="Acciones Rápidas"
actions={[
{
label: 'Programar Mantenimiento',
onPress: () => {},
icon: <IconCalendar />,
},
{
label: 'Ver Reportes',
onPress: () => {},
icon: <IconReport />,
},
]}
/>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
flex: 1,
padding: 16,
},
metricsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
metricCard: {
flex: 1,
minWidth: '45%',
},
});Ejemplo 4: Modal de Confirmación
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import {
Modal,
ModalConfirmation,
Button,
Alert,
useModal,
useTheme,
} from '@tipmexico/tm_theme';
export function DeleteConfirmationExample() {
const { openModal, closeModal } = useModal();
const { theme } = useTheme();
const handleDelete = () => {
openModal(
<ModalConfirmation
title="¿Eliminar vehículo?"
message="Esta acción no se puede deshacer. El vehículo será eliminado permanentemente."
confirmLabel="Eliminar"
cancelLabel="Cancelar"
variant="danger"
onConfirm={async () => {
// Lógica de eliminación
await deleteVehicle();
closeModal();
showSuccessAlert();
}}
onCancel={closeModal}
/>
);
};
return (
<View style={styles.container}>
<Button
title="Eliminar Vehículo"
variant="danger"
onPress={handleDelete}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 16,
},
});Ejemplo 5: Formulario con Validación
import React, { useState } from 'react';
import { View, ScrollView, StyleSheet } from 'react-native';
import {
Card,
TextField,
TextArea,
Dropdown,
DatePicker,
TimePicker,
Switch,
Button,
Alert,
useTheme,
useColor,
} from '@tipmexico/tm_theme';
export function MaintenanceFormScreen() {
const { theme } = useTheme();
const [formData, setFormData] = useState({
vehicle: '',
serviceType: '',
provider: '',
date: new Date(),
time: '',
notes: '',
urgent: false,
});
const [errors, setErrors] = useState({});
const validateForm = () => {
const newErrors = {};
if (!formData.vehicle) newErrors.vehicle = 'Selecciona un vehículo';
if (!formData.serviceType) newErrors.serviceType = 'Selecciona un servicio';
if (!formData.provider) newErrors.provider = 'Selecciona un proveedor';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async () => {
if (!validateForm()) {
return;
}
try {
// Lógica de envío
await submitMaintenance(formData);
// Mostrar éxito
} catch (error) {
// Mostrar error
}
};
return (
<ScrollView style={styles.container}>
<Card>
<View style={styles.form}>
<Dropdown
label="Vehículo"
placeholder="Selecciona un vehículo"
value={formData.vehicle}
onValueChange={(value) =>
setFormData({ ...formData, vehicle: value })
}
options={[
{ label: 'ABC-123', value: '1' },
{ label: 'XYZ-789', value: '2' },
]}
error={errors.vehicle}
/>
<Dropdown
label="Tipo de Servicio"
placeholder="Selecciona un servicio"
value={formData.serviceType}
onValueChange={(value) =>
setFormData({ ...formData, serviceType: value })
}
options={[
{ label: 'Mantenimiento Preventivo', value: 'preventive' },
{ label: 'Reparación', value: 'repair' },
]}
error={errors.serviceType}
/>
<Dropdown
label="Proveedor"
placeholder="Selecciona un proveedor"
value={formData.provider}
onValueChange={(value) =>
setFormData({ ...formData, provider: value })
}
options={[
{ label: 'Taller ABC', value: '1' },
{ label: 'Servicio XYZ', value: '2' },
]}
error={errors.provider}
/>
<DatePicker
label="Fecha"
value={formData.date}
onChange={(date) =>
setFormData({ ...formData, date })
}
/>
<TimePicker
label="Hora"
value={formData.time}
onChange={(time) =>
setFormData({ ...formData, time })
}
/>
<TextArea
label="Notas"
placeholder="Detalles adicionales..."
value={formData.notes}
onChangeText={(notes) =>
setFormData({ ...formData, notes })
}
maxLength={500}
/>
<View style={styles.switchContainer}>
<Text>Urgente</Text>
<Switch
value={formData.urgent}
onValueChange={(urgent) =>
setFormData({ ...formData, urgent })
}
/>
</View>
<Button
title="Programar Mantenimiento"
onPress={handleSubmit}
fullWidth
style={{ marginTop: theme.spacing.lg }}
/>
</View>
</Card>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
form: {
gap: 16,
padding: 16,
},
switchContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 8,
},
});Ejemplo 6: Tabla con Paginación
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import {
TableStriped,
Pagination,
Badge,
IconButton,
useTheme,
IconEdit,
IconTrash,
} from '@tipmexico/tm_theme';
export function VehicleTableScreen({ data }: Props) {
const { theme } = useTheme();
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
const columns = [
{ key: 'plate', header: 'Placa', width: '15%' },
{ key: 'model', header: 'Modelo', width: '25%' },
{ key: 'year', header: 'Año', width: '10%' },
{
key: 'status',
header: 'Estado',
width: '15%',
render: (value) => (
<Badge
label={value}
variant={value === 'Activo' ? 'success' : 'error'}
/>
),
},
{
key: 'actions',
header: 'Acciones',
width: '15%',
render: (_, row) => (
<View style={styles.actions}>
<IconButton
icon={<IconEdit size={20} />}
onPress={() => handleEdit(row)}
/>
<IconButton
icon={<IconTrash size={20} />}
onPress={() => handleDelete(row)}
/>
</View>
),
},
];
const paginatedData = data.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
);
return (
<View style={styles.container}>
<TableStriped
columns={columns}
data={paginatedData}
/>
<Pagination
currentPage={currentPage}
totalPages={Math.ceil(data.length / itemsPerPage)}
onPageChange={setCurrentPage}
style={{ marginTop: theme.spacing.lg }}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
actions: {
flexDirection: 'row',
gap: 8,
},
});Storybook - Documentación Interactiva
TM Theme incluye Storybook con documentación completa de todos los componentes.
Ejecutar Storybook
# Iniciar Storybook en modo desarrollo
yarn storybook
# Abrir en navegador
# http://localhost:6006Construir Storybook para Producción
# Generar build estático
yarn build-storybook
# Resultado en ./storybook-staticCaracterísticas de Storybook
Coverage Completo:
- ✅ 27 Atoms documentados
- ✅ 14 Molecules documentados
- ✅ 5 Organisms documentados
- ✅ 1 Template documentado
- ✅ 2 Providers documentados
- ✅ Sistema de colores
- ✅ Tipografía
Funcionalidades:
- Playground Interactivo - Prueba props en tiempo real
- Controls - Modifica propiedades dinámicamente
- Docs - Documentación auto-generada
- Viewport - Prueba en diferentes tamaños
- Accessibility - Validación de accesibilidad
Ejemplo de Story:
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Atoms/Button',
component: Button,
parameters: {
docs: {
description: {
component: 'Botón versátil con múltiples variantes y tamaños',
},
},
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline', 'ghost'],
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
title: 'Primary Button',
variant: 'primary',
},
};
export const Secondary: Story = {
args: {
title: 'Secondary Button',
variant: 'secondary',
},
};Mejores Prácticas
1. Uso de Componentes
✅ Recomendado:
// Usa componentes del sistema siempre que sea posible
import { Button, TextField } from '@tipmexico/tm_theme';
// Usa hooks para acceder a theme tokens
const primaryColor = useColor('primary');
const h1Size = useTypography('h1');❌ Evitar:
// No uses valores hardcodeados
<Text style={{ color: '#FF5733', fontSize: 18 }}>Title</Text>
// Usa en su lugar:
const primaryColor = useColor('primary');
const h1Size = useTypography('h1');
<Text style={{ color: primaryColor, fontSize: h1Size }}>Title</Text>2. Espaciado Consistente
✅ Recomendado:
const { theme } = useTheme();
<View style={{
padding: theme.spacing.md,
gap: theme.spacing.sm,
marginBottom: theme.spacing.lg,
}} />❌ Evitar:
// Valores arbitrarios
<View style={{ padding: 13, gap: 7, marginBottom: 23 }} />3. Colores Semánticos
✅ Recomendado:
// Usa colores semánticos
const successColor = useColor('green');
const errorColor = useColor('red');
const warningColor = useColor('yellow');❌ Evitar:
// Colores hardcodeados sin significado
const color1 = '#10B981';
const color2 = '#EF4444';4. Tipografía Escalable
✅ Recomendado:
// Usa la escala tipográfica
const titleSize = useTypography('h2');
const bodySize = useTypography('regular');
const captionSize = useTypography('small');❌ Evitar:
// Tamaños arbitrarios
<Text style={{ fontSize: 23 }}>Title</Text>
<Text style={{ fontSize: 15 }}>Body</Text>5. Responsive Design
✅ Recomendado:
import { isWeb, isIOS, isAndroid } from '@tipmexico/tm_theme';
// Adapta según plataforma
const spacing = isWeb ? theme.spacing.xl : theme.spacing.md;6. Composición de Componentes
✅ Recomendado:
// Compone componentes para crear UIs complejas
<Card>
<CardTitle title="Vehículo" subtitle="ABC-123" />
<Divisor />
<CardMetrics value="1,234 km" label="Kilometraje" />
<CardActions
actions={[
{ label: 'Editar', onPress: handleEdit },
{ label: 'Eliminar', onPress: handleDelete },
]}
/>
</Card>7. Accesibilidad
✅ Recomendado:
// Siempre proporciona labels y hints
<TextField
label="Email"
placeholder="[email protected]"
accessibilityLabel="Campo de email"
accessibilityHint="Ingresa tu correo electrónico"
/>
<Button
title="Enviar"
accessibilityLabel="Enviar formulario"
accessibilityRole="button"
/>8. Manejo de Estados
✅ Recomendado:
// Muestra estados de carga y error
function MyComponent() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
if (loading) return <Spinner />;
if (error) return <Alert type="error" message={error} />;
return <Content />;
}9. Performance
✅ Recomendado:
// Usa React.memo para componentes puros
import React, { memo } from 'react';
export const VehicleCard = memo(({ vehicle }) => {
return <Card>{/* ... */}</Card>;
});
// Usa useMemo para cálculos costosos
const filteredVehicles = useMemo(
() => vehicles.filter(v => v.status === 'active'),
[vehicles]
);10. Testing
✅ Recomendado:
// Testea componentes con testing-library
import { render, fireEvent } from '@testing-library/react-native';
import { Button, ThemeProvider } from '@tipmexico/tm_theme';
test('button calls onPress when clicked', () => {
const onPress = jest.fn();
const { getByText } = render(
<ThemeProvider>
<Button title="Click me" onPress={onPress} />
</ThemeProvider>
);
fireEvent.press(getByText('Click me'));
expect(onPress).toHaveBeenCalled();
});Utilidades
1. Platform Helpers
import { isWeb, isIOS, isAndroid, isMobile } from '@tipmexico/tm_theme';
// Renderizado condicional por plataforma
{isWeb && <WebOnlyComponent />}
{isMobile && <MobileOnlyComponent />}
{isIOS && <IOSSpecificComponent />}2. Dimension Helpers
import {
screenWidth,
screenHeight,
wp, // width percentage
hp, // height percentage
} from '@tipmexico/tm_theme';
// Uso
const cardWidth = wp(90); // 90% del ancho de pantalla
const headerHeight = hp(10); // 10% del alto de pantalla3. Component Helpers
Utilidades para trabajar con componentes:
import { getTestID, getAccessibilityProps } from '@tipmexico/tm_theme';
// Auto-generar props de accesibilidad
<Button {...getAccessibilityProps('submit-button', 'button')} />Guía de Contribución
Agregar un Nuevo Componente
1. Crear estructura de archivos
src/components/atoms/my-component/
├── MyComponent.tsx # Componente
├── MyComponent.types.ts # TypeScript interfaces
├── MyComponent.styles.ts # Estilos (si aplica)
├── index.ts # Barrel export2. Implementar componente
// MyComponent.tsx
import React from 'react';
import { View, Text } from 'react-native';
import { useTheme, useColor } from '../../../hooks';
import { MyComponentProps } from './MyComponent.types';
export const MyComponent: React.FC<MyComponentProps> = ({
title,
variant = 'default',
...props
}) => {
const { theme } = useTheme();
const primaryColor = useColor('primary');
return (
<View style={{ padding: theme.spacing.md }}>
<Text style={{ color: primaryColor }}>{title}</Text>
</View>
);
};3. Definir tipos
// MyComponent.types.ts
export interface MyComponentProps {
title: string;
variant?: 'default' | 'primary' | 'secondary';
onPress?: () => void;
}4. Crear Story
// stories/components/atoms/MyComponent.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from '../../../components/atoms/my-component';
const meta: Meta<typeof MyComponent> = {
title: 'Atoms/MyComponent',
component: MyComponent,
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const Default: Story = {
args: {
title: 'My Component',
variant: 'default',
},
};5. Exportar
// src/components/atoms/index.ts
export * from './my-component';Standards de Código
TypeScript:
- Usar tipos explícitos
- Evitar
any - Definir interfaces para props
Naming:
- PascalCase para componentes
- camelCase para funciones y variables
- UPPER_CASE para constantes
Styling:
- Usar hooks de tema (
useTheme,useColor,useTypography) - No usar valores hardcodeados
- Preferir StyleSheet.create()
Testing:
- Unit tests con Jest
- Component tests con Testing Library
- Coverage mínimo: 80%
Arquitectura Técnica
Estructura de Directorios
src/
├── assets/ # Assets estáticos
│ ├── CarDefault.tsx
│ ├── ErrorConnection.tsx
│ ├── ErrorEmpty.tsx
│ └── ErrorNotFound.tsx
│
├── components/ # Componentes UI
│ ├── atoms/ # 27 componentes básicos
│ ├── molecules/ # 14 componentes compuestos
│ ├── organisms/ # 5 componentes complejos
│ └── templates/ # 1 plantilla
│
├── constants/ # Constantes globales
│ └── globals.constants.ts
│
├── hooks/ # Custom hooks
│ ├── useAppFonts.ts
│ ├── useColor.ts
│ ├── useTypography.ts
│ └── useSearch.ts
│
├── icons/ # 60+ iconos SVG
│ ├── IconCar.tsx
│ ├── IconHome.tsx
│ └── ...
│
├── interfaces/ # TypeScript interfaces
│ ├── colors.interface.ts
│ ├── typography.interface.ts
│ ├── theme.interface.ts
│ └── icons.interface.ts
│
├── providers/ # Context providers
│ ├── loader/
│ └── modal/
│
├── theme/ # Sistema de temas
│ ├── brands/
│ │ ├── bitcar/
│ │ │ └── light.ts
│ │ ├── tip/
│ │ │ └── light.ts
│ │ └── ofs/
│ │ └── light.ts
│ └── ThemeContext.tsx
│
├── types/ # TypeScript types
│ ├── colors.type.ts
│ ├── components.type.ts
│ ├── icons.type.ts
│ ├── scales.type.ts
│ ├── sizes.type.ts
│ ├── theme.type.ts
│ └── typography.type.ts
│
├── utils/ # Utilidades
│ ├── components.ts
│ ├── dimensions.ts
│ └── platform.ts
│
└── index.ts # Entry pointTecnologías y Dependencias
Core Dependencies
| Tecnología | Versión | Uso | | ---------------- | ------- | --------------- | | React | 19.0.0 | Framework UI | | React Native | 0.79.5 | Framework móvil | | TypeScript | 5.x | Type safety | | Expo | 53.0.19 | Toolchain |
UI Dependencies
| Dependencia | Versión | Uso | | --------------------------- | -------- | ------------------------ | | React Native SVG | 15.11.2 | Iconos vectoriales | | Expo Linear Gradient | 14.1.5 | Gradientes | | React Native Calendars | 1.1312.0 | Componente de calendario | | React Native Pager View | 6.9.1 | Swipeable views | | RN UI DatePicker | 3.1.2 | Selector de fecha |
Fuentes
| Fuente | Uso | | ----------- | ----------------- | | Poppins | Headings, títulos | | Roboto | Párrafos, UI text |
Dev Dependencies
| Herramienta | Versión | Uso | | ------------- | ------- | --------------- | | Storybook | 9.0.13 | Documentación | | Vitest | 3.0.9 | Testing | | ESLint | 9.24.0 | Linting | | Prettier | 3.5.3 | Formateo código | | Husky | 9.1.7 | Git hooks |
Build y Deploy
Build para Producción
# Limpiar build anterior
yarn clean
# Compilar TypeScript
yarn build
# Resultado en ./build/Publicar a NPM
# Incrementar versión
npm version patch|minor|major
# Publicar
npm publish --access publicConfiguración de Package
El módulo se publica como scoped package bajo @tipmexico:
{
"name": "@tipmexico/tm_theme",
"version": "1.2.34",
"main": "build/index.js",
"types": "build/index.d.ts"
}Troubleshooting
Problema: Fuentes no se cargan
Solución:
// Asegúrate de que ThemeProvider esté correctamente implementado
import { ThemeProvider } from '@tipmexico/tm_theme';
export default function App() {
return (
<ThemeProvider initialBrand="bitcar">
<YourApp />
</ThemeProvider>
);
}Problema: Storybook no inicia
Solución:
# Limpiar caché
rm -rf node_modules/.cache
# Reinstalar
yarn install
# Reintentar
yarn storybook¿Cómo contribuyo al proyecto?
- Fork del repositorio
- Crear feature branch
- Implementar cambios con tests
- Crear Pull Request
- Code review por el equipo
Soporte y Contacto
Repositorio
Issue Tracker
Documentación Adicional
- Storybook: http://localhost:6006
- NPM Package: @tipmexico/tm_theme
- Confluence: Documentación interna de TipMéxico
Glosario
Atomic Design: Metodología de diseño que organiza componentes en 5 niveles: átomos, moléculas, organismos, templates y páginas.
Design System: Colección de componentes reutilizables y guías de estilo para crear interfaces consistentes.
Design Tokens: Valores atómicos de diseño (colores, espaciado, tipografía) que definen el lenguaje visual.
Theme: Conjunto de tokens de diseño que define la apariencia visual de una aplicación.
Brand: Marca o submarca con su propia identidad visual y tema personalizado.
Component: Pieza reutilizable de UI con comportamiento y estilo encapsulado.
Hook: Función de React que permite usar características como estado y contexto.
Storybook: Herramienta para desarrollar y documentar componentes UI de forma aislada.
Type Safety: Verificación de tipos en tiempo de compilación con TypeScript.
Peer Dependency: Dependencia que debe ser instalada por el consumidor del paquete.
Changelog
v1.2.34 (Actual - Noviembre 2025)
Agregado:
- 27 componentes Atoms completos
- 14 componentes Molecules
- 5 componentes Organisms
- Sistema de tematización multi-marca
- 60+ iconos SVG optimizados
- Storybook con documentación completa
- 4 hooks personalizados
- Design tokens completos
- Soporte TypeScript completo
Mejorado:
- Performance de renderizado
- Type safety
- Documentación
Corregido:
- Bugs de espaciado
- Issues de accesibilidad
- Problemas de tipado
v1.2.x
- Implementación de componentes base
- Setup de arquitectura Atomic Design
- Integración con Expo 53
- Soporte multi-plataforma
v1.1.x
- Versión inicial
- Componentes básicos
- Sistema de temas básico
Actualización
Última actualización: Noviembre 2025
Versión de documentación: 1.0
Mantenedor: Cristian Bedoya
Estado: ✅ Producción - Activamente mantenido
