DESIGN.md
# Veetcuna Design System
Referencia visual de Veetcuna para diseño, desarrollo y agentes de IA.
Portal: https://platform.veetcuna.com/docs/design-system
## Identidad y fuentes de verdad
Veetcuna utiliza una sola identidad visual en CRM, Finance, CMS, HR, Events,
Zuyo y superficies públicas. CRM es la referencia visual actual. El prefijo
histórico `Crm` de una primitiva visual no limita su uso al módulo CRM.
Los tokens y componentes implementan el sistema; este documento resume las
reglas aprobadas. Si el código difiere de una regla, registra la diferencia y
consulta la referencia especializada antes de reproducirla. No conviertas un
estilo legacy en un nuevo estándar.
- Tokens: `apps/platform/src/app/globals.css`.
- Tipografías: `apps/platform/src/app/fonts.ts`.
- Escalas y breakpoints: `packages/config-tailwind/tailwind.config.ts`.
- Inventario: `apps/platform/src/shared/COMPONENT_INVENTORY.md`.
- Contrato visual detallado: `.agents/skills/use-global-look-and-feel/SKILL.md`.
- Modales: `.agents/skills/use-global-look-and-feel/references/modal-standard.md`.
## Brandbook
Guía vigente v1.1 (septiembre de 2026):
https://platform.veetcuna.com/docs/design-system/brandbook
El brandbook define identidad, posicionamiento, voz, logo, fotografía y
principios de composición para la marca y su ecosistema. El design system
conserva la autoridad sobre tokens, componentes, estados y patrones de interfaz.
Navy es el ancla institucional; Light Blue es el acento de interacción digital.
La paleta extendida aporta contexto y los colores semánticos comunican estado.
El lenguaje conceptual conecta prioridad, oportunidad, capacidad, operación,
evidencia y evolución.
Los tamaños mínimos del logo son provisionales hasta su validación óptica.
La identidad digital vigente utiliza el nombre «veetcuna» en minúsculas y una
vicuña iridiscente de perfil a la derecha. El nombre se muestra en un ensayo
de Inter Semibold (peso 600); el lettering
de la imagen original se conserva para comparación en el brandbook. El símbolo
raster deriva de la referencia recibida el 29 de septiembre y no sustituye
un archivo maestro vectorial.
El brandbook muestra el isotipo solo, el logo principal con el isotipo a la
derecha y una variante secundaria a la izquierda. Documenta la versión a color
sobre blanco y las variantes monocromas sobre negro, navy y blanco.
La vista previa social de 1200 × 630 px usa el logo principal y el mensaje
«Evoluciona tu empresa con eficiencia», acompañado de la propuesta de AI y
productos, la descripción de la plataforma y «Explorar la plataforma». A la
derecha, el gráfico Prioridad → Capacidad → Operación reemplaza el isotipo; el
favicon usa solo el isotipo.
Los pills de módulo comparten el glass sutil de los campos, con iconos y acentos
cian, lila y rosa coordinados con la vicuña.
## Motivación y estilo
Buscamos un espacio de trabajo tangible, sereno y coherente. Nuestra interpretación
del neoesqueuomorfismo utiliza profundidad suave: superficies elevadas agrupan
contenido, campos glass aportan reflejos y transparencia, regiones inset agrupan
contenido y acentos contenidos señalan acciones.
La luz y la sombra sugieren materialidad; la jerarquía debe seguir siendo legible
sin ellas. Conserva contraste, foco, etiquetas y densidad adecuados al trabajo.
El neumorfismo inspira la continuidad entre fondo y controles. Las transparencias
pueden aportar contexto a overlays y cabeceras; el vidrio y los reflejos de las
referencias no implican convertir tablas o formularios en superficies translúcidas.
Motivación y estilo:
https://platform.veetcuna.com/docs/design-system/motivacion-y-estilo
## Color y superficies
Usa roles semánticos, sin copiar valores hexadecimales a cada pantalla.
| Rol | Token |
| --- | --- |
| Acción primary | `--vee-primary`, `--vee-primary-hover`, `--vee-on-primary` |
| Fondo de trabajo | `--vee-workspace` |
| Tarjeta elevada | `--vee-surface-raised` |
| Región hundida | `--vee-surface-inset` |
| Control | `--vee-surface-control` |
| Overlay y panel flotante | `--vee-surface-overlay`, `--vee-surface-floating` |
| Bordes | `--vee-border`, `--vee-border-subtle`, `--vee-border-floating` |
| Texto | `--vee-text-primary`, `--vee-text-secondary`, `--vee-text-muted` |
| Foco | `--vee-focus` |
| Carga | `--vee-skeleton`, `--vee-skeleton-strong` |
El tema global controla claro y oscuro. Usa las mismas primitivas en ambos.
Los acentos distinguen estados y contexto; no deben crear sistemas visuales
independientes por módulo. El primary de Platform es grafito translúcido con texto blanco (`#ffffff`),
compartido con el selector de tenant mediante `--vee-primary`,
`--vee-primary-hover` y `--vee-primary-reflection`.
Los acentos de selección y los colores semánticos son independientes.
## Tipografía y densidad
Inter es la familia de interfaz por defecto. Lexend Deca está disponible en la
configuración; no la conviertas en una segunda jerarquía global sin referencia.
Usa `font-semibold` como peso máximo: no `font-bold`, `font-extrabold` ni
`font-black`. Código e identificadores técnicos usan monoespaciada.
Los labels de campos compartidos y RizzUI usan gris atenuado (`--vee-text-muted`), 12 px, peso normal (400) y
altura de línea de 16 px, mediante los tokens globales `--vee-field-label-*`.
Conserva la densidad de CRM: títulos claros, etiquetas compactas, separación
consistente y texto secundario con contraste suficiente. Reutiliza tamaños,
alturas, radios y espaciado de las primitivas antes de añadir valores locales.
## Radios por función
Usa una escala común en todos los módulos, estados y temas:
| Función | Token | Valor |
| --- | --- | --- |
| Campos, textareas, selectores y opciones | `--vee-radius-control` | 14 px |
| Dropdowns y paneles compactos | `--vee-radius-panel` | 16 px |
| Cards y secciones | `--vee-radius-card` | 24 px |
| Chips de una línea, pills y acciones | `--vee-radius-pill` | 9999 px (cápsula) |
El radio de una cápsula se limita a la mitad de su altura: pills de distinta
altura conservan la misma geometría, aunque su radio efectivo en píxeles cambie.
El selector de módulo del shell (aunque se llame pill CRM) usa el radio de
control en su fondo SVG, hover y foco, igual que el selector de tenant.
Los chips con descripción usan el radio de control para no recortar contenido.
Los grupos de pestañas suman su padding al radio de los controles interiores.
Avatares, puntos de estado e iconos circulares conservan su geometría circular;
los modales siguen su contrato especializado. Reutiliza tokens y primitivas,
no valores locales nuevos. Las superficies permiten adaptar el radio mediante
`className` cuando el rol del contenedor lo requiere.
## Componentes preferidos
| Necesidad | Componente e importación |
| --- | --- |
| Acciones | `CrmActionButton`, `CrmActionLink`, `CrmActionAnchor`, `CrmActionDownload` desde `@modules/crm/components/action-button` |
| Superficies | `CrmInsetSurface`, `CrmRaisedSurface`, `getCrmPlasticSurfaceStyle` desde `@modules/crm/components/inset-surface` |
| Campos | `CrmCompactInput`, `CrmCompactTextarea`, `CrmFieldShell`, `CrmChipSelectField` desde `@modules/crm/components/form-ui` |
| Tablas | `CrmEditableTable` (default) desde `@modules/crm/components/editable-table` |
| Selección booleana | `CrmCheckboxButton` desde `@modules/crm/components/checkbox-button` |
| Secciones | `CrmInsetTabSwitcher` desde `@modules/crm/components/inset-tab-switcher` |
| Personas | `UserAvatar` (default) desde `@shared/components/user-avatar` |
| Modales | `PlatformFormModal`, `PlatformModalBody` desde `@shared/components/platform-form-modal` |
| Ayuda de campos | `CrmInfoPopover` desde `@modules/crm/components/info-popover` |
| Explicación breve | `PlatformTooltip` desde `@shared/components/platform-tooltip` |
Para asignar responsables usa `AdvisorMenu`, no un selector genérico. Revisa el
inventario para cargas de archivos, fechas, asociaciones y patrones de negocio.
Usa RizzUI/template cuando no exista una primitiva más específica.
## Composición y elevación
Usa `--vee-shadow-raised`, `--vee-shadow-floating`, `--vee-shadow-inset` y
`--vee-shadow-plastic` mediante las primitivas compartidas. No copies recetas
de sombras. Las tarjetas agrupan contenido; las superficies inset agrupan
controles. La elevación flotante corresponde a contenido superpuesto.
Las tablas reciben las acciones por `headerActions`. Los formularios mantienen
el estado en su propietario común más cercano. Los modales usan el shell global,
con cabecera y acciones fuera del cuerpo desplazable. Respeta `isBusy` y
`dismissible={false}`; no añadas rutas de cierre a un onboarding obligatorio.
## Estados, acciones y accesibilidad
Documenta y conserva estados normal, foco, hover, seleccionado, deshabilitado,
carga y error cuando correspondan. No uses solamente color para explicar estados.
Proporciona etiquetas accesibles y foco visible; verifica navegación por teclado.
Usa enlaces para navegación segura y botones para acciones. El icono de abrir
enlace (`PiArrowSquareOutBold` o equivalente) puede aparecer al final de un
control de navegación con estilo de texto (`tone="text"`, `<a>`, `Link`, etc.)
solo si abre una pestaña nueva (`target="_blank"`) y no tiene ya un icono a la
izquierda. No lo pongas en controles con apariencia de botón, descargas ni
enlaces que permanecen en la misma pestaña; decide por apariencia y comportamiento,
no solo por la etiqueta HTML. Exportar, descargar o mutar no debe activarse
mediante prefetch. Usa `CrmActionDownload` para descargas nativas.
`UserAvatar` preserva fotos y proporciona fallback ilustrado: pasa un ID estable
como `seed`. No recrees avatares con iniciales como sustituto global.
Los uploads ocultan el input nativo detrás de un control accesible y muestran
formatos, límites y nombres seleccionados. La validación visual no sustituye la
validación de archivos del servidor en flujos reales.
## Responsive y movimiento
Usa los breakpoints del Tailwind compartido. Evita anchos rígidos que desborden;
las columnas se apilan y las tablas usan su presentación responsive existente.
Conserva acciones alcanzables y scroll del cuerpo en modales. Comprueba al menos
390, 768 y 1440 px y ambos temas.
Las transiciones explican cambios de estado. Respeta `prefers-reduced-motion`;
reutiliza animaciones compartidas en vez de crear efectos decorativos por módulo.
## Reglas de contribución
Busca primero en shared, core y primitivas globales históricas de CRM. Promueve o
adapta cuando corresponda; evita duplicados y archivos grandes. Registra cada
componente nuevo en el inventario, incluso si su alcance es local.
Una primitiva global nueva o modificada actualiza su ficha y ejemplo del portal
en el mismo cambio. Actualiza este documento cuando cambie una regla global.
Las demos usan datos ficticios, estado local y nunca ejecutan operaciones reales.
Los componentes de negocio sin demo se identifican como Solo referencia.
`AGENTS.md` mantiene las reglas de trabajo, validación, permisos y despliegue.
Este documento no autoriza Chrome, commits, despliegues ni cambios de datos.
La composición principal separa el nombre y el símbolo 72 px en el arte base.
El isotipo mide el 220 % de la altura visible de las letras.
El wordmark digital mide 150 × 41 unidades; los descriptores
de módulo se colocan después sin alterar el logo corporativo.
## Material global de campos
Inputs, contraseñas, textareas, selectores y multiselectores utilizan glass:
fondo blanco translúcido al 92 % en claro (98 % en hover), más claro que la tarjeta,
reflejo diagonal, borde luminoso, blur y sombra exterior suave.
`CRM_GLASS_INPUT_SURFACE_CLASS_NAME` y su alias histórico
`CRM_INSET_INPUT_SURFACE_CLASS_NAME` comparten `vee-glass-control`.
El baseline RizzUI y los inputs nativos existentes usan el mismo material.
Tokens: `--vee-glass-fill`, `--vee-glass-fill-hover`, `--vee-glass-border`,
`--vee-glass-reflection` y `--vee-shadow-glass`, con variantes claras y oscuras.
Mantén foco visible, error rojo, texto legible y atenuación de deshabilitado.
Los importes compuestos usan una sola capa glass, con moneda transparente.
Los menús conservan mayor opacidad para que el fondo no dificulte su lectura.
El foco de campos refuerza un único borde neutro (slate) al navegar con teclado;
no añade un contorno exterior separado. Los enlaces conservan foco de teclado neutro. No mostrar
halos celestes ni anillos al abrir dropdowns o activar enlaces con el mouse.
Las opciones activas de todos los menús de módulos, submenús y navegación colapsada reutilizan `CRM_PRIMARY_ACTIVE_SURFACE_CLASS_NAME`: grafito global con texto e iconos blancos. No crear variantes locales celestes para la selección de navegación.
Los campos compartidos admiten `labelIcon`: icono decorativo de 12 px junto al label mediante `CrmFieldLabel`, con el mismo gris y sin alterar el nombre accesible.
La selección de hora reutiliza `PlatformTimeField`: campo glass con reloj, hora de 12 horas y menú compacto con horarios cada media hora, AM/PM y entrada exacta; persiste HH:mm en formato de 24 horas; evitar el picker nativo del navegador en el campo de fecha compartido.
Se retira el celeste brillante de paletas y acentos del producto por su escaso contraste sobre blanco. Usar verde petróleo (`#0f766e`) en su lugar. Las series y los iconos de gráficos deben conservar contraste suficiente sobre superficies claras; sus leyendas usan texto neutro legible y no dependen sólo del color.
`PlatformDateTimeRangeField` reúne el día e Inicio/Fin dentro de una tarjeta compacta. La duración aparece centrada entre ambas horas como «1h» o «45 min», interrumpiendo una línea horizontal gris suave (slate-300 al 80%, slate-600 en oscuro) alineada con el centro del texto. Cada hora abre un modal independiente con Cancelar/Listo; Fin permite indicar el día siguiente. Admite `hideLabel`, límites de fecha, estado deshabilitado y duración efectiva suministrada por el dominio para zonas horarias. `PlatformDateTimeField` admite `embedded` para componer la fecha sin duplicar superficies. La demo Campos permite editar el intervalo.
La variante `compact` de `PlatformDateTimeField` muestra día destacado y mes al lado en una tarjeta horizontal de 56 px de alto. Con `showTime={false}` y `allowClear` sirve para fechas opcionales: «Sin fecha» en el modal elimina el valor. Conserva límites, nombre accesible y edición con Cancelar/Listo. La demo Campos incluye esta variante.
`fullWidth` permite que la tarjeta compacta ocupe el ancho de su contenedor. En selectores de personas, reutilizar la presentación compartida de avatar, nombre y segunda línea de contexto (rol para socios, correo para cuentas). En HR, `PartnerSelectField` admite `label` para seleccionar también al organizador con la misma presentación.
`CrmBooleanChipSwitch` admite `tone="primary"` para el estado activo grafito, con la superficie y tokens globales de las acciones primary. `fullWidth` lo alinea al ancho del contenedor y mantiene etiqueta e interruptor en extremos opuestos. El estado apagado conserva el gris neutro. La demo Selectores incluye edición y estado deshabilitado de esta variante.
`PlatformIconSelectField` presenta un disparador cuadrado glass de 44 px alineado con los inputs. Recibe `value`, `onChange`, `options` (value, label, icon), `label` y `disabled`. Al pulsarlo abre una cuadrícula de cuatro columnas mediante CardSelectChip con portal; cada icono tiene nombre accesible, color semántico y cierre al elegir; la opción activa usa selección primary e icono blanco para conservar contraste. El disparador centra su icono en ambos ejes. La demo Selectores incluye ocho opciones.
`CRM_ACTION_ICON_BUTTON_CLASS_NAME` compone la acción text global como control de icono de 24 × 24 px, sin padding adicional, para esquinas de tarjetas. El icono se centra en ambos ejes y el encabezado usa alineación central con el título. Las acciones de toolbar conservan su tamaño habitual. La demo Botones muestra el patrón.
`PlatformIconSelectField` permite `color` en cada opción: aplica el fondo plástico de color y el icono blanco tanto a las opciones como al disparador. La opción seleccionada mantiene su color y se distingue por un anillo grafito. La demo Selectores muestra los ocho colores.
Los grids `ResponsiveDashboardGrid` / `CrmResponsiveDashboardGrid` admiten
`flushEdges` para alinear sus bordes con tablas/cards contiguas. Mantiene gaps
internos y apilado móvil; sólo elimina padding exterior del grid. Documentado y
demostrado en Navegación.
Los calendarios por día reutilizan `PlatformCalendarLanes` / `PlatformCalendarDayLane`
y `PlatformCalendarCard` de `shared/components/calendar`: lanes inset, cards
elevadas, horario Inicio–duración–Fin y participantes con `UserAvatar`.
HR y CRM comparten la composición semanal y mensual (filas de siete días),
con scroll horizontal accesible y fechas civiles controladas por cada módulo.
Ficha y demo: Navegación.