
Guía Base UI UX, Personalidad y Arquitectura Compartida (Angular + Flutter)
Case study data record.
Tech_Stack / Tags
Meta
Una metodología en 5 pasos para construir productos que se vean profesionales, tengan carácter propio y compartan la misma arquitectura sin importar el framework.
Cómo leer esta guía
El orden importa. Cada paso depende del anterior, y saltártelos es la razón #1 por la que una app "se ve hecha por defecto". La regla mental es:
Primero decides qué (Paso 1) → luego el ADN visual (Paso 2) → cómo se siente (Paso 3) → cómo se comporta (Paso 4) → y recién al final cómo lo organizas en código (Paso 5).
No abras el editor de código hasta el Paso 5. En serio.
Voy a usar un ejemplo que arrastraré por toda la guía para que sea concreto: una app para iPad que gestiona facturas de forma rápida.
Paso 1 — La idea y el alcance (sin diseño ni código)
1.1 El propósito en una frase
Escribe una sola frase que diga qué hace tu producto y para quién. Si necesitas un "y" para unir dos ideas, tu producto todavía no está enfocado.
"Una app para iPad que permite a freelancers crear y enviar facturas en menos de 30 segundos."
Fíjate que esa frase ya esconde tres cosas valiosas: el usuario (freelancers), el trabajo principal (crear/enviar factura) y una promesa (en 30 segundos). Esa promesa es la que vas a defender en cada decisión de diseño.
1.2 Lista de funcionalidades (Features)
Divide la idea en bloques que el usuario necesita para cumplir su trabajo. Cada bloque será una carpeta en tu código. Para cada feature anota su trabajo (qué resuelve), no su pantalla.
| Feature | Trabajo del usuario | ¿Imprescindible para v1? |
|---|---|---|
auth |
Entrar de forma segura a sus datos | Sí |
dashboard |
Ver de un vistazo cuánto le deben | Sí |
invoicing |
Crear, ver y enviar facturas | Sí |
clients |
Guardar a quién le factura | Quizás (puede ir en v2) |
settings |
Configurar su negocio (logo, impuestos) | No (v2) |
El truco de principiante que cambia todo: marca qué es v1 y qué no. Una app pequeña pero terminada siempre se ve más profesional que una app grande llena de pantallas a medias.
Paso 2 — El sistema de diseño (tus tokens, tu ADN visual)
Los tokens son las variables de tu diseño. Definirlos primero garantiza que, aunque la app sea sencilla, se vea coherente — y coherencia es el 80% de lo que el ojo lee como "profesional".
2.1 La idea clave que casi nadie te explica: tokens en dos capas
No definas un solo nivel de tokens. Define dos:
Primitivos — la paleta cruda, sin significado. Solo nombres descriptivos:
slate-50: #F8F9FA
slate-800: #1E293B
white: #FFFFFF
indigo-600:#4F46E5
Semánticos — el rol que cumple cada color, apuntando a un primitivo:
color-background → slate-50
color-surface → white
color-primary → indigo-600
color-text → slate-800
color-text-muted → slate-500
color-border → slate-200
¿Por qué molestarte? Porque el día que quieras cambiar de "indigo" a "negro brutalista", tocas una línea (color-primary → black) y toda la app cambia. Tus botones nunca dicen #4F46E5; dicen color-primary. Esta separación es lo que hace que un sistema escale.
2.2 Los seis grupos de tokens (no solo color)
La mayoría de principiantes define color y tipografía y se detiene. Un sistema profesional define seis grupos. Define todos antes de diseñar:
1. Color (semántico, máximo 4–6 roles) Background, Surface, Primary, Text, Text-muted, Border. Una regla de oro: el texto nunca es negro puro (#000) sobre blanco puro — usa un gris muy oscuro (#1E293B). El negro puro "vibra" y cansa la vista.
2. Tipografía (máximo 2 familias)
- Display: con personalidad, para títulos. Úsala con restricción (solo títulos grandes).
- Body: limpísima y legible, para texto largo y botones (Inter, Roboto, Geist).
- Define también una escala de tamaños fija, no inventes tamaños:
xs 12 · sm 14 · base 16 · lg 20 · xl 28 · 2xl 40.
3. Espaciado (escala de 4 en 4) 4 · 8 · 12 · 16 · 24 · 32 · 48 · 64. Nunca pongas un margen de "23px sobre la marcha". Esta es la regla más fácil de seguir y la que más rápido hace que todo se vea ordenado.
4. Radios (la decisión de tu vibe)
0px→ crudo, brutalista, técnico.8px→ minimalista amigable (lo más seguro para empezar).16px+→ suave, juguetón, consumer. Elige uno como base y sé consistente. Mezclar radios al azar es la marca del amateur.
5. Elevación / sombras Define 2–3 niveles (none · sm · md) en vez de inventar sombras. En estilos brutalistas, en lugar de sombras difusas usa sombras duras y desplazadas (4px 4px 0 #000), que es parte de su firma visual.
6. Movimiento (¡sí, las animaciones también son tokens!) Esto es lo que casi nadie hace y lo que separa una app "viva" de una rígida:
duration-fast: 150ms (hovers, cambios pequeños)
duration-base: 250ms (transiciones de UI, aparecer/desaparecer)
duration-slow: 400ms (cambios de pantalla, modales)
easing-standard: cubic-bezier(0.4, 0, 0.2, 1) (entradas y salidas normales)
easing-spring: curva con rebote (para deleite, botones, éxito)
Definir esto antes evita que cada animación dure un tiempo distinto y se sienta caótica.
2.3 La REGLA DE ORO cross-platform
Define los tokens UNA sola vez, en un formato neutral (JSON), y genera desde ahí el tema de cada plataforma. Una única fuente de verdad.
📄 tokens.json ← la ÚNICA fuente de verdad (color, type, spacing, motion…)
│
├──► theme.dart (para Flutter)
└──► theme.ts (para Angular)
La herramienta estándar para esto se llama Style Dictionary (de Amazon): lee tu tokens.json y escupe automáticamente las variables para Dart, para CSS/SCSS, para TypeScript, etc. Así, cuando cambias un color, ambas apps cambian a la vez y nunca se desincronizan.
Cuando empiezas, puedes hacerlo a mano (un theme.dart y un theme.ts que copien los mismos valores), pero piensa siempre en tokens.json como el "original" y los demás como "copias generadas". Esa mentalidad es la que hace el diseño verdaderamente compartido.
Paso 3 — Personalidad y micro-animaciones (lo que hace que se sienta vivo)
Aquí está tu objetivo #2. Una UI sin movimiento se siente como una foto; con el movimiento correcto, se siente como un objeto físico que responde. Pero animar por animar arruina una app (y delata que fue generada sin criterio). Cada animación debe tener un trabajo.
3.1 Las cuatro razones legítimas para animar
- Feedback — confirmar que el sistema te oyó. Un botón que se hunde 2px al presionarlo.
- Continuidad — explicar de dónde viene y a dónde va algo. Un modal que crece desde el botón que lo abrió, en vez de aparecer de golpe.
- Jerarquía / atención — guiar el ojo. El total de la factura que cuenta hacia arriba (
$0 → $1,250) cuando se calcula. - Deleite — el toque de personalidad. Un check que se dibuja con un pequeño rebote al marcar "factura pagada".
Si una animación no cae en ninguna de estas cuatro, bórrala.
3.2 La regla del "gasta tu osadía en un solo lugar"
No hagas que todo tenga personalidad — eso es ruido. Elige un elemento firma: el momento que tu usuario recordará. En la app de facturas podría ser la animación de "factura enviada" (el papel que se dobla y sale volando). Todo lo demás: discreto, rápido, casi invisible.
3.3 Catálogo de micro-interacciones para empezar
| Elemento | Qué pasa al interactuar | Token de tiempo |
|---|---|---|
| Botón | Baja de escala a 0.97 + se aclara al presionar |
fast |
| Card / tarjeta | Sube 2–4px y gana sombra md al hacer hover |
fast |
| Aparición lista | Cada item entra con fade + slide-up, escalonado (50ms) | base |
| Cambio de pantalla | Desliza/funde con easing-standard |
slow |
| Éxito (enviado) | Check animado con easing-spring (rebote) |
base |
| Error | Shake horizontal corto del campo | fast |
| Carga | Skeleton que pulsa (no un spinner genérico, ver Paso 4) | loop |
3.4 Cómo se traduce a cada framework (misma idea, distinta herramienta)
| Concepto | Flutter | Angular |
|---|---|---|
| Animación implícita | AnimatedContainer, AnimatedOpacity |
Transiciones CSS sobre cambio de clase |
| Animación con control | AnimationController + Tween |
Angular Animations (trigger, state) |
| Rebote / spring | CurvedAnimation con Curves.elasticOut |
cubic-bezier con overshoot / @angular/animations |
| Entrada escalonada | flutter_animate (paquete) |
@stagger() en Angular Animations |
| Respetar reduce-motion | MediaQuery.disableAnimations |
prefers-reduced-motion media query |
Piso de calidad obligatorio: siempre respeta
prefers-reduced-motion. Hay personas a las que el movimiento les causa mareo. Si el usuario lo desactivó en su sistema, tus animaciones deben reducirse a un simple fundido o desaparecer.
Paso 4 — UX: cómo se comporta (no solo cómo se ve)
Tu borrador era fuerte en UI; esta capa es la UX que le faltaba. Es lo que hace que la app se sienta terminada en vez de rota.
4.1 Los cuatro estados de TODA pantalla
El error de principiante #1: diseñar solo el estado "lleno de datos bonitos". Toda pantalla que muestre datos tiene en realidad cuatro estados, y debes diseñar los cuatro:
- Vacío — el usuario aún no tiene datos. No es una pantalla en blanco: es una invitación a actuar. ("Aún no tienes facturas. Crea la primera →").
- Cargando — usa skeletons (siluetas grises del contenido que viene), no un spinner centrado. El skeleton hace que la espera se sienta más corta y predice el layout.
- Error — explica qué pasó y cómo arreglarlo, en la voz de la app, sin disculpas vagas. ("No pudimos cargar tus facturas. Revisa tu conexión y reintenta.")
- Con datos — el estado feliz.
Diseña siempre vacío → cargando → error → datos. Esta sola disciplina te pone por encima del 90% de las apps de principiante.
4.2 El texto es parte del diseño
Las palabras de tu interfaz son material de diseño, no decoración. Tres reglas que rinden mucho:
- Nombra las cosas por lo que el usuario controla, no por cómo está hecho el sistema. El usuario "envía una factura", no "ejecuta un POST".
- El botón dice exactamente qué pasa. "Enviar factura", no "Aceptar". Y el nombre se mantiene por todo el flujo: si el botón dice "Enviar", el mensaje de éxito dice "Factura enviada".
- Voz activa, frases cortas, sin relleno. "Guardar cambios" pesa más que "Proceder a guardar".
4.3 El piso de accesibilidad (no negociable)
No es opcional ni "para después". Es barato si lo haces desde el inicio:
- Contraste: el texto debe tener contraste suficiente sobre su fondo (mínimo 4.5:1 para texto normal). Hay extensiones que lo miden en segundos.
- Áreas táctiles: mínimo
44×44pxen lo que se toca (especialmente en iPad). - Foco visible: quien navega con teclado debe ver dónde está parado.
- No solo color: no comuniques "error" únicamente con rojo; añade un ícono o texto (hay personas daltónicas).
Paso 5 — Arquitectura compartida: Feature-Driven (recién aquí tocas código)
La gran idea: en lugar de agrupar por tipo de archivo (todos los botones juntos, todas las pantallas juntas), agrupas por funcionalidad (feature). Cuando trabajas en "facturación", todo lo de facturación está en una sola carpeta.
Lo que se comparte entre Angular y Flutter NO es el código — es la estructura y la filosofía de capas. El mismo plano, materiales distintos.
5.1 La estructura de carpetas (idéntica en ambos frameworks)
📁 tu_proyecto/
├── 📁 core/ ← lo global: lo que usa TODA la app
│ ├── 📄 theme # tus tokens (theme.dart / theme.ts)
│ ├── 📄 api_service # conexión a la base de datos / backend
│ └── 📁 utils/ # helpers, constantes, formateadores
│
├── 📁 shared/ ← componentes REUTILIZABLES sin lógica de negocio
│ └── 📁 ui/ # AppButton, AppTextField, AppCard… (usan los tokens)
│
└── 📁 features/ ← ¡la magia del Feature-Driven!
├── 📁 auth/
│ ├── 📁 presentation/ # pantallas + widgets SOLO de auth
│ ├── 📁 domain/ # las reglas: "qué es un usuario válido"
│ └── 📁 data/ # de dónde vienen los datos (API, repos)
│
├── 📁 dashboard/
│ ├── 📁 presentation/ # pantalla + tarjetas resumen, gráficos
│ ├── 📁 domain/
│ └── 📁 data/
│
└── 📁 invoicing/
├── 📁 presentation/
├── 📁 domain/
└── 📁 data/
5.2 Por qué tres capas dentro de cada feature
Esto es lo que sube tu objetivo #3 (la "mejor arquitectura"). Cada feature se parte en tres capas con una regla de dependencia: presentation → domain → data, nunca al revés.
presentation— lo que el usuario ve y toca (pantallas, widgets/componentes, estado de UI). No sabe de dónde salen los datos; solo los pide.domain— el corazón limpio: las reglas de negocio y los modelos. "Una factura tiene fecha, cliente y total"; "una factura no puede enviarse sin cliente". Esta capa no depende de Flutter ni de Angular ni del internet — es Dart/TypeScript puro. Por eso es la más fácil de portar y la más fácil de testear.data— el plomero: habla con la API o la base de datos y entrega modelos limpios al domain. Si mañana cambias de backend, solo tocas esta capa.
La ventaja: puedes cambiar el diseño (presentation) sin tocar las reglas (domain), o cambiar el backend (data) sin tocar la pantalla. Cada parte cambia por su cuenta.
5.3 Mapa de conceptos Flutter ↔ Angular
Misma arquitectura, distinto vocabulario. Esta tabla es tu traductor:
| Concepto | Flutter | Angular |
|---|---|---|
| Bloque de UI | Widget | Component |
| Pantalla | Screen / Page (un Widget) | Routed Component |
| Estilos globales | ThemeData |
CSS variables / theme service |
| Estado / lógica de UI | Riverpod, BLoC, Provider | Services + Signals / RxJS |
| Inyección de dependencias | get_it, Riverpod |
Inyector nativo de Angular |
| Llamadas HTTP | dio / http |
HttpClient |
| Modelo de datos | class Invoice {...} |
interface Invoice {...} |
| Navegación | go_router |
Angular Router |
Decide tu manejador de estado y mantenlo. En Flutter, Riverpod es una opción moderna y recomendable para empezar. En Angular, Signals (lo más nuevo) o Services + RxJS.
5.4 La regla que mantiene todo limpio
Un componente en
shared/ui(comoAppButton) solo puede usar tokens delcore/theme. Nunca un color "a mano". Un widget dentro defeatures/invoicingsolo vive ahí; si lo necesitas en dos features, entonces lo subes ashared/. No antes.
Esto evita el caos donde "todo usa todo" y nadie sabe qué rompe qué.
El orden de ataque (tu checklist)
Cuando arranques un proyecto nuevo, sigue exactamente este orden:
- [ ] Escribe el propósito en una frase.
- [ ] Lista las features y marca cuáles son v1.
- [ ] Crea
tokens.jsoncon los 6 grupos (color, type, spacing, radius, elevation, motion). - [ ] Elige tu elemento firma (la animación/momento que te van a recordar).
- [ ] Para cada pantalla, diseña los 4 estados (vacío, cargando, error, datos).
- [ ] Escribe el texto de botones y errores en voz activa.
- [ ] Crea la estructura de carpetas
core / shared / features. - [ ] Construye primero los componentes
shared/ui(botón, input, card) usando solo tokens. - [ ] Construye feature por feature, empezando por la más imprescindible (
autho la principal). - [ ] Revisa accesibilidad y
reduce-motionantes de dar por terminada cada pantalla.
Errores comunes de principiante (y su antídoto)
- Inventar márgenes y tamaños sobre la marcha. → Usa siempre la escala de espaciado y tipografía.
- Diseñar solo el estado "con datos". → Diseña los 4 estados, siempre.
- Negro puro sobre blanco puro. → Gris muy oscuro sobre blanco hueso.
- Mezclar radios y sombras al azar. → Un radio base, 2–3 niveles de sombra, y basta.
- Animar todo. → Una firma con personalidad; el resto, discreto y rápido.
- Agrupar por tipo de archivo. → Agrupa por feature.
- Copiar colores
#hexen cada botón. → Apunta siempre al token semántico. - Dejar accesibilidad "para el final". → Es barata al inicio, carísima al final.
Una última idea
La elegancia no es agregar; es ejecutar bien una visión simple. Una app con 4 pantallas, 6 colores, 2 fuentes y una sola animación memorable — toda coherente — se ve más profesional que una con 20 pantallas y todo distinto. Empieza pequeño, termínalo bien, y crece desde ahí.
Comentarios (0)