Higinio Bruma Lavanda · v1.0

Sistema de diseño · Higinio Sosa

Bruma Lavanda

Sistema de marca light-first, calmo y preciso, para los productos de seguimiento, datos personales y lectura larga de Higinio Sosa: web, móvil, dashboards y landing pages. La calma viene del orden, no de la suavidad: cada dato está donde se espera y nada compite por la atención.

01

Orden antes que adorno

Todo tiene su sitio. La jerarquía la ponen el espacio, el borde y el peso tipográfico, no la decoración.

02

Legibilidad primero

Una tipografía de UI pensada para baja visión y contrastes holgados en los dos modos. Si un dato no se lee a la primera, el diseño falló.

03

Un acento a la vez, y pequeño

La lavanda #5b578a marca la acción, el estado activo y el foco. Nunca rellena áreas grandes ni compite consigo misma.

04

La niebla es el fondo, no el texto

Fondos planos y fríos, superficies opacas. Nada difuminado: sin blur, sin degradados, sin texto sobre veladuras.

05

Movimiento corto y preciso

La animación confirma lo que pasó y se aparta. Es breve porque el control no espera.

Regla de oro

Los componentes consumen solo tokens semánticos (--surface, --accent, --link…), nunca hex directos. El cambio de tema es un atributo, no un refactor.

Fundamentos

Voz y contenido

Serena, precisa y directa: frases cortas que informan, el dato antes que el adjetivo. Clara sin ser seca, amable sin ser melosa.

ReglaSíNo
Idioma: español LatAm neutro, de "tú"Guarda tu registroGuarde usted su registro
Sentence case en todoExportar datosExportar Datos / EXPORTAR
Botones imperativos y concretos, de 1–3 palabrasRegistrarHaz clic aquí para registrar
Precisión: qué pasó y con qué cifraGuardado a las 14:32¡Listo!
Sin urgencia: los plazos se dicen con calmaPuedes registrarlo hasta las 23:00¡Última oportunidad!
Sin culpa: se cuenta lo hecho, no lo que falta3 de 8 vasosTe faltan 5
Errores que dicen qué hacer, sin dramatizarNo se pudo guardar. Revisa tu conexión e inténtalo de nuevo.Error
Estados vacíos que dicen qué falta y cómo empezarAún no hay registros esta semana. Añade el primero desde «Registrar».No hay datos
Logros sobrios, con el dato y sin exclamaciones ni emojiMeta del día cumplida: 8 de 8¡Lo lograste! 🎉
Voz en segunda personaTus datosNuestros planes para ti

Fundamentos

Colores

Un solo acento — lavanda #5b578a, apagada y grisácea — sobre neutrales de niebla derivados de la tinta #2c2b38. Haz clic en cualquier muestra para copiar su variable.

Primario — Lavanda

Neutrales — Niebla

Semánticos — ramp de 3 tonos

Cada estado tiene el sólido (-500), el fondo suave (-100) y el tono de texto AA sobre ese fondo (-text). Los cuatro van algo desaturados para convivir con la niebla; info es un azul pizarra propio, para no diluir el acento.

Aliases semánticos

Los componentes consumen solo esta capa — así el cambio de tema es un cambio de atributo.

AliasValor (light)Rol

Regla AA del acento

primary-500 sobre --surface da 6.40:1, pero el texto de links y botones ghost usa siempre --link = primary-600 (7.98:1), para que cada rol tenga su token. primary-400 (3.98:1) solo en iconos y UI; primary-300 (2.61:1) jamás como texto.

Regla de área del acento

--accent es un color de control, no de superficie: solo en controles, estados activos e indicadores, y anillos de foco. Si una pieza mide más que un botón grande, no va en lavanda sólida: una tarjeta destacada se marca con borde --border-strong o con un indicador de 2–3 px en --accent. Los tintes --accent-soft / --accent-soft-strong quedan para selección y hover, no para colorear secciones.

Regla de éxito

El acento lavanda nunca comunica éxito. Un resultado positivo usa --success-* (verde azulado, ΔE76 52.6 frente al acento) + icono ✓ + texto. Un botón lavanda y un registro «guardado» no pueden verse iguales.

Registro guardado

Proporción 60-30-10

60% niebla y superficies
30% tinta y estructura
10%

Un solo botón primario visible por pantalla; el resto secundarios o ghost. Fondos siempre planos de niebla: sin degradados de ningún tipo, sin texturas, granulado ni patrones, sin veladuras. El acento pierde poder si supera el 10 %, y nunca se concentra en una sola área grande. Estado nunca solo con color: color + icono + texto, y el éxito nunca con la lavanda del acento.

Fundamentos

Tipografía

Tres familias con roles estrictos. Escala 1.25 (tercera mayor) desde 16 px.

Bricolage Grotesque

Ag Bruma

Display 600–700 · solo h1–h2, cifras hero y títulos de marketing. Nunca UI ni párrafos. Se carga sin itálica.

Atkinson Hyperlegible Next

Il1 O0 Ag

Body/UI 400–700 · párrafos, botones, formularios, h3–h6. Diseñada para baja visión: la I, la l y el 1 no se parecen, ni el 0 y la O.

Atkinson Hyperlegible Mono

0123.45

Mono 400–500 · datos numéricos, código, IDs, cantidades en tablas. Comparte las formas de la de texto.

Escala

Tracking: -0.02em en display ≥31px · 0 en body · +0.04em en labels uppercase. Longitud de línea máx. 65ch. Números en tablas con tabular-nums. Mínimo absoluto 12 px.

Fundamentos

Espaciado

Escala base 4 px — cero valores mágicos. El número del token = px ÷ 4. Densidad algo compacta: tarjetas y secciones respiran menos para mostrar más datos sin apretar.

Aliases de uso

AliasValorUso
--card-padding20pxPadding de tarjetas (16px en móvil)
--section-gap48pxSeparación entre secciones
--form-gap20pxGap entre campos de formulario
--button-padding-md12px 20pxBotón md (sm: 8px 14px)
--control-height-*36 / 44 / 52Alturas de controles sm / md / lg
--gutter24pxGutter del grid de 12 columnas (16px móvil)

Fundamentos

Bordes, radios y focus

Bordes de 1 px, radios contenidos y un focus siempre visible. Las formas más rectas ordenan la pantalla y alinean mejor en rejilla, sin llegar a la esquina viva.

Radios

sm · 6
md · 8
lg · 12
xl · 16
full

Borde por defecto 1px solid var(--border). Focus ring global de teclado: 2px var(--focus-color) (el acento, en los dos modos) con offset 2px vía :focus-visible; el click de ratón no lo dispara. Único uso de transparencia: overlay de modal y ring de focus. No hay blur ni glassmorphism en Bruma Lavanda: la niebla es un color de fondo, no un efecto; las superficies son opacas y planas.

Fundamentos

Elevación

La elevación se apoya en el borde más que en la sombra: cada capa se separa por su superficie y su contorno, y la sombra, mínima y teñida con la tinta rgba(44,43,56,…) (nunca negro puro), solo confirma lo que el borde ya dice. Máximo 3 niveles visibles por pantalla. Sin resplandor de acento.

Niveles

Nivel 0
reposo · sin sombra
Nivel 1
borde + shadow-xs/sm · inputs, tarjetas
Nivel 2
borde --border-strong · hover de tarjeta
Nivel 3
shadow-lg/xl · menús, modales

Sombras

xs
sm
md
lg
xl

Fundamentos

Iconografía

Set sustituto: Lucide — trazo 1.5–2 px y esquinas redondeadas, que casan con los radios contenidos y la lectura limpia de la marca. Si existe un set propio de marca, reemplazar.

home
search
bell
user
folder
chart

Fundamentos

Marca / wordmark

No hay logo ni imágenes de marca. Donde un mockup necesite logotipo se renderiza «Higinio» (o el nombre del producto) en --font-display (Bricolage Grotesque) — nunca se dibuja un logo aproximado.

Higinio HiginioTus datos en orden Higinio

Biblioteca

Componentes

Los 35 componentes del sistema, renderizados en vivo con las clases del framework bruma-lavanda.css. Cada bloque incluye su código de uso.

Botones

Alturas 36 / 44 / 52 px · radius md · peso 600 · un solo primario visible por pantalla. Ghost y outline usan --link (AA), no primary-500. Confirmar es una acción y va en lavanda: ningún botón se pinta de --success-500. El botón no se estira al ancho de una sección: sería un área de color.

Ver código
<button class="btn btn--primary">Guardar cambios</button>
<button class="btn btn--secondary">Cancelar</button>
<button class="btn btn--outline">Exportar</button>
<button class="btn btn--ghost">Ver detalles</button>
<button class="btn btn--danger">Eliminar cuenta</button>

<!-- Tamaños: btn--sm · (md por defecto) · btn--lg -->
<button class="btn btn--primary btn--lg">Grande</button>

IconButton

Botón cuadrado solo-icono para toolbars y chrome. Default ghost. Siempre con aria-label.

Ver código
<button class="icon-btn icon-btn--ghost" aria-label="Buscar">
  <svg>…icono 18px…</svg>
</button>
<!-- Variantes: --ghost (default) · --secondary · --primary
     Tamaños: --sm 32px · (md 44px) · --lg 48px -->

Spinner

Trazo 2.5, rotación 0.8 s linear. Si la carga dura <300 ms, no mostrar spinner. Blanco dentro de botones primarios.

Ver código
<svg class="spinner" viewBox="0 0 24 24" fill="none">
  <circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2.5"/>
  <path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor"
        stroke-width="2.5" stroke-linecap="round"/>
</svg>

Input

Altura 44 px · label siempre visible (nunca placeholder como label) · focus con borde --accent + ring · obligatorio con .field--required (asterisco --danger-text + required/aria-required).

Tu correo no se comparte con nadie.
La contraseña necesita al menos 8 caracteres. Agrega más.
Ver código
<div class="field">
  <label class="field__label" for="correo">Correo electrónico</label>
  <input class="input" id="correo" type="email" placeholder="tu@correo.com">
  <span class="field__help">Tu correo no se comparte con nadie.</span>
</div>

<!-- Obligatorio: .field--required en el .field + required/aria-required en el input -->
<div class="field field--required">
  <label class="field__label" for="nombre">Nombre completo</label>
  <input class="input" id="nombre" required aria-required="true">
</div>

<!-- Estados: input--error (+ .field__error) · input--success · disabled
     Icono leading: envolver en .input-icon -->

Textarea

Min-height 96 px · resize: vertical · contador opcional alineado a la derecha.

0 / 200
Ver código
<div class="field">
  <label class="field__label" for="desc">Descripción</label>
  <textarea class="textarea" id="desc" maxlength="200"></textarea>
  <span class="textarea-counter">0 / 200</span>
</div>

Select

Trigger igual que input + chevron 16 px que rota al abrir. Menú con shadow-lg y opción seleccionada en --accent-soft.

Ver código
<div class="select-wrap">
  <select class="select">
    <option>Ciudad de México (GMT-6)</option>
    <option>Bogotá (GMT-5)</option>
  </select>
</div>

SegmentedControl

2–4 opciones mutuamente excluyentes. Indicador blanco con shadow-xs. Para más de 4 opciones, usar Select.

Ver código
<div class="segmented" role="radiogroup" aria-label="Vista">
  <input type="radio" name="vista" id="lista" checked><label for="lista">Lista</label>
  <input type="radio" name="vista" id="tablero"><label for="tablero">Tablero</label>
</div>
<!-- Compacto: añadir segmented--sm -->

Slider

Track 6 px con porción rellena en --accent · thumb blanco 18 px con borde de acento que escala 1.15 al presionar.

Límite de gasto $ 350
Ver código
<div class="slider-row">
  <span class="slider-label">Límite de gasto</span>
  <span class="slider-value">$ 350</span>
</div>
<input class="slider" type="range" min="0" max="1000" value="350">
<!-- JS: actualizar --slider-fill = ((v-min)/(max-min))*100 + '%' -->

Checkbox y Radio

20 px · borde 2 px · check que se dibuja con ease-spring · label clicable con gap 10 px · estado indeterminate (guion) para «seleccionar todo» parcial · marcar es seleccionar, no un estado de éxito.

Ver código
<label class="checkbox">
  <input type="checkbox" checked>
  <span class="checkbox__box">
    <svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>
  </span>
  Recibir novedades
</label>

<label class="radio">
  <input type="radio" name="plan" checked>
  <span class="radio__dot"></span>
  Plan mensual
</label>

<!-- Indeterminate: propiedad del DOM, no atributo HTML -->
<!-- checkbox.indeterminate = true (guion horizontal) -->

Switch

Track 44×24 px · thumb 20 px que desliza 20 px en 180 ms ease-spring.

Ver código
<label class="switch">
  <input type="checkbox" checked>
  <span class="switch__track"></span>
  Notificaciones
</label>

Badge y Tag

Fondo suave semántico (*-100) + texto --*-text (AA garantizado). Estado nunca solo con color: color + icono + texto. El badge de éxito lleva siempre su ✓.

Borrador Activo Pendiente Vencido Beta
diseño frontend revisión
Ver código
<span class="badge badge--success">
  <svg>…icono…</svg>Activo
</span>
<!-- Tonos: (neutral) · --success · --warning · --danger · --info -->

<span class="tag">diseño
  <button class="tag__close" aria-label="Quitar etiqueta">
    <svg>…x…</svg>
  </button>
</span>

Avatar

Fallback automático a iniciales sobre chip primary-100. Dot de presencia: 28 % del diámetro con anillo de superficie.

HS HS MG AL HS MG AL RD
Ver código
<span class="avatar avatar--lg">
  <span class="avatar__initials">HS</span>
  <i class="avatar__dot avatar__dot--online"></i>
</span>
<!-- Tamaños: --sm 28 · (md 36) · --lg 48 · --xl 64
     Presencia: --online · --busy · --away · (offline) -->

AvatarGroup

Stack de avatares solapados (-8px) con anillo 2 px de --surface que los separa · avatar-group__more para el +N de overflow.

HS MG AL RD +3
Ver código
<div class="avatar-group">
  <span class="avatar"><span class="avatar__initials">HS</span></span>
  <span class="avatar"><span class="avatar__initials">MG</span></span>
  <span class="avatar"><span class="avatar__initials">AL</span></span>
  <span class="avatar"><span class="avatar__initials">RD</span></span>
  <span class="avatar-group__more">+3</span>
</div>
<!-- Solape -8px · cada avatar con anillo 2px de --surface -->

Divider

Regla hairline 1 px --border · variante con label centrado (patrón "o continúa con") · vertical para flex.


FiltrosOrdenarExportar
Ver código
<hr class="divider">
<div class="divider--label" role="separator">o continúa con</div>
<span class="divider--vertical" role="separator"></span>

Accordion

Altura animada con grid-template-rows 0fr→1fr (sin reflow) · chevron rota 180° · aria-expanded.

Sí. El cambio se aplica de inmediato y el cobro se prorratea según los días restantes del ciclo.
Desde Ajustes → Datos puedes descargar todo en CSV o JSON. El archivo llega a tu correo en unos minutos.
Tu cuenta queda en pausa 30 días por si cambias de opinión. Después, los datos se eliminan de forma permanente.
Ver código
<div class="accordion">
  <div class="accordion__item is-open">
    <button class="accordion__header" aria-expanded="true">
      Pregunta <svg>…chevron…</svg>
    </button>
    <div class="accordion__panel"><div>
      <div class="accordion__content">Respuesta</div>
    </div></div>
  </div>
</div>
<!-- JS: alternar .is-open en .accordion__item + aria-expanded -->

Tooltip

Delay 400 ms · fade + translateY(4→0) · máx. 1 línea; si necesita más, usar popover. Pasa el cursor por los botones.

Ver código
<button class="btn btn--secondary" data-tooltip="Duplica el proyecto">
  Duplicar
</button>
<!-- Solo CSS: atributo data-tooltip. Fondo se invierte en dark. -->

Alert

Banner inline y persistente (vs Toast, transitorio). Icono *-500 + texto --*-text sobre fondo *-100. role="status". En éxito el ✓ no es opcional.

Nueva versión disponible

La versión 2.4 incluye exportación a PDF. Actualiza cuando quieras.

Pago confirmado

El recibo se envió a tu correo.

Tu plan se renueva el 12 de julio

Puedes revisar tu forma de pago hasta esa fecha.

No se pudo guardar el formulario

Revisa los campos marcados e inténtalo de nuevo.

Ver código
<div class="alert alert--warning" role="status">
  <svg>…icono…</svg>
  <div>
    <p class="alert__title">Tu plan se renueva el 12 de julio</p>
    <p class="alert__body">Puedes revisar tu forma de pago hasta esa fecha.</p>
  </div>
  <button class="alert__close" aria-label="Cerrar aviso">…x…</button>
</div>
<!-- Tonos: --info · --success · --warning · --danger -->

Toast

Top-right (desktop) · 360 px · borde izquierdo 3 px semántico · auto-dismiss 5 s pausable con hover · máx. 3 apilados.

Ver código
<div class="toast-stack" role="status" aria-live="polite">
  <div class="toast toast--success">
    <svg>…icono…</svg>
    <div>
      <p class="toast__title">Cambios guardados</p>
      <p class="toast__body">El proyecto se actualizó.</p>
    </div>
    <button class="icon-btn icon-btn--ghost" aria-label="Cerrar">…x…</button>
  </div>
</div>

Progress y ProgressRing

Solo progreso determinado — para carga indeterminada usar Skeleton. Width / dashoffset animan 240 ms. Mientras se avanza, el relleno es --accent; solo al cumplir la meta pasa a success-500, con ✓ y el texto que lo dice.

Almacenamiento68%
Meta de pasos cumplida8,000/8,000
68%
Ver código
<div class="progress-row">
  <span class="progress-label">Almacenamiento</span>
  <span class="progress-value">68%</span>
</div>
<div class="progress">
  <div class="progress__bar" style="width:68%"
       role="progressbar" aria-valuenow="68"
       aria-valuemin="0" aria-valuemax="100"></div>
</div>
<!-- Tonos de barra: (accent) · --success · --warning · --danger -->

Modal

role="dialog" · aria-modal · aria-labelledby + aria-describedby · al abrir el foco entra al panel, queda atrapado (Tab/Shift+Tab ciclan) y vuelve al disparador al cerrar · Esc y click en overlay cierran, salvo flujos destructivos (el de confirmación no cierra por overlay).

Ver código
<div class="modal" id="miModal" role="dialog" aria-modal="true"
     aria-labelledby="miModalTitulo" aria-describedby="miModalDesc">
  <!-- overlay sin data-modal-close en flujos destructivos -->
  <div class="modal__overlay" data-modal-close></div>
  <div class="modal__panel" tabindex="-1">
    <div class="modal__header">
      <h4 class="modal__title" id="miModalTitulo">¿Eliminar proyecto?</h4>
      <button class="icon-btn icon-btn--ghost" data-modal-close aria-label="Cerrar">…x…</button>
    </div>
    <p id="miModalDesc">Esta acción no se puede deshacer.</p>
    <div class="modal__footer">
      <button class="btn btn--secondary" data-modal-close>Cancelar</button>
      <button class="btn btn--danger">Eliminar</button>
    </div>
  </div>
</div>
<!-- Panel ancho: modal__panel--lg (640px) -->
<!-- JS: al abrir guarda el disparador y enfoca el panel; Tab/Shift+Tab
     atrapados dentro; al cerrar (Esc, ×, Cancelar) el foco vuelve al disparador -->

Menu

Panel 190 px mín. con shadow-lg · entrada fade + translateY(-4→0) 160 ms · ítem danger con hover danger-100.

Ver código
<div class="menu-wrap">
  <button class="btn btn--secondary">Acciones</button>
  <div class="menu" role="menu">
    <button class="menu__item" role="menuitem">…Editar</button>
    <div class="menu__sep" role="separator"></div>
    <button class="menu__item menu__item--danger" role="menuitem">…Eliminar</button>
  </div>
</div>
<!-- JS: alternar .is-open; cerrar con click fuera, Esc o al seleccionar -->

Tabla

Header uppercase +0.04em · filas 52 px · numéricos a la derecha con tabular-nums. Variantes: .table--zebra · .table--sticky (header fijo) · columna ordenable con aria-sort · selección con .row--selected y «seleccionar todo» indeterminate.

Cliente Estado
Constructora Delta Activo $ 48,200.00
Grupo Andrade Pendiente $ 9,850.00
Studio Nébula Activo $ 132,400.00
Farmacias Luna Vencido $ 1,120.00
Ver código
<table class="table table--zebra table--sticky">
  <thead>
    <tr>
      <th class="col-select" scope="col">
        <label class="checkbox"><input type="checkbox" aria-label="Seleccionar todo">
          <span class="checkbox__box"><svg>…check…</svg></span></label>
      </th>
      <th scope="col">Cliente</th>
      <th class="num" scope="col" aria-sort="none">
        <button class="th-sort th-sort--num" data-sort="monto">
          Monto<svg class="sort-icon" aria-hidden="true">…</svg></button>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr class="row--selected">
      <td class="col-select"><label class="checkbox"><input type="checkbox" checked
        aria-label="Seleccionar Grupo Andrade">…</label></td>
      <td>Grupo Andrade</td><td class="num" data-monto="9850">$ 9,850.00</td>
    </tr>
  </tbody>
</table>
<!-- JS: click en .th-sort alterna aria-sort asc/desc y reordena tbody;
     el checkbox del header pasa a indeterminate con selección parcial -->

Stat

KPI único, componer dentro de Card. Valor en mono text-3xl · el delta indica tendencia, no éxito: neutro por defecto (--text-secondary), y --up / --down solo cuando el producto decide qué sentido es bueno · count-up al entrar en viewport.

Ingresos del mes
$ 0 +12.4%
Clientes activos
0 -2.1%
Proyectos abiertos
0 0%
Ver código
<div class="card">
  <div class="stat">
    <span class="stat__label"><svg>…</svg>Ingresos del mes</span>
    <div class="stat__row">
      <span class="stat__value">$ 128,400</span>
      <span class="stat__delta stat__delta--up"><svg>…</svg>+12.4%</span>
    </div>
  </div>
</div>
<!-- Delta: neutro por defecto (text-secondary) · --up (success-text) y
     --down (danger-text) solo cuando el producto decide qué sentido es bueno -->

Chip de métrica

Pastilla de una línea con rótulo y valor en --link · fila con gap que salta de línea en móvil · el chip interactivo es un <button> de 44 px con nombre accesible completo · al llegar a la meta lo dicen un ✓ en --success-500 y el nombre accesible, no la lavanda del valor. Pulsa «Agua» hasta llegar a 8.

Pasos 6,240 Sueño 7 h 20 min
Ver código
<div class="metric-chips">
  <span class="metric-chip">
    <span class="metric-chip__label">Pasos</span> <span class="metric-chip__value">6,240</span>
  </span>
  <button class="metric-chip" type="button" aria-label="Agua: 3 de 8 vasos. Sumar un vaso">
    <svg aria-hidden="true">…plus…</svg>
    <span class="metric-chip__label">Agua</span> <span class="metric-chip__value">3 de 8</span>
  </button>
</div>
<!-- El aria-label se actualiza con el valor; empieza por el texto visible -->

<!-- Meta cumplida: .metric-chip--met pinta el ✓ en --success-500 y el nombre accesible lo dice -->
<button class="metric-chip metric-chip--met" type="button"
        aria-label="Agua: 8 de 8 vasos, meta cumplida. Empezar de nuevo">
  <svg aria-hidden="true">…check…</svg>
  <span class="metric-chip__label">Agua</span> <span class="metric-chip__value">8 de 8</span>
</button>

Pagination

1-indexado, bajo una tabla · trunca con elipsis a partir de 7 páginas · página activa como chip sólido con aria-current="page".

    Ver código
    <ul class="pagination" aria-label="Paginación">
      <li><button class="pagination__btn" aria-label="Anterior">…‹…</button></li>
      <li><button class="pagination__btn">1</button></li>
      <li><span class="pagination__ellipsis">…</span></li>
      <li><button class="pagination__btn is-active" aria-current="page">5</button></li>
      <li><button class="pagination__btn">6</button></li>
      <li><button class="pagination__btn" aria-label="Siguiente">…›…</button></li>
    </ul>

    Navegación

    Navbar 64 px · sidebar 260 px · tabs con indicador de 2 px · breadcrumbs con separador / tipográfico.

    Vista general del proyecto con sus métricas clave.
    Ver código
    <nav class="navbar">
      <a class="navbar__brand" href="/">Higinio</a>
      <div class="navbar__nav">
        <a class="navbar__link is-active" href="#">Resumen</a>
        <a class="navbar__link" href="#">Proyectos</a>
      </div>
    </nav>
    
    <nav class="sidebar">
      <button class="sidebar__item is-active"><svg>…</svg>Inicio</button>
    </nav>
    
    <div class="tabs" role="tablist">
      <button class="tabs__tab is-active" role="tab">Resumen</button>
    </div>
    
    <nav class="breadcrumbs">
      <a href="#">Inicio</a><span class="breadcrumbs__sep">/</span>
      <span class="breadcrumbs__current">Panel</span>
    </nav>

    Sidebar contraíble

    Expandido 260 px · riel de iconos de 64 px con grupos separados por una línea · en el riel, el nombre aparece como tooltip al pasar el cursor o al enfocar con teclado · el ancho cambia sin transición y solo los rótulos se funden · por debajo de 768 px lo sustituye un Drawer --left.

    Inicio

    El contenido ocupa el ancho que deja el sidebar: la rejilla auto 1fr sigue al riel al instante, sin animar el ancho.

    Ver código
    <nav class="sidebar sidebar--collapsible" id="appNav" aria-label="Principal">
      <div class="sidebar__group" role="group" aria-labelledby="navTrabajo">
        <span class="sidebar__group-label" id="navTrabajo">Trabajo</span>
        <a class="sidebar__item is-active" href="/" aria-current="page">
          <svg aria-hidden="true">…</svg><span class="sidebar__label">Inicio</span>
        </a>
      </div>
      <button class="sidebar__item sidebar__toggle" type="button"
              aria-expanded="true" aria-controls="appNav">
        <svg aria-hidden="true">…chevron-left…</svg><span class="sidebar__label">Contraer</span>
      </button>
    </nav>
    <!-- Contraído: .is-collapsed en el nav, aria-expanded="false" y texto «Expandir».
         Por debajo de 768 px: botón ☰ .sidebar-trigger que abre un .drawer.drawer--left con los mismos ítems. -->
    <button class="icon-btn sidebar-trigger" type="button" aria-label="Abrir navegación"
            aria-expanded="false" aria-controls="navDrawer">…menu…</button>

    Skeleton y EmptyState

    Bloque plano que pulsa en opacidad (1 → 0.5, 1.2 s por tramo, loop alternado), sin degradado; radius igual al contenido que reemplaza. Un estado vacío siempre dice qué falta y cómo empezar.

    Aún no hay registros esta semana

    Añade el primero desde «Registrar».

    Ver código
    <div class="skeleton" style="height:10px;width:85%"></div>
    <div class="skeleton skeleton--circle" style="width:40px;height:40px"></div>
    
    <div class="empty-state">
      <svg>…icono 40px…</svg>
      <p class="empty-state__title">Aún no hay registros esta semana</p>
      <p class="empty-state__desc">Añade el primero desde «Registrar».</p>
      <button class="btn btn--primary">Registrar</button>
    </div>

    Card, Link y Code

    Tarjeta con padding 20 px y shadow-sm; en la interactiva el hover se lee en el borde (--border → --border-strong), sin desplazamiento ni sombra. Link con subrayado que crece. Code inline en mono.

    Tarjeta estática

    Contenido en reposo con borde --border y shadow-sm.

    Tarjeta interactiva

    Pasa el cursor: el borde pasa a --border-strong y la tarjeta no se mueve. Consulta la guía de motion.

    Ver código
    <div class="card">…</div>
    <div class="card card--interactive">…</div>
    
    <a class="link" href="#">Enlace con subrayado animado</a>
    <span class="code">--accent</span>

    Combobox y búsqueda

    role="combobox" · aria-expanded + aria-controls + aria-activedescendant · lista role="listbox", opciones role="option" · ↑/↓ mueven, Enter elige, Esc cierra, Home/End a extremos. La búsqueda se limpia con la ×.

      Ver código
      <div class="combobox">
        <input class="combobox__input" type="text" role="combobox"
          aria-expanded="false" aria-controls="lista" aria-autocomplete="list"
          aria-activedescendant="opt-0" autocomplete="off">
        <ul class="combobox__list" id="lista" role="listbox" aria-label="Ciudades">
          <li class="combobox__option is-active" role="option" id="opt-0"
              aria-selected="false">Guadalajara</li>
          <li class="combobox__option" role="option" id="opt-1"
              aria-selected="true">Monterrey
            <svg class="combobox__check">…check…</svg></li>
        </ul>
      </div>
      
      <div class="search">
        <svg class="search__icon" aria-hidden="true">…lupa…</svg>
        <input class="search__input" type="search" placeholder="Buscar…">
        <button class="search__clear" aria-label="Limpiar">…x…</button>
      </div>

      Drawer / Sheet

      role="dialog" · aria-modal · foco atrapado y devuelto al disparador (patrón del Modal) · overscroll-behavior: contain · entra deslizando desde el borde y sale más corto · Esc y click en overlay cierran. Variantes --right (default), --left, --bottom.

      Ver código
      <div class="drawer drawer--right" id="miDrawer" role="dialog"
           aria-modal="true" aria-labelledby="miDrawerTitulo">
        <div class="drawer__overlay" data-drawer-close></div>
        <div class="drawer__panel" tabindex="-1">
          <div class="drawer__header">
            <h4 class="drawer__title" id="miDrawerTitulo">Filtrar proyectos</h4>
            <button class="icon-btn icon-btn--ghost" data-drawer-close aria-label="Cerrar">…x…</button>
          </div>
          <!-- campos… -->
          <div class="drawer__footer">
            <button class="btn btn--secondary" data-drawer-close>Cancelar</button>
            <button class="btn btn--primary" data-drawer-close>Aplicar</button>
          </div>
        </div>
      </div>

      Popover

      Se abre por click (no hover, a diferencia del tooltip) y lleva contenido interactivo · trigger aria-haspopup="dialog" + aria-expanded · panel role="dialog" · aparece desde el trigger (fade + scale 0.96→1) · Esc y click fuera cierran.

      Ver código
      <div class="popover">
        <button class="btn btn--secondary popover__trigger"
                aria-haspopup="dialog" aria-expanded="false"
                aria-controls="pop">Detalles del plan</button>
        <div class="popover__panel" id="pop" role="dialog"
             aria-label="Detalles del plan" tabindex="-1">
          <p>Contenido con <a class="link" href="#">un enlace</a>.</p>
        </div>
      </div>

      ButtonGroup

      Botones adyacentes con bordes compartidos (radius solo en los extremos, bordes internos colapsados) · role="group" + aria-label · usa los .btn existentes. Para un único-seleccionado, usa SegmentedControl.

      Ver código
      <div class="btn-group" role="group" aria-label="Navegación de semana">
        <button class="btn btn--secondary">Anterior</button>
        <button class="btn btn--secondary">Hoy</button>
        <button class="btn btn--secondary">Siguiente</button>
      </div>

      DatePicker

      Input + calendario en popover · rejilla role="grid", encabezados role="columnheader" con aria-label completo, días role="gridcell" con aria-label de la fecha entera y aria-selected · las flechas mueven entre días · el input abre el calendario (click), Esc cierra y el foco entra al calendario · hoy marcado y un día seleccionado.

      Ver código
      <div class="datepicker">
        <input class="datepicker__input" type="text" readonly
               aria-haspopup="dialog" aria-controls="cal">
        <svg class="datepicker__icon" aria-hidden="true">…calendario…</svg>
        <div class="datepicker__calendar" id="cal" role="dialog"
             aria-label="Elegir fecha" tabindex="-1">
          <div class="calendar">
            <div class="calendar__header">
              <button aria-label="Mes anterior">…</button>
              <span class="calendar__title">julio 2026</span>
              <button aria-label="Mes siguiente">…</button>
            </div>
            <div class="calendar__table" role="grid" aria-label="julio 2026">
            <div class="calendar__weekdays" role="row">
              <span class="calendar__weekday" role="columnheader"
                    aria-label="lunes">lu</span> …
            </div>
            <div class="calendar__grid" role="rowgroup">
             <div class="calendar__week" role="row">
              <button class="calendar__day calendar__day--today" role="gridcell"
                      aria-label="viernes, 10 de julio de 2026"
                      aria-selected="false">10</button>
              <button class="calendar__day calendar__day--selected" role="gridcell"
                      aria-label="viernes, 17 de julio de 2026"
                      aria-selected="true">17</button> …
             </div>
            </div>
            </div>
          </div>
        </div>
      </div>

      FileUpload

      Dropzone + lista · el input file real va oculto pero con label asociado y aria-describedby de formatos y tamaño · la zona describe la acción · estado .is-dragover al arrastrar · el error no se comunica solo por color (icono + texto) · el item que sube reusa la barra .progress.

      • informe-anual-2026.pdf 2.4 MB · Subiendo 45%
      • portada.png 820 KB · Completo
      • video-demo.mov Formato no admitido · elige PDF, PNG o JPG
      Ver código
      <div class="file-upload">
        <input class="file-upload__input" type="file" multiple
               aria-describedby="hint">
        <label class="file-upload__label" for="…">
          <svg aria-hidden="true">…subir…</svg>
          <span class="file-upload__title">Arrastra archivos o elige</span>
          <span class="file-upload__button">Elegir archivos</span>
          <span class="file-upload__hint" id="hint">PDF, PNG o JPG · hasta 10 MB</span>
        </label>
      </div>
      <ul class="file-upload__list">
        <li class="file-upload__item">
          <svg class="file-upload__icon">…</svg>
          <div class="file-upload__meta">
            <span class="file-upload__name">informe.pdf</span>
            <span class="file-upload__size">2.4 MB · Subiendo 45%</span>
            <div class="progress"><div class="progress__bar" style="width:45%"></div></div>
          </div>
          <button class="file-upload__remove" aria-label="Cancelar informe.pdf">×</button>
        </li>
        <li class="file-upload__item file-upload__item--error">
          <svg class="file-upload__icon">…alerta…</svg>
          <div class="file-upload__meta">
            <span class="file-upload__name">video.mov</span>
            <span class="file-upload__size">…alerta… Formato no admitido</span>
          </div>
          <button class="file-upload__remove" aria-label="Quitar video.mov">×</button>
        </li>
      </ul>

      Stepper / Wizard

      <ol> · aria-current="step" en el paso actual · el completo muestra un check (no solo color), el próximo muestra su número · estados --complete/--current/--upcoming · .step__connector entre pasos · variante --vertical · un paso completo marca avance y va en lavanda; el resultado del flujo se confirma aparte, con el tono de éxito y su ✓.

      1. Cuenta
      2. Perfil
      3. 3 Facturación
      4. 4 Confirmar
      1. Borrador guardado
      2. 2 Revisión del equipo
      3. 3 Publicación
      Ver código
      <ol class="stepper" aria-label="Progreso de configuración">
        <li class="step step--complete">
          <span class="step__connector" aria-hidden="true"></span>
          <span class="step__marker">…check…</span>
          <span class="step__label">Cuenta</span>
        </li>
        <li class="step step--current" aria-current="step">
          <span class="step__connector" aria-hidden="true"></span>
          <span class="step__marker">3</span>
          <span class="step__label">Facturación</span>
        </li>
        <li class="step step--upcoming">
          <span class="step__marker">4</span>
          <span class="step__label">Confirmar</span>
        </li>
      </ol>
      
      <!-- Vertical: añade la clase --vertical al <ol> -->
      <ol class="stepper stepper--vertical">…</ol>

      Kbd

      Tecla en <kbd> con borde, sombra sutil de tinta y tipografía mono · tamaño pequeño, mínimo 20px · combos con "+" entre teclas.

      Esc Tab ⏎ ⌘ + K Ctrl + S ⌘ + ⇧ + P

      Inline en el texto: abre la paleta con ⌘ K y guarda con Ctrl S.

      Ver código
      <kbd class="kbd">Esc</kbd>
      
      <!-- Combo: "+" literal entre teclas -->
      <span class="kbd-combo">
        <kbd class="kbd">⌘</kbd> + <kbd class="kbd">K</kbd>
      </span>

      Sistema

      Motion

      Corto y preciso: el movimiento confirma lo que pasó y se aparta, y esa rapidez es parte de la sensación de control. El feedback táctil es inmediato y el techo no pasa de 300 ms. Solo transform y opacity, la salida ~20 % más corta que la entrada y distancias de 4–8 px.

      TokenValorUso
      --duration-instant100msPress, toggle
      --duration-fast140msHover, focus, color
      --duration-base180msDropdowns, tooltips, switches
      --duration-slow240msModales, toasts, drawers
      --duration-page300msTransiciones de página o vista (techo)

      Easings (pasa el cursor)

      --ease-out · entradas --ease-in · salidas --ease-in-out · moverse de A a B --ease-spring · toggles y checks

      Sistema

      Accesibilidad

      WCAG AA mínimo: 4.5:1 texto normal, 3:1 texto grande y componentes UI. Touch targets de 44×44 px. Estado nunca comunicado solo con color.

      CombinaciónContrasteVeredicto
      neutral-800 sobre --surface13.46:1AAA
      Blanco sobre primary-5006.61:1AA texto normal
      --link (primary-600) sobre --surface7.98:1AA texto normal
      primary-400 sobre --surface3.98:1Solo iconos y UI
      primary-300 sobre --surface2.61:1Nunca como texto
      Tonos --*-text sobre *-1005.58–6.35:1AA garantizado
      Éxito frente a acento (--success-500 / --accent)ΔE76 52.6Distinguibles (mín. 15)
      Acento sin violeta saturado (--accent claro)HSL 22.7 % · C*ab 31.3Dentro del límite (≤ 25 % · ≤ 40)

      Sistema

      Tokens y descarga

      El sistema completo vive en dist/bruma-lavanda/: bruma-lavanda.css (framework completo), tokens.css (solo variables), tailwind.css (@theme para Tailwind v4) y tokens.json (W3C DTCG 2025.10, interoperable con Figma / Tokens Studio / Style Dictionary).

      Variables CSS

      :root {
        /* Primario (Lavanda) */
        --primary-50: #e5e3f1; --primary-100: #d8d5ea; --primary-200: #bdb9da;
        --primary-300: #9d99c4; --primary-400: #7c78a8; --primary-500: #5b578a;
        --primary-600: #4d4979; --primary-700: #403d66; --primary-800: #35334f;
        --primary-900: #2c2b38;
      
        /* Neutrales (Niebla) */
        --neutral-0: #fbfbfd; --neutral-50: #f2f1f6; --neutral-100: #e9e8f0;
        --neutral-200: #dedce8; --neutral-300: #b8b6c8; --neutral-400: #86849a;
        --neutral-500: #5d5b6e; --neutral-600: #48475a; --neutral-700: #393846;
        --neutral-800: #2c2b38; --neutral-900: #1f1e28;
      
        /* Semánticos (ramp de 3) */
        --success-500: #2f7a68; --success-100: #dcefe9; --success-text: #235e50;
        --warning-500: #b07f2a; --warning-100: #f5ecd9; --warning-text: #7a5716;
        --danger-500: #b24c55;  --danger-100: #f6e3e5;  --danger-text: #8b3540;
        --info-500: #4f7896;    --info-100: #e1eaf1;    --info-text: #3a5b74;
      
        /* Aliases (los componentes consumen SOLO esto) */
        --bg: var(--neutral-50); --surface: var(--neutral-0); --surface-alt: var(--neutral-100);
        --border: var(--neutral-200); --border-strong: var(--neutral-400);
        --text-primary: var(--neutral-800); --text-secondary: var(--neutral-600);
        --text-muted: var(--neutral-500); --text-disabled: var(--neutral-400);
        --text-on-primary: #ffffff; --text-on-primary-solid: var(--primary-700);
        --accent: var(--primary-500); --accent-hover: var(--primary-600);
        --accent-active: var(--primary-700); --accent-soft: var(--primary-50);
        --accent-soft-strong: var(--primary-100); --link: var(--primary-600);
      
        /* Tipografía */
        --font-display: "Bricolage Grotesque", system-ui, sans-serif;
        --font-body: "Atkinson Hyperlegible Next", system-ui, -apple-system, sans-serif;
        --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, monospace;
        --text-xs: .75rem; --text-sm: .875rem; --text-base: 1rem; --text-lg: 1.125rem;
        --text-xl: 1.25rem; --text-2xl: 1.5625rem; --text-3xl: 1.9375rem;
        --text-4xl: 2.4375rem; --text-5xl: 3.0625rem;
        --tracking-display: -0.02em; --tracking-label: 0.04em; --measure: 65ch;
      
        /* Espaciado (base 4px; densidad algo compacta) */
        --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
        --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;
        --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px;
        --card-padding: var(--space-5); --section-gap: var(--space-12);
        --control-height-sm: 36px; --control-height-md: 44px; --control-height-lg: 52px;
      
        /* Radios y sombras (mínimas, teñidas de tinta: la elevación va en el borde) */
        --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px;
        --radius-xl: 16px; --radius-full: 9999px;
        --shadow-xs: 0 1px 1px rgba(44,43,56,.04);
        --shadow-sm: 0 1px 2px rgba(44,43,56,.06);
        --shadow-md: 0 2px 6px rgba(44,43,56,.07), 0 1px 2px rgba(44,43,56,.04);
        --shadow-lg: 0 8px 20px rgba(44,43,56,.09), 0 2px 6px rgba(44,43,56,.05);
        --shadow-xl: 0 16px 36px rgba(44,43,56,.12);
        --ring: 0 0 0 3px rgba(91,87,138,.25);
      
        /* Motion (corto y preciso) */
        --duration-instant: 100ms; --duration-fast: 140ms; --duration-base: 180ms;
        --duration-slow: 240ms; --duration-page: 300ms;
        --ease-out: cubic-bezier(0.2, 0, 0, 1);
        --ease-in: cubic-bezier(0.4, 0, 1, 1);
        --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
        --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
      }

      Tailwind CSS v4 — bloque @theme

      @import "tailwindcss";
      @import "./tokens.css";
      
      @theme {
        /* Colores → genera bg-primary-500, text-neutral-800, border-primary-200… */
        --color-primary-50: #e5e3f1;  --color-primary-100: #d8d5ea;
        --color-primary-200: #bdb9da; --color-primary-300: #9d99c4;
        --color-primary-400: #7c78a8; --color-primary-500: #5b578a;
        --color-primary-600: #4d4979; --color-primary-700: #403d66;
        --color-primary-800: #35334f; --color-primary-900: #2c2b38;
      
        --color-neutral-0: #fbfbfd;   --color-neutral-50: #f2f1f6;
        --color-neutral-100: #e9e8f0; --color-neutral-200: #dedce8;
        --color-neutral-300: #b8b6c8; --color-neutral-400: #86849a;
        --color-neutral-500: #5d5b6e; --color-neutral-600: #48475a;
        --color-neutral-700: #393846; --color-neutral-800: #2c2b38;
        --color-neutral-900: #1f1e28;
      
        --color-success-500: #2f7a68; --color-success-100: #dcefe9; --color-success-700: #235e50;
        --color-warning-500: #b07f2a; --color-warning-100: #f5ecd9; --color-warning-700: #7a5716;
        --color-danger-500: #b24c55;  --color-danger-100: #f6e3e5;  --color-danger-700: #8b3540;
        --color-info-500: #4f7896;    --color-info-100: #e1eaf1;    --color-info-700: #3a5b74;
      
        /* Tipografía → font-display, font-body, font-mono */
        --font-display: "Bricolage Grotesque", system-ui, sans-serif;
        --font-body: "Atkinson Hyperlegible Next", system-ui, sans-serif;
        --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, monospace;
      
        /* Radios → rounded-sm/md/lg/xl */
        --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px;
      
        /* Sombras → shadow-xs…shadow-xl (mínimas, teñidas de tinta) */
        --shadow-xs: 0 1px 1px rgba(44,43,56,.04);
        --shadow-sm: 0 1px 2px rgba(44,43,56,.06);
        --shadow-md: 0 2px 6px rgba(44,43,56,.07), 0 1px 2px rgba(44,43,56,.04);
        --shadow-lg: 0 8px 20px rgba(44,43,56,.09), 0 2px 6px rgba(44,43,56,.05);
        --shadow-xl: 0 16px 36px rgba(44,43,56,.12);
      
        /* Easings → ease-out-brand, ease-in-brand, ease-in-out-brand, ease-spring */
        --ease-out-brand: cubic-bezier(0.2, 0, 0, 1);
        --ease-in-brand: cubic-bezier(0.4, 0, 1, 1);
        --ease-in-out-brand: cubic-bezier(0.65, 0, 0.35, 1);
        --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
      }

      Cómo consumirlo en un proyecto nuevo

      1) Instala @higinio/design-css (e importa @higinio/design-css/bruma/framework.css) o copia dist/bruma-lavanda/bruma-lavanda.css (o solo tokens.css) e impórtalo antes de tus estilos, o pega el bloque @theme en tu CSS de Tailwind v4. 2) Aplica .bruma-reset al body. 3) Consume solo aliases semánticos. 4) Alterna data-theme en <html> para light/dark. 5) Declara <meta name="theme-color" content="#f2f1f6"> (la niebla de --bg) y cámbialo a #16151c con el tema oscuro.

      Sistema

      Instalación

      El sistema se distribuye como paquete npm (@higinio/design-css) y también como archivos sueltos en dist/bruma-lavanda/. Instálalo o cópialos, y elige qué importar según tu stack (A/B/C). En todas: light por defecto; <html data-theme="dark"> activa el modo oscuro, y los componentes consumen solo aliases semánticos, nunca hex.

      Instalar desde npm (recomendado)

      Un solo paquete con todos los sistemas, cada uno en su subruta; Bruma Lavanda vive en /bruma/. Instálalo y usa las vías A/B/C apuntando a la subruta, en vez de a un archivo copiado.

      npm i @higinio/design-css
      
      /* framework completo (vía A) */
      @import "@higinio/design-css/bruma/framework.css";
      /* o solo tokens (vía B) */
      @import "@higinio/design-css/bruma/tokens.css";
      /* o Tailwind v4 (vía C) */
      @import "@higinio/design-css/bruma/tailwind.css";

      A · CSS drop-in (framework completo)

      Para cualquier proyecto — HTML plano o cualquier framework. Trae variables + clases de componentes listas (tipo Bootstrap) y ya importa las fuentes. Aplica .bruma-reset al <body>.

      <link rel="stylesheet" href="/estilos/bruma-lavanda.css">
      
      <body class="bruma-reset">
        <button class="btn btn--primary">Guardar</button>
        <div class="card">…</div>
      </body>

      B · Solo tokens

      Si diseñas tus propios componentes: solo las variables, sin clases.

      @import "./tokens.css";
      
      .mi-panel {
        background: var(--surface);
        color: var(--text-primary);
        border: 1px solid var(--border);
      }

      C · Tailwind v4

      Tailwind v4 se configura por CSS. El tailwind.css ya incluye @import "tailwindcss", los tokens y el bloque @theme — úsalo como tu hoja de entrada (mantén tokens.css a su lado, que es lo que importa).

      # 1) instala Tailwind v4
      npm i tailwindcss @tailwindcss/postcss
      
      # 2) copia tailwind.css + tokens.css a tu carpeta de CSS
      #    y usa tailwind.css como entrada. Ya tienes utilidades:
      <button class="bg-primary-500 text-neutral-0 rounded-md">Guardar</button>

      D · Pipelines de tokens

      Para Figma, Tokens Studio o Style Dictionary: importa tokens.json (formato estándar W3C DTCG 2025.10).

      Con Claude no copias nada

      Invoca la skill bruma-lavanda-design o conecta el MCP (sección siguiente) y construye consumiendo tokens, componentes y reglas del sistema, con validación automática.

      Sistema

      MCP

      El paquete @higinio/design-mcp expone los sistemas de diseño de Higinio Sosa, entre ellos Bruma Lavanda (system="bruma"), a cualquier IDE o cliente compatible con Model Context Protocol: tokens, componentes, reglas duras y validación de código. Complementa a las skills de Claude, no las reemplaza: misma fuente de verdad, accesible también fuera del entorno de Claude.

      Las 11 tools

      ToolQué hace
      list_design_systemsLista los sistemas de diseño disponibles.
      get_tokenResuelve un alias semántico o un token DTCG por path a su valor.
      search_tokensBusca tokens por nombre, categoría o valor.
      list_componentsLista los componentes documentados de un sistema.
      get_component_specDevuelve la especificación completa de un componente: estados, medidas, variantes.
      get_rulesDevuelve las reglas duras (no negociables) del sistema.
      validate_codeAudita CSS/HTML contra las reglas del sistema — hex directos, contraste, aliases.
      validate_motionAudita el motion contra las reglas del sistema: transition sobre todas las propiedades, ease-in en entradas, propiedades de layout, duraciones por encima de --duration-page.
      check_slopDetecta clichés de diseño generado por IA: gradientes, glassmorphism, sombras de negro puro, fuentes genéricas y copy de marketing.
      checklist_preflightChecklist estático de pre-producción sobre HTML/CSS: metadatos, headings, imágenes y accesibilidad de controles.
      generate_snippetGenera un snippet de componente listo para usar, con los aliases correctos.

      Conectar (remoto)

      Añade el servidor a tu .mcp.json:

      {
        "mcpServers": {
          "design": {
            "type": "http",
            "url": "https://design-mcp.higiniolab.tech/mcp",
            "headers": { "Authorization": "Bearer <tu-token>" }
          }
        }
      }

      Ejemplo

      Consultar un token de este sistema:

      get_token(system="bruma", name="--accent")
      → "#5b578a"

      Auditar código antes de integrarlo:

      validate_code(system="bruma", code="…")
      → { ok: false, issues: ["hex directo #5b578a en línea 12 — usa var(--accent)"] }

      MCP y skills, misma fuente de verdad

      Usa las skills dentro de Claude; el MCP para IDEs y clientes MCP externos. Ambos leen DESIGN-bruma-lavanda.md.