Empty
templates/components/Empty
Empty é o marcador da AUSÊNCIA de conteúdo: sua função é explicar POR QUE nada aparece e conduzir o próximo passo. O mesmo slot cobre situações distintas (primeiro uso, busca sem resultados, filtro vazio, erro, sem permissão, offline) e costuma ser injetado dinamicamente após uma operação assíncrona — por isso seus enriquecedores tratam de MOTIVO, RECUPERAÇÃO e ESTADO, nunca de estética.
Base congelada
Capacidades 8
Conteúdo
Motivo do vazio (preset de contexto)
Escolhe o cenário do vazio e…
Ação primária (CTA)
Habilita o botão-âncora do próximo passo…
Link de ajuda / documentação
Acrescenta um link discreto "Saiba mais…
Comportamento
Estado de carregamento
Distingue "ainda carregando" de "vazio de…
Tentar novamente (retry)
Botão que redispara o carregamento; opt-in…
Limpar filtros / busca
Ação que reseta os filtros ou…
Zona de soltar (arrastar e soltar)
Transforma o Empty em alvo de…
Acessibilidade
Anúncio para leitor de tela (região viva)
Quando o Empty é injetado dinamicamente…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Empty (UI) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Congelados e imutáveis: o contêiner centralizado (flex-col items-center justify-center), a borda tracejada com rounded-xl, o padding p-6, o gap-4, o text-center text-balance; o media em chip icon (size-8 rounded-lg bg-muted, svg size-4); o Title em cn-font-heading text-sm font-medium tracking-tight; a Description em text-muted-foreground text-sm/relaxed com links sublinhados; as larguras max-w-sm de Header e Content e seus gaps. Nenhum enricher toca fonte, cor, cantos, espaçamento ou os data-slots — só somam comportamento e conteúdo dentro dos slots existentes.
USO: <twig:Empty />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Motivo do vazio (preset de contexto): Escolhe o cenário do vazio e ajusta o ícone padrão e o tom da copy (mensagem) sem tocar no visual — a assinatura do Empty, já que o mesmo slot serve a razões diferentes. [select → Primeiro uso (onboarding) / Sem resultados de busca / Filtro sem correspondência / Erro ao carregar / Sem permissão / acesso / Offline / sem conexão]- Estado de carregamento: Distingue "ainda carregando" de "vazio de fato": mostra spinner/esqueleto no lugar da mensagem enquanto busca e revela o Empty só quando termina sem dados. Evita piscar "nada aqui" durante o fetch. [toggle]- Ação primária (CTA): Habilita o botão-âncora do próximo passo dentro do Empty:Content (ex.: "Criar projeto"), com opção de uma ou duas ações. É o coração do Empty: oferecer a saída. [select → Nenhuma / Só primária / Primária + secundária]- Tentar novamente (retry): Botão que redispara o carregamento; opt-in para os motivos de erro e offline. Comportamento puro, sem alterar a base. [toggle]- Limpar filtros / busca: Ação que reseta os filtros ou o termo de busca que esvaziaram a lista, religando o Empty ao componente que o gerou. Opt-in para os motivos de busca/filtro vazios. [toggle]- Zona de soltar (arrastar e soltar): Transforma o Empty em alvo de upload — a borda tracejada da base já lê como dropzone. Adiciona o comportamento de drop e o realce transitório ao arrastar por cima, sem restilizar a base. [toggle]- Link de ajuda / documentação: Acrescenta um link discreto "Saiba mais →" (com ícone externo) para orientar quem chegou ao estado vazio; escolhível acima ou abaixo das ações. [toggle]- Anúncio para leitor de tela (região viva): Quando o Empty é injetado dinamicamente (após busca/filtro/erro), anuncia a mudança via região viva — polite para vazios comuns, assertivo para erro. Aditivo e sem qualquer efeito visual. [toggle]FAÇA:
- Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.
- Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.
- Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}.
NÃO FAÇA:
- Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.
- Não crie variante/fork para a mesma coisa — existe UMA base por componente.
- Não reimplemente o componente nem adicione toolchain Node.
TESTE ANTES DE MUDAR: "é função ou é base?" — função = capacidade opt-in; base = mude o token na fonte única (assets/styles/app.css), para todos.
A11Y (herdada da base): Semântica, foco visível e ARIA herdados da base congelada — não reimplementar.
LLM / MCP
Via MCP: tool get_component com {"id": "empty"} · list_capabilities("empty").
Spec crua: config/ds-specs/empty.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "empty",
"component": "Empty",
"eixo": "ui",
"particularidade": "Empty é o marcador da AUSÊNCIA de conteúdo: sua função é explicar POR QUE nada aparece e conduzir o próximo passo. O mesmo slot cobre situações distintas (primeiro uso, busca sem resultados, filtro vazio, erro, sem permissão, offline) e costuma ser injetado dinamicamente após uma operação assíncrona — por isso seus enriquecedores tratam de MOTIVO, RECUPERAÇÃO e ESTADO, nunca de estética.",
"base_congelada": "Congelados e imutáveis: o contêiner centralizado (flex-col items-center justify-center), a borda tracejada com rounded-xl, o padding p-6, o gap-4, o text-center text-balance; o media em chip icon (size-8 rounded-lg bg-muted, svg size-4); o Title em cn-font-heading text-sm font-medium tracking-tight; a Description em text-muted-foreground text-sm/relaxed com links sublinhados; as larguras max-w-sm de Header e Content e seus gaps. Nenhum enricher toca fonte, cor, cantos, espaçamento ou os data-slots — só somam comportamento e conteúdo dentro dos slots existentes.",
"props": [],
"capacidades": [
{
"id": "motivo-do-vazio-preset-de-contexto",
"nome": "Motivo do vazio (preset de contexto)",
"descricao": "Escolhe o cenário do vazio e ajusta o ícone padrão e o tom da copy (mensagem) sem tocar no visual — a assinatura do Empty, já que o mesmo slot serve a razões diferentes.",
"controle": "select",
"opcoes": [
"Primeiro uso (onboarding)",
"Sem resultados de busca",
"Filtro sem correspondência",
"Erro ao carregar",
"Sem permissão / acesso",
"Offline / sem conexão"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "estado-de-carregamento",
"nome": "Estado de carregamento",
"descricao": "Distingue \"ainda carregando\" de \"vazio de fato\": mostra spinner/esqueleto no lugar da mensagem enquanto busca e revela o Empty só quando termina sem dados. Evita piscar \"nada aqui\" durante o fetch.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "a-o-prim-ria-cta",
"nome": "Ação primária (CTA)",
"descricao": "Habilita o botão-âncora do próximo passo dentro do Empty:Content (ex.: \"Criar projeto\"), com opção de uma ou duas ações. É o coração do Empty: oferecer a saída.",
"controle": "select",
"opcoes": [
"Nenhuma",
"Só primária",
"Primária + secundária"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "tentar-novamente-retry",
"nome": "Tentar novamente (retry)",
"descricao": "Botão que redispara o carregamento; opt-in para os motivos de erro e offline. Comportamento puro, sem alterar a base.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "limpar-filtros-busca",
"nome": "Limpar filtros / busca",
"descricao": "Ação que reseta os filtros ou o termo de busca que esvaziaram a lista, religando o Empty ao componente que o gerou. Opt-in para os motivos de busca/filtro vazios.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "zona-de-soltar-arrastar-e-soltar",
"nome": "Zona de soltar (arrastar e soltar)",
"descricao": "Transforma o Empty em alvo de upload — a borda tracejada da base já lê como dropzone. Adiciona o comportamento de drop e o realce transitório ao arrastar por cima, sem restilizar a base.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "link-de-ajuda-documenta-o",
"nome": "Link de ajuda / documentação",
"descricao": "Acrescenta um link discreto \"Saiba mais →\" (com ícone externo) para orientar quem chegou ao estado vazio; escolhível acima ou abaixo das ações.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "an-ncio-para-leitor-de-tela-regi-o-viva",
"nome": "Anúncio para leitor de tela (região viva)",
"descricao": "Quando o Empty é injetado dinamicamente (após busca/filtro/erro), anuncia a mudança via região viva — polite para vazios comuns, assertivo para erro. Aditivo e sem qualquer efeito visual.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/empty; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:Empty />",
"exemplo_demo": "<twig:Empty>\n <twig:Empty:Header>\n <twig:Empty:Media variant=\"icon\">\n <twig:ux:icon name=\"tabler:folder-code\" />\n </twig:Empty:Media>\n <twig:Empty:Title>No Projects Yet</twig:Empty:Title>\n <twig:Empty:Description>\n You haven't created any projects yet. Get started by creating your first project.\n </twig:Empty:Description>\n </twig:Empty:Header>\n <twig:Empty:Content class=\"flex-row justify-center gap-2\">\n <twig:Button>Create Project</twig:Button>\n <twig:Button variant=\"outline\">Import Project</twig:Button>\n </twig:Empty:Content>\n <twig:Button variant=\"link\" as=\"a\" href=\"#\" size=\"sm\" class=\"text-muted-foreground\">\n Learn More <twig:ux:icon name=\"lucide:arrow-up-right\" />\n </twig:Button>\n</twig:Empty>",
"regras": {
"faca": [
"Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.",
"Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.",
"Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}."
],
"nao_faca": [
"Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.",
"Não crie variante/fork para a mesma coisa — existe UMA base por componente.",
"Não reimplemente o componente nem adicione toolchain Node."
]
},
"a11y": "Semântica, foco visível e ARIA herdados da base congelada — não reimplementar.",
"mcp": {
"tool": "get_component",
"args": {
"id": "empty"
}
},
"props_nota": "Sem props próprias declaradas: a base repassa atributos nativos via {{ attributes }} (id, name, type, value, aria-*, data-*). Props específicas, quando existem, ficam nos subcomponentes."
}