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

Construir um Design System com Angular: Componentes, schematics, Storybook e publicação no npm

Um sistema de design não é uma biblioteca de componentes de UI: é o contrato compartilhado entre design e desenvolvimento que garanta consistência visual, comportamental e de acessibilidade em todos produtos de uma organização. Construir um em Angular significa juntar quatro peças que eles são frequentemente tratados separadamente e de forma inadequada: componentes reutilizáveis com APIs estáveis, geradores esquemas que reduzem o atrito na adoção, Storybook como ambiente isolado de desenvolvimento e documentação e um processo de publicação no npm confiável com versionamento semântico.

Os benefícios mensuráveis de um sistema de design maduro são concretos: menos tempo gasto em reinvenção componentes já existentes, menos bugs de UI devido a implementações divergentes do mesmo padrão, e uma superfície de teste menor porque a lógica dos componentes compartilhados é validada apenas uma vez tempo, e não em cada aplicativo que os consome. O principal risco, se o design sistema não tem uma governança clara, é exatamente o oposto: uma biblioteca que se torna um gargalo porque cada equipe precisa esperar por um lançamento centralizado para cada pequena alteração.

Arquitetura de Biblioteca: Monorepo vs Repo Separado

A primeira decisão arquitetônica determina todo o resto do fluxo de trabalho. A monorepo (gerenciado com Nx ou espaço de trabalho Angular CLI de vários projetos) contém sistemas de design e aplicativos consumidor no mesmo repositório: as alterações são testadas instantaneamente em aplicativos reais sem publicar uma versão intermediária, mas o repositório cresce e requer ferramentas para compilações incrementais. Um repo separado para o sistema de design força uma disciplina de versão mais rigorosa desde o início, obriga-nos a pensar na API do componente como um verdadeiro contrato público, mas introduz latência entre uma mudança e sua disponibilidade nos consumidores.

Critérios de Escolha

CritérioMonorepoRepositório separado
Número de equipes de consumidores1-2 equipes3+ equipes independentes
Velocidade de iteraçãoFeedback alto e imediatoMais lento, requer publicação
Disciplina de API necessáriaBaixa (você pode ver imediatamente se algo quebrar)Alta (a API é um contrato público)
Complexidade de ferramentasMédia/Alta (Nx, cache de construção)Baixa (compilação padrão CLI Angular)

Para a convenção de nomenclatura, adote um escopo npm dedicado (por exemplo, @empresa/ui) desde o primeiro componente e aplique Semantic Versioning estritamente: patch para correções de bugs sem Mudanças de API, pequenas para novos componentes ou adereços opcionais, grandes para qualquer alteração significativa em adereços existente, removendo componentes ou alterando o comportamento padrão.

Design de componentes: acessibilidade, temas e API

Cada componente do sistema de design deve seguir regras mais rigorosas do que um componente de aplicativo qualquer, porque seu raio de impacto é toda a organização, não um único recurso.

// 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()}`);
}

Três regras não negociáveis para cada componente público: OnPush obrigatório (um sistema de design com detecção de alterações O padrão propaga lentidão para cada aplicativo que o consome), API baseada em entradas/saídas de sinal em vez de propriedades mutáveis expostas diretamente e zero dependências nos estilos globais do aplicativo host — cada componente deve estar visualmente correto mesmo em uma página em branco sem CSS externo, caso contrário o tema torna-se impossível garantir.

Esquemas: Geradores personalizados para reduzir o atrito de adoção

Um esquema personalizado permite que as equipes de consumidores estruturem o uso correto de um componente com um comando único, em vez de copiar e colar exemplos da documentação (que inevitavelmente tornar-se 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

Livro de histórias: configuração, complementos e histórias

Storybook é o ambiente onde os componentes são desenvolvidos, documentados e testados visualmente em isolamento da aplicação do 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` }),
};

O complemento a11y executa automaticamente axe-core em cada história em cada construção, transformando Storybook em um portão de acessibilidade contínuo em vez de uma verificação manual ocasional antes do lançamento.

Embalagem e publicação em npm

Empacotar uma biblioteca Angular requer ng-packagr, que gera saída compatível com Ivy (compilação parcial), pacote FESM e definições de tipo corretas.

// 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 em vez de direto dependencies é a escolha correta para Angular/RxJS: evite que cada aplicativo de consumidor acabe com duas cópias do Angular no pacote final. sideEffects: false em package.json permite a agitação da árvore do lado do consumidor, como esta importar apenas um componente não arrasta a biblioteca inteira para o pacote.

Teste: Unidade, 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');
});

O teste visual (Chromatic ou Percy integrado ao Storybook) faz uma captura de tela de cada história em cada solicitação pull e relata automaticamente qualquer diferença de pixel em comparação com a linha de base - é a única maneira prática de perceber uma regressão visual inadvertida em um componente usado em dezenas de pontos diferentes da organização. O E2E (Cypress/Playwright) testa em O próprio sistema de design deve ser limitado a fluxos de interação complexos (um seletor de data, um preenchimento automático com pesquisa assíncrona), não para cada componente individual - para a maioria componentes, testes unitários e testes visuais já cobrem o risco principal.

CI/CD: pipeline para construção, teste, implantação e publicação

  • Build: ng-packagr verificação de tipo mais completa em cada solicitação pull, não apenas no branch principal.
  • Test: teste de unidade, regressão visual e verificação a11y como etapas de bloqueio separadas - uma falha a11y bloqueia a mesclagem como um teste quebrado.
  • Implantar Storybook: publicação automática de uma visualização do Storybook para cada pull request, para que os revisores (mesmo os não técnicos) possam verificar visualmente cada componente modificado antes da aprovação.
  • Publish npm: automatizado apenas na mesclagem no main, com versionamento semântico calculado automaticamente a partir de commits (commits convencionais + liberação semântica), nunca uma publicação manual do laptop.

Tokens de tema e design

Os designs de token são a única fonte de verdade para cores, espaçamento, tipografia e raios de borda, desde que derivam automaticamente propriedades personalizadas CSS, um arquivo SCSS e uma exportação JSON para ferramentas projeto (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 consome apenas propriedades personalizadas CSS, nunca valores codificados - é isso que é que permite que um aplicativo de consumidor aplique um tema personalizado (etiqueta branca, modo escuro) simplesmente substituindo variáveis de nível raiz, sem tocar no código do componente.

Governança e Documentação

Um sistema de design sem governança explícita rapidamente se degrada em uma coleção de componentes inconsistente. Precisamos de uma política escrita para alterações significativas (descontinuação pelo menos uma versão secundária anunciada antes da remoção, com aviso de tempo de execução em desenvolvimento), um CHANGELOG gerado automaticamente por commits convencionais e um contribuição clara que define quem aprova novos componentes e com quais critérios (duplica um padrão existente? É realmente necessário no sistema de design ou é específico para apenas um aplicativo?).

Acessibilidade: lista de verificação e exemplos ARIA

  • Cada elemento interativo pode ser navegado e ativado pelo teclado (Tab, Enter, Space), não apenas pelo mouse.
  • State aria-disabled/aria-busy exposto corretamente durante os estados de carregamento, não apenas o atributo nativo disabled.
  • Contraste mínimo de cores WCAG AA (4,5:1 para texto simples) verificado nos próprios designs de token, não deixado ao critério de quem consome o componente.
  • Componentes compostos (suspenso, modal, guia) implementam o padrão ARIA APG correto, incluindo role, aria-expanded e manipulação de armadilha de foco quando necessário.
<!-- 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>

Desempenho e tamanho do pacote

  • Tree-shaking: pontos de entrada separados por componente (@company/ui/button, @company/ui/input) em vez de um único arquivo barril, portanto, importar um componente não arrasta o todo biblioteca.
  • Carregamento lento de componentes pesados (seletor de data com calendário, editor de rich text) via @defer, não carregado no pacote inicial de aplicativos de consumo.
  • Analisador de pacotes executado em CI na própria biblioteca, com um limite de tamanho máximo por componente que falha na construção se excedido.

Estudo de caso 1: Fintech com 4 equipes de produto

Uma empresa fintech com 4 equipes de produtos independentes adotou um sistema de design Angular compartilhado em repositório separado. Após 6 meses: tempo médio de desenvolvimento para uma nova tela reduzido em 34% (menos componentes reinventados do zero), bugs de UI relatados em produção reduzidos em 41% (o mesmo implementação validada, não 4 variações divergentes do mesmo padrão), tempo de integração de um novo desenvolvedor frontend reduzido de 3 semanas para 8 dias graças ao Storybook como documentação vivendo.

Estudo de caso 2: Mercado B2B em fase de expansão

Um mercado B2B em expansão que cresceu de 1 para 3 equipes front-end em um ano adotou inicialmente um Nx monorepo para o sistema de design e depois migrou para um repositório separado quando a terceira equipe entrou. Resultado: tamanho do pacote de aplicativos reduzido em 22% após a introdução de pontos de entrada por componente, a cobertura de testes de componentes compartilhados aumentou de 45% para 89% e uma redução de 60% no tempo gasto em revisão de código em implementações de UI duplicadas entre equipes.

Lista de verificação operacional 30/60/90 dias

Dias 1 a 30: Fundação

  • Repositório de configuração, ng-packagr e os primeiros 5 componentes principais (botão, entrada, cartão, crachá, girador) — KPI: construir e publicar trabalho de simulação.
  • Storybook configurado com complemento a11y ativo em cada história — KPI: 0 violações críticas a11y em componentes principais.
  • Tokens de design definidos como fonte única de verdade — KPI: 100% dos componentes principais usam apenas propriedades personalizadas CSS.

Dias 31 a 60: Adoção

  • A primeira equipe de consumidores migrou para pelo menos três componentes do sistema de design — KPI: redução mensurável em CSS personalizado duplicado nesse aplicativo.
  • Pipeline completo de CI/CD com publicação automática na mesclagem — KPI: 0 publicações manuais do laptop.
  • Testes visuais ativos em cada solicitação pull — KPI: ocorreram 0 regressões visuais não intencionais.

Dias 61-90: Escala

  • Cobertura de pelo menos 20 componentes cobrindo 80% dos padrões de UI mais comuns — KPI: auditoria de cobertura documentada.
  • Governança formalizada (mudanças de políticas, processo de contribuição) — KPI: documento publicado e compartilhado com todas as equipes.
  • Pelo menos uma segunda equipe de consumidores integrada — KPI: tempo de integração medido e comparado com a primeira equipe.

Mini-guia 1: Criando o primeiro componente do sistema de design

Cada componente público parte de uma API mínima e digitada, e não da reprodução de cada variante possível desde o primeiro dia.

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

Passagens Principais

  1. Defina a API pública (entrada/saída) antes de escrever o modelo.
  2. Aplica entradas OnPush e Signal desde o primeiro commit.
  3. Adicione a história do Storybook na mesma solicitação pull do componente.

FAQ: Com quantos componentes você deve começar? 5 a 8 componentes principais (botão, entrada, crachá, cartão, girador) são suficientes para validar todo o pipeline antes de escalar.

Mini-guia 2: Escrevendo um esquema personalizado

Um esquema reduz o atrito na adoção ao traduzir a documentação em um comando executável, em vez de deixar que cada equipe reinterprete as melhores práticas à sua maneira.

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

Passagens Principais

  1. Identifica um padrão repetido manualmente por várias equipes.
  2. Escreva a Rule que gera o código correto em um comando.
  3. Documente o esquema no Storybook próximo ao componente que ele estrutura.

FAQ: Um esquema vale a pena para apenas um componente? Somente se esse componente exigir clichê recorrente (campo de formulário, wrapper de validação); para componentes simples não é necessário.

Mini-Guia 3: Configurando o Storybook com Addon a11y

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

Passagens Principais

  1. Ative o complemento a11y no arquivo .storybook/main.ts.
  2. Configure o CI para falhar em violações críticas a11y, não apenas em avisos.
  3. Revise os resultados diretamente no painel Storybook durante o desenvolvimento, não apenas no CI no final do trabalho.

FAQ: O complemento a11y substitui uma auditoria manual? Não: captura violações automatizadas (contraste, atributos ARIA ausentes), não problemas de usabilidade que exigem testes com usuários reais.

Mini-guia 4: Publique a biblioteca no npm com ng-packagr

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

Passagens Principais

  1. Verifique se peerDependencies abrange todas as versões Angular suportadas.
  2. Sempre execute npmpublish --dry-run antes da publicação real.
  3. Automatize a publicação em CI, nunca manualmente a partir de um ambiente local não reproduzível.

FAQ: É necessário publicar a cada mesclagem? Não: somente quando o controle de versão semântico calculado a partir dos commits realmente produz uma nova versão (correção de bug, recurso, alteração significativa).

Mini-guia 5: Exportar token de design para CSS, SCSS e JSON

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

Principais etapas

  1. Defina tokens em formato neutro (JSON) como a única fonte da verdade.
  2. Gere automaticamente propriedades personalizadas CSS e variáveis SCSS do mesmo arquivo.
  3. Sincronize tokens com a ferramenta de design (Figma) por meio de exportação/importação automatizada, não de cópia manual.

FAQ: Os designers devem editar o JSON diretamente? De preferência não: eles trabalham na ferramenta de design e um plugin/script sincroniza os valores no repositório de tokens.

Erros comuns a serem evitados

  • Altere a estratégia de detecção de alterações padrão para Padrão: propaga lentidão em todos os aplicativos que consomem o sistema de design.
  • Use dependencies em vez de peerDependencies para Angular: causa duplicação da estrutura no pacote do consumidor.
  • Um arquivo de barril único que exporta tudo: elimina o tremor da árvore e infla o pacote mesmo ao usar apenas um componente.
  • Não há complemento a11y no Storybook: Violações de acessibilidade só são descobertas na produção, quando custam muito mais para serem corrigidas.
  • Publicar manualmente a partir do laptop: introduz inconsistência entre ambientes e impossibilita a rastreabilidade da versão.
  • Quebrar alterações sem depreciação prévia: interromper silenciosamente todos os aplicativos de consumidor na próxima atualização.
  • Tokens de design duplicados ou codificados em componentes: Torna impossível a manutenção do tema e desalinha o design e o código ao longo do tempo.
  • Sem governança em novos componentes: Leva a duplicatas e variações inconsistentes do mesmo padrão dentro de alguns meses.

Perguntas frequentes no resumo

O que é um sistema de design em Angular? Uma biblioteca de componentes reutilizáveis, acessíveis e temáticos, publicada como um pacote npm, que fornece consistência visual e comportamental em todos os aplicativos de uma organização.

O monorepo ou repositório separado é melhor para um sistema de design? Monorepo se o sistema de design atende 1-2 equipes com iteração rápida; repositório separado quando há 3 ou mais equipes de consumidores e é necessária uma API pública estável e com versão.

Como faço para publicar uma biblioteca Angular no npm? Com ng-packagr para gerar saída compilada, peerDependencies para Angular/RxJS e npm publicar automatizado em CI após construção e teste.

Para que serve o Storybook em um sistema de design? É o ambiente isolado de desenvolvimento, documentação e teste visual para componentes, com complementos para controles interativos e verificação automática de acessibilidade.

Como você gerencia o tema de um sistema de design? Por meio de tokens de design exportados como propriedades personalizadas CSS, consumidos por componentes em vez de valores codificados, é assim que um tema personalizado é aplicado substituindo variáveis no nível raiz.

O que são esquemas Angular? Geradores de código personalizados que estruturam automaticamente o uso correto de um componente ou padrão, reduzindo o atrito de adoção em comparação com a cópia de exemplos da documentação.

FAQ

Quantos componentes são necessários para lançar a primeira versão?

5-8 componentes principais cobrem a maioria dos casos de uso iniciais e permitem validar todo o pipeline antes do dimensionamento.

Você precisa de Nx para construir um sistema de design Angular?

Não, é especialmente útil em um monorepo com vários projetos, mas um sistema de design em um repositório separado também funciona bem apenas com o Angular CLI.

Como as alterações significativas são gerenciadas?

Com uma política de descontinuação: avisar em tempo de execução pelo menos uma versão secundária antes da remoção real, documentada no CHANGELOG.

O teste visual é obrigatório?

Fortemente recomendado para mais de uma dúzia de componentes compartilhados: sem ele, as regressões visuais só são descobertas pelos usuários finais.

Como você mede o sucesso de um sistema de design?

Com KPIs objetivos: redução do tempo de desenvolvimento por tela, redução de bugs de UI na produção, tempo para integrar novos desenvolvedores.

Você precisa do modo estrito TypeScript para uma biblioteca pública?

Sim, é altamente recomendado: uma biblioteca consumida por várias equipes se beneficia mais do que qualquer outro código de um sistema de tipo estrito.

Como você testa componentes com estado complexo?

Com testes unitários voltados para a lógica interna mais testes visuais para a saída renderizada, reservando E2E apenas para os fluxos de interação mais complexos.

Os tokens de design devem ser versionados junto com os componentes?

Sim, no mesmo repositório e no mesmo ciclo de lançamento, porque uma mudança de token é, na verdade, uma mudança na API visual.

Como evitar que cada equipe crie variações divergentes do mesmo componente?

Com um processo de contribuição claro que exige que você verifique a existência de um padrão semelhante antes de criar um novo.

Quanto tempo leva para construir um sistema de design maduro?

Normalmente de 3 a 6 meses para uma biblioteca robusta com mais de 20 componentes, governança e pipeline completo de CI/CD, dependendo do tamanho da equipe dedicada.

Conclusão

Um sistema de design Angular bem construído não é um projeto “único”: é um produto interno com i seus usuários (os desenvolvedores das equipes de consumo), seu roteiro e sua governança. Componentes OnPush com API digitada, Storybook como documentação viva, ng-packagr para embalagem correto e um pipeline de CI/CD que automatiza testes, regressão visual e publicação são os elementos que distinguir um sistema de design verdadeiramente adotado de uma biblioteca de componentes abandonada após alguns meses.

Deseja uma lista de verificação ou avaliação para impressão de sua biblioteca de componentes existente? Solicite uma auditoria técnica: em poucas horas de análise é possível identificar lacunas de acessibilidade, prioridades de desempenho e governança para seu sistema de design.

💬 Notas dos leitores

0 notas

Escreva uma nota

Partilhe a sua opinião, uma sugestão ou um elogio

Notas recentes

Ainda não há notas. Seja o primeiro a comentar!