Tabs
templates/components/Tabs
Tabs é um navegador de painéis mutuamente exclusivos: uma barra de gatilhos revela um de vários painéis por vez, com um único estado de "aba ativa" (Stimulus data-controller=tabs). Sua particularidade não é estilizar cada aba, mas gerir muitos gatilhos numa barra estreita e preservar/compartilhar qual painel está aberto.
Base congelada
Capacidades 8
Conteúdo
Contador por aba
Badge numérico aditivo no gatilho (ex.:…
Overflow de abas
Quando os gatilhos não cabem na…
Lembrar última aba
Persiste a última aba aberta em…
Carregamento tardio do painel
Adia render/fetch do conteúdo até a…
Proteção de edição não salva
Marca com ponto (•) as abas…
Comportamento
Aba fechável
Adiciona botão × ao gatilho para…
Botão "nova aba" (+)
Botão + na barra para criar…
Sincronizar aba com a URL
Reflete a aba ativa na URL…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Tabs (UX) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Congelados e imutáveis: tipografia (text-sm, font-medium), cantos (rounded-lg na List, rounded-md no Trigger), espaçamento (p-[3px], gaps), cores (bg-muted, text-muted-foreground/foreground), altura h-8, as duas variantes visuais default/line e a orientação horizontal/vertical (props da base), os slots de ícone inline-start/inline-end já previstos no Trigger, e toda a a11y — role=tablist/tab/tabpanel, aria-selected/controls/labelledby, foco visível (ring), estado disabled e suporte RTL/LTR. O ato de trocar de aba é o comportamento-base. Nenhum enricher toca fonte/cor/cantos/espaçamento; por isso orientação, variantes e ícone NÃO entram como enrichers (são base).
USO: <twig:Tabs>…</twig:Tabs>
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Contador por aba: Badge numérico aditivo no gatilho (ex.: Inbox 12) para sinalizar volume ou pendências de cada painel. [toggle]- Aba fechável: Adiciona botão × ao gatilho para o usuário fechar/remover aquela aba, emitindo um evento de fechamento. [toggle]- Botão "nova aba" (+): Botão + na barra para criar abas dinamicamente, estilo navegador/editor, sem sair do componente. [toggle]- Overflow de abas: Quando os gatilhos não cabem na largura, define como acomodá-los mantendo a barra em linha única. [select → Rolagem horizontal com setas / Menu "mais" (…)]- Sincronizar aba com a URL: Reflete a aba ativa na URL para deep-link, favoritar, compartilhar e usar voltar/avançar do navegador. [select → Hash (#aba) / Query (?tab=)]- Lembrar última aba: Persiste a última aba aberta em localStorage e a restaura automaticamente no próximo acesso. [toggle]- Carregamento tardio do painel: Adia render/fetch do conteúdo até a aba ser ativada pela 1ª vez — ideal para painéis pesados ou via AJAX. [select → Renderizar ao ativar / Buscar via AJAX / Sempre recarregar]- Proteção de edição não salva: Marca com ponto (•) as abas com edições pendentes e pede confirmação antes de trocar de painel em edição. [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): Congelados e imutáveis: tipografia (text-sm, font-medium), cantos (rounded-lg na List, rounded-md no Trigger), espaçamento (p-[3px], gaps), cores (bg-muted, text-muted-foreground/foreground), altura h-8, as duas variantes visuais default/line e a orientação horizontal/vertical (props da base), os slots de ícone inline-start/inline-end já previstos no Trigger, e toda a a11y — role=tablist/tab/tabpanel, aria-selected/controls/labelledby, foco visível (ring), estado disabled e suporte RTL/LTR. por isso orientação, variantes e ícone NÃO entram como enrichers (são base)
LLM / MCP
Via MCP: tool get_component com {"id": "tabs"} · list_capabilities("tabs").
Spec crua: config/ds-specs/tabs.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "tabs",
"component": "Tabs",
"eixo": "ux",
"particularidade": "Tabs é um navegador de painéis mutuamente exclusivos: uma barra de gatilhos revela um de vários painéis por vez, com um único estado de \"aba ativa\" (Stimulus data-controller=tabs). Sua particularidade não é estilizar cada aba, mas gerir muitos gatilhos numa barra estreita e preservar/compartilhar qual painel está aberto.",
"base_congelada": "Congelados e imutáveis: tipografia (text-sm, font-medium), cantos (rounded-lg na List, rounded-md no Trigger), espaçamento (p-[3px], gaps), cores (bg-muted, text-muted-foreground/foreground), altura h-8, as duas variantes visuais default/line e a orientação horizontal/vertical (props da base), os slots de ícone inline-start/inline-end já previstos no Trigger, e toda a a11y — role=tablist/tab/tabpanel, aria-selected/controls/labelledby, foco visível (ring), estado disabled e suporte RTL/LTR. O ato de trocar de aba é o comportamento-base. Nenhum enricher toca fonte/cor/cantos/espaçamento; por isso orientação, variantes e ícone NÃO entram como enrichers (são base).",
"props": [
{
"name": "defaultValue",
"type": "string",
"default": "",
"description": "define the open Tabs at initial rendering. Defaults to `` #}"
},
{
"name": "orientation",
"type": "'horizontal'|'vertical'",
"default": "horizontal",
"description": "define the visual orientation. Defaults to `horizontal` #}"
}
],
"capacidades": [
{
"id": "contador-por-aba",
"nome": "Contador por aba",
"descricao": "Badge numérico aditivo no gatilho (ex.: Inbox 12) para sinalizar volume ou pendências de cada painel.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "aba-fech-vel",
"nome": "Aba fechável",
"descricao": "Adiciona botão × ao gatilho para o usuário fechar/remover aquela aba, emitindo um evento de fechamento.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "bot-o-nova-aba",
"nome": "Botão \"nova aba\" (+)",
"descricao": "Botão + na barra para criar abas dinamicamente, estilo navegador/editor, sem sair do componente.",
"controle": "toggle",
"opcoes": [],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "overflow-de-abas",
"nome": "Overflow de abas",
"descricao": "Quando os gatilhos não cabem na largura, define como acomodá-los mantendo a barra em linha única.",
"controle": "select",
"opcoes": [
"Rolagem horizontal com setas",
"Menu \"mais\" (…)"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "sincronizar-aba-com-a-url",
"nome": "Sincronizar aba com a URL",
"descricao": "Reflete a aba ativa na URL para deep-link, favoritar, compartilhar e usar voltar/avançar do navegador.",
"controle": "select",
"opcoes": [
"Hash (#aba)",
"Query (?tab=)"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "lembrar-ltima-aba",
"nome": "Lembrar última aba",
"descricao": "Persiste a última aba aberta em localStorage e a restaura automaticamente no próximo acesso.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "carregamento-tardio-do-painel",
"nome": "Carregamento tardio do painel",
"descricao": "Adia render/fetch do conteúdo até a aba ser ativada pela 1ª vez — ideal para painéis pesados ou via AJAX.",
"controle": "select",
"opcoes": [
"Renderizar ao ativar",
"Buscar via AJAX",
"Sempre recarregar"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "prote-o-de-edi-o-n-o-salva",
"nome": "Proteção de edição não salva",
"descricao": "Marca com ponto (•) as abas com edições pendentes e pede confirmação antes de trocar de painel em edição.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/tabs; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:Tabs>…</twig:Tabs>",
"exemplo_demo": "<twig:Tabs defaultValue=\"overview\" class=\"w-[400px]\">\n <twig:Tabs:List>\n <twig:Tabs:Trigger value=\"overview\">Overview</twig:Tabs:Trigger>\n <twig:Tabs:Trigger value=\"analytics\">Analytics</twig:Tabs:Trigger>\n <twig:Tabs:Trigger value=\"reports\">Reports</twig:Tabs:Trigger>\n <twig:Tabs:Trigger value=\"settings\">Settings</twig:Tabs:Trigger>\n </twig:Tabs:List>\n <twig:Tabs:Content value=\"overview\">\n <twig:Card>\n <twig:Card:Header>\n <twig:Card:Title>Overview</twig:Card:Title>\n <twig:Card:Description>\n View your key metrics and recent project activity. Track progress\n across all your active projects.\n </twig:Card:Description>\n </twig:Card:Header>\n <twig:Card:Content class=\"text-muted-foreground text-sm\">\n You have 12 active projects and 3 pending tasks.\n </twig:Card:Content>\n </twig:Card>\n </twig:Tabs:Content>\n <twig:Tabs:Content value=\"analytics\">\n <twig:Card>\n <twig:Card:Header>\n <twig:Card:Title>Analytics</twig:Card:Title>\n <twig:Card:Description>\n Track performance and user engagement metrics. Monitor trends and\n identify growth opportunities.\n </twig:Card:Description>\n </twig:Card:Header>\n <twig:Card:Content class=\"text-muted-foreground text-sm\">\n Page views are up 25% compared to last month.\n </twig:Card:Content>\n </twig:Card>\n </twig:Tabs:Content>\n <twig:Tabs:Content value=\"reports\">\n <twig:Card>\n <twig:Card:Header>\n <twig:Card:Title>Reports</twig:Card:Title>\n <twig:Card:Description>\n Generate and download your detailed reports. Export data in\n multiple formats for analysis.\n </twig:Card:Description>\n </twig:Card:Header>\n <twig:Card:Content class=\"text-muted-foreground text-sm\">\n You have 5 reports ready and available to export.\n </twig:Card:Content>\n </twig:Card>\n </twig:Tabs:Content>\n <twig:Tabs:Content value=\"settings\">\n <twig:Card>\n <twig:Card:Header>\n <twig:Card:Title>Settings</twig:Card:Title>\n <twig:Card:Description>\n Manage your account preferences and options. Customize your\n experience to fit your needs.\n </twig:Card:Description>\n </twig:Card:Header>\n <twig:Card:Content class=\"text-muted-foreground text-sm\">\n Configure notifications, security, and themes.\n </twig:Card:Content>\n </twig:Card>\n </twig:Tabs:Content>\n</twig:Tabs>",
"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": "Congelados e imutáveis: tipografia (text-sm, font-medium), cantos (rounded-lg na List, rounded-md no Trigger), espaçamento (p-[3px], gaps), cores (bg-muted, text-muted-foreground/foreground), altura h-8, as duas variantes visuais default/line e a orientação horizontal/vertical (props da base), os slots de ícone inline-start/inline-end já previstos no Trigger, e toda a a11y — role=tablist/tab/tabpanel, aria-selected/controls/labelledby, foco visível (ring), estado disabled e suporte RTL/LTR. por isso orientação, variantes e ícone NÃO entram como enrichers (são base)",
"mcp": {
"tool": "get_component",
"args": {
"id": "tabs"
}
}
}
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.