Hover Card
templates/components/HoverCard
É um overlay de "espiada": revela conteúdo rico e suplementar (prévia de perfil, definição, metadados) ancorado a um gatilho, disparado por HOVER/FOCO — nunca por clique. Sua natureza gira em torno de tempo (delay para não piscar), lugar (posição relativa ao gatilho) e a travessia do cursor até o card.
Base congelada
Capacidades 9
Conteúdo
Lado de exibição
Face do gatilho onde o card…
Alinhamento ao gatilho
Ancora o card ao início, centro…
Reposicionar para caber (auto-flip)
Se o lado preferido estourar a…
Seta apontadora
Caret aditivo ligando o card ao…
Comportamento
Atraso de intenção (abrir/fechar)
Tempo em ms antes de mostrar…
Ponte de hover (área segura)
Mantém o card aberto enquanto o…
Fixar ao clicar
Clicar no gatilho trava o card…
Carregar conteúdo sob demanda
Busca o conteúdo do card via…
Abrir por toque (sem hover)
Em dispositivos sem hover (toque), o…
Claude Code
Cole no Claude Code — ele acerta de primeira.
HoverCard (UX) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): A aparência do card é imutável: largura w-64, cantos rounded-lg, cor bg-popover/text-popover-foreground, padding interno p-2.5, tipografia text-sm, shadow-md, contorno ring-1, camada z-50, estado inicial fechado, semântica role="tooltip" e disparo por mouseenter/mouseleave + focus/blur com trigger tabindex=0. Os enriquecedores só mexem em tempo, posicionamento, comportamento de abertura/permanência e conteúdo aditivo — nunca em fonte, cor, cantos ou espaçamento interno do card.
USO: <twig:HoverCard />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Atraso de intenção (abrir/fechar): Tempo em ms antes de mostrar e antes de ocultar (openDelay/closeDelay); filtra passagens acidentais do mouse e evita piscar. [number]- Lado de exibição: Face do gatilho onde o card aparece: acima, abaixo, à esquerda ou à direita. [position → Acima / Abaixo / Esquerda / Direita]- Alinhamento ao gatilho: Ancora o card ao início, centro ou fim do gatilho ao longo do lado escolhido, para casar com gatilhos largos/estreitos. [select → Início / Centro / Fim]- Reposicionar para caber (auto-flip): Se o lado preferido estourar a viewport, vira automaticamente para o lado oposto para não cortar o card nas bordas da tela. [toggle]- Ponte de hover (área segura): Mantém o card aberto enquanto o cursor atravessa o vão entre gatilho e card, evitando o fechamento no meio do caminho pela diagonal. [toggle]- Fixar ao clicar: Clicar no gatilho trava o card aberto para ler, copiar ou clicar links do conteúdo sem ele fechar ao tirar o mouse; toque fora ou ESC solta. [toggle]- Carregar conteúdo sob demanda: Busca o conteúdo do card via requisição no primeiro hover (ex.: prévia de perfil/registro), mostrando estado de carregando e cacheando nas próximas vezes. [toggle]- Seta apontadora: Caret aditivo ligando o card ao gatilho para indicar de onde ele saiu; herda bg-popover/ring e acompanha o lado escolhido. [toggle]- Abrir por toque (sem hover): Em dispositivos sem hover (toque), o primeiro toque no gatilho abre o card e um toque fora o fecha, cobrindo o cenário em que hover não existe. [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): 5, tipografia text-sm, shadow-md, contorno ring-1, camada z-50, estado inicial fechado, semântica role="tooltip" e disparo por mouseenter/mouseleave + focus/blur com trigger tabindex=0
LLM / MCP
Via MCP: tool get_component com {"id": "hover-card"} · list_capabilities("hover-card").
Spec crua: config/ds-specs/hover-card.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "hover-card",
"component": "HoverCard",
"eixo": "ux",
"particularidade": "É um overlay de \"espiada\": revela conteúdo rico e suplementar (prévia de perfil, definição, metadados) ancorado a um gatilho, disparado por HOVER/FOCO — nunca por clique. Sua natureza gira em torno de tempo (delay para não piscar), lugar (posição relativa ao gatilho) e a travessia do cursor até o card.",
"base_congelada": "A aparência do card é imutável: largura w-64, cantos rounded-lg, cor bg-popover/text-popover-foreground, padding interno p-2.5, tipografia text-sm, shadow-md, contorno ring-1, camada z-50, estado inicial fechado, semântica role=\"tooltip\" e disparo por mouseenter/mouseleave + focus/blur com trigger tabindex=0. Os enriquecedores só mexem em tempo, posicionamento, comportamento de abertura/permanência e conteúdo aditivo — nunca em fonte, cor, cantos ou espaçamento interno do card.",
"props": [
{
"name": "openDelay",
"type": "number",
"default": "0",
"description": "Delay in milliseconds before showing the content. Defaults to `0` #}"
},
{
"name": "closeDelay",
"type": "number",
"default": "0",
"description": "Delay in milliseconds before hiding the content. Defaults to `0` #}"
}
],
"capacidades": [
{
"id": "atraso-de-inten-o-abrir-fechar",
"nome": "Atraso de intenção (abrir/fechar)",
"descricao": "Tempo em ms antes de mostrar e antes de ocultar (openDelay/closeDelay); filtra passagens acidentais do mouse e evita piscar.",
"controle": "number",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (number). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "lado-de-exibi-o",
"nome": "Lado de exibição",
"descricao": "Face do gatilho onde o card aparece: acima, abaixo, à esquerda ou à direita.",
"controle": "position",
"opcoes": [
"Acima",
"Abaixo",
"Esquerda",
"Direita"
],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (position). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "alinhamento-ao-gatilho",
"nome": "Alinhamento ao gatilho",
"descricao": "Ancora o card ao início, centro ou fim do gatilho ao longo do lado escolhido, para casar com gatilhos largos/estreitos.",
"controle": "select",
"opcoes": [
"Início",
"Centro",
"Fim"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "reposicionar-para-caber-auto-flip",
"nome": "Reposicionar para caber (auto-flip)",
"descricao": "Se o lado preferido estourar a viewport, vira automaticamente para o lado oposto para não cortar o card nas bordas da tela.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "ponte-de-hover-rea-segura",
"nome": "Ponte de hover (área segura)",
"descricao": "Mantém o card aberto enquanto o cursor atravessa o vão entre gatilho e card, evitando o fechamento no meio do caminho pela diagonal.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "fixar-ao-clicar",
"nome": "Fixar ao clicar",
"descricao": "Clicar no gatilho trava o card aberto para ler, copiar ou clicar links do conteúdo sem ele fechar ao tirar o mouse; toque fora ou ESC solta.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "carregar-conte-do-sob-demanda",
"nome": "Carregar conteúdo sob demanda",
"descricao": "Busca o conteúdo do card via requisição no primeiro hover (ex.: prévia de perfil/registro), mostrando estado de carregando e cacheando nas próximas vezes.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "seta-apontadora",
"nome": "Seta apontadora",
"descricao": "Caret aditivo ligando o card ao gatilho para indicar de onde ele saiu; herda bg-popover/ring e acompanha o lado escolhido.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "abrir-por-toque-sem-hover",
"nome": "Abrir por toque (sem hover)",
"descricao": "Em dispositivos sem hover (toque), o primeiro toque no gatilho abre o card e um toque fora o fecha, cobrindo o cenário em que hover não existe.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/hover-card; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:HoverCard />",
"exemplo_demo": "<twig:HoverCard openDelay=\"10\" closeDelay=\"100\">\n <twig:HoverCard:Trigger>\n <twig:Button variant=\"link\" {{ ...hover_card_trigger_attrs }}>Hover Here</twig:Button>\n </twig:HoverCard:Trigger>\n <twig:HoverCard:Content class=\"flex w-64 flex-col gap-0.5\">\n <div class=\"font-semibold\">@symfony</div>\n <div>The PHP framework for web applications — created by @fabpot.</div>\n <div class=\"mt-1 text-xs text-muted-foreground\">Joined October 2010</div>\n </twig:HoverCard:Content>\n</twig:HoverCard>",
"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": "5, tipografia text-sm, shadow-md, contorno ring-1, camada z-50, estado inicial fechado, semântica role=\"tooltip\" e disparo por mouseenter/mouseleave + focus/blur com trigger tabindex=0",
"mcp": {
"tool": "get_component",
"args": {
"id": "hover-card"
}
}
}