Card
templates/components/Card
O Card é a superfície composicional do sistema: um bloco autocontido que agrupa Header (Title/Description + slot Action no canto), Content, Footer e mídia opcional, usado como "tile" em grades. Sua base já antecipa mídia full-bleed (arredonda a 1ª/última imagem) e reserva um canto de ação — ganchos naturais para enriquecer sem tocar no visual.
Base congelada
Capacidades 8
Conteúdo
Descartar (dispensável)
Botão × no slot Action para…
Mídia de capa (cover)
Insere imagem/mídia full-bleed reaproveitando o arredondamento…
Selo de status
Marca o estado do card (ativo/pendente/erro…)…
Comportamento
Card clicável (superfície-link)
Torna o card inteiro um alvo…
Selecionável (tile de escolha)
Transforma o card em opção selecionável…
Recolher conteúdo (colapsável)
Adiciona um gatilho no header que…
Rolagem interna do conteúdo
Limita a altura do Card:Content e…
Skeleton de carregamento
Estado de carregamento: exibe placeholders (pulse)…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Card (UI) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Cantos rounded-xl, ring-1 ring-foreground/10, cores bg-card/text-card-foreground, título em cn-font-heading e corpo text-sm, espaçamento gap-4/py-4/px-4 (variante sm: gap-3/py-3/px-3), a estrutura Header→Content→Footer, o Footer com border-top + bg-muted/50, o slot Action no topo-direita, o arredondamento automático de imagem full-bleed (img:first/last-child) e a semântica data-slot. Nada disso muda ao ligar capacidades.
USO: <twig:Card size="default" />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Card clicável (superfície-link): Torna o card inteiro um alvo clicável (padrão stretched-link): navega ao clicar em qualquer área, com foco por teclado e ARIA. Reaproveita o ring já existente na base para o foco. [toggle]- Selecionável (tile de escolha): Transforma o card em opção selecionável (plano/tile de escolha). Indica o estado marcado pelo anel que a base já tem e expõe seleção única ou múltipla — comportamento, não estilo. [select → Escolha única (radio) / Múltipla escolha (checkbox)]- Recolher conteúdo (colapsável): Adiciona um gatilho no header que recolhe/expande o Card:Content (disclosure), mantendo o Header sempre visível. Puro comportamento de mostrar/ocultar. [toggle]- Descartar (dispensável): Botão × no slot Action para dispensar/remover o card — casos de aviso, onboarding e notificação. Conteúdo aditivo + comportamento, sem alterar a base. [toggle]- Mídia de capa (cover): Insere imagem/mídia full-bleed reaproveitando o arredondamento que a base já aplica à 1ª/última imagem. Escolhe se a capa fica no topo ou na base do card. [select → Topo / Rodapé]- Rolagem interna do conteúdo: Limita a altura do Card:Content e rola por dentro, mantendo Header e Footer fixos — útil para listas longas dentro do card. Só comportamento de overflow. [select → 240px / 320px / 480px / Altura livre]- Skeleton de carregamento: Estado de carregamento: exibe placeholders (pulse) no lugar de título/linhas/mídia usando os tokens muted da base e troca pelo conteúdo real quando pronto. [toggle]- Selo de status: Marca o estado do card (ativo/pendente/erro…) no header, com cor livre por estado — mesmo precedente do Status da Tabela. Conteúdo aditivo, posicionável no header. [select → Ponto + texto / Badge com ícone / Badge sem ícone]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): Cantos rounded-xl, ring-1 ring-foreground/10, cores bg-card/text-card-foreground, título em cn-font-heading e corpo text-sm, espaçamento gap-4/py-4/px-4 (variante sm: gap-3/py-3/px-3), a estrutura Header→Content→Footer, o Footer com border-top + bg-muted/50, o slot Action no topo-direita, o arredondamento automático de imagem full-bleed (img:first/last-child) e a semântica data-slot
LLM / MCP
Via MCP: tool get_component com {"id": "card"} · list_capabilities("card").
Spec crua: config/ds-specs/card.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "card",
"component": "Card",
"eixo": "ui",
"particularidade": "O Card é a superfície composicional do sistema: um bloco autocontido que agrupa Header (Title/Description + slot Action no canto), Content, Footer e mídia opcional, usado como \"tile\" em grades. Sua base já antecipa mídia full-bleed (arredonda a 1ª/última imagem) e reserva um canto de ação — ganchos naturais para enriquecer sem tocar no visual.",
"base_congelada": "Cantos rounded-xl, ring-1 ring-foreground/10, cores bg-card/text-card-foreground, título em cn-font-heading e corpo text-sm, espaçamento gap-4/py-4/px-4 (variante sm: gap-3/py-3/px-3), a estrutura Header→Content→Footer, o Footer com border-top + bg-muted/50, o slot Action no topo-direita, o arredondamento automático de imagem full-bleed (img:first/last-child) e a semântica data-slot. Nada disso muda ao ligar capacidades.",
"props": [
{
"name": "size",
"type": "'default'|'sm'",
"default": "default",
"description": "Size variant of the card. Defaults to `'default'` #}"
}
],
"capacidades": [
{
"id": "card-clic-vel-superf-cie-link",
"nome": "Card clicável (superfície-link)",
"descricao": "Torna o card inteiro um alvo clicável (padrão stretched-link): navega ao clicar em qualquer área, com foco por teclado e ARIA. Reaproveita o ring já existente na base para o foco.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "selecion-vel-tile-de-escolha",
"nome": "Selecionável (tile de escolha)",
"descricao": "Transforma o card em opção selecionável (plano/tile de escolha). Indica o estado marcado pelo anel que a base já tem e expõe seleção única ou múltipla — comportamento, não estilo.",
"controle": "select",
"opcoes": [
"Escolha única (radio)",
"Múltipla escolha (checkbox)"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "recolher-conte-do-colaps-vel",
"nome": "Recolher conteúdo (colapsável)",
"descricao": "Adiciona um gatilho no header que recolhe/expande o Card:Content (disclosure), mantendo o Header sempre visível. Puro comportamento de mostrar/ocultar.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "descartar-dispens-vel",
"nome": "Descartar (dispensável)",
"descricao": "Botão × no slot Action para dispensar/remover o card — casos de aviso, onboarding e notificação. Conteúdo aditivo + comportamento, sem alterar a base.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "m-dia-de-capa-cover",
"nome": "Mídia de capa (cover)",
"descricao": "Insere imagem/mídia full-bleed reaproveitando o arredondamento que a base já aplica à 1ª/última imagem. Escolhe se a capa fica no topo ou na base do card.",
"controle": "select",
"opcoes": [
"Topo",
"Rodapé"
],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "rolagem-interna-do-conte-do",
"nome": "Rolagem interna do conteúdo",
"descricao": "Limita a altura do Card:Content e rola por dentro, mantendo Header e Footer fixos — útil para listas longas dentro do card. Só comportamento de overflow.",
"controle": "select",
"opcoes": [
"240px",
"320px",
"480px",
"Altura livre"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "skeleton-de-carregamento",
"nome": "Skeleton de carregamento",
"descricao": "Estado de carregamento: exibe placeholders (pulse) no lugar de título/linhas/mídia usando os tokens muted da base e troca pelo conteúdo real quando pronto.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "selo-de-status",
"nome": "Selo de status",
"descricao": "Marca o estado do card (ativo/pendente/erro…) no header, com cor livre por estado — mesmo precedente do Status da Tabela. Conteúdo aditivo, posicionável no header.",
"controle": "select",
"opcoes": [
"Ponto + texto",
"Badge com ícone",
"Badge sem ícone"
],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/card; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:Card size=\"default\" />",
"exemplo_demo": "<twig:Card class=\"w-full max-w-sm\">\n <twig:Card:Header>\n <twig:Card:Title>Login to your account</twig:Card:Title>\n <twig:Card:Description>\n Enter your email below to login to your account\n </twig:Card:Description>\n <twig:Card:Action>\n <twig:Button variant=\"link\">Sign Up</twig:Button>\n </twig:Card:Action>\n </twig:Card:Header>\n <twig:Card:Content>\n <form>\n <div class=\"flex flex-col gap-6\">\n <div class=\"grid gap-2\">\n <twig:Label for=\"email\">Email</twig:Label>\n <twig:Input\n id=\"email\"\n type=\"email\"\n placeholder=\"m@example.com\"\n required\n />\n </div>\n <div class=\"grid gap-2\">\n <div class=\"flex items-center\">\n <twig:Label for=\"password\">Password</twig:Label>\n <a\n href=\"#\"\n class=\"ml-auto inline-block text-sm underline-offset-4 hover:underline\"\n >\n Forgot your password?\n </a>\n </div>\n <twig:Input id=\"password\" type=\"password\" required />\n </div>\n </div>\n </form>\n </twig:Card:Content>\n <twig:Card:Footer class=\"flex-col gap-2\">\n <twig:Button type=\"submit\" class=\"w-full\">\n Login\n </twig:Button>\n <twig:Button variant=\"outline\" class=\"w-full\">\n Login with Google\n </twig:Button>\n </twig:Card:Footer>\n</twig:Card>",
"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": "Cantos rounded-xl, ring-1 ring-foreground/10, cores bg-card/text-card-foreground, título em cn-font-heading e corpo text-sm, espaçamento gap-4/py-4/px-4 (variante sm: gap-3/py-3/px-3), a estrutura Header→Content→Footer, o Footer com border-top + bg-muted/50, o slot Action no topo-direita, o arredondamento automático de imagem full-bleed (img:first/last-child) e a semântica data-slot",
"mcp": {
"tool": "get_component",
"args": {
"id": "card"
}
}
}
O que mudou 1
-
v0.1.0 Camada de IA: scaffold de página + specs íntegras
- Novo tool MCP get_page_scaffold: devolve a MOLDURA de uma página nova (layout + blocks reais, contrato do renderPage, receita de rota + buildNav, tokens de layout lidos de app.css e snippet .html.twig válido) — dá pra montar página sem ler o controller/layout.
- Fonte única do scaffold em config/ds-specs/_page-scaffold.json (padrão v1.0); tokens semânticos derivados de assets/styles/app.css em runtime.
- Fix: exemplo_demo estava truncado em 1600 bytes em 5 specs (card, field, table, tabs, typography) — reconstituídos íntegros a partir dos demos, fechando todas as tags.
- Nova seção Updates (este changelog versionado) como 9º eixo do shell.
- Docs: CLAUDE.md e AI-LAYER.md citam o novo tool; contadores de tools atualizados (6 → 7).
Changelog completo em Updates.