Slots
Slots são pontos de entrada para conteúdo dentro de um Web Component. Eles permitem que você componha a interface com textos, ícones, imagens, links e outros elementos sem depender de uma propriedade específica para cada variação de conteúdo.
Na prática, o componente controla a estrutura, acessibilidade e comportamento. A aplicação fornece o conteúdo que entra nos espaços definidos pelo componente.
Quando usar
Use slots quando o conteúdo precisa ser flexível, sem alterar o contrato principal do componente.
Exemplos comuns:
- Texto ou ícone dentro de um botão.
- Mensagem de erro, sucesso ou ajuda dentro de um campo.
- Itens de navegação dentro de breadcrumb, menu, footer ou tabela.
- Conteúdo complementar, como imagem institucional em
br-sign-in.
Slot padrão
O slot padrão recebe o conteúdo que não possui o atributo slot.
<br-button emphasis="primary"> Enviar solicitação </br-button>
Nesse exemplo, o texto Enviar solicitação entra no slot padrão do br-button.
Slots nomeados
Slots nomeados recebem conteúdo marcado com slot="nome-do-slot". Eles são usados quando o componente expõe mais de uma área de composição.
<br-input label="CPF" placeholder="000.000.000-00">
<br-message slot="feedback" state="danger"> Informe um CPF válido. </br-message>
</br-input>
No exemplo acima, o br-message ocupa o slot feedback do br-input. Isso mantém a mensagem conectada visualmente ao campo, sem exigir que a aplicação conheça a estrutura interna do componente.
Compondo componentes e encadeamento de slots
Slots também podem receber outros Web Components. Esse padrão é a base dos componentes compostos, como tabela, menu, footer e breadcrumb.
<br-table>
<br-table-header slot="header">
<br-table-header-cell>Serviço</br-table-header-cell>
<br-table-header-cell>Situação</br-table-header-cell>
</br-table-header>
<br-table-body slot="body">
<br-table-row>
<br-table-cell>Agendamento</br-table-cell>
<br-table-cell>Disponível</br-table-cell>
</br-table-row>
</br-table-body>
</br-table>
Repasse de slots (Slot forwarding)
Quando componentes orquestradores delegam partes de sua UI para subcomponentes internos, pode ocorrer o encadeamento de slots (slot forwarding): o componente pai expõe um slot e o repassa diretamente para o slot de um componente filho no seu template:
<!-- No template do componente orquestrador -->
<div class="container">
<slot name="header" slot="header"></slot>
<slot></slot>
</div>
Esse encadeamento preserva a semântica e permite que a aplicação consumidora customize níveis mais profundos sem quebrar o encapsulamento.
Fallback e conteúdo alternativo
O conteúdo colocado dentro da tag <slot> no template do Web Component funciona como valor padrão (fallback). Ele só é exibido se o consumidor não passar nenhum nó para aquele slot:
<!-- Definição interna no componente -->
<slot name="title">
<span>Título Padrão do Componente</span>
</slot>
Se o consumidor usar o componente sem fornecer nó para o slot title:
<!-- O fallback interno será renderizado -->
<meu-card>
<p>Conteúdo do slot padrão</p>
</meu-card>
Por outro lado, quando o consumidor fornece um nó com slot="title", o fallback é descartado e o conteúdo da aplicação assume a posição.
Exemplo real em br-sign-in:
<br-sign-in shape="pill" aria-label="Entrar com conta gov.br">
<br-icon
slot="icon"
svg='<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm0 2c-4.42 0-8 2.24-8 5v1h16v-1c0-2.76-3.58-5-8-5Z"/></svg>'
></br-icon>
Entrar com gov.br
<img slot="image" src="/img/logo-positive.svg" alt="GovBR-DS" />
</br-sign-in>
Contrato de composição: Light DOM vs Shadow DOM
Um aspecto crucial dos Web Components é a separação de escopos entre o que entra pelo slot e o que é desenhado internamente:
Como um Web Component não pode (e não deve) restringir à força tudo o que a aplicação injeta em seus slots, a biblioteca define um contrato de composição: estabelecemos quais elementos são esperados (ex: ícones, tags de texto, botões de ação), orientando o desenvolvedor a manter a coerência visual e a acessibilidade.
Escopo de CSS e fontes
- Conteúdo projetado (Light DOM): Os nós que você passa via slot pertencem ao Light DOM da aplicação consumidora. Eles continuam recebendo os estilos globais, folhas de estilo e fontes externas (como Font Awesome ou classes de utilitário) carregadas pela sua aplicação.
- Conteúdo de Fallback (Shadow DOM): O fallback mora dentro do Shadow DOM do Web Component. Caso seu fallback dependa de classes ou fontes externas que não estejam disponíveis globalmente ou encapsuladas, elas não alcançarão o mesmo resultado. Por isso, a biblioteca prioriza SVG embutido ou estilos próprios para fallbacks.
O evento slotchange
Em componentes avançados que precisam inspecionar ou reagir ao conteúdo que foi injetado (por exemplo, para contar itens, ajustar densidade ou sincronizar acessibilidade), o navegador dispara o evento nativo slotchange no elemento <slot> sempre que seus nós atribuídos mudam:
// Dentro da implementação de um Web Component (ex: Stencil ou Vanilla JS)
const slot = this.el.shadowRoot.querySelector('slot[name="actions"]');
slot.addEventListener('slotchange', () => {
// Retorna nós diretos atribuídos (incluindo texto)
const nodes = slot.assignedNodes({ flatten: true });
// Retorna apenas elementos HTML atribuídos
const elements = slot.assignedElements({ flatten: true });
console.log(`Quantidade de elementos no slot: ${elements.length}`);
});
O evento slotchange só é acionado quando os próprios nós imediatos atribuídos ao slot mudam (adição ou remoção). Modificações internas nos nós filhos dos elementos atribuídos não disparam slotchange automaticamente.
Nomenclatura semântica e lógica direcional
Ao escolher ou documentar nomes de slots, o Design System adota convenções consistentes:
- Nomes com propósito semântico:
header,footer,feedback,actions,trigger. - Direção lógica em vez de física: Dê preferência a nomes como
starteendem vez delefteright. Nomes lógicos respeitam a internacionalização e fluxos de texto bidirecionais (LTR e RTL), mantendo o código agnóstico à direção de leitura.
Boas práticas
- Consulte a seção Slots da página de cada componente para saber quais nomes estão disponíveis.
- Use o slot padrão para conteúdo principal e slots nomeados para áreas específicas.
- Evite inserir elementos interativos dentro de outro elemento interativo, como um botão dentro de
br-button. - Preserve nomes de slots exatamente como documentados, incluindo o caractere
-. - Em frameworks (Angular, React, Vue), defina o atributo
slotno elemento de nível superior que deve ser projetado.
Diferença entre slots e propriedades
- Use propriedades para configurar comportamento, densidade e estado do componente.
- Use slots para fornecer conteúdo e composição estrutural.
<br-button emphasis="primary" density="small"> Salvar </br-button>
Nesse exemplo, emphasis e density configuram o componente. O texto Salvar é conteúdo projetado no slot padrão.