Pular para o conteúdo principal

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 usando class-variance-authority (CVA).
  • @horushabsp/tokens: A fonte de verdade das variáveis de design (ADR-002). Define e distribui os arquivos CSS base (como o semantic.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

  1. 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.
  2. 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).
  3. Padrão ESM First: Todo o sistema de módulos (interop) exige a resolução estrita de extensões .js ao 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:

ComandoDescrição
pnpm devExecuta o build em watch mode. Útil durante o desenvolvimento.
pnpm storybookInicia exclusivamente o servidor local de testes no localhost:6006.
pnpm testExecuta a suite de testes no Vitest para todos os pacotes.
pnpm buildGera 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:

  1. [nome].tsx: Implementação usando CVA + React Aria.
  2. [nome].stories.tsx: Documentação via CSF3.
  3. [nome].test.tsx: Teste unitário e de acessibilidade com Vitest + RTL + axe.
  4. 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).


  • 📘 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.