Ir para o conteúdo principal

Web Components – @govbr-ds/webcomponents

npm (next)

A biblioteca de Web Components do GovBR-DS é desenvolvida utilizando o Stencil, um compilador que cria Web Components (Custom Elements). O Stencil combina os melhores conceitos dos frameworks mais populares em uma ferramenta simples e eficiente. Para mais informações, visite o site oficial do Stencil.

Demonstração 🚀

Confira nossos componentes em ação:

Tecnologias 💻

Este projeto utiliza as seguintes tecnologias:

  1. GovBR-DS
  2. Stencil
  3. Font Awesome
  4. Fonte Rawline
  5. NX Monorepo

O que são Web Components? ❓

Web Components são um conjunto de tecnologias que permitem criar novos elementos HTML personalizados, reutilizáveis e encapsulados para uso em páginas e aplicativos web. Baseados em padrões da web, são suportados por todos os navegadores modernos.

O diagrama abaixo ilustra os três principais componentes dos Web Components:

  • Elementos HTML personalizados: Tags HTML definidas com JavaScript, usadas como qualquer outro elemento HTML.
  • Shadow DOM: Um espaço de nome encapsulado para cada elemento HTML personalizado, garantindo que estilos e scripts não interfiram no restante da página.
  • Templates: Fragmentos de HTML reutilizáveis em vários elementos.
  • Slots: Áreas em um elemento HTML personalizado onde templates podem ser inseridos.

Ciclo de Vida

Para entender melhor o ciclo de vida dos componentes, acesse a página https://stenciljs.com/docs/component-lifecycle.

Integração com frameworks 🔗

Integrar Web Components com frameworks pode ser desafiador em algumas situações. Para facilitar, criamos bibliotecas de integração (wrappers), que encapsulam os Web Components em bibliotecas nativas de frameworks, simplificando a integração com funcionalidades como binding.

Para mais detalhes, consulte a documentação do Stencil sobre integrações.

Vale lembrar que, em alguns casos, a integração pode não ser possível, dependendo da evolução da especificação de Web Components e do suporte dos frameworks.

Instalação 📦

Instale o pacote e os estilos base do DS como dependências de produção:

npm install @govbr-ds/webcomponents @govbr-ds/core
# ou
pnpm add @govbr-ds/webcomponents @govbr-ds/core
# ou
yarn add @govbr-ds/webcomponents @govbr-ds/core

[!NOTE] O pacote @govbr-ds/core fornece os tokens/estilos base que os componentes utilizam.

Nota importante: pnpm e tree-shaking

Se ao consumir estes pacotes você notar que o bundler não está removendo código não utilizado (tree‑shaking), pode haver uma incompatibilidade com o layout padrão do pnpm.

Solução rápida (opcional, somente se precisar): crie um arquivo .npmrc na raiz do seu projeto com:

node-linker=hoisted

Por que isso ajuda: por padrão, o pnpm organiza as dependências em pastas isoladas com symlinks. Alguns bundlers/otimizadores se baseiam na estrutura de node_modules e no campo sideEffects para decidir o que pode ser eliminado. O layout hoisted aproxima o formato “achatado” (similar ao npm/yarn), facilitando essa análise e, em muitos casos, restaurando o tree‑shaking.

Observações:

  • Use apenas se o tree‑shaking realmente não estiver funcionando.
  • Pode aumentar o uso de disco e alterar a resolução de dependências do seu projeto.

Uso 📚

Font Awesome e Fonte Rawline

Nossos componentes utilizam a Fonte Rawline e a Font Awesome padrão do DS. Ambas são essenciais para garantir a estética e funcionalidade dos componentes. Consulte a documentação no site do GovBR-DS para mais detalhes sobre como importá-las via CDN.

  • Font Awesome: Biblioteca amplamente utilizada para adicionar ícones vetoriais e logotipos aos projetos, fácil de integrar e personalizar.
  • Fonte Rawline: Fonte moderna e elegante, alinhada à identidade visual do Design System do GovBR-DS.

Passo-a-passo

Os Web Components GOVBR são elementos HTML regulares ou personalizados (Web Components). Em uma página HTML simples, podem ser usados como qualquer outro elemento.

<html>
<head>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@govbr-ds/webcomponents/dist/webcomponents/webcomponents.esm.js"
></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@govbr-ds/core/dist/core-tokens.min.css" />
</head>
<body>
<br-button>Clique aqui</br-button>
</body>
</html>

Eventos

Você pode escutar eventos padrão, como clique e mouseover, da mesma forma que faria com elementos HTML normais. Muitos componentes também emitem eventos personalizados, que recomendamos fortemente utilizar. Esses eventos funcionam como os eventos padrão, mas possuem nomes específicos. Consulte a documentação de cada componente para obter detalhes sobre os nomes e dados dos eventos.

<br-button>Label</br-button>

<script>
const button = document.querySelector('br-button')
button.addEventListener('click', (event) => {
console.log('Botão foi clicado', event.detail.valor)
})
</script>

Exemplos de uso

Disponibilizamos exemplos de como usar este projeto com diferentes tecnologias. Consulte o nosso grupo no GitLab e procure pelos projetos de 'Quickstart' para mais detalhes.

Desenvolvimento 👨‍💻

Estrutura do Projeto

├── 📁 src
├── 📁 components
├── 📁 br-component
├── 📁 sections
├── 📄 migrate.md
├── 📁 _tests
├── 📄 br-component.e2e.ts
├── 📄 br-component.spec.tsx
├── 📄 readme.md
├── 📄 br-component.scss
├── 📄 br-component.tsx
├── 📁 pages
├── 📁 components
├── 📁 br-component
├── 📄 index.js
├── 📄 exemplo.html
├── 📁 scripts
├── 📄 index.html
├── 📁 value-accessors
├── 📄 stencil.config.ts
├── ...
  • src: Contém o código-fonte do projeto.
    • components: Diretório com os componentes Web criados com Stencil. Cada componente possui sua própria pasta.
      • br-component: Pasta específica para o componente br-component.
        • sections: Contém arquivos markdown para inclusão no site.
          • migrate.md: Explica como migrar o componente da versão anterior para a nova.
        • _tests: Contém os arquivos de teste.
          • br-component.e2e.ts: Testes end-to-end (E2E) do componente.
          • br-component.spec.tsx: Testes unitários (Unit) do componente.
        • br-component.tsx: Arquivo principal do componente, com lógica e estrutura.
        • br-component.scss: Arquivo de estilo do componente.
        • readme.md: Documentação gerada automaticamente durante o build.
    • pages: Contém exemplos dos componentes Web criados com Stencil.
      • components: Diretório com exemplos dos componentes.
        • br-component: Pasta específica para o componente br-component.
          • index.js: Arquivo para executar ações nos exemplos, como escutar eventos.
          • exemplo.html: Arquivo com o código de cada exemplo.
      • scripts: Scripts usados no ambiente de desenvolvimento.
    • value-accessors: Configurações para 2-way binding em Angular e Vue.
    • index.html: Página inicial do servidor de desenvolvimento.
  • stencil.config.ts: Arquivo principal de configuração do projeto Stencil, onde são definidas opções como saída do build, plugins e scripts globais.

Scripts Disponíveis

No arquivo package.json e project.json, você encontrará diversos scripts úteis. Abaixo está uma lista com a descrição de cada um deles:

  • nx start webcomponents: Inicia o site localmente para desenvolvimento.
  • nx build webcomponents: Realiza a compilação do projeto para produção.
  • nx test:unit webcomponents: Executa os testes unitários.
  • nx lint:tsx webcomponents: Realiza a análise estática nos arquivos TypeScript.
  • nx lint:md webcomponents: Realiza a análise estática nos arquivos markdown.
  • nx lint:styles webcomponents: Realiza a análise estática nos arquivos de estilo.

Gerenciar baseline de tamanho:

# Da raiz do monorepo:
pnpm run baseline:update:webcomponents # Atualizar baseline
pnpm run baseline:compare:webcomponents # Comparar com baseline atual

Build

Ao gerar o build deste projeto Stencil, são automaticamente criados:

  • O arquivo readme.md dentro da pasta de cada componente.
  • Documentações dos componentes na pasta apps/site/docs/stencil-generated-docs, que podem ser utilizadas e versionadas pelo site.
nx build webcomponents

Testes

Nossa estratégia de testes é dividida em duas abordagens principais: testes unitários e testes end-to-end (E2E).

Testes Unitários (*.spec.tsx)

Os testes unitários são ideais para validar a lógica interna dos componentes de forma isolada e eficiente. Utilizamos o método newSpecPage() para verificar:

  • Estado inicial: comportamento do componente sem propriedades.
  • Propriedades: valores válidos, inválidos e alterações em tempo de execução.
  • Classes CSS: presença e ausência de classes condicionais.
  • Slots: renderização correta do conteúdo.
  • Lógica interna: métodos, cálculos e transformações.
  • Validações básicas: required, minLength, maxValue, entre outras.
import { newSpecPage } from '@stencil/core/testing'
import { BrComponent } from '../component'

it('deve inicializar com o estado padrão', async () => {
const page = await newSpecPage({
components: [BrComponent],
html: /*html*/ `<br-component></br-component>`,
})
expect(page.root).toEqualHtml(`<br-component>...</br-component>`)
})

Testes E2E (*.e2e.ts)

Os testes E2E são realizados em um navegador real utilizando o método newE2EPage(). Embora sejam mais lentos, são fundamentais para validar:

  • Eventos do componente: clique, blur, change, entre outros.
  • Interações do usuário: drag-drop, digitação, atalhos.
  • Integrações DOM: forms, validação nativa, atributos aria-*.
  • Estilos visuais: dimensões, cores, transições.
  • Shadow DOM: slots, parts, estilos encapsulados.
import { newE2EPage } from '@stencil/core/testing'

describe('br-component', () => {
it('deve renderizar', async () => {
const page = await newE2EPage()
await page.setContent(/*html*/ `<br-component></br-component>`)

const element = await page.find('br-component')

expect(element).toHaveClass('hydrated')
})

it('deve ter shadow root', async () => {
const page = await newE2EPage()
await page.setContent(/*html*/ `<br-component-component></br-component-component>`)

const element = await page.find('br-component')

expect(element.shadowRoot).not.toBeNull()
})
})

Para mais informações sobre testes no Stencil, consulte a documentação oficial.

Formatos do build 📦

A tarefa nx build webcomponents lê as definições de saída em stencil.config.ts e gera múltiplos formatos dentro de dist/webcomponents/dist. Cada pasta/arquivo atende um cenário específico de consumo da biblioteca. O resumo abaixo ajuda a identificar qual artefato utilizar em cada contexto.

Visão rápida

Pasta/arquivoOrigem no buildQuando usarObservações
cjs/outputTargets: { type: 'dist' }Bundlers ou runtimes que ainda resolvem CommonJS (require)Referenciado pelo campo main do package.json e inclui loader.cjs.js
esm/outputTargets: { type: 'dist' }Bundlers modernos/ESBuild/Vite/Rollup com suporte a ESM e tree-shakingReferenciado pelo campo module do package.json
components/outputTargets: { type: 'dist-custom-elements' }Importações diretas de componentes individuais, geração de wrappers Angular/React/VueGera .js + .d.ts por componente
collection/outputTargets: { type: 'dist' } (manifest Stencil)Projetos Stencil que desejam consumir os componentes como dependênciaDescrito pelos campos collection e collection:main do package.json
hydrate/outputTargets: { type: 'dist-hydrate-script' }SSR/pré-renderização em Node, integrações como React SSR targetExporta scripts CommonJS/ESM para @govbr-ds/webcomponents/dist/hydrate
loader/outputTargets: { type: 'dist' }Registro manual dos componentes em apps vanilla, integrações legadasDisponibiliza defineCustomElements() em múltiplos formatos
webcomponents/outputTargets: { type: 'dist' }Consumo via CDN (ex.: <script type="module" ...>)Contém webcomponents.esm.js e os chunks dinâmicos p-*.entry.js; os estilos vêm de @govbr-ds/core
types/generateTypeDeclarations: true (custom elements + d.ts globais)Autocomplete/TypeScript/IDE hintsReferenciado por types em package.json
custom-elements.json e webcomponents.html-custom-data.jsonDerivados do compiladorIntelliSense e validação em IDEs/editoresVS Code utiliza via html.customData
index.js / index.cjs.jsEntradas principais do pacoteRe-exportam os módulos esm/cjs e definem side-effectsApontados por module/main

cjs/ – bundles CommonJS

  • Contém index.cjs.js, webcomponents.cjs.js e um arquivo *.entry.js por componente, acompanhados dos source maps (.map).
  • É resolvido sempre que o consumidor faz require('@govbr-ds/webcomponents') ou quando o bundler privilegia o campo main definido em package.json.
  • Use este formato em toolchains legadas (Node 14/16, Webpack < 4, Jest sem suporte a ESM) ou quando precisar depurar módulos CommonJS. Inclui também loader.cjs.js, permitindo registrar os elementos manualmente com require('@govbr-ds/webcomponents/dist/loader').

esm/ – bundles ES Modules

  • Equivalente modular do cjs/, com chunks otimizados (p-*.entry.js) e webcomponents.esm.js.
  • É o alvo do campo module do pacote e atende bundlers modernos (Vite, Webpack 5+, Rollup, ESBuild) que fazem tree-shaking. Como os chunks mantêm imports explícitos, apenas o necessário é incluído no bundle final.
  • Prefira esta saída quando o projeto já executa em módulos nativos ou quando precisa de melhor performance em builds SPA/MPA modernos.

components/dist-custom-elements

  • Resultado do outputTargets: { type: 'dist-custom-elements', customElementsExportBehavior: 'single-export-module' } definido em stencil.config.ts.
  • Cada componente possui um arquivo isolado (br-button.js) e o respectivo .d.ts, permitindo importar diretamente import { BrButton } from '@govbr-ds/webcomponents/dist/components/br-button' sem puxar o bundle completo.
  • Esta pasta também alimenta os geradores Angular/React/Vue configurados no mesmo arquivo (customElementsDir aponta para dist/components). Use-a para criar wrappers customizados ou testar componentes isoladamente em Storybook.

collection/ – manifest Stencil

  • Inclui collection-manifest.json, cópias de assets/páginas e os proxies de acesso (index.js).
  • Necessário apenas para quem desenvolve outra biblioteca Stencil e deseja usar @govbr-ds/webcomponents como dependência, preservando metadados como slots, eventos e estilos.
  • Os campos collection e collection:main no package.json apontam para esses arquivos para que o compilador Stencil dos consumidores encontre as definições.

hydrate/ – script para SSR/pré-render

  • Produzido pelo target dist-hydrate-script e exportado como módulo CommonJS/ESM (index.js, index.mjs).
  • Permite renderizar componentes em ambientes sem DOM (Node.js) e hidratar o HTML no cliente. O React SSR output target e a configuração de Vue (hydrateModule: '@govbr-ds/webcomponents/dist/hydrate') dependem desta pasta.
  • Utilize quando precisar pré-renderizar páginas (Next.js, Astro, custom SSR) ou ao gerar imagens/relatórios estáticos com os componentes já resolvidos.

loader/ – helpers para registro manual

  • Disponibiliza defineCustomElements() e applyPolyfills() em diferentes formatos (index.js, index.cjs.js, index.es2017.js, cdn.js).
  • Ideal para projetos vanilla ou quando o bundle principal não deve executar automaticamente o defineCustomElements. Você importa apenas o loader, registra os componentes no momento oportuno e mantém controle fino sobre o Custom Elements Registry.
  • Use cdn.js em páginas HTML estáticas (por exemplo, <script src="https://cdn.jsdelivr.net/npm/@govbr-ds/webcomponents/dist/loader/cdn.js"></script>) quando precisa apenas do registro.

webcomponents/ – pacote pronto para CDN

  • É a forma mais simples de consumo: webcomponents.esm.js + chunks p-*.entry.js, prontos para serem servidos via CDN.
  • Recomendada para páginas estáticas, protótipos, Storybook do Design System ou integrações que não passam por bundlers. Lembre-se de importar os estilos de @govbr-ds/core (tokens) separadamente.
  • Inclui chunks nomeados resolvidos dinamicamente pelo loader gerado pelo Stencil, permitindo lazy loading automático.

types/ – declarações TypeScript

  • Resultado da opção generateTypeDeclarations: true da saída dist-custom-elements.
  • Contém components.d.ts com todas as interfaces de propriedades/eventos e uma pasta components/ com os tipos específicos. O campo types do package.json aponta para dist/types/index.d.ts.
  • Necessário para projetos TypeScript, IDEs e também para os wrappers Angular/React/Vue, que reexportam esses tipos.

Metadados auxiliares

  • custom-elements.json: arquivo padrão da especificação Custom Elements Manifest. Importante para ferramentas de documentação e IDEs.
  • webcomponents.html-custom-data.json: arquivo usado pelo VS Code via html.customData para habilitar autocomplete e validação nos editores.
  • index.js / index.cjs.js: reexportam os bundles ESM/CJS e expõem defineCustomElements() / setNonce() gerados pelo Stencil; raramente precisam ser importados diretamente.
  • Documentação Markdown: gerada automaticamente em apps/site/docs/stencil-generated-docs via outputTargets.docs-custom, mas não é distribuída dentro de dist/webcomponents.

Como escolher o formato correto

  1. Aplicações modernas com bundler: instale o pacote e deixe seu bundler resolver o campo module → carregará esm/ automaticamente.
  2. SSR ou pré-render: combine esm/ no cliente com hydrate/ no servidor para renderização isomórfica.
  3. Integrações framework-nativas: use os pacotes @govbr-ds/angular|react|vue, gerados a partir de components/ e com tipos em types/.
  4. Páginas estáticas/CDN: carregue diretamente os arquivos em webcomponents/ e use o loader/ se precisar controlar o registro manualmente.
  5. Toolchains legadas: force o campo main e consuma cjs/ para manter compatibilidade com CommonJS.

Manter estes contextos claros evita duplicidade de código, falta de tree-shaking ou registros repetidos de Custom Elements.

VS Code IntelliSense

Durante o desenvolvimento de nossos Web Components, utilizamos custom elements. O VS Code, por padrão, não reconhece esses componentes, o que impede sugestões inteligentes no autocomplete. Para resolver isso, geramos um arquivo com as definições dos componentes, disponibilizado junto ao pacote npm.

Para importar no seu VS Code, adicione o seguinte campo, ajustando o caminho para o local onde o node_modules está armazenado no seu projeto:

{
"html.customData": ["./node_modules/@govbr-ds/webcomponents/dist/webcomponents/webcomponents.html-custom-data.json"]
}

Documentações Complementares 📖

Consulte a seção sobre Web Components na nossa Wiki para obter mais informações sobre este projeto.

Para mais detalhes sobre a especificação de Web Components, recomendamos a consulta ao MDN.

Contribuindo 🤝

Antes de abrir um Merge Request, considere as seguintes orientações:

  • Este é um projeto open-source, e contribuições são sempre bem-vindas.
  • Para facilitar a aprovação da sua contribuição, utilize um título claro e explicativo no MR e siga os padrões descritos em nossa wiki.
  • Deseja contribuir? Consulte o nosso guia como contribuir.

Reportar Bugs/Problemas ou Sugestões 🐛

Caso encontre problemas ou tenha sugestões de melhorias, abra uma issue. Utilize o modelo apropriado e forneça o máximo de detalhes possível.

Nos comprometemos a responder a todas as issues.

Commits 📝

Este projeto segue um padrão específico para branches e commits. Consulte a documentação em nossa wiki para entender mais sobre esses padrões.

Precisa de ajuda? 🆘

Por favor, não crie issues para dúvidas gerais.

Utilize os canais abaixo para esclarecer suas dúvidas:

Créditos 🎉

Os Web Components do GovBR-DS foram desenvolvidos pelo SERPRO em parceria com a comunidade.

Nota sobre pnpm e tree-shaking

Em alguns cenários, para que o tree‑shaking funcione corretamente (especialmente em bundlers que dependem da estrutura de node_modules e da resolução de sideEffects), pode ser necessário configurar o pnpm para usar o layout de dependências "hoisted".

Crie um arquivo .npmrc na raiz do seu projeto (caso ainda não exista) com:

node-linker=hoisted

Por quê: o layout padrão do pnpm usa pastas isoladas com symlinks, o que pode confundir determinadas ferramentas de build/otimização ao analisar limites de módulos e sideEffects. O node-linker=hoisted aproxima o layout do de npm/yarn (flat/hoisted), facilitando a análise estática e, consequentemente, o tree‑shaking. Use essa opção apenas se perceber problemas de eliminação de código morto; ela pode aumentar o uso de disco e alterar a resolução de dependências.