<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Construir un Design System con Angular: Componentes, schematics, Storybook y publicación en npm

Un sistema de diseño no es una biblioteca de componentes de interfaz de usuario: es el contrato compartido entre diseño y desarrollo que garantice la coherencia visual, de comportamiento y de accesibilidad en todos productos de una organización. Construir uno en Angular significa juntar cuatro piezas que a menudo se abordan por separado y de manera deficiente: componentes reutilizables con API estables, generadores esquemas que reducen la fricción en la adopción, Storybook cómo entorno aislado de desarrollo y documentación, y un proceso de publicación en npm confiable con versiones semánticas.

Los beneficios mensurables de un sistema de diseño maduro son concretos: menos tiempo dedicado a reinventar componentes ya existentes, menos errores de UI debido a implementaciones divergentes del mismo patrón, e una superficie de prueba más pequeña porque la lógica de los componentes compartidos se valida solo una vez tiempo en lugar de en cada aplicación que los consume. El principal riesgo, si el diseño El sistema no tiene una gobernanza clara, es exactamente lo contrario: una biblioteca que se convierte en un cuello de botella. porque cada equipo tiene que esperar una versión centralizada para cada pequeño cambio.

Arquitectura de biblioteca: Monorepo vs repositorio separado

La primera decisión arquitectónica determina todo lo demás en el flujo de trabajo. A monorepo (administrado con Nx o espacio de trabajo Angular CLI multiproyecto) contiene sistemas y aplicaciones de diseño consumidor en el mismo repositorio: los cambios se prueban instantáneamente con aplicaciones reales sin publicar una versión intermedia, pero el repositorio crece y requiere herramientas para compilaciones incrementales. Un repo separado para el sistema de diseño obliga a una disciplina de control de versiones más rigurosa Desde el principio, nos obliga a pensar en la API del componente como un contrato público real, pero introduce Latencia entre un cambio y su disponibilidad en los consumidores.

Criterios de elección

CriterioMonorepoRepositorio separado
Número de equipos de consumidores1-2 equipos3+ equipos independientes
Velocidad de iteraciónRetroalimentación alta e inmediataMás lenta, requiere publicación
Se requiere disciplina APIBaja (puedes ver inmediatamente si algo se rompe)Alta (la API es un contrato público)
Complejidad de herramientasMedia/Alta (Nx, caché de compilación)Baja (compilación estándar de CLI angular)

Para la convención de nomenclatura, adopte un alcance npm dedicado (por ejemplo, @company/ui) desde el primer y aplique Semantic Versioning estrictamente: parche para corregir errores sin Cambios de API, menores para componentes nuevos o accesorios opcionales, importantes para cualquier cambio importante en los accesorios existentes, eliminando componentes o cambiando el comportamiento predeterminado.

Diseño de componentes: accesibilidad, tematización y API

Cada componente del sistema de diseño debe seguir reglas más estrictas que un componente de aplicación. cualquiera, porque su radio de impacto es toda la organización, no una sola característica.

// Component design: standalone, OnPush, API tipizzata con Signal-based inputs
@Component({
  selector: 'ds-button',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    
      @if (loading()) {  }
      
    
  `,
})
export class DsButtonComponent {
  variant = input<'primary' | 'secondary' | 'danger'>('primary');
  disabled = input(false);
  loading = input(false);
  pressed = output();

  protected variantClass = computed(() => `ds-btn ds-btn--${this.variant()}`);
}

Tres reglas no negociables para cada componente público: OnPush obligatorio (un sistema de diseño con detección de cambios (por defecto propaga ralentizaciones a cada aplicación que lo consume), API basada en entradas/salidas de señal en lugar de propiedades mutables expuestas directamente y cero dependencias de los estilos globales de la aplicación host: cada componente debe ser visualmente correcto incluso en una página en blanco sin CSS externo; de lo contrario, la temática resulta imposible de garantizar.

Esquemas: Generadores personalizados para reducir la fricción de adopción

Un esquema personalizado permite a los equipos de consumidores estructurar el uso correcto de un componente con un comando único, en lugar de copiar y pegar ejemplos de la documentación (lo que inevitablemente quedar obsoleto).

// schematics/add-form-field/index.ts
export function addFormField(options: AddFormFieldOptions): Rule {
  return (tree: Tree, context: SchematicContext) => {
    const componentPath = `${options.path}/${options.name}.component.ts`;
    const content = `
import { Component, input } from '@angular/core';
import { DsInputComponent } from '@azienda/ui/input';

@Component({
  selector: 'app-${options.name}',
  standalone: true,
  imports: [DsInputComponent],
  template: \`\`,
})
export class ${strings.classify(options.name)}Component {
  label = input.required();
  control = input.required();
}
`;
    tree.create(componentPath, content);
    context.logger.info(`✅ Creato ${componentPath}`);
    return tree;
  };
}
# Uso dello schematic da parte di un team consumer
ng generate @azienda/ui:add-form-field --name=email-field --path=src/app/features/checkout

Libro de cuentos: configuración, complementos e historias

Storybook es el entorno donde se desarrollan, documentan y prueban visualmente los componentes en aislamiento de la aplicación del consumidor.

# Setup iniziale in un progetto Angular esistente
npx storybook@latest init

# Addon essenziali per un design system: controls, docs automatica, a11y
npm install --save-dev @storybook/addon-a11y @storybook/addon-docs
// ds-button.stories.ts
const meta: Meta = {
  title: 'Components/Button',
  component: DsButtonComponent,
  tags: ['autodocs'],
  argTypes: {
    variant: { control: 'select', options: ['primary', 'secondary', 'danger'] },
  },
};
export default meta;

export const Primary: StoryObj = {
  args: { variant: 'primary' },
  render: (args) => ({ props: args, template: `Conferma` }),
};

El complemento a11y ejecuta automáticamente axe-core en cada historia en cada construcción, transformando Libro de cuentos en una puerta de accesibilidad continua en lugar de una revisión manual ocasional antes del lanzamiento.

Empaquetado y publicación en npm

Empaquetar una biblioteca Angular requiere ng-packagr, que genera una salida compatible con Ivy (compilación parcial), paquete FESM y definiciones de tipos correctas.

// ng-package.json
{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/ui",
  "lib": {
    "entryFile": "src/public-api.ts"
  }
}
// package.json della libreria — peerDependencies, non dependencies dirette
{
  "name": "@azienda/ui",
  "version": "3.4.0",
  "peerDependencies": {
    "@angular/core": "^17.0.0 || ^18.0.0",
    "@angular/common": "^17.0.0 || ^18.0.0"
  },
  "sideEffects": false
}
# Build, verifica del pacchetto e publish
npx ng-packagr -p ng-package.json
npm pack --dry-run dist/ui
npm publish dist/ui --access public

peerDependencies en lugar de dependencies directas es la opción correcta para Angular/RxJS: evite que cada aplicación de consumo termine con dos copias de Angular en el paquete final. sideEffects: false en package.json permite la agitación de árboles del lado del consumidor, como esta importar solo un componente no arrastra toda la biblioteca al paquete.

Pruebas: Unidad, Visual, E2E

// Unit test con snapshot dell'output renderizzato
it('applica la classe corretta per variant="danger"', () => {
  const fixture = TestBed.createComponent(DsButtonComponent);
  fixture.componentRef.setInput('variant', 'danger');
  fixture.detectChanges();
  expect(fixture.nativeElement.querySelector('button').className).toContain('ds-btn--danger');
});

La prueba visual (Chromatic o Percy integrado con Storybook) toma una captura de pantalla de cada historia en cada solicitud de extracción e informa automáticamente cualquier diferencia de píxeles en comparación con la línea de base: es la única forma práctica de notar una regresión visual involuntaria en un componente usado en decenas de puntos diferentes de la organización. Las pruebas E2E (Cypress/Dramaturgo) en El sistema de diseño en sí debe limitarse a flujos de interacción complejos (un selector de fechas, un autocompletar con búsqueda asincrónica), no para cada componente individual, para la mayoría componentes, las pruebas unitarias más las pruebas visuales ya cubren el riesgo principal.

CI/CD: canalización para compilar, probar, implementar y publicar

  • Build: ng-packagr verificación de tipo más completa en cada solicitud de extracción, no solo en la rama principal.
  • Prueba: prueba unitaria, regresión visual y verificación total como pasos de bloqueo separados: una falla total bloquea la fusión como una prueba fallida.
  • Implementar Storybook: publicación automática de una vista previa de Storybook para cada solicitud de extracción, para que los revisores (incluso los que no sean técnicos) puedan verificar visualmente cada componente modificado antes de su aprobación.
  • Publish npm: automatizado solo al fusionar en main, con versiones semánticas calculadas automáticamente a partir de confirmaciones (Commits convencionales + liberación semántica), nunca una publicación manual desde una computadora portátil.

Fichas de temática y diseño

Los diseños de tokens son la única fuente de verdad para los colores, el espaciado, la tipografía y los radios de los bordes, desde que deriva automáticamente propiedades personalizadas de CSS, un archivo SCSS y una exportación JSON para herramientas diseño (Figma).

// tokens/colors.json — fonte di verità
{
  "color": {
    "primary": { "value": "#2563eb" },
    "danger": { "value": "#dc2626" }
  }
}
/* Output generato: CSS custom properties */
:root {
  --ds-color-primary: #2563eb;
  --ds-color-danger: #dc2626;
}

Cada componente consume solo propiedades personalizadas de CSS, nunca valores codificados: eso es lo que es que permite que una aplicación de consumo aplique un tema personalizado (etiqueta blanca, modo oscuro) simplemente anulando las variables de nivel raíz, sin tocar el código del componente.

Gobernanza y documentación

Un sistema de diseño sin una gobernanza explícita se degrada rápidamente a una colección de componentes inconsistente. Necesitamos una política escrita para cambios importantes (obsolescencia al menos una versión menor anunciada antes de la eliminación, con advertencia de tiempo de ejecución en desarrollo), una CHANGELOG generado automáticamente por confirmaciones convencionales, y un contribución clara que define quién aprueba nuevos componentes y con qué criterios (duplica un patrón existente? ¿Es realmente necesario en el sistema de diseño o es específico de una sola aplicación?).

Accesibilidad: lista de verificación y ejemplos ARIA

  • Cada elemento interactivo se puede navegar y activar mediante el teclado (Tab, Enter, Espacio), no solo con el mouse.
  • State aria-disabled/aria-busy expuesto correctamente durante los estados de carga, no solo el atributo nativo disabled.
  • Contraste de color mínimo WCAG AA (4,5:1 para texto sin formato) verificado en los diseños de los tokens, no dejado a la discreción de quienes consumen el componente.
  • Los componentes compuestos (desplegable, modal, pestaña) implementan el patrón ARIA APG correcto, incluido role, aria-expanded y el manejo de trampas de enfoque cuando sea necesario.
<!-- Esempio: componente tab conforme ARIA APG -->
<div role="tablist" aria-label="Impostazioni account">
  <button role="tab" [attr.aria-selected]="active() === 'profile'" id="tab-profile">Profilo</button>
  <button role="tab" [attr.aria-selected]="active() === 'security'" id="tab-security">Sicurezza</button>
</div>

Rendimiento y tamaño del paquete

  • Tree-shaking: puntos de entrada separados por componente (@company/ui/button, @company/ui/input) en lugar de un archivo de un solo barril, por lo que importar un componente no arrastra todo biblioteca.
  • Carga diferida de componentes pesados (selector de fechas con calendario, editor de texto enriquecido) a través de @defer, no cargados en el paquete inicial de aplicaciones para consumidores.
  • Analizador de paquetes se ejecuta en CI en la propia biblioteca, con un umbral de tamaño máximo por componente que falla en la compilación si se excede.

Estudio de caso 1: Fintech con 4 equipos de productos

Una empresa de tecnología financiera con 4 equipos de productos independientes adoptó un sistema de diseño angular compartido en repositorio separado. Después de 6 meses: el tiempo medio de desarrollo de una nueva pantalla se redujo en un 34% (menos componentes reinventados desde cero), los errores de UI reportados en producción se redujeron en un 41% (lo mismo implementación validada, no 4 variaciones divergentes del mismo patrón), tiempo de incorporación de un nuevo desarrollador frontend reducido de 3 semanas a 8 días gracias a Storybook como documentación viviendo.

Estudio de caso 2: Mercado B2B en fase de ampliación

Un mercado B2B en expansión que creció de 1 a 3 equipos frontend en un año adoptó inicialmente un Nx monorepo para el sistema de diseño, luego migró a un repositorio separado cuando se unió el tercer equipo. Resultado: El tamaño del paquete de aplicaciones se redujo en un 22% después de introducir puntos de entrada por componente. la cobertura de prueba de componentes compartidos aumentó del 45% al 89% y una reducción del 60% en el tiempo dedicado a revisión de código sobre implementaciones de UI duplicadas entre equipos.

Lista de verificación operativa 30/60/90 días

Días 1-30: Fundación

  • Configurar el repositorio, ng-packagr y los primeros 5 componentes principales (botón, entrada, tarjeta, insignia, control giratorio) — KPI: crear y publicar el funcionamiento en seco.
  • Storybook configurado con todos los complementos activos en cada historia — KPI: 0 violaciones críticas a11y en los componentes principales.
  • Tokens de diseño definidos como fuente única de verdad: KPI: el 100 % de los componentes principales utilizan solo propiedades personalizadas de CSS.

Días 31-60: Adopción

  • El primer equipo de consumidores migró a al menos 3 componentes del sistema de diseño: KPI: reducción mensurable de CSS personalizado duplicado en esa aplicación.
  • Canalización completa de CI/CD con publicación automática al fusionar — KPI: 0 publicaciones manuales desde una computadora portátil.
  • Pruebas visuales activas en cada solicitud de extracción — KPI: 0 regresiones visuales no intencionales ocurrieron.

Días 61-90: Escala

  • Cobertura de al menos 20 componentes que cubren el 80% de los patrones de UI más comunes — KPI: auditoría de cobertura documentada.
  • Gobernanza formalizada (cambio radical de políticas, proceso de contribución) — KPI: documento publicado y compartido con todos los equipos.
  • Al menos un segundo equipo de consumidores incorporado: KPI: tiempo de incorporación medido y comparado con el primer equipo.

Miniguía 1: Creación del primer componente del sistema de diseño

Cada componente público parte de una API mínima y tipificada, no de la reproducción de cada uno posible variante desde el primer día.

@Component({ selector: 'ds-badge', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush,
  template: `` })
export class DsBadgeComponent { tone = input<'neutral' | 'success' | 'error'>('neutral'); }

Pasajes clave

  1. Defina la API pública (entrada/salida) antes de escribir la plantilla.
  2. Aplicar entradas OnPush y Signal desde la primera confirmación.
  3. Agregue la historia de Storybook en la misma solicitud de extracción que el componente.

Preguntas frecuentes: ¿Con cuántos componentes debería comenzar? De 5 a 8 componentes principales (botón, entrada, insignia, tarjeta, control giratorio) son suficientes para validar todo el proceso antes de escalar.

Mini-Guía 2: Escribir un esquema personalizado

Un esquema reduce la fricción de adopción al traducir la documentación en un comando ejecutable. en lugar de dejar que cada equipo reinterprete las mejores prácticas a su manera.

ng generate @azienda/ui:add-form-field --name=email --path=src/app/checkout

Pasajes clave

  1. Identifica un patrón repetido manualmente por varios equipos.
  2. Escribe la Regla que genera el código correcto en un solo comando.
  3. Documente el esquema en Storybook junto al componente que lo estructura.

Preguntas frecuentes: ¿Vale la pena un esquema para un solo componente? Solo si ese componente requiere texto estándar recurrente (campo de formulario, contenedor de validación); para componentes simples no es necesario.

Mini-Guía 3: Configurar Storybook con el complemento a11y

npx storybook@latest init
npm install --save-dev @storybook/addon-a11y

Pasajes clave

  1. Active el complemento a11y en el archivo .storybook/main.ts.
  2. Configure el CI para que falle en todas las violaciones críticas, no solo en las advertencias.
  3. Revise los resultados directamente en el panel Storybook durante el desarrollo, no solo en CI al final del trabajo.

Preguntas frecuentes: ¿El complemento a11y reemplaza una auditoría manual? No: detecte infracciones automatizables (contraste, atributos ARIA faltantes), no problemas de usabilidad que requieran pruebas con usuarios reales.

Mini-Guía 4: Publicar la biblioteca en npm con ng-packagr

npx ng-packagr -p ng-package.json
npm publish dist/ui --access public

Pasajes clave

  1. Compruebe que peerDependencies cubra todas las versiones de Angular compatibles.
  2. Siempre ejecute npm Publish --dry-run antes de la publicación real.
  3. Automatizar la publicación en CI, nunca manualmente desde un entorno local no reproducible.

Preguntas frecuentes: ¿Es necesario publicar en cada fusión? No: solo cuando el control de versiones semántico calculado a partir de las confirmaciones realmente produce una nueva versión (corrección de errores, característica, cambios importantes).

Mini-Guía 5: Exportar token de diseño a CSS, SCSS y JSON

{ "color": { "primary": { "value": "#2563eb" } } }

Pasos clave

  1. Defina tokens en formato neutral (JSON) como única fuente de verdad.
  2. Genere automáticamente propiedades personalizadas de CSS y variables SCSS desde el mismo archivo.
  3. Sincronizar tokens con la herramienta de diseño (Figma) mediante exportación/importación automatizada, no mediante copia manual.

Preguntas frecuentes: ¿Deberían los diseñadores editar el JSON directamente? Preferiblemente no: trabajan en la herramienta de diseño y un complemento/script sincroniza los valores en el repositorio de tokens.

Errores comunes que se deben evitar

  • Cambie la estrategia de detección de cambios predeterminada a Predeterminada: propaga ralentizaciones en cada aplicación que consume el sistema de diseño.
  • Utilice dependencies en lugar de peerDependencies para Angular: provoca la duplicación del marco en el paquete del consumidor.
  • Un archivo de un solo barril que exporta todo: elimina las sacudidas de los árboles e infla el paquete incluso cuando se usa solo un componente.
  • No hay complementos de 11 años en Storybook: las infracciones de accesibilidad solo se descubren en producción, cuando su reparación cuesta mucho más.
  • Publicar manualmente desde una computadora portátil: introduce inconsistencia entre entornos y hace imposible la trazabilidad de la liberación.
  • Cambios importantes sin desaprobación previa: interrumpe silenciosamente todas las aplicaciones de consumo en la próxima actualización.
  • Fichas de diseño duplicadas o codificadas en componentes: hace que sea imposible mantener la temática y desalinea el diseño y el código con el tiempo.
  • No hay gobernanza sobre nuevos componentes: genera duplicados y variaciones inconsistentes del mismo patrón en unos pocos meses.

Resumen de preguntas frecuentes

¿Qué es un sistema de diseño en Angular? Una biblioteca de componentes temáticos, accesibles y reutilizables, publicada como un paquete npm, que proporciona coherencia visual y de comportamiento en todas las aplicaciones de una organización.

¿Es mejor monorepo o repositorio separado para un sistema de diseño? Monorepo si el sistema de diseño sirve a 1 o 2 equipos con iteración rápida; repositorio separado cuando hay 3 o más equipos de consumidores y se necesita una API pública versionada y estable.

¿Cómo publico una biblioteca Angular en npm? Con ng-packagr para generar resultados compilados, peerDependencies para Angular/RxJS y publicación npm automatizada en CI después de la compilación y prueba.

¿Para qué sirve Storybook en un sistema de diseño? Es el entorno aislado de desarrollo, documentación y pruebas visuales para componentes, con complementos para controles interactivos y verificación automática de accesibilidad.

¿Cómo se gestiona la temática de un sistema de diseño? A través de tokens de diseño exportados como propiedades personalizadas de CSS, consumidos por componentes en lugar de valores codificados, así es como se aplica un tema personalizado anulando variables en el nivel raíz.

¿Qué son los esquemas angulares? Generadores de código personalizados que automáticamente implementan el uso correcto de un componente o patrón, reduciendo la fricción de adopción en comparación con copiar ejemplos de la documentación.

Preguntas frecuentes

¿Cuántos componentes se necesitan para lanzar la primera versión?

5-8 componentes principales cubren la mayoría de los casos de uso iniciales y le permiten validar todo el proceso antes de escalar.

¿Necesitas Nx para construir un sistema de diseño Angular?

No, es especialmente útil en un monorepo con múltiples proyectos, pero un sistema de diseño en un repositorio separado también funciona bien solo con Angular CLI.

¿Cómo se gestionan los cambios importantes?

Con una política de obsolescencia: advertir en tiempo de ejecución al menos una versión menor antes de su eliminación real, documentada en CHANGELOG.

¿Es obligatoria la prueba visual?

Se recomienda encarecidamente más de una docena de componentes compartidos: sin ellos, las regresiones visuales solo las descubren los usuarios finales.

¿Cómo se mide el éxito de un sistema de diseño?

Con KPI objetivos: reducción del tiempo de desarrollo por pantalla, reducción de errores de UI en producción, tiempo para incorporar nuevos desarrolladores.

¿Necesita el modo estricto de TypeScript para una biblioteca pública?

Sí, se recomienda encarecidamente: una biblioteca consumida por varios equipos se beneficia más que cualquier otro código de un sistema de tipos estricto.

¿Cómo se prueban componentes con estado complejo?

Con pruebas unitarias dirigidas a la lógica interna más pruebas visuales para la salida renderizada, reservando E2E solo para los flujos de interacción más complejos.

¿Se deben versionar los tokens de diseño junto con los componentes?

Sí, en el mismo repositorio y en el mismo ciclo de lanzamiento, porque un cambio de token es en efecto un cambio de la API visual.

¿Cómo se evita que cada equipo cree variaciones divergentes del mismo componente?

Con un proceso de contribución claro que requiere que verifiques la existencia de un patrón similar antes de crear uno nuevo.

¿Cuánto tiempo lleva construir un sistema de diseño maduro?

Normalmente, de 3 a 6 meses para una biblioteca sólida con más de 20 componentes, gobernanza y canalización completa de CI/CD, según el tamaño del equipo dedicado.

Conclusión

Un sistema de diseño Angular bien construido no es un proyecto "único": es un producto interno con i sus usuarios (los desarrolladores de los equipos de consumidores), su hoja de ruta y su gobernanza. Componentes OnPush con API escrita, Storybook como documentación viva, ng-packagr para empaquetado correcto y un proceso de CI/CD que automatiza las pruebas, la regresión visual y la publicación son los elementos que distinguir un sistema de diseño verdaderamente adoptado de una biblioteca de componentes abandonados después de unos pocos meses.

¿Quiere una lista de verificación imprimible o una evaluación de su biblioteca de componentes existente? Solicitar una auditoría técnica: en pocas horas de análisis es posible identificar brechas de accesibilidad, prioridades de rendimiento y gobernanza para su sistema de diseño.

💬 Notas de los lectores

0 notas

Escribe una nota

Comparte tu opinión, una sugerencia o un cumplido

Últimas notas

Aún no hay notas. ¡Sé el primero en comentar!