Guía del UI kit — Expreso Brasilia
@aspsols/brasilia-ui · Design System de Expreso Brasilia

Guía técnica y de uso

La librería de componentes que unifica la identidad visual de Brasilia.

Un paquete Angular privado con design tokens, tema, iconos y componentes de marca. Se desarrolla una vez, se publica en el registro privado de GitLab y se consume desde cada proyecto del cliente — con una identidad visual idéntica y garantizada.

v0.1.0 publicada en el GitLab Package Registry · ya consumida por Relevo Tarjetón
Angular 21 · signals PrimeNG 21 Tailwind v4 Storybook 10 Vitest ng-packagr GitLab Registry
Para dirección: qué resuelve y por qué así Para el equipo: arquitectura, API y cómo consumirla

En una frase: es la fuente única de verdad de la interfaz del cliente. Cada botón, campo o color vive en un solo sitio versionado; los proyectos lo instalan como dependencia. Un cambio se hace una vez y se propaga a todos con una nueva versión.

Qué es y qué problema resuelve

De copiar-pegar estilos a una dependencia versionada

El objetivo de negocio: que todos los proyectos del cliente se vean iguales, sin reinventar ni mantener la interfaz por separado en cada repositorio.

Antes
  • Cada proyecto copiaba y ajustaba sus propios botones, inputs y estilos.
  • Los mismos componentes se veían distintos entre aplicaciones.
  • Un cambio de marca obligaba a tocar cada repositorio a mano.
  • Los mismos bugs de UI se arreglaban varias veces.
Con la librería
  • Los componentes viven en un repositorio y se instalan como paquete.
  • Identidad visual idéntica y garantizada por el mismo tema compartido.
  • Un cambio se hace una vez, se publica una versión, y todos la reciben.
  • Un bug se corrige una vez; todos los proyectos se benefician.

Arquitectura

Tres capas, de lo abstracto a lo concreto

La librería no es solo componentes: es un sistema en capas donde cada nivel se apoya en el anterior. Esta es la clave de que todo se vea coherente.

1

Tokens de diseño y tema

La base: la paleta, tipografía, espaciados, radios y sombras como design tokens. Un módulo TypeScript es la fuente de verdad de los valores; de ahí se derivan el preset de PrimeNG (que tinta todos sus componentes de azul de marca) y las utilidades de Tailwind. Ningún componente hardcodea un color.

tokens.tsbrasilia-preset.ts_tokens.scss_tailwind-theme.scss
2

Sistema de iconos

La familia de iconos de marca (SVG propios, optimizados) se sirve inline con <bui-icon>. El trazo usa currentColor, así que cada icono hereda el color del texto que lo rodea. Un registro central los carga al arrancar; un proyecto puede añadir iconos propios sin tocar la librería.

bui-iconIconRegistry36 iconos
3

Componentes

Componentes de presentación (standalone, OnPush, signals) que envuelven PrimeNG tras una API de marca estable — o migran componentes propios. El equipo consume <bui-button> en vez de repetir la configuración cruda de PrimeNG por todos lados.

bui-buttonbui-inputbui-select

El motor del tema

Cómo se aplica la marca a todo, sin hardcodear

Este es el corazón técnico: cómo un solo azul de marca (#1c59ba) llega de forma consistente a PrimeNG, a Tailwind y a los componentes.

PiezaRol
tokens.tsFuente de verdad en TypeScript de los valores crudos (rampa de azul, neutros, estado). Con tests que verifican contraste AA.
BrasiliaPresetdefinePreset(Aura, …): sobrescribe el primitivo blue con la rampa de marca, de modo que la variable --p-primary-* de PrimeNG resuelve al azul Expreso. Ajusta botón y select.
_tokens.scssReplica los tokens como variables CSS (--color-*, --space-*, --radius-*) que consumen los componentes.
@theme (Tailwind)Puente que genera las utilidades oficiales (bg-primary, rounded-sm, text-body) desde esos mismos valores.
Orden de capas CSS@layer theme, base, primeng, components, utilities: garantiza que los estilos del kit ganen a los de PrimeNG sin usar !important.

Registrar el tema en una app es una sola llamada:

import { providePrimeNG } from 'primeng/config';
import { BrasiliaPreset, PRIMENG_ES } from '@aspsols/brasilia-ui';

providePrimeNG({
  translation: PRIMENG_ES,
  theme: { preset: BrasiliaPreset },
});

Catálogo

Componentes y su API

Todos son standalone: se importan y se usan. Cada uno trae su documentación viva en Storybook (con código copiable) y sus pruebas. Despliega cada uno para ver su API.

<bui-button>Botón de marca

Envuelve p-button + BrasiliaPreset tras una API de variant, en vez de combinar severity/outlined/text a mano.

Uso

<!-- variantes, icono del catálogo, estados -->
<bui-button label="Guardar" variant="primary" />
<bui-button label="Cancelar" variant="secondary" />
<bui-button label="Eliminar" variant="danger" [loading]="saving()" />
<bui-button label="Nueva solicitud" icon="plus" />

Inputs principales

variantprimary · secondary · tertiary · danger
sizesmall · normal · large
icon / iconPosIcono del catálogo (izquierda/derecha)
loading · disabled · fullWidth · typeEstados y tipo (submit)
<bui-input>Campo de formulario

Campo completo: label + input + error/ayuda, con los id de accesibilidad enlazados. Implementa ControlValueAccessor, así que funciona con formControlName.

Uso

<bui-input
  label="Email"
  type="email"
  formControlName="requesterEmail"
  [error]="errorFor('requesterEmail')"
  required />

Inputs principales

label · requiredEtiqueta y asterisco de requerido
typetext · email · password · number · tel · url · search
error · hintMensaje de error (marca inválido) o ayuda
readonly · disabledEstados
<bui-select>Desplegable

Sobre p-select / p-multiSelect. Las variantes se controlan por input(): básico, filtrable, panel de tabla y multiselección. ControlValueAccessor + proyección de plantillas para filas a medida.

Uso

<!-- panel de tabla: basta con optionMeta + rótulos -->
<bui-select
  [options]="vehiculos()"
  optionLabel="placa" optionMeta="numeroBus"
  headerLabel="Placa" metaLabel="Bus"
  [filter]="true" formControlName="placa" />

Capacidades

multipleSimple o multiselección
optionMeta + headerLabel/metaLabelPanel de tabla de dos columnas, sin plantillas
filter · filterByBuscador interno
invalid · errorText · loadingEstados
plantillas #item/#selectedItemFilas personalizadas proyectadas
<bui-status-pill>Chip de estado

Píldora de estado con 5 tonos semánticos (neutro, info, éxito, aviso, peligro). Cada proyecto la envuelve para mapear sus propios estados de dominio al tono correcto, sin duplicar el estilo.

Uso

<bui-status-pill label="Aprobado" tone="success" />
<bui-status-pill label="Pendiente" tone="warning" />
<bui-icon>Icono de marca

Renderiza inline un SVG del catálogo por su name. Hereda el color del texto (currentColor) y admite tamaños sm/md/lg o un valor CSS.

<bui-icon name="bus" size="lg" />

// icono propio de un proyecto, sin tocar la librería
iconRegistry.register('mi-icono', '<svg>…</svg>');

El catálogo incluye además bui-card (superficie), bui-datepicker (fecha en español) y bui-page-subheader (cabecera de página). 7 componentes, 55 pruebas en verde.

Para el equipo

Cómo consumir la librería en un proyecto

  1. 1 · Instalar

    Tras autenticar el scope @aspsols contra el registro de GitLab (una vez, con un token):

    pnpm add @aspsols/brasilia-ui
  2. 2 · Un solo import de estilos

    En el styles.scss de la app. Incluye Tailwind, los tokens, el puente @theme y el chrome de los overlays:

    @use '@aspsols/brasilia-ui/styles';
  3. 3 · Registrar el tema

    En app.config.ts (ver el bloque de «El motor del tema» arriba).

  4. 4 · Usar los componentes

    Se importan como cualquier standalone y se usan en la plantilla:

    import { ButtonComponent } from '@aspsols/brasilia-ui';
    // …
    <bui-button label="Enviar" type="submit" [loading]="saving()" />

Decisiones técnicas

Por qué está construida así

Las preguntas que un revisor técnico hará, respondidas.

¿Por qué GitLab Registry y no npm público?

GitLab Registry
  • Privado: solo quien tiene acceso al GitLab del cliente puede instalarla.
  • Sin costo extra: incluido en el mismo GitLab del código.
  • Mismos permisos y tokens que ya usa el equipo.
  • Estándar para librerías internas de empresa.
npm público — descartado
  • Abierto al mundo: expondría el código del cliente.
  • La privacidad exige un plan de pago aparte.
  • Gestión de organización y tokens en un tercero.

Componentes envoltura (wrappers), no forks de PrimeNG. Exponemos variant="primary" en vez de la mezcla cruda de PrimeNG. Ventaja: una API de marca estable que aísla a las apps de los cambios internos de PrimeNG, y un punto único donde encapsular variantes, estados y accesibilidad.

Estilos encapsulados salvo los overlays. Los estilos de cada componente van encapsulados (no se escapan). La excepción son los paneles que PrimeNG monta en <body> (appendTo="body", para que no los recorte un contenedor): esos deben ser globales y viajan en la hoja de estilos del kit. Es un híbrido deliberado, no una fuga.

Angular y PrimeNG como peerDependencies. La librería no empaqueta Angular ni PrimeNG: los declara como «pares». La app consumidora manda la versión; así no hay duplicados ni conflictos de versión.

Ingeniería y calidad

Cómo está construida y cómo se mantiene sana

HerramientaRol
Angular 21Framework de las apps del cliente; componentes standalone, OnPush y signals.
PrimeNG 21 + @primeuix/themesComponentes ricos ya resueltos y el motor de tema (preset).
Tailwind v4Utilidades de layout y espaciado, atadas a los tokens.
Storybook 10Documentación viva: cada componente con sus estados y código copiable.
VitestPruebas unitarias; un componente no se da por terminado sin ellas en verde.
ng-packagrEmpaqueta la librería en el formato estándar (FESM + tipos) que se publica.
pnpm · Node 22Gestor y runtime, fijados para builds reproducibles.

Definición de hecho por componente: API con input()/output(), standalone + OnPush, cero valores hardcodeados (todo por token), story con estados y spec en verde.

Distribución

Publicación y versionado

1

Desarrollo

El componente se construye y aprueba en Storybook, con pruebas.

2

Empaquetado

ng-packagr genera el paquete en dist/.

3

Versión

Se sube el número (SemVer) y se publica al GitLab Registry.

4

Consumo

Cada proyecto actualiza a la nueva versión cuando le conviene.

El versionado semántico (mayor.menor.parche) le dice a cada proyecto si una actualización trae cambios que rompen (mayor), funciones nuevas (menor) o solo correcciones (parche). Nadie se actualiza a la fuerza.

Adopción en producción

Relevo Tarjetón ya consume la librería

La versión 0.1.0 está publicada en el GitLab Registry y es el primer proyecto que la instala como dependencia real — no un enlace local, sino el paquete descargado del registro.

0.1.0
publicada en el Registry (proyecto 1664)
^0.1.0
declarada como dependencia en Relevo
2
componentes del kit ya en uso: bui-button y bui-status-pill
60+
usos de bui-button en la app, en una veintena de pantallas
Ya migrado en Relevo
  • bui-button reemplaza a p-button en botones de acción y diálogos de toda la app.
  • bui-status-pill: el chip de estado propio ahora envuelve el del kit, con los 5 tonos semánticos unificados.
  • El paquete se instala desde el Registry privado vía .npmrc con token de GitLab.
Pendiente en Relevo
  • Adoptar el tema/preset y la hoja de estilos del kit (hoy usa su PRIMENG_ES local).
  • Sustituir los p-button crudos que aún quedan en algunas plantillas.
  • Migrar el resto: input, select, card, datepicker y subheader.

Estado del proyecto

En qué vamos

Hecho

Fase 0 · Base del proyecto y tema

Workspace, librería, azul de marca y tokens, Storybook funcionando.

Hecho

Fase 1 · Sistema de iconos

36 iconos de marca como bui-icon, con galería y pruebas.

Hecho

Fase 2 · Componentes base

7 componentes migrados (botón, input, select, chip, tarjeta, datepicker, subheader), 55 pruebas en verde.

En curso

Fase 3 · Publicación y adopción

v0.1.0 publicada en el Registry. Relevo Tarjetón ya consume el paquete: botón y chip de estado adoptados; falta el tema y el resto de componentes.

Fase 4 · Resto de componentes

Buscador, estado vacío, diálogo de confirmación y más.