SHAB-HorusHab-DS — Design System
Um Design System robusto, acessível e escalável desenvolvido para as aplicações do SHAB (Sistema Habitacional). Construído com foco em Acessibilidade (WAI-ARIA), Developer Experience (DX) e Manutenção de Longo Prazo.
1. Visão Geral
O SHAB-HorusHab-DS não é apenas uma biblioteca de componentes; é a fonte de verdade visual e de interação para os produtos HorusHab.
Objetivos Principais:
- Zero configuração de acessibilidade manual: Toda a acessibilidade (aria-attributes, navegação por teclado, screen readers) é resolvida a nível de arquitetura através do
react-aria-components. - Estilização Restrita e Semântica: Consumidores não pensam em valores HEX. Toda a UI é mapeada através de tokens de design restritos (Tailwind CSS v4).
- Consistência Absoluta: O DS fornece as primitivas visuais; a aplicação de negócio consome sem reimplementar comportamentos base de UI.
2. Arquitetura
O projeto utiliza a arquitetura de Monorepo orquestrada por pnpm workspaces e Turborepo (ADR-001).
Separação de Responsabilidades
@horushabsp/ui: O pacote principal contendo a implementação headless dos componentes e os bindings visuais usandoclass-variance-authority(CVA).@horushabsp/tokens: A fonte de verdade das variáveis de design (ADR-002). Define e distribui os arquivos CSS base (como osemantic.css) e expõe a fundação temática para o Tailwind v4.storybook: Aplicação de documentação visual (v8/CSF3) isolada, garantindo que componentes funcionem de forma autônoma antes de chegarem à aplicação real.
Filosofia Arquitetural Observada
- Composição vs. Configuração: Componentes complexos não recebem um JSON enorme via prop. Eles são compostos usando Compound Components (Ex:
Table,TableBody,Row,Cell), seguindo o padrão nativo do React Aria. - Separação de UI e Lógica: O DS implementa comportamento de UI (dropdown open/close, focus ring), nunca lógica de negócios (API calls, validação de domínio).
- Padrão ESM First: Todo o sistema de módulos (interop) exige a resolução estrita de extensões
.jsao exportar arquivos locais.
3. Estrutura do Projeto
├── apps/
│ └── storybook/ # Documentação iterativa e catálogo de UI
├── docs/
│ └── adr/ # Architecture Decision Records (Histórico de decisões)
├── packages/
│ ├── tokens/ # Tokens CSS, escalas estruturais e cores semânticas
│ └── ui/ # Biblioteca principal de componentes React
├── turbo.json # Configuração de cache e build pipeline do Turborepo
├── pnpm-workspace.yaml # Definição dos pacotes do monorepo
├── biome.jsonc # Regras de linting e formatting (TS/JS/JSON)
└── package.json # Root config, scripts de dev, publish e git hooks
4. Setup, Instalação e Consumo
O pacote do Design System é distribuído de forma privada via GitHub Packages e exige autenticação.
Configuração e Instalação
Adicione o escopo do repositório no seu arquivo .npmrc na raiz do seu projeto consumidor, fornecendo um GitHub PAT (Personal Access Token) com a permissão read:packages:
@horushabsp:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=SEU_TOKEN_AQUI
Em seguida, instale as dependências:
npm install @horushabsp/ui tailwindcss-animate
# ou
pnpm install @horushabsp/ui tailwindcss-animate
Configuração CSS (Tailwind v4 Setup)
Se você utiliza Tailwind CSS v4 na sua aplicação, sua configuração CSS deve importar os tokens brutos, o tema e as fontes do DS. No seu arquivo global.css ou index.css:
/* 1. Inicia o Tailwind e plugins necessários */
@import 'tailwindcss';
@plugin "tailwindcss-animate";
/* 2. Importa os tokens puros (variáveis --sys-* em :root e .dark) */
@import '@horushabsp/ui/tokens.css';
/* 3. Importa o mapeamento de utilities (@theme inline) para o Tailwind */
@import '@horushabsp/ui/theme.css';
/* 4. Indica ao Tailwind para escanear os componentes do UI em busca de classes */
@source "../node_modules/@horushabsp/ui/dist";
/* 5. Importa animações e keyframes base dos componentes */
@import '@horushabsp/ui/styles.css';
/* 6. (Opcional) Importa os resets de tipografia base (body, a, h1-h6) */
@import '@horushabsp/ui/preflight.css';
Para aplicações SEM Tailwind (Legado)
Se a sua aplicação não utiliza Tailwind CSS, você pode importar a versão pré-compilada que já contém todos os utilitários, base e tokens do sistema:
/* Importe apenas este arquivo se você NÃO usa Tailwind */
@import '@horushabsp/ui/standalone.css';
Configuração de Fontes Necessárias
O Design System requer as fontes oficiais Rawline, Raleway (como fallback) e JetBrains Mono (para códigos). O DS não as injeta automaticamente. Você precisa importá-las no <head> do seu HTML ou no entrypoint do seu framework:
@import url('https://cdngovbr-ds.estaleiro.serpro.gov.br/design-system/fonts/rawline/css/rawline.css');
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700;800;900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap');
Uso Básico
import { Button, Badge, Avatar, TextField, Card, Spinner } from '@horushabsp/ui'
export function Example() {
return (
<Button variant="primary" size="md">
Confirmar
</Button>
)
}
5. Desenvolvimento e Contribuição
Para os mantenedores que precisarem trabalhar no repositório do Design System propriamente dito, o ambiente utiliza estritamente o pnpm.
# Instalação local limpa
pnpm install
Graças ao Turborepo, todos os comandos abaixos rodam em paralelo com sistema de cache estruturado:
| Comando | Descrição |
|---|---|
pnpm dev | Executa o build em watch mode. Útil durante o desenvolvimento. |
pnpm storybook | Inicia exclusivamente o servidor local de testes no localhost:6006. |
pnpm test | Executa a suite de testes no Vitest para todos os pacotes. |
pnpm build | Gera o build de produção final (dist/). |
Releasing (Publicação de nova versão)
O versionamento é gerenciado através de Changesets.
# 1. Descreva o que mudou (patch/minor/major)
pnpm changeset
# 2. Commit e push (a CI abrirá um Version Packages PR)
git add .
git commit -m "feat: novo componente de table"
git push
Ao aprovar e mergiar o PR do Changesets, a CI publicará a nova versão no GitHub Packages Registry sob o scope @horushabsp e criará a GitHub Release.
6. Padrões de Componentes Internos
Ao criar novos componentes dentro da estrutura, obedeça às seguintes regras arquiteturais:
A Regra dos 4 Arquivos
Todo novo componente deve viver na pasta packages/ui/src/<nome-do-componente>/ contendo exatamente 4 arquivos:
[nome].tsx: Implementação usando CVA + React Aria.[nome].stories.tsx: Documentação via CSF3.[nome].test.tsx: Teste unitário e de acessibilidade com Vitest + RTL + axe.index.ts: Re-export padrão ESM. Exemplo:export * from './button.js'.
Estrutura do Código (Anatomia Padrão)
A arquitetura não usa polimorfismo agressivo (como asChild). Utilizamos o mapeamento 1:1 do React Aria Components (RAC) estilizado via CVA e mesclado usando o utilitário cn() (tailwind-merge + clsx).
Exemplo do padrão recorrente de componente:
import React, { forwardRef } from 'react'
import { Button as AriaButton, type ButtonProps as AriaButtonProps } from 'react-aria-components'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '../lib/cn.js'
const buttonVariants = cva(
[
// Base styles
'inline-flex items-center justify-center rounded-sm font-medium transition-colors cursor-pointer',
'focus-visible:outline-none focus-visible:shadow-focus-ring',
'disabled:pointer-events-none disabled:opacity-50',
],
{
variants: {
/* ... */
},
defaultVariants: {
/* ... */
},
}
)
export interface ButtonProps extends AriaButtonProps, VariantProps<typeof buttonVariants> {
className?: string
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ variant, size, className, children, ...props }, ref) => (
<AriaButton ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>
{children}
</AriaButton>
)
)
Button.displayName = 'Button'
7. Tokens e Styling
Adotamos a especificação de Tokens Semânticos via variáveis CSS distribuídas pelo Tailwind CSS v4 (ADR-002).
❌ Anti-Patterns de Estilo
- NUNCA utilizar classes literais de cor (
text-blue-600,bg-white). - NUNCA utilizar medidas arbitrárias de gap/layout fora da escala (
gap-3,gap-5). - NUNCA remover o outline (
outline-none) sem aplicar uma borda de foco equivalente.
✅ Práticas Exigidas (Tokens permitidos)
Todas as referências visuais devem ser chamadas com base nos alias de tema predefinidos no projeto em packages/tokens/src/semantic.css:
- Cores:
bg-action-primary,text-feedback-error-text,text-text-primary. - Superfícies:
bg-bg-default,bg-surface-floating(para dropdowns). - Raio (Border Radius):
rounded-sm(4px),rounded-md(8px),rounded-full. - Focus Ring:
focus-visible:shadow-focus-ring.
8. Data Display e Componentes Complexos
Componentes densos e de interação pesada (Tabelas, Formulários, Modais e Comboboxes) adotam o padrão de "Partes de Composição Exportadas". Ao invés de abstrações excessivas que engolem o DOM, componentes expõem as primitivas de renderização do RAC.
import { Table, TableHeader, Column, TableBody, Row, Cell } from '@horushabsp/ui'
;<Table>
<TableHeader>
<Column>ID</Column>
<Column>Status</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>1001</Cell>
<Cell>
<Badge variant="success">Ativo</Badge>
</Cell>
</Row>
</TableBody>
</Table>
9. Convenções Críticas do Projeto
Sistema de Exports (ESM Completo)
Sempre declare a extensão .js nos seus imports/exports relativos. Isso é mandatório no TypeScript rodando em type: "module".
// ✅ Correto
export * from './button.js'
Exportação no package.json
Todo novo componente deve ser registrado no exports de packages/ui/package.json para suportar tree-shaking correto no consumo.
Controle de Foco Oculto
Ícones estéticos devem obrigatoriamente carregar aria-hidden="true". Componentes icon-only exigem labels ou hidden text explícito para tecnologias assistivas.
10. Acessibilidade (A11y) como Fundação (ADR-003)
Nenhum PR avança sem testes unitários focados em restrições da WCAG. Utiliza-se o plugin dinâmico do Axe Core via vitest-axe.
it('has no accessibility violations', async () => {
const { container } = render(<MyComponent>Label</MyComponent>)
const { axe } = await import('vitest-axe') // Importação ESM dinâmica exigida
expect(await axe(container)).toHaveNoViolations()
})
11. Storybook (Documentação)
Toda a vitrine do DS vive em apps/storybook/. O projeto usa o padrão Component Story Format 3 (CSF3). Mantenha as props nativas do componente limpas de estilos layout/wrapper nas views isoladas. Utilize a tag render para orquestrar componentes mais complexos ou múltiplas variantes (ex: AllVariants).
12. Deploy (Storybook)
O Storybook pode ser feito deploy para o GCP (Google Cloud Platform) Cloud Run. O projeto já inclui um Dockerfile configurado para compilar a aplicação estática e servi-la usando Nginx.
Deploy no Cloud Run
O comando abaixo realiza o deploy para o projeto GCP sduh-prod na região southamerica-east1:
gcloud run deploy shab-horus-ds-storybook \
--project=sduh-prod \
--region=southamerica-east1 \
--source=. \
--ingress=internal-and-cloud-load-balancing
(Nota: a flag --ingress=internal-and-cloud-load-balancing garante que a aplicação não seja acessível diretamente pela URL default do Cloud Run, exigindo tráfego através de um Load Balancer configurado na nuvem).
13. Links Úteis e Referências Internas
- 📘 ADRs (Architecture Decision Records):
docs/adr/ - 🎨 Tokens Palette Reference:
docs/token-palette-reference.md - 🤝 Guia de Contribuição e Setup:
CONTRIBUTING.md - 🗺️ Plano de Implementação (DS Isolation):
PLANNING.md - 📜 Histórico de Versões:
CHANGELOG.md - 🧩 React Aria Components: Documentação Oficial
- 🛠 Biome: Documentação Oficial
Lembrete de Integração para Futuros Mantenedores: O objetivo não é "criar o componente que faz tudo", mas fornecer primitivas robustas, estilizadas sob as regras do negócio e altamente acessíveis, onde as complexidades de uso fiquem a cargo do desenvolvedor consumidor nas aplicações front-end de produto. Em caso de dúvida, adie as abstrações arbitrárias. Consistência é inegociável.