🎯 Contexto y Objetivo de Diseño
NutriPlan v2.0 reemplaza una interfaz Bootstrap 3 heredada por un sistema vibrante, coherente y accesible. Los profesionales sanitarios usan la plataforma 6–8 horas/día, por lo que la legibilidad, los estados de interacción explícitos y el confort visual son requisitos no negociables.
Principios rectores
- 01Energía con propósito — Cada color señaliza significado (progreso, alerta, acción). No se usa color solo para decorar.
- 02Claridad de estado — Todo componente interactivo expone sus estados: default, hover, focus, active, disabled, error.
- 03Accesibilidad no opcional — WCAG 2.2 AA en cada texto y UI component. Si hay conflicto estético, gana la accesibilidad.
- 04Tokens primero — Nunca hardcodear valores de color o espaciado. Usar siempre tokens semánticos.
Componentes Fase 1
🎨 Tokens de Color
Paleta de 5 familias semánticas + neutros. Usar siempre el token semántico en componentes, nunca el valor hex directamente. Los gradientes son tokens de primera clase para superficies de énfasis.
Colores primarios y de marca
Gradientes de marca
Tabla de contraste (WCAG 2.2)
| Muestra | Token | Valor | Sobre blanco | Nivel | Uso semántico |
|---|---|---|---|---|---|
| --color-primary-700 | #1D4ED8 | 7.3:1 | AAA | Texto enlace | |
| --color-primary-600 | #2563EB | 5.9:1 | AAA | Hover state | |
| --color-primary-500 | #3B82F6 | 3.6:1 | AA UI | Botón primario (texto blanco sobre fondo azul: 4.5+) | |
| --color-success-700 | #166534 | 8.7:1 | AAA | Texto badge "Óptimo" | |
| --color-danger-700 | #991B1B | 9.2:1 | AAA | Texto badge "Déficit" | |
| --text-primary | #111827 | 18.1:1 | AAA | Texto corporal, etiquetas | |
| --text-secondary | #374151 | 10.7:1 | AAA | Texto meta, hints, placeholders visibles |
Aa Tipografía
Escala de 7 pasos sobre una base de 16px. Inter para cuerpo e UI; JetBrains Mono para valores numéricos clínicos (kcal, macros, métricas). Pesos disponibles: 100–900.
↔ Espaciado — Grid de 8pt
Todos los márgenes, paddings y gaps deben ser múltiplos de 8px. La excepción permitida es 4px (--space-1) para ajustes internos de componentes pequeños.
⬡ Botones
Touch target mínimo de 44px de altura. Todos los botones deben tener :focus-visible visible con un outline de 3px en --color-primary-300. El estado :active añade translateY(1px) para retroalimentación táctil.
Variantes
Tamaños
Estados
Reglas de uso
- ✓ Debe Usar
btn-primarypara la acción principal de la página (máximo 1 por vista). - ✓ Debe Usar
btn-dangersolo para acciones destructivas e irreversibles. - ✓ Debe Mostrar indicador de carga durante operaciones asíncronas; deshabilitar el botón mientras carga.
- ✗ No debe Usar
btn-gradientpara acciones secundarias — reservar para CTAs de onboarding/upsell. - ✗ No debe Usar texto genérico como "OK" o "Sí" — el label debe describir la acción: "Guardar plan", "Eliminar paciente".
◉ Badges de Estado Nutricional
Cinco variantes semánticas para comunicar el estado del plan nutricional de un paciente. Siempre incluyen un dot de color para no depender solo del color (accesibilidad daltonismo).
Anatomía
- Dot: 6×6px, border-radius 50%. Refuerza el significado sin texto.
- Label: 12px, weight 600. Máximo 2 palabras.
- Padding: 3px vertical, 10px horizontal.
- Radius:
--radius-full(pill).
Tokens por variante
| Óptimo | success-50 / success-700 |
| Déficit | danger-50 / danger-700 |
| Revisar | warning-50 / warning-600 |
| En revisión | primary-50 / primary-700 |
| Sin datos | neutral-100 / neutral-700 |
👤 Patient Card
Tarjeta de resumen de paciente. El borde superior de 4px codifica el estado nutricional. Incluye avatar con gradiente, estadísticas clave en monoespacio y barra de progreso calórico.
Reglas del componente
- Anatomía: borde-top (4px) → header (avatar + nombre + meta) → stats (3 cols) → progress → footer (badge + CTA).
- Avatar: Iniciales (2 chars), 44×44px mínimo, gradiente según contexto. Si hay foto, usar
<img alt="Foto de [nombre]">. - Stats: Valores en
--font-monopara alineación tabular. Máximo 3 métricas.
- Hover:
box-shadow: var(--shadow-lg); translateY(-2px). Solo siprefers-reduced-motion: no-preference. - Focus:
outline: 3px solid --color-primary-300cuando recibe foco por teclado. - aria-label: Obligatorio:
"Paciente [nombre], estado [estado]".
⌨ Formularios — Plan Alimentario
Inputs con altura mínima de 44px. Borde de 2px para visibilidad; pasa a --color-primary-500 en focus con ring de 3px. El estado error usa --color-danger-500 y siempre incluye mensaje de texto (no solo color).
Alergias, intolerancias, preferencias culturales.
⚡ Alertas y Banners de Sistema
Cuatro variantes semánticas. Siempre incluyen título + descripción (nunca solo icono). Los banners críticos (danger) también deben enviarse por notificación al dietista responsable.
♿ Accesibilidad — Requisitos y Criterios Testables
WCAG 2.2 AA como mínimo no negociable. Los criterios de aceptación deben estar en los tickets de QA y ser verificables con axe DevTools, Lighthouse y prueba manual de teclado.
Criterios obligatorios
- AA Contraste texto normal ≥ 4.5:1. Verificar con axe DevTools en cada PR.
- AA Contraste UI components y bordes de focus ≥ 3:1.
-
AA
Toda la interacción navegable por teclado. Tab order lógico (DOM order). Focus visible en
:focus-visible, nuncaoutline: none. - AA Touch targets ≥ 44×44px. Verificar con Chrome DevTools > Layers en mobile viewport.
-
AA
Errores de formulario en texto (
role="alert"oaria-describedby). Nunca solo en color. -
AA
prefers-reduced-motion: deshabilitar todas las transiciones y animaciones. Testar en macOS: Accesibilidad > Reducir movimiento. -
AA
Imágenes y avatares decorativos:
aria-hidden="true". Contenidos:altdescriptivo. -
Sem
HTML semántico primero, ARIA solo cuando el semántico no es suficiente. No usar
role="button"en un<div>cuando se puede usar<button>.
Herramientas de test
npm i axe-core. Zero violations en cada PR.lighthouserc.json.✕ Anti-patrones y Migraciones
Patrones heredados de Bootstrap 3 que deben eliminarse en el rediseño. Cada anti-patrón tiene su alternativa correcta y notas de migración.
color: #9CA3AF; background: #F3F4F6
--text-secondary sobre blanco.
color: --text-secondary (#374151) — ratio 10.7:1
<button>OK</button>
<button>Guardar plan nutricional</button>
border: 2px solid red; /* sin mensaje */
role="alert".
border-color: --color-danger-500;
<span role="alert">Campo requerido</span>
background: #3B82F6;
padding: 11px 19px;
border-radius: 6px;
background: var(--color-primary-500);
padding: var(--space-3) var(--space-5);
border-radius: var(--radius-md);
Guía de migración desde Bootstrap 3
| Patrón Bootstrap 3 | Reemplazo NutriPlan DS | Notas |
|---|---|---|
btn btn-default | btn btn-ghost | Actualizar todos los botones de cancelar/secundarios |
btn btn-primary | btn btn-primary | Mantener semántica, actualizar estilos CSS |
label label-success | badge badge-optimal | Añadir dot de color para no-daltonismo |
alert alert-info | alert alert-info | Añadir role="status" o role="alert" según criticidad |
form-control | input | Altura 44px, borde 2px, focus ring obligatorio |
panel panel-default | patient-card | Añadir borde semántico, role="article", tabindex |
✓ QA Checklist — Code Review
Lista de verificación ejecutable en cada PR que afecte a componentes del sistema de diseño. Copiar al description del PR y marcar antes de solicitar review.
- Todos los colores usan variables CSS (
var(--color-...)), sin hex hardcodeados - Todos los espaciados son múltiplos de 8px (o 4px para ajustes micro)
- Tipografía usa escala definida (
--text-xsa--text-3xl) - Gradientes solo desde tokens
--gradient-* - Radios de borde desde tokens
--radius-*
- axe DevTools: cero violaciones
- Lighthouse Accessibility ≥ 95
- Todos los botones accesibles por teclado (Tab + Enter/Space)
- Focus visible en todos los interactivos (
:focus-visible, nuncaoutline:none) - Touch targets ≥ 44×44px en viewport móvil
- Contraste texto ≥ 4.5:1 (normal) o ≥ 3:1 (UI components)
- Errores de formulario en texto, no solo en color
prefers-reduced-motiondeshabilita animaciones
- Botones tienen todos los estados: default, hover, focus, active, disabled, loading
- Patient cards tienen
role="article",aria-labelytabindex="0" - Badges siempre incluyen dot de color + texto (no dependen solo del color)
- Alertas críticas tienen
role="alert"oaria-live="assertive" - Inputs tienen label visible asociado con
for/id - Select tiene
appearance:nonecon icono custom
- Testar en viewport 375px (iPhone SE) y 1440px
- Textos largos en patient cards no rompen layout (overflow ellipsis)
- Formularios apilados en 1 columna en mobile
- Botones de texto largo: mínimo 44px altura, texto no cortado
- Progress bars no desborden su contenedor
- Badges no se rompen si el texto del estado es largo
- Estado vacío (0 pacientes) diseñado y no muestra layout roto