MF-01 · galería · ago 2026

Mobile · sistema visual · componentes

La app no tiene otro sistema: tiene el de la web, primitivo por primitivo.

Dieciocho pantallas de 390 × 844 con cada primitivo de @workspace/mobile-ui en todos sus estados, leído contra lo que apps/client renderiza hoy: tokens semánticos de shadcn con --primary #3765c0, la capa aditiva mogos-*, Geist / Poppins / Lemon Milk Pro con el mismo reparto que la web, radio 10, sombras a marino donde la web ya las tiene. La galería RN /(dev)/gallery replica estas filas una a una: Fabrizio abre el teléfono junto al navegador y no distingue el sistema.

Verdad visual
apps/client hoy · Contexto B
Tipografía
Geist · Poppins · Lemon Milk
Radio
6 · 8 · 10 · 14 · full
Primario
#3765c0 · botón #213e83
Tema
light-only v1 · tokens por esquema
Pantallas
18 · una por grupo de estados

01Tokens: la ley que la app copia

globals.css · color-variables.css · mogos-brand.css · apps/client/app/globals.css

Antes de un solo componente, la fila cero de la galería muestra los valores: el sistema semántico de shadcn tal como lo pinta el client (--primary #3765c0, no el gris neutro del package), la capa de marca mogos-* que ya viste el 17 % de las pantallas, las tres familias con su reparto real, la escala de radios derivada de --radius 10 px y las dos sombras que conviven en la web. Nada de esto es del estudio: es lo que hay.

9:41●●●
Galería · 00
Tokens
Semánticos + capa mogos · Geist / Poppins / Lemon Milk · radios · sombras
Semánticos globals.css:26-52 · client:8-11
primary#3765c0
brand-700#213e83
brand-600#274da2
destructive#f04438
secondary#f9fafb
mutedoklch .97
input#d0d5dd
gray-900#101828
Capa mogos mogos-brand.css:20-64
marino#021B54
celeste#AAE4F3
lima#DFFD8F
rojo#C9313C
ink-soft#3A4A72
hueso#F7F9FC
line#D5DEEC
celeste-soft#E4F5FB
Tipografía layout.tsx:72 · globals.css:422-424
Envío MGS-0142Lemon Milk · display
Poppins 600 · títulosfont-family-sans
Geist 400 · cuerpo de páginafont-sans · body
GEIST MONO 0123456789font-mono
Radios --radius .625rem · globals.css:151-154
sm 6
md 8
lg 10
xl 14
full
Sombras tailwind shadow-xs/md · mogos-menu · fab
xs · rgb 0 0 0/.05
md · negro
menú · marino .10
fab · #213e83 .45
  1. --primary es del client, no del package

    packages/ui deja --primary en gris neutro; apps/client/app/globals.css:8-11 lo repinta a #3765c0 junto con --ring. Todo lo que lea bg-primary (switch, radio, tooltip, día seleccionado) es azul producto. mobile-ui ya tiene brand.500 #3765c0: se conserva.

  2. Tres azules en el mismo formulario

    El botón primario es brand-700 #213e83 (button.tsx:14), el checkbox marcado es brand-600 #274da2 (checkbox.tsx:17) y el foco/switch es #3765c0. La app los replica los tres; no se «unifican».

  3. El reparto tipográfico real

    Geist en el body (next/font, layout.tsx:72-90); Poppins en cada componente que lleva font-family-sans (257 usos); Lemon Milk Pro en font-family-display: números y títulos de cards, títulos de diálogo y de página (97 usos). Ni «Geist en todo» ni «Lemon Milk en todo» como hace mobile-client hoy.

  4. Radios: un paso más abierto que mobile-ui

    La web deriva de --radius 10 px: sm 6 · md 8 · lg 10 · xl 14. mobile-ui va un paso más cerrado (lg 8, xl 10): se corrige la escala entera en theme/radii.ts.

  5. Dos sombras conviven

    Los primitivos shadcn llevan shadow-xs de Tailwind (negro al 5 %, un hairline); las superficies elevadas de mogos ya van tintadas: menú rgba(2,27,84,.10), FAB rgba(33,62,131,.45), barras rgba(15,23,42,.04). En RN, un solo shadowColor por preset; ver decisión 4.

  6. Light-only, tokens por esquema

    userInterfaceStyle: light (DECISIONS #3/#21). Los tokens se estructuran light / dark desde el inicio; dark queda vacío hasta que la web lo active.

02Texto y botón

button.tsx:7-37 · presets de Text

Siete variantes en la web (default, destructive, outline, secondary, tertiary, ghost, link), no cinco: el brief contaba las de shadcn y la casa añadió tertiary y cambió secondary a blanco con borde. Cuatro tamaños (40 / 32 / 48 / icono 36), el disabled del primario es gris explícito, y el foco es un anillo de 3 px al 50 % del ring. Antes, los siete presets de Text que espejan el reparto tipográfico real.

9:41●●●
Galería · 01
Text
7 presets que espejan el reparto de la web · en contexto
Presets clase web que espejan
Envío MGS-0142display · Lemon Milk 24
$1,240.50num · Lemon Milk 30/500 tnum
Título de secciónheading · Poppins 18/600
Cuerpo de página en Geist, 16/24.body
Etiqueta y filas · 14/500sm · Poppins
Descripción secundariamuted · #667085
GEIST MONO 0123456789mono · 12
En contexto card de flete · freight-card.tsx
CN-MAR-07
Guangzhou → La Guaira · salió el 12 ago
Tarifa por m³$180

Llega al puerto en 35–45 días. Te avisamos cuando pase por aduana.

9:41●●●
Galería · 02
Button
7 variantes × 4 tamaños · loading / disabled / focus / press · con icono
Variantes button.tsx:13-24
Crear envíoCancelar envíoVer detalleSecundarioTerciarioGhostVer todos
Tamaños default 40 · sm 32 · lg 48 · icon 36
PequeñoNormalGrande
Estados disabled · loading · focus-visible · press
DeshabilitadoGuardando…Con focoPresionadoEliminarDeshabilitado
Con icono · ancho completo [&_svg]:size-4 · gap 8
Crear envíoCompartir tracking
  1. Siete variantes, no cinco

    A shadcn la casa le añadió tertiary (bg-gray-100) y cambió secondary a blanco con borde input (button.tsx:18-20). Las siete entran en mobile-ui/Button; link se vuelve un Pressable con texto subrayado a 4 px.

  2. Primario = brand-700, disabled gris explícito

    bg-brand-700 … disabled:bg-gray-100 disabled:text-gray-500: el disabled del primario no es opacidad, es gris. Los demás usan opacity-50. mobile-ui ya usa brand[700] en el botón: DECISIONS #3 lo daba por error; leído el cva, es correcto — lo que corrige MF-01 es el foco/ring (#3765c0) y la tipografía.

  3. Texto Poppins 14/600, altura 40, radio 8

    font-family-sans text-sm font-semibold rounded-md h-10 px-4: el botón es Poppins semibold sobre radio md 8, no lg 10. Icono 16 con gap 8; con icono el padding se queda en 16.

  4. Foco y press

    Foco: border-ring ring-ring/50 ring-[3px] → borde #3765c0 + anillo 3 px al 50 %. Press: la web no tiene active: en Button; el shell móvil sí usa active:scale-95 con 150 ms. En RN, press = scale .95, 160 ms para todo botón (sección 10) — la única libertad que se toma, y va en motion, no en el sistema.

  5. Loading

    La web no tiene prop loading: cada pantalla mete un Loader2 animate-spin. mobile-ui/Button lo formaliza: loading reemplaza el icono por el spinner de 16 y bloquea el press sin cambiar el color.

  6. Presets de Text

    display (Lemon Milk 24/1.25), num (Lemon Milk 30/500 tabular), heading (Poppins 18/600), body (Geist 16/24), sm (Poppins 14/500), muted (Poppins 14 #667085), mono (Geist Mono 12). Cada preset apunta a la clase web que espeja; PARITY.md lo lista.

03Campos: Input, Label, Field, Textarea, sufijo y OTP

input.tsx:10 · label.tsx:16 · field.tsx · numeric-input-with-suffix.tsx · otp-input-group.tsx:87

El input mide 40 px, radio 8, borde #d0d5dd, texto Poppins 16 (evita el zoom de iOS), foco con anillo de 2 px (el input es la excepción: botón y textarea usan 3). El error pinta el borde destructivo y el label. El OTP que usa la app es el de auth: seis cajas separadas de 44 × 48 con tinta marino y foco celeste, no el agrupado de shadcn.

9:41●●●
Galería · 03
Campos
Input · Label · Field · con icono · NumericInputWithSuffix
Input · rest / focus / error / disabled input.tsx:10-15
Correo
tu@correo.com
Teléfono
+58 412 555 01|
Cédula
V-1234
Ingresa una cédula válida: V-12345678.
Código de cliente
MG-00421
Lo asigna Mogos al activar la cuenta.
Con icono · sufijo numérico numeric-input-with-suffix.tsx:29-44
Buscar por tracking o código
12.5
kg
0.00
USD
9:41●●●
Galería · 04
Textarea y OTP
Textarea · InputOTP de auth (rest / lleno / error / deshabilitado) · InputOTP shadcn
Textarea textarea.tsx:10 · min-h 64 · shadow-xs
Instrucciones de entrega
Dejar en recepción con
Opcional · máximo 300 caracteres.
InputOTP de auth otp-input-group.tsx:87 · 44×48 · r11 · marino
482
482910
482910
48
Reenviar código en 0:42
InputOTP shadcn input-otp.tsx:53 · 36×36 agrupado · codigos secundarios
735
  1. Anatomía del input

    h-10 rounded-md border-input bg-base-white px-3 text-base font-family-sans: 40 px, radio 8, borde #d0d5dd, Poppins 16. Placeholder text-muted-foreground #667085. En RN, TextInput con fontSize 16 y lineHeight 24: en iOS evita el zoom automático y en Android alinea la línea base.

  2. Foco de 2 px, no de 3

    focus-visible:ring-2 en Input y Select; ring-[3px] en Button, Textarea, Checkbox, Radio, Switch. mobile-ui/Input pinta borde #3765c0 + anillo rgba(55,101,192,.5) de 2 px con onFocus; sin outline nativo.

  3. Error = borde + label + mensaje

    aria-invalid:border-destructive en el control y data-[invalid=true]:text-destructive en el Field (field.tsx:58): el label también se pinta. FieldError 14 px destructivo; FieldDescription 14 px muted. Gap interno 6 px (gap-1.5).

  4. Sufijo numérico

    Un solo borde envolvente con focus-within, el input pierde su borde, el sufijo va sobre bg-muted con border-l y Poppins 14/500. Se usa en peso y dimensiones del envío y en la calculadora.

  5. Dos OTP, uno por uso

    /verify-otp y /verify-whatsapp-otp renderizan otp-input-group.tsx: seis cajas 44 × 48, radio 11, texto marino 20/500 tabular, foco celeste-deep con halo rgba(95,185,216,.18) y -translate-y-px, inválido rojo con halo rgba(201,49,60,.14). El InputOTP agrupado de shadcn (36 × 36) queda como segundo primitivo para códigos fuera de auth. La app implementa los dos; MF-02 usa el primero.

  6. Teclado

    Sin KeyboardHintBar (no aplica). El patrón KeyboardAvoiding de PageFrame desplaza el campo activo 16 px por encima del teclado; en iOS el inputAccessoryView nativo trae «Listo».

04Selección: Select, MultiSelect, Checkbox, Radio, Switch

select.tsx:46,116 · multi-select.tsx · checkbox.tsx:17 · radio-group.tsx:30 · switch.tsx:16-24

El Select comparte anatomía con el input (40, radio 8, borde input) y abre un popover blanco con sombra md; en móvil ese popover se convierte en bottom sheet con las mismas filas. Checkbox 16 px con radio 6 y relleno brand-600; radio de 16 con punto de 8 en primary; switch 32 × 18, pista input → primary.

9:41●●●
Galería · 05
Select
Select rest / disabled / open · lista · MultiSelect
Select · rest / disabled / open + lista select.tsx:46 · item :116
Tipo de flete
Selecciona
Aéreo
Marítimo
Tipo de flete
Marítimo
Aéreo
Terrestre · próximamente
MultiSelect multi-select.tsx:107-175
Estatus
En tránsito Entregado
En tránsito
Entregado
Retenido
9:41●●●
Galería · 06
Checkbox, Radio, Switch
Cada control en rest / marcado / foco / error / deshabilitado · fila de opción
Checkbox checkbox.tsx:17 · 16 · r6 · brand-600
Sin marcarMarcadoCon focoError · debes aceptar los términosDeshabilitado
RadioGroup radio-group.tsx:30 · 16 · punto 8 primary · gap 12
Recoger en sedeDelivery a mi direcciónCon focoDeshabilitado
Switch switch.tsx:16-24 · 32×18.4 · thumb 16
ApagadoNotificarme por WhatsAppCon focoDeshabilitado
  1. Select = input con chevron

    h-10 rounded-md border-input px-3 py-2 text-base + chevron 16 al 50 %. Abierto: data-[state=open]:ring-2, mismo anillo que el foco. Placeholder muted. En RN el trigger es idéntico; la lista abre en bottom sheet (sección 8) con las filas de SelectItem: 14 px, padding 6/8, radio 6, check a la derecha, bg-accent en la resaltada.

  2. MultiSelect = botón outline + chips neutral

    Trigger Button variant=outline con peso normal y muted cuando está vacío; lo elegido sale como Badge neutral con una X de 12 en un hit-area de 28 (multi-select.tsx:175). Las opciones llevan un cuadro de 16 con borde primary que se rellena al marcar — distinto al Checkbox (brand-600). Se replica tal cual.

  3. Checkbox brand-600, borde primary

    size-4 rounded-sm border-input → 16 px, radio 6 (la escala custom: rounded-sm = 10 − 4). Marcado: bg-brand-600 border-primary con check de 14 blanco. Sin animación de check en la web (transition-none): en RN tampoco.

  4. Radio y switch beben de --primary

    Punto de 8 fill-primary = #3765c0; switch data-[state=checked]:bg-primary con pista input apagada, thumb 16 blanco que viaja calc(100% − 2px). En RN se dibuja propio (no el Switch nativo: iOS lo pinta verde y de 51 × 31).

  5. Filas de opción de 44

    El control mide 16 pero la fila tocable mide 44 (min-h-11 del shell móvil): label Poppins 14/500 secondary-foreground con gap 12. Disabled: opacity-50 en control y label (label.tsx:16).

05Card, Badge y pills, Avatar, Separator

card.tsx · badge.tsx:7-33 · order/freight.constants.ts · corridor-badge.tsx · avatar.tsx

La Card de la web no tiene borde ni sombra: es blanco sobre bg-secondary con radio 14, padding 24 y título en Lemon Milk. El Badge es radio 6, Poppins 14/500 con siete variantes vivas (la octava, info, no compila en la web). Las pills de estatus son el mapa de order.constants.ts y freight.constants.ts, y el corredor tiene dos paletas que conviven.

9:41●●●
Galería · 07
Card y Avatar
Card · card compacta de listado · Avatar × 3 fallbacks · Separator
Card card.tsx:10-78 · r14 · p24 · sin borde ni sombra
MGS-2026-0142
Flete CN-MAR-07 · salió el 12 ago
En tránsito
Guangzhou → Caracas3 cajas
Ver detalleCompartir
Card compacta de listado shipment-card.tsx · 16/12
1Z 999 AA1 01 2345 6784
UPS · recibido en Miami
Por aprobar
Miami → Caracas · 28 ago
Avatar · Separator avatar.tsx · mobile-top-bar.tsx:142 · separator.tsx:20
ACACACmuted · brand · mogos · imagen · 24
9:41●●●
Galería · 08
Badge y pills
Badge × 7 · pills de estatus · corredor (dos paletas) · chips de listado
Badge badge.tsx:11-22 · r6 · 14/500
defaultsuccesswarningerrordestructiveoutlineneutralinfo · no compilacon icono 12
Pills de estatus order.constants.ts:26-43 · freight.constants.ts:21-33
PendienteRecibido en almacénEn tránsitoEn hub destinoListo para retiroEntregadoRetrasadoPor aprobarAprobadoFlete en tránsito
Corredor corridor-badge.tsx:14-21 · corridor-pill.tsx:95 · corridor-theme.ts
🇨🇳CN-VE🇺🇸US-VETodos
🇨🇳Desde China🇺🇸Desde EE. UU.
Unidad · ChinaUnidad · EE. UU.
Chips de listado list-status-chips.tsx:74-78 · h40 · brand-500
Todos 24En tránsito 8Listos 3Entregados 13
  1. Card plana

    bg-card rounded-xl py-6 gap-6 (card.tsx:10): sin borde ni sombra; el contraste lo da bg-secondary #f9fafb detrás. Título font-family-display text-2xl text-gray-900; descripción Poppins muted; CardAction se alinea arriba a la derecha. La versión compacta (16/12) es la de las cards de listado del client.

  2. Badge r6, sin mayúsculas

    rounded-sm border px-2 py-0.5 text-sm font-medium: 14 px Poppins, radio 6, borde de la familia 200, fondo 50, texto 700. Ninguna variante pone mayúsculas ni cambia de peso. mobile-ui/Badge hoy usa Lemon Milk y radio 4: se reescribe.

  3. info no existe

    bg-info text-info-foreground no tiene token en el tema (badge.tsx:31): en la web se ve sin fondo. La app no lo implementa; si un flujo lo necesita, se usa default. Los hovers bg-error/90 etc. tampoco compilan — irrelevantes en táctil.

  4. Mapa de estatus, dos tablas

    Envíos: pendiente/recibido/revisión/listo → neutral; hub origen/tránsito/hub destino → default; almacén destino/retiro/entregado → success; retrasado/devuelto → warning. Fletes igual salvo IN_TRANSIT → warning (freight.constants.ts:24). Aprobación: true success «Aprobado», false warning «Por aprobar». La app importa estas constantes literalmente.

  5. Corredor: dos paletas, por componente

    Badge y pill del top bar usan red-500 / blue-500 de Tailwind al 10 % (corridor-badge.tsx:14); banners y unidades usan mogos rojo / info (lib/corridor-theme.ts:25-41). Se replican las dos, cada una en su componente, sin reconciliarlas: reconciliarlas es una decisión de la web.

  6. Tres fallbacks de avatar

    AvatarFallback bg-muted (shadcn), degradado brand 500→700 con iniciales 11/600 blancas (mobile-top-bar.tsx:142) y mogos-avatar-fallback con --mogos-grad-fresh y tinta marino (mogos-chrome.css:114). Tamaños 24 / 32 / 40; la imagen va con expo-image y caché en disco (DECISIONS #23).

06Feedback: Skeleton, Spinner, Progress, Alert, Empty

skeleton.tsx:7 · spinner.tsx:10 · progress.tsx:17-24 · alert.tsx:6-22 · empty.tsx

Todo lo que dice «espera» o «no hay nada» en la web: el skeleton es bg-accent con pulso de 2 s, el spinner es Loader2 de 16, la barra de progreso mide 8 con relleno brand-700, el Alert tiene tres tonos y el Empty es el único primitivo ya retintado a mogos: hueso, línea discontinua, icono sobre celeste-soft y título Lemon Milk.

9:41●●●
Galería · 09
Feedback
Skeleton · Spinner · Progress · Alert × 3
Skeleton · Spinner bg-accent animate-pulse · Loader2 size-4
Progress progress.tsx:17-24 · h8 · brand-700 · indeterminada 1.5s
Alert · default / destructive / success alert.tsx:6-22 · Geist
Tasa BCV actualizadaBs 36,52 por dólar · hoy 9:00.
No pudimos guardar la direcciónRevisa la conexión e inténtalo de nuevo.
Envío creadoTe avisamos cuando llegue al almacén.
9:41●●●
Galería · 10
Empty
Estado vacío que invita · error de carga con reintento
Empty empty.tsx:13-95 · hueso · r14 · discontinua · media 64

Aún sin envíos

Crea tu primer envío y síguelo desde aquí.

Crear envío
Empty · error de carga notification-empty.tsx:19 · stroke 1.6

No pudimos cargar tus fletes

Revisa la conexión y vuelve a intentarlo.

Reintentar
  1. Skeleton

    bg-accent animate-pulse rounded-md: gris oklch .97, radio 8, pulso de 2 s a opacidad .5. En RN con Reanimated: withRepeat(withTiming(.5, 1000)); con reduce-motion, estático. ScreenSkeleton compone filas de card como la de arriba para cada listado.

  2. Spinner

    Loader2Icon size-4 animate-spin con role=status. Dos tamaños en la galería (16 muted, 24 brand-700); en botones siempre 16 y hereda el color del texto. Rotación 1 s lineal.

  3. Progress

    Pista h-2 rounded-full bg-gray-100, indicador bg-brand-700 sin radio propio, movimiento por translateX. Indeterminada: progress-indeterminate 1.5s ease-in-out (−100 % → 300 %) de globals.css.

  4. Alert en Geist

    Alert no lleva font-family-sans: hereda Geist del body. Radio 10, borde, padding 12/16, icono 16 bajado 2 px, título 500 con tracking −.025em, descripción muted. destructive: texto error-600 sobre error-50; success: success-600 sobre success-50 con borde success-200. AlertDialog va en la sección 8.

  5. Empty ya es mogos

    El único primitivo que la web retintó del todo: border-mogos-line bg-mogos-hueso rounded-[14px] border-dashed, media 64 sobre celeste-soft con icono marino de 24, título Lemon Milk 18/500, descripción ink-soft 14/1.625, botón primario debajo. La copia dice qué hacer, no «no hay datos».

07Navegación: Tabs, Accordion, ListRow, menú de acciones

tabs.tsx:97-160 · accordion.tsx · mobile-more-sheet.tsx:185 · dropdown-menu.tsx:77 · mobile-sub-nav.tsx:39-52

Los tabs tienen dos pieles: pill (pista muted, thumb blanco que se desliza 300 ms) y underline (banda de 2 px con el degradado de marca). La tabla se vuelve ListRow: la fila de 44 del shell móvil, o la de 72 cuando reemplaza una celda de tabla. El DropdownMenu en táctil es un action sheet con las mismas filas.

9:41●●●
Galería · 11
Tabs y Accordion
Tabs pill y underline · sub-nav del shell · Accordion
Tabs · pill tabs.tsx:99,111,158 · thumb 300ms ease-out
PendientesHistorialTasas
Tabs · underline tabs.tsx:98,125,153 · mogos-tab-band
DetalleDocumentosHistorial
Sub-nav del shell mobile-sub-nav.tsx:39-52 · min-h 44
Accordion accordion.tsx:38-61 · 18/500 · chevron 200ms
¿Cuánto tarda un flete marítimo?
Entre 35 y 45 días desde la salida del puerto de Guangzhou.
¿Qué incluye el precio?
¿Cómo pago?
9:41●●●
Galería · 12
ListRow y menú
Fila de 44 · fila de 72 (tabla) · menú de acciones
ListRow · 44 mobile-more-sheet.tsx:185 · press .98 + bg-accent
Perfil
Direcciones3 guardadas
Pagos2 pendientes
Cerrar sesión
ListRow · 72 table.tsx:101 · celda h-[72px] p-4 · código display
CN-MAR-07Guangzhou → La Guaira · 12 agoEn tránsito
US-AIR-12Miami → Caracas · 28 agoEntregado
Menú de acciones dropdown-menu.tsx:77 · en táctil: action sheet
Ver detalle
Copiar tracking
Eliminar
  1. Tabs pill

    Lista bg-muted rounded-lg p-[3px]; thumb bg-background border-border/60 shadow-xs rounded-md que se mide y se desliza duration-300 ease-out (tabs.tsx:111); trigger 14/500 muted, activo text-primary. En RN, Animated.View con layout medido; con reduce-motion, salto directo (motion-reduce:transition-none ya lo dice la web).

  2. Tabs underline

    border-b + banda mogos-tab-band de 2 px con el degradado marino → celeste-deep → lima (mogos-components.css:135); trigger 16/500 muted, activo brand-700. La única pincelada de marca en un primitivo de navegación: se conserva.

  3. Sub-nav del shell

    Pista bg-muted/40 rounded-full border p-1; pastilla activa blanca con shadow-sm, 11/500, min-h-11. Es de MF-03, pero se construye aquí porque es el mismo primitivo que los tabs pill con otra piel.

  4. Accordion

    Trigger Poppins 18/500 gray-900, py-4, chevron muted 16 que rota 180° en 200 ms; contenido 16 gray-700 con pb-4; separador border-b entre ítems. Animación de altura accordion-down 0.2s ease-out.

  5. Table → ListRow

    La tabla no existe en móvil (spec Vistas §8). ListRow tiene dos alturas: 44 (fila de sheet, min-h-11 gap-3 rounded-xl px-3 py-2.5 text-sm font-medium) y 72 (la celda h-[72px] p-4 de table.tsx:101) con código en display, meta en 12 muted y pill a la derecha. Press: active:bg-accent active:scale-[0.98]. Divisor gray-200.

  6. Menú → action sheet

    Ítems px-2 py-1.5 text-sm rounded-sm gap-2 con icono muted de 16, variante destructiva en text-destructive, separador -mx-1 my-1. En táctil el mismo listado abre en bottom sheet (sección 8); el atajo de teclado no se muestra.

08Overlays: Dialog, AlertDialog, Sheet, banda noche, Toast, Tooltip

dialog.tsx:42-64 · alert-dialog.tsx · drawer.tsx:79-138 · mogos-band.css · toast.tsx + sileo-theme.css · tooltip.tsx:49

El velo es marino al 70 %, nunca negro. El diálogo es blanco, radio 10, padding 24, título Lemon Milk 24 y botones apilados en móvil. El bottom sheet (Vaul) es claro: asa muted, cabecera centrada con línea mogos-line; los paneles laterales y los diálogos «noche» llevan la banda con --mogos-grad-rail. El toast es Sileo: una pastilla oscura de 350 × 40 arriba al centro, que se expande con resorte.

9:41●●●
Galería · 13
Toast y diálogos
Toast sileo · success / error / warning (+ loading gris) toast.tsx:136 · sileo-theme.css:15-25 · 350×40
Envío creadoNo pudimos guardarSin conexión
Tooltip → popover táctil tooltip.tsx:49 · bg-primary · 12px
Copiado al portapapelesTasa BCV del díaBs 36,52
AlertDialog alert-dialog.tsx · velo marino/70 · título 18/600 Geist
¿Cancelar el envío MGS-0142?
Se libera el espacio en el flete y no se puede deshacer.
VolverSí, cancelar
Dirección guardada

La usaremos como destino por defecto.

Deshacer
9:41●●●
Galería · 14
Sheet y banda
Banda noche de paneles · Dialog · bottom sheet claro (Vaul)
Banda noche mogos-band.css:98-340 · SheetHeader lateral · DialogHeader tone=noche
En tránsito
CN-MAR-07
Guangzhou → La Guaira · llega el 24 sep
Dialog dialog.tsx:64 · r10 · p24 · Lemon Milk 24 · zoom-in-95 200ms
Bienvenido a Mogos
Completa tu perfil para crear tu primer envío.
Completar perfil
Tipo de flete
Elige cómo viaja tu carga
Marítimo35–45 días · desde $180/m³
Aéreo7–10 días · desde $9/kg
Continuar
  1. El velo es marino

    bg-mogos-marino/70 en Dialog, AlertDialog, Sheet y Drawer (dialog.tsx:42): rgba(2,27,84,.70). Es la marca entrando por el fondo, no por la superficie. En RN el Modal y el sheet comparten ese overlay con fade de 200 ms.

  2. Dialog

    rounded-lg border p-6 gap-4 shadow-lg max-w-[calc(100%-2rem)]: radio 10, padding 24, 16 px de margen lateral. Título font-family-display text-2xl text-gray-900 centrado en móvil; footer flex-col-reverse gap-2: el primario abajo, ancho completo. Entra con fade-in zoom-in-95 en 200 ms; la X arriba a la derecha al 70 %.

  3. AlertDialog es Geist

    Su título no lleva font-family-display: text-lg font-semibold en Geist 18/600 (alert-dialog.tsx). Acción = botón default, cancelar = outline. Se replica así — la diferencia con Dialog es real en la web.

  4. Bottom sheet claro, panel noche

    DrawerHeader resuelve el tono por dirección (drawer.tsx:125): bottom/top = light (borde mogos-line, título Lemon Milk 16/600 ink, descripción ink-soft), left/right = noche con mogos-band. En móvil casi todo es bottom: el sheet es claro con asa bg-muted de 100 × 8. La banda queda para el panel de notificaciones y los diálogos con tone=noche.

  5. Banda noche

    --mogos-grad-rail + halo celeste al 22 % arriba a la derecha y lima al 9 % abajo (mogos-band.css:124-148); chip rgba(255,255,255,.1) con borde al 20 % y punto de estado (#4fb37a success); meta al 70 %; cerrar en on-marino-soft sobre wash. En RN: expo-linear-gradient para el rail y dos View con blur de expo-blur o un SVG radial.

  6. Toast = Sileo, no Sonner

    providers.tsx monta Toaster de @workspace/ui/components/toast: fachada con API de Sonner sobre Sileo, position=top-center. Pastilla 350 × 40 con fill #1a1a1a (tema claro), badge de 24 con el tono al 20 %, título .825rem/500 en el color del tono (sileo-theme.css: info/acción oklch .68 .13 262, success .75 .12 158, error .66 .16 25, warning .8 .11 85, loading gris), text-transform: none. Expandida: descripción al 50 % y botón de acción al 15 %. Resorte de 600 ms. En RN: sonner-native vs propio con Reanimated se decide en la tabla de goal-phases; la forma es esta.

  7. Tooltip → popover táctil

    bg-primary text-primary-foreground rounded-md px-3 py-1.5 text-xs con flecha de 10 rotada: azul #3765c0. En táctil no hay hover: aparece al mantener presionado (long-press 300 ms) o al tocar un icono de ayuda, y se cierra al tocar fuera.

09Calendario y DatePicker, MarkdownViewer, Section y PageFrame

calendar.tsx:34 · markdown-viewer.tsx:30-40 · section.tsx · PageFrame.tsx:27-71

El calendario es react-day-picker con celda de 32, días como botones ghost, seleccionado en bg-primary y hoy en bg-accent; el DatePicker es un botón outline con icono que abre el calendario en sheet. El MarkdownViewer (para Mogui) es prose-sm en Geist. Y el marco de página: fondo bg-secondary, padding 16, título Lemon Milk 24 normal en tinta marino.

9:41●●●
Galería · 15
DatePicker y Calendar
Trigger vacío y con rango · calendario con hoy, rango, elegido, deshabilitado
DatePicker · trigger Button outline · icono muted · «Elige una fecha»
Fecha estimada de salidaElige una fecha
16 sep 2026 – 19 sep 2026
Calendar calendar.tsx:34 · celda 32 · primary · hoy accent · rango
lumamijuvido
311234567891011121314151617181920212223242526272829301234
Leyenda selected-single · today · range · outside · disabled
elegidohoy / rangodeshabilitado
9:41●●●
Galería · 16
Markdown y marco
MarkdownViewer (Mogui) · PageFrame · Section
MarkdownViewer markdown-viewer.tsx:30-40 · prose-sm · Geist

Tu flete sale el 16

Cargamos 3 cajas (0,42 m³) en el contenedor CN-MAR-07. Puedes ver el tracking o pedir la factura.

Próximos pasos
  • Salida estimada: 16 sep
  • Llegada a La Guaira: 24 oct
  • Aduana y entrega: 3–5 días
PageFrame PageFrame.tsx:27,39,64 · bg-secondary · p16 · gap 16
Mis envíos
Todo lo que viaja contigo, en un solo lugar.
Section section.tsx:8-70 · bg-section · r14 · título Geist 600
Resumen
El bloque blanco dentro del marco gris.
Fletes activos2
  1. Calendar

    [--cell-size:--spacing(8)] → 32 px; navegación con botones ghost de 32; caption 14/500; días de la semana .8rem muted; día = Button ghost size=icon con radio 8: seleccionado bg-primary text-primary-foreground, hoy bg-accent, fuera de mes muted, deshabilitado muted al 50 %, rango con extremos redondeados y medio plano. Con sombra md porque vive en popover; en móvil, dentro del sheet, sin sombra.

  2. DatePicker

    El trigger es un Button outline ancho completo, peso normal, icono calendario muted y texto muted hasta elegir. Formato «16 sep 2026» con date-fns y locale es, como la web.

  3. MarkdownViewer

    prose prose-sm en Geist: h2 20/600 gray-900, h3 18/600, párrafos gray-600 leading-relaxed, viñetas con marcador gray-400, strong gray-900, enlaces text-blue-600 (oklch .546 .245 262.881: Tailwind, no brand) subrayados. Para Mogui en MF-11 con react-native-markdown-display o propio; los estilos son estos.

  4. PageFrame

    bg-secondary p-4 gap-4 con título font-family-display text-mogos-ink text-2xl leading-tight font-normal (26/34 en desktop) y descripción Poppins ink-soft 16/24 (PageFrame.tsx:27,64). En RN incluye safe areas, KeyboardAvoiding y el scroll: todas las pantallas de MF-02 a MF-13 nacen aquí.

  5. Section

    bg-section rounded-xl p-6 gap-6, título 24/600 en Geist, descripción 14 muted; en móvil padding 16. Es el bloque blanco dentro del marco gris.

10Motion e iconografía

tokens.css:173-183 · mobile-shell/*.tsx · aurora-auth.css · lucide stroke 2 / 1.6

Aquí va la creatividad que el sistema no admite: la web tiene motion pero no lo formaliza (150/200/300/500 ms sueltos, cinco usos de framer-motion). La app lo formaliza por primera vez con la escala del estudio — 80 · 160 · 260 · 420 · 640 ms y las easings out / in-out / spring — mapeando cada gesto de la web a su token más cercano. Iconos lucide-react-native, trazo 2 por defecto y 1.6 en las superficies suaves, tamaños 12 / 14 / 16 / 20 / 24. Todo respeta isReduceMotionEnabled.

9:41●●●
Galería · 17
Motion e iconos
Press · sheet · fade/zoom · tabs · resorte · reduced motion · lucide
Press scale .95 · 160 ms ease-out · haptics light
Crear envíoEntregados 13
Catálogo de transiciones web → token del estudio
press / hover160
dialog fade+zoom260
accordion · tabs260
sheet abre420
toast resorte640
stack pushnativo
Reduced motion AccessibilityInfo.isReduceMotionEnabled
Con «Reducir movimiento»: press sin escala, fades de 80 ms, skeleton estático, toast sin resorte. Usable sin una sola animación.
lucide-react-native stroke 2 · 1.6 en suaves · tamaños web
12
14
16
20
24
1.6
1.6
1.6
  1. Press = la única invención, y es de motion

    La web solo tiene active:scale-95 en el shell (mobile-fab.tsx:27, mobile-bottom-tabs.tsx:109) y scale-[0.98] en filas de sheet. La app lo generaliza: todo Pressable escala .95 (filas .98) en 160 ms ease-out con expo-haptics light en acciones primarias. Es la micro-interacción base de use-haptic-feedback + Reanimated.

  2. Mapa web → tokens del estudio

    150 → 160 fast (press, color); 200 → 260 base (dialog fade/zoom, accordion, tabs thumb 300 también); 500/300 del sheet → 420 slow abre y 260 cierra; Sileo 600 → 640 slower con ease-spring. Diferencias de ±60 ms: imperceptibles y a cambio la app tiene una escala. Ver decisión 5.

  3. Stack y sheet

    El push de pantalla es el nativo de react-native-screens (iOS slide, Android fade-through): no se reimplementa. El sheet sube 420 ms con ease-in-out y velo fade 200 → 260; la librería (gorhom / true-sheet / propio) sale de la tabla comparativa de goal-phases, la curva es esta.

  4. Aurora y plate quedan para MF-02

    Los keyframes de aurora-auth.css (drift 26/37 s, breathe 13/17 s, plate 600 ms spring) son de las puertas de auth: se catalogan aquí como tokens de duración pero se construyen en MF-02.

  5. Reduced motion en serio

    AccessibilityInfo.isReduceMotionEnabled() + listener → contexto useReducedMotion() en mobile-ui: escala 1, duraciones 80, sin resortes, skeleton fijo. Espeja el @media (prefers-reduced-motion) que la web ya honra en tabs y aurora.

  6. lucide-react-native

    154 iconos distintos en el client, todos lucide; mobile-ui/Icon deja de envolver Ionicons. Trazo 2 por defecto; 1.6 donde la web lo pide (campana, notificaciones, calendario de salidas, empty). Tamaños: 16 el default de los primitivos, 12 en badge, 14 en checks, 20 en filas y tabs (size-[18px] en la barra inferior), 24 en media.

11Lo que mobile-ui hace hoy y lo que la web renderiza

packages/mobile-ui/src/theme/*.ts · components/*.tsx

La auditoría token a token que MF-01 ejecuta. mobile-ui es cantera (DECISIONS #5/#20): se conserva lo que ya coincide, se corrige lo que no y se añade lo que falta. La tabla comparativa de enfoques (StyleSheet propio vs Unistyles vs reusables, sheets, toasts) la produce la primera fase de goal-phases; esta es la de valores.

Token / primitivomobile-ui hoyWeb (apps/client)MF-01
Primariobrand.500 #3765c0 ✓--primary #3765c0 (client override)se conserva
Button defaultbrand[700] #213e83bg-brand-700 #213e83 (button.tsx:14)correcto; corrige texto (Poppins 14/600), radio 8, disabled gris, foco ring
TipografíaLemon Milk Pro en todo (fontFamily.regular = LemonMilkPro)Geist body · Poppins componentes · Lemon Milk display · Geist Monotres familias con expo-font + presets de Text
Radiossm 4 · md 6 · lg 8 · xl 10 · 2xl 12sm 6 · md 8 · lg 10 · xl 14 · fullescala web; alias 2xl = 14 para no romper imports
SombrasshadowColor #000 en los cuatro presetshairline negro 5 % en primitivos · marino/índigo en elevadasdecisión 4
Grisesgray-light ✓ (#f2f4f7 …)--color-gray-* = gray-lightse conserva
Capa mogosmarino / celeste / lima / rojo + soft/deep, ink-soft, hueso, line, gradientesse añade colors.mogos.* y gradients.*
Estadossuccess/error/warning escala Untitled ✓las mismas escalas + mogos success/warning/infose conserva; se añaden los mogos
Iconoswrapper de Ioniconslucide (154), stroke 2 / 1.6lucide-react-native; Icon mantiene su firma
BadgeLemon Milk, r4, 4 variantesPoppins 14/500, r6, 7 variantes vivasse reescribe; mapa de estatus importado de constants
Cardborde + sombra smsin borde ni sombra, r14, p24se corrige
Input / Selecth44, r8, borde gray-300h40, r8, borde #d0d5dd, Poppins 16, foco 2 pxaltura y foco; Field con error en label
Checkbox / Radio / Switchbrand.500 rellenobrand-600 / primary / primaryse corrige checkbox; switch propio 32×18
DatePickernativo del SOreact-day-picker celda 32calendario propio en sheet, estilos web
Motion150/200/300/500 sueltostokens 80/160/260/420/640 + easings + useReducedMotion
FaltanOTP, MultiSelect, Avatar, Spinner, Progress, Tabs, Accordion, Alert, Dialog, Sheet, Menu, Toast, Empty, Tooltip, ListRow, Markdown, Section, PageFramese construyen, uno por fase, con C9

Fuentes: packages/mobile-ui/src/theme/{tokens,radii,shadows}.ts, components/button.tsx (leídos 2026-08-30) · packages/ui/src/components/*.tsx · packages/ui/src/styles/*.css · apps/client/app/globals.css.

12Decisiones

las que toma esta pieza · las que esperan a Fabrizio