Densidad y estados
Vacío, carga, error, feedback sonoro y densidad.
Inputs desnudos, líneas mínimas, seleccionado neutro
La propuesta cambia de dónde sale la señal: el reposo deja de dibujar una caja, el foco pone un anillo del color de enlace, y el error se dice en la línea y en la letra, sin fondo rojo.
Hasta 2026-09-20 la entrada era una línea bajo el texto, seleccionado usaba el par azul suave y la caja se separaba por color y sombra sin radio. Ahora la entrada no dibuja nada hasta el foco y seleccionado es gris.
La entrada no existe hasta que la tocas
En reposo hay un icono y un placeholder sobre un hairline al 12 % — nada más. Al foco aparece el anillo del color de enlace, que en oscuro es #7aa3f0 y en claro el navy, con un halo al 28 % del mismo color. El cursor también es de ese color.
El error no pinta el campo: pone la línea en el rojo del sistema y escribe abajo qué pasó. Un campo con fondo rojo sobre una isla oscura es una mancha, y además tapa lo que la persona escribió.
El campo, por estado
- Ancho
- 240px · alto 32px · radio pastilla
- Reposo
- inset 0 0 0 1px hairline fuerte · sin fondo
- Foco
- inset 0 0 0 1px anillo + 0 0 0 3px anillo al 28 %
- Error
- inset 0 0 0 1px --bad · texto en --bad debajo
- Anillo · oscuro
- #3B6FD1 · cursor #7AA3F0
- Anillo · claro
- #2457A8 · cursor #2457A8
Las líneas son mínimas y sólo separan filas
Blanco al 4 % en oscuro, negro al 4,5 % en claro. Entre filas de tabla y de lista, y nada más: no rodean, no dividen zonas, no hacen de borde de control.
Una línea que no separa dos filas de texto es una caja disfrazada. Esa es la prueba: si la sacas y sigue leyéndose, no hacía falta.
El separador
- Hairline · oscuro
- rgb(255 255 255 / 4%)
- Hairline · claro
- rgb(0 0 0 / 4.5%)
- Grosor
- 1px
- Dónde
- entre filas de texto, nunca alrededor
La caja lleva relleno y sombra, nunca las dos cosas con línea
Fondo sutil, una línea de luz arriba al 4 %, una sombra corta y una larga hacia abajo. Sin hairline: el relleno ya lo separa.
Relleno y hairline juntos es la caja dentro de la caja. Es la regla de 08 · Geometría, dicha acá para el caso concreto: la traza del agente, «siguiente paso» y la tarjeta de campaña son los tres objetos que la usan.
La caja con relleno
- Fondo · oscuro
- #1A1A1A
- Fondo · claro
- #F6F6F6
- Sombra · oscuro
- inset 0 1px 0 blanco 4 % · 0 1px 2px negro 40 % · 0 12px 28px -16px negro 70 %
- Sombra · claro
- 0 1px 2px negro 4 % · 0 12px 28px -16px negro 12 %
- Radio
- 16px
- Hairline
- ninguno — con relleno nunca
Seleccionado y hover son neutros
La fila, la pestaña y la navegación seleccionadas se marcan con blanco al 6 % (negro al 5 % en claro) y peso de texto. El hover es la mitad de eso. Nada de azul: el azul ya tiene sus cuatro trabajos.
Si un estado de selección se pinta con el color de acción, deja de distinguirse del botón que actúa, y la vista se llena de azul que no es accionable.
Los dos lavados
- Seleccionado · oscuro
- rgb(255 255 255 / 6%)
- Seleccionado · claro
- rgb(0 0 0 / 5%)
- Hover · oscuro
- rgb(255 255 255 / 4%)
- Hover · claro
- rgb(0 0 0 / 3%)
- Color
- ninguno — gris y peso de texto
El chip «Nuevo» es el único estado en navy
Lleva el navy de fondo con letra #dbe7ff en oscuro; en claro se invierte a #e6edf8 con letra navy. Es la tercera de las cuatro cosas que gastan el azul.
El resto de los chips vive en sus propios pares semánticos — éxito, aviso, peligro, persona — y ninguno usa el color de acción.
Canon vivo
Los estados de un control
Cinco estados, cinco pares de tokens. Deshabilitado baja el contraste y pierde la sombra al hover — un control muerto no reacciona. Seleccionado usa el par azul suave, nunca un borde.
Vacío, cargando y error son pantallas de primera clase
El vacío dice qué va a pasar y ofrece la acción que lo llena. El error dice qué falló y qué quedó a salvo, nunca un código pelado. La consulta breve usa señal y texto en una sola línea. Un esqueleto sólo aparece cuando conservar la geometría evita un salto real.
El trabajo largo pertenece a Inteligencia y declara tiempo transcurrido. Ninguno de estos estados lleva ilustración: son estados de trabajo, no momentos de marca.
Sin conversaciones
Cuando actives la campaña, las respuestas llegan aquí.
No se pudo enviar
WhatsApp rechazó el mensaje. El hilo quedó intacto.
La atención pendiente persiste hasta que alguien actúa
Un handoff abierto no es un unread común. La fila conserva un lavado frío-cálido, un filete de señal y el badge explícito mientras una persona todavía debe actuar. Marcar leídas no lo apaga.
Al tomarlo, el loop termina y aparece el dueño. Al cerrarlo, queda historial quieto. Con reduced motion, Requiere acción conserva el filete y el lavado estático: se pierde movimiento, nunca significado.
Centro de señales
Notificaciones
Handoff abierto · AltoAndes Cloud
Requiere DPA y propuesta enterprise custom.
Handoff tomado · Trenque Data
Ty coordina el horario con el contacto.
Handoff resuelto · Nexora
La conversación sigue como historial.
El toast es un recibo breve, no otra bandeja
Sonner conserva la región viva, la pila y el gesto para cerrar. Striker le pone papel blanco y tinta negra en ambos modos. Una línea semántica de 2px comunica el estado; fog separa elementos sin convertir cada notificación en una tarjeta de color.
Vive arriba a la derecha, alineado bajo la campana, y no se escala al apilarse. Una tarea que debe quedar, abrirse después o ser asignada vive en Notificaciones, no en un toast.
El título nombra el resultado. La segunda línea explica qué cambió o qué quedó a salvo. Los estados recuperables pueden ofrecer una sola acción; los procesos largos actualizan el mismo recibo de cargando a listo o error.
El tipo del toast decide su respuesta sonora: información, listo, advertencia o error. La bandeja conserva cues más largos para hitos y atención humana. Todo empieza apagado y el texto sigue explicando el estado cuando el sonido no está activo.
Reunión agendada
La invitación quedó enviada y registrada en el lead.
Necesita atención
El contacto pidió hablar con una persona.
No se pudo enviar
El hilo quedó intacto. Revisa la conexión antes de reintentar.
Un OS tiene vocabulario, no un único bip
El dashboard reconoce tres planos. Interacción hace táctiles los botones, links, tabs, toggles y overlays. Respuesta acompaña los estados de Sonner. Operación reserva las frases más largas para un hito comercial o algo que necesita una persona.
Hover, escritura y scroll permanecen silenciosos. El click corto puede repetirse; las señales de operación conservan el cooldown de 10 segundos, se agrupan en ráfagas y no superan tres por minuto.
Cada control reproduce exactamente la receta compartida por el dashboard. La Brand System no mantiene una copia aproximada: documenta y ejecuta el mismo contrato.
Interacción
Táctiles y muy cortos. Construyen la sensación de OS.
Respuesta
Confirman el estado que también muestra el toast.
Operación
Hitos y señales que pueden llegar sin un clic inmediato.
Muestra activa
Tap
interaction
Botones y acciones simples
minimal/tap · 16 ms
Elige un sonido para escucharlo.
La densidad es un interruptor, no un rediseño
Las dos columnas usan la misma clase: h-[var(--sds-control-height)]. La compacta solo agrega data-density="compact" al contenedor y el token hace el resto.
En pantallas táctiles la densidad se ignora hacia abajo: ningún control queda bajo los 44px, tampoco en modo compacto.
Cómoda · 40px
Compacta · 36px
- Seleccionado
- #E7EAFD / #142DB8
- Deshabilitado
- #E9EAF1 / #8A8C99 · 0.58
- Cargando
- #E7EAFD / #1E3CE6
- Error
- danger-surface / foreground
- Toast
- white / ink · fog hairlines · estado 2px
- Atención pendiente
- wash 5.2s · ring 2.8s · persiste
- Atención resuelta
- sin loop · queda historial
- Sonido
- 14 cues · 3 planos · opt-in personal
- Densidad compacta
- 28 / 36 / 44 px
- Piso táctil
- 44px siempre