Fundamentos del sistema de diseño
Propósito
Este documento define las primitivas visuales y espaciales del sistema de diseño de GreenGrass: espaciado, color, tipografía, cuadrícula, elevación, iconografía y movimiento. De estos fundamentos salen todos los componentes y todas las pantallas de la plataforma.
Todavía no hay marca. Este sistema de diseño está hecho para que se le apliquen temas: cada organización pone su propia identidad visual encima de estos fundamentos estructurales. Los fundamentos definen las reglas; el tema define el aspecto. En theming-strategy.md está cómo funciona la identidad visual por organización.
Tokens de diseño
Todos los valores fundamentales se expresan como tokens de diseño: valores con nombre, reutilizables, que cada tema puede sobrescribir. Los tokens se implementan como propiedades personalizadas de CSS (variables) y se referencian por nombre, nunca con el valor crudo.
/* Nunca así: */
padding: 16px;
color: #2563eb;
/* Siempre así: */
padding: var(--space-4);
color: var(--color-primary);
Esa indirección es lo que permite aplicar temas por organización sin tocar el código de los componentes.
Espaciado
Escala
Una unidad base de 8px con una escala geométrica. Todo valor espacial del sistema —relleno, margen, separación, tamaño de los componentes— usa esta escala.
| Token | Valor | Uso |
|---|---|---|
--space-0 |
0px | Reinicio, estados plegados |
--space-px |
1px | Bordes, separadores |
--space-0.5 |
2px | Espaciado en línea ajustado |
--space-1 |
4px | Relleno de iconos, espaciado de distintivos |
--space-2 |
8px | Relleno interno compacto, separaciones pequeñas |
--space-3 |
12px | Separación en línea por defecto, relleno de elementos de lista |
--space-4 |
16px | Relleno estándar de componentes, relleno de tarjetas |
--space-5 |
20px | Relleno holgado de secciones |
--space-6 |
24px | Separación entre secciones, espaciado entre campos de formulario |
--space-8 |
32px | Separaciones grandes entre secciones |
--space-10 |
40px | Espaciado entre secciones de página |
--space-12 |
48px | Separaciones mayores de maquetación |
--space-16 |
64px | Márgenes de página (escritorio) |
--space-20 |
80px | Espaciado amplio de maquetación |
--space-24 |
96px | Referencia de altura máxima de componente |
Pautas de uso
- Relleno interno de los componentes:
--space-3(compacto) o--space-4(estándar) - Espacio entre campos de formulario:
--space-6 - Espacio entre secciones:
--space-8o--space-10 - Relleno de tarjetas:
--space-4(móvil) o--space-6(escritorio) - Margen de página (móvil):
--space-4 - Margen de página (escritorio): de
--space-8a--space-16
Áreas táctiles
Tamaño mínimo del área táctil en móvil: 44x44px (WCAG 2.1 AA). Es un piso duro: ningún elemento interactivo puede ser más pequeño.
En el modo de campo, las áreas táctiles suben a 48x48px como mínimo. El modo de campo está pensado para usarse con una mano en condiciones difíciles: las áreas más grandes reducen los errores.
Color
Arquitectura de color
El sistema de color tiene tres capas:
- Paleta primitiva: valores de color crudos (códigos hex). No se usan directamente en los componentes.
- Tokens semánticos: colores con nombre que describen su propósito, no su matiz. Son los que referencian los componentes.
- Sobrescrituras de tema: personalizaciones por organización que reasignan los tokens semánticos a otros valores primitivos.
Tokens de color semánticos
| Token | Propósito | Valor predeterminado | Notas |
|---|---|---|---|
| Superficie | |||
--color-surface |
Fondo de la página o la aplicación | #ffffff |
|
--color-surface-raised |
Tarjetas, paneles, modales | #ffffff |
Se diferencia por la sombra de elevación |
--color-surface-sunken |
Áreas hundidas, fondos de campos | #f8fafc |
|
--color-surface-overlay |
Fondos de superposición de modales y paneles laterales | rgba(0,0,0,0.5) |
|
| Texto | |||
--color-text |
Texto principal | #0f172a |
|
--color-text-secondary |
Texto secundario o de ayuda | #475569 |
|
--color-text-tertiary |
Texto de ejemplo, texto deshabilitado | #94a3b8 |
|
--color-text-inverse |
Texto sobre fondos oscuros | #ffffff |
|
--color-text-link |
Enlaces | --color-primary |
|
| Principal | |||
--color-primary |
Acciones principales, estados activos | #2563eb |
La organización puede aplicarle su marca |
--color-primary-hover |
Estado del principal al pasar el cursor | #1d4ed8 |
|
--color-primary-active |
Estado del principal al presionarlo | #1e40af |
|
--color-primary-subtle |
Fondos del principal, distintivos | #eff6ff |
|
--color-on-primary |
Texto sobre el principal | #ffffff |
Se calcula solo para el contraste |
| Estado | |||
--color-success |
Estados de éxito, confirmaciones | #16a34a |
|
--color-success-subtle |
Fondos de éxito | #f0fdf4 |
|
--color-warning |
Advertencias, requiere atención | #d97706 |
|
--color-warning-subtle |
Fondos de advertencia | #fffbeb |
|
--color-error |
Errores, acciones destructivas | #dc2626 |
|
--color-error-subtle |
Fondos de error | #fef2f2 |
|
--color-info |
Informativo | #2563eb |
|
--color-info-subtle |
Fondos informativos | #eff6ff |
|
| Interactivo | |||
--color-border |
Bordes por defecto | #e2e8f0 |
|
--color-border-strong |
Bordes enfatizados | #cbd5e1 |
|
--color-border-focus |
Anillos de foco | --color-primary |
|
--color-input-border |
Bordes de los campos de formulario | #cbd5e1 |
|
--color-disabled |
Fondos deshabilitados | #f1f5f9 |
|
--color-disabled-text |
Texto deshabilitado | #94a3b8 |
|
| Sincronización y conectividad | |||
--color-sync-connected |
En línea o sincronizado | #16a34a |
Punto verde |
--color-sync-stale |
Conectado pero desactualizado | #d97706 |
Ámbar |
--color-sync-offline |
Sin conexión | #94a3b8 |
Gris |
--color-sync-error |
Error de sincronización | #dc2626 |
Rojo |
| Visualización de datos | |||
De --color-chart-1 a --color-chart-8 |
Series de gráficas | Paleta secuencial | Se distinguen en simulaciones de daltonismo |
Accesibilidad del color
- Contraste del texto: todo el texto cumple el mínimo de WCAG 2.1 AA: 4.5:1 en texto normal, 3:1 en texto grande (18px+ o 14px+ en negrita).
- Contraste de los componentes de interfaz: los elementos interactivos (botones, campos, iconos) cumplen 3:1 frente a los colores adyacentes.
- Nada codificado solo con color: toda información que transmite el color se transmite además con texto, icono o patrón. Un distintivo rojo dice además "Error". Un punto verde dice además "Conectado" (o lo dice su globo de ayuda).
- Seguridad para el daltonismo: la paleta de visualización de datos se prueba contra simulaciones de deuteranopía, protanopía y tritanopía. Los colores de estado (éxito, advertencia, error) se diferencian por matiz y luminancia, no solo por matiz.
- Modo oscuro: todos los tokens tienen equivalente en modo oscuro. Ver
theming-strategy.md.
Modo de alto contraste
Para quienes necesitan el máximo contraste (ajuste en las preferencias de accesibilidad):
- Todos los bordes pasan a
2px soliden vez de1px solid - Los colores de texto se reducen a dos valores: texto principal (
#000000) y texto secundario (#333333) - Los colores de fondo se reducen a
#ffffffy#f5f5f5 - Los anillos de foco pasan a
3px solidcon desplazamiento - Todos los colores de fondo sutiles se vuelven más marcados
Tipografía
Pila de fuentes
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji';
--font-mono: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas,
'DejaVu Sans Mono', monospace;
Por qué fuentes del sistema: cero carga de fuentes. Ninguna solicitud de red, nada de FOIT/FOUT, ningún salto de maquetación. El texto se dibuja al instante con la fuente que el dispositivo ya tiene, algo crítico en celulares de gama baja con conexiones lentas, donde descargar una fuente propia añade segundos al first contentful paint. Las fuentes del sistema también tienen el mejor soporte nativo de escrituras: San Francisco en Apple, Segoe UI en Windows, Roboto en Android y Noto Sans como alternativa de reserva amplia de Unicode, que cubre de forma nativa el latino, el tailandés, el hindi, el árabe y los diacríticos del portugués.
Sin fuentes propias, sin dependencia de Google Fonts. Es una restricción deliberada. Todo dispositivo de nuestros mercados objetivo trae una sans-serif de sistema de buena calidad, optimizada para su propia pantalla. Cargar una fuente web para reemplazarla cambia rendimiento por control de marca, y todavía no hay marca. Si más adelante se adopta una tipografía de marca, se puede poner al frente de la pila sin tocar el código de ningún componente.
Cobertura de escrituras: las pilas de fuentes del sistema manejan de forma nativa el renderizado multiescritura. system-ui en Android resuelve a Roboto para el latino y a Noto Sans Thai/Devanagari/Arabic para esas escrituras. En iOS, San Francisco delega en la fuente adecuada de la plataforma según la escritura. No hace falta ninguna sobrescritura de fuente por idioma: lo resuelve el sistema operativo.
Escala tipográfica
Una escala modular con razón 1.25 (tercera mayor), anclada en el texto de cuerpo de 16px.
| Token | Tamaño | Interlineado | Peso | Uso |
|---|---|---|---|---|
--text-xs |
12px | 16px | 400 | Distintivos, epígrafes, marcas de tiempo |
--text-sm |
14px | 20px | 400 | Texto de ayuda, rótulos secundarios, celdas de tabla |
--text-base |
16px | 24px | 400 | Texto de cuerpo, campos de formulario, elementos de lista |
--text-lg |
18px | 28px | 500 | Cuerpo enfatizado, títulos de tarjeta |
--text-xl |
20px | 28px | 600 | Encabezados de sección, títulos de diálogo |
--text-2xl |
24px | 32px | 600 | Títulos de página, encabezados de los widgets del panel |
--text-3xl |
30px | 36px | 700 | Encabezados mayores |
--text-4xl |
36px | 40px | 700 | Métricas destacadas del panel |
--text-5xl |
48px | 48px | 700 | Total del termómetro de recaudación, resultados de la noche electoral |
Pesos tipográficos
| Token | Peso | Uso |
|---|---|---|
--font-normal |
400 | Texto de cuerpo, la mayoría del contenido |
--font-medium |
500 | Texto enfatizado, elementos de navegación, rótulos de formulario |
--font-semibold |
600 | Encabezados, texto de los botones, títulos de tarjeta |
--font-bold |
700 | Encabezados mayores, métricas destacadas |
Reglas de tipografía
- Largo de línea: máximo 75 caracteres por línea en el texto de cuerpo (legibilidad). En escritorio, las áreas de contenido limitan el ancho; en móvil, el ancho completo es aceptable con los tamaños de fuente móviles.
- Espaciado entre párrafos:
--space-4. - Espaciado de los encabezados:
--space-8arriba y--space-3debajo (más espacio arriba para separarlos del contenido anterior, menos abajo para unirlos con el que sigue). - Nada de texto menor de 12px. Ni siquiera en los epígrafes y las marcas de tiempo: 12px es el piso. En pantallas móviles de baja resolución, un texto más chico no se lee.
- Tamaños tipográficos del modo de campo: el texto de cuerpo en modo de campo es
--text-lg(18px) como mínimo. Los rótulos de formulario son--text-base(16px). Los botones de opción de respuesta usan--text-lg. Más grandes que lo estándar porque quien está en campo puede estar leyendo en malas condiciones.
Cuadrícula
Cuadrícula de maquetación de escritorio
┌────────────────────────────────────────────────────────────────────────────────────┐
│ Barra lateral (240px / 64px) │ Contenido (fluido) │
│ │ │
│ │ ┌──────────────────────────────────────────────┐ │
│ │ │ Cuadrícula de 12 columnas │ │
│ │ │ Separación entre columnas: --space-6 (24px) │ │
│ │ │ Ancho máximo: 1280px │ │
│ │ │ Margen: auto (centrado) │ │
│ │ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────────────────┘
- Ancho de la barra lateral: 240px desplegada, 64px plegada (la preferencia se guarda)
- Área de contenido: fluida, llena el espacio restante
- Cuadrícula de contenido: 12 columnas dentro del área de contenido
- Separación entre columnas:
--space-6(24px) - Ancho máximo del contenido: 1280px (centrado en el área de contenido cuando el viewport lo supera)
- Relleno del contenido:
--space-8a cada lado
Cuadrícula de maquetación móvil
┌──────────────────────────────────────────────┐
│ Contenido a ancho completo │
│ │
│ Cuadrícula de 4 columnas │
│ Separación entre columnas: --space-4 (16px) │
│ Margen de página: --space-4 (16px) │
│ │
└──────────────────────────────────────────────┘
- Sin barra lateral: contenido a ancho completo
- 4 columnas dentro del área de contenido
- Separación entre columnas:
--space-4(16px) - Margen de página:
--space-4(16px) a cada lado
Cuadrícula de tableta
- 8 columnas
- Separación entre columnas:
--space-4(16px) - Barra lateral: plegada por defecto, se despliega como superposición
Patrones de disposición comunes
| Patrón | Columnas en escritorio | Tableta | Móvil |
|---|---|---|---|
| Contenido a ancho completo | 12 | 8 | 4 |
| Contenido + detalle en barra lateral | 8 + 4 | 5 + 3 | Apilado (completo + completo) |
| Formulario de dos columnas | 6 + 6 | 4 + 4 | Apilado |
| Panel de tres columnas | 4 + 4 + 4 | 4 + 4 (la tercera pasa de línea) | Apilado |
| Página de ajustes | 3 (navegación) + 9 (contenido) | Completa (pestañas en vez de navegación) | Completa |
Elevación
La elevación (la profundidad de la sombra) comunica el apilamiento: qué está encima de qué.
| Token | Sombra | Uso |
|---|---|---|
--elevation-0 |
ninguna | Elementos planos, filas de tabla |
--elevation-1 |
0 1px 2px rgba(0,0,0,0.05) |
Tarjetas, superficies elevadas |
--elevation-2 |
0 2px 4px rgba(0,0,0,0.1) |
Desplegables, globos contextuales |
--elevation-3 |
0 4px 8px rgba(0,0,0,0.1) |
Modales, paneles laterales, paneles flotantes |
--elevation-4 |
0 8px 16px rgba(0,0,0,0.15) |
Avisos emergentes, snackbars |
Reglas
- Las tarjetas usan
--elevation-1. Al pasar el cursor (si son interactivas), suben a--elevation-2. - Los desplegables y los globos contextuales usan
--elevation-2. - Los modales y los paneles laterales usan
--elevation-3. - Los avisos emergentes usan
--elevation-4(el más alto: flotan por encima de todo). - La barra lateral usa
--elevation-1en escritorio. En móvil (como superposición),--elevation-3. - La elevación no se acumula: si un modal contiene un desplegable, el desplegable usa
--elevation-3(el mismo que el modal, pero dibujado encima con z-index), no--elevation-5.
Bordes y radio
| Token | Valor | Uso |
|---|---|---|
--radius-none |
0px | Sin redondeo (celdas de tabla, algunos campos) |
--radius-sm |
4px | Distintivos, etiquetas, elementos pequeños |
--radius-md |
6px | Botones, campos, tarjetas |
--radius-lg |
8px | Tarjetas grandes, modales, paneles laterales |
--radius-xl |
12px | Cuadros de diálogo, paneles flotantes |
--radius-full |
9999px | Avatares, distintivos en forma de píldora, botones circulares |
Reglas
- Botones:
--radius-md - Campos de formulario:
--radius-md - Tarjetas:
--radius-lg - Modales:
--radius-xl(solo las esquinas superiores en las hojas inferiores de móvil) - Avatares:
--radius-full - Etiquetas y distintivos:
--radius-sm
Iconografía
Sistema de iconos
- Biblioteca de iconos: Lucide Icons (código abierto, licencia MIT, cuadrícula consistente de 24px, más de 1000 iconos). Autoalojada, sin dependencia externa.
- Tamaño por defecto: 20px (dentro de una caja de 24px que deja relleno para el área táctil)
- Grosor de trazo: 1.5px (coincide con el peso visual de la fuente Inter en los tamaños de cuerpo)
Tamaños de icono
| Token | Tamaño | Uso |
|---|---|---|
--icon-xs |
14px | En línea con texto pequeño, iconos de distintivo |
--icon-sm |
16px | En línea con el texto de cuerpo, iconos de los campos de formulario |
--icon-md |
20px | Elementos de navegación, iconos de botón, iconos de los elementos de lista |
--icon-lg |
24px | Encabezados de sección, iconos sueltos |
--icon-xl |
32px | Iconos de funcionalidad, ilustraciones de estado vacío |
--icon-2xl |
48px | Ilustraciones destacadas, incorporación |
Reglas de los iconos
- Los iconos direccionales se reflejan en RTL. Las flechas, los chevrones y los indicadores direccionales se invierten en horizontal en modo RTL. Los iconos no direccionales (marcas de verificación, estrellas, etc.) no se invierten. El componente de icono lo resuelve reconociendo las propiedades lógicas de CSS.
- El color sigue al texto. Los iconos heredan por defecto el color del texto adyacente. Solo se sobrescribe en los indicadores de estado (marcas verdes, errores en rojo).
- Nada de iconos decorativos. Todo icono transmite un significado o ayuda a navegar. Los iconos van acompañados de rótulos de texto (salvo en los botones de solo icono, que exigen un globo de ayuda o un aria-label).
- Iconos del modo de campo:
--icon-lg(24px) como mínimo. La mayoría de los iconos de acción usan--icon-xl(32px) para que sea más fácil tocarlos.
Movimiento
Transiciones predeterminadas
| Token | Duración | Curva de aceleración | Uso |
|---|---|---|---|
--duration-fast |
100ms | ease-out | Estados al pasar el cursor, anillos de foco |
--duration-normal |
200ms | ease-in-out | Apertura y cierre de paneles, cambio de pestañas |
--duration-slow |
300ms | ease-in-out | Apertura y cierre de modales, deslizamiento del panel lateral |
--duration-slower |
500ms | ease-in-out | Transiciones de página, animaciones de elementos grandes |
Reglas de movimiento
- Respeta
prefers-reduced-motion. Cuando el sistema operativo está configurado para reducir el movimiento, todas las transiciones son instantáneas (duration: 0ms). No es opcional: es un requisito de accesibilidad. - Nada de rebotes ni física de resorte. El movimiento es funcional, no lúdico. Los elementos se mueven para comunicar cambios de estado (un panel que se abre, un elemento que se añade o se quita, una carga), no para entretener.
- Modo de campo: movimiento mínimo. En el modo de campo, las transiciones bajan a
--duration-fasto se eliminan. En campo se trabaja rápido y la interfaz tiene que seguirle el paso. - Indicadores de carga: un pulso o una rotación sutil, no animaciones complejas. El icono de sincronización gira; los esqueletos de carga laten. Hasta ahí llegan los estados de carga animados.
- Nada de animaciones de entrada al cargar la página. El contenido aparece de inmediato. Las entradas escalonadas pierden tiempo y empeoran el rendimiento percibido, justo lo contrario de lo que necesitan los dispositivos de gama baja.
Escala de z-index
Una escala de z-index acotada para evitar las guerras de z-index:
| Token | Valor | Uso |
|---|---|---|
--z-base |
0 | Contenido por defecto |
--z-raised |
10 | Encabezados fijos, botones de acción flotantes |
--z-dropdown |
20 | Desplegables, globos contextuales, globos de ayuda |
--z-sidebar |
30 | Barra lateral (cuando se superpone al contenido en móvil) |
--z-modal |
40 | Modales, paneles laterales |
--z-toast |
50 | Avisos emergentes |
--z-overlay |
60 | Fondos de superposición de los modales |
--z-panic |
100 | Pantalla de bloqueo del botón de pánico (siempre encima) |
Densidad
El sistema de diseño admite dos modos de densidad para adaptarse a contextos distintos:
| Modo | Uso | Diferencias |
|---|---|---|
| Predeterminada | Móvil, modo de campo, interfaces de cara al voluntariado | Áreas táctiles más grandes (44px+), más espaciado, texto más grande |
| Compacta | Administración en escritorio, pantallas con mucha información | Filas más bajas (36px), espaciado más ajustado, las tablas de datos muestran más en pantalla |
La densidad no la configura quien usa el sistema: la determinan el dispositivo y el contexto:
- Móvil: siempre densidad predeterminada
- Modo de campo: siempre densidad predeterminada
- Escritorio: densidad compacta en las vistas de lista y las tablas de datos, densidad predeterminada en los formularios y las áreas de contenido
Preguntas abiertas
-
Personalización de los iconos. ¿Deberían las organizaciones poder personalizar el juego de iconos (por ejemplo, reemplazar el icono de "persona" por defecto con una alternativa culturalmente adecuada)? Prioridad baja, pero relevante en contextos del Sur Global.
-
Estilos de impresión. ¿Debería el sistema de diseño incluir tokens específicos para impresión? Algunas pantallas se imprimirán (informes de cumplimiento, estados de fin de año, hojas de registro de entrada a los eventos). Los tokens de impresión quitarían el color, ajustarían el espaciado al papel y usarían fuentes serif para la legibilidad.