Pular para o conteúdo

Collapsible

templates/components/Collapsible

É uma ÚNICA região de disclosure: exatamente um gatilho que controla um único bloco de conteúdo, revelando/ocultando sob demanda. Diferente do Accordion, não tem conjunto de itens nem lógica de único-vs-múltiplo — sua identidade inteira é a transição mostrar/ocultar e o vínculo de a11y entre gatilho e conteúdo.

Base congelada

Order #4189

Status Shipped

Conteúdo

Indicador de estado com rotação

Vincula um chevron ao data-state do…

Vincula um chevron ao data-state do gatilho, girando-o ao abrir/fechar como sinal do disclosure; sem alterar cor/tamanho da base. posicionável

Conteúdo sob demanda (lazy)

Só renderiza/carrega o conteúdo interno na…

Só renderiza/carrega o conteúdo interno na primeira abertura, evitando custo quando permanece fechado.

Estado inicial responsivo

Decide o aberto/fechado inicial conforme o…

Decide o aberto/fechado inicial conforme o viewport, sem tocar no prop nem no visual da base.

Rótulo dinâmico do gatilho

Troca o texto (e o sr-only)…

Troca o texto (e o sr-only) do gatilho conforme o estado, ex.: 'Mostrar detalhes' quando fechado ↔ 'Ocultar detalhes' quando aberto.

Prévia quando fechado

Mostra um resumo aditivo do conteúdo…

Mostra um resumo aditivo do conteúdo oculto no cabeçalho enquanto fechado (ex.: contagem '3 itens' ou primeira linha), somindo ao abrir. posicionável

Comportamento

Animação de transição

Como o conteúdo entra/sai ao alternar:…

Como o conteúdo entra/sai ao alternar: instantâneo (hidden) ou animando altura/opacidade, sempre respeitando prefers-reduced-motion.

Lembrar estado (persistência)

Guarda aberto/fechado por id no storage…

Guarda aberto/fechado por id no storage do navegador e restaura o mesmo estado na próxima visita.

Área de clique do gatilho

Escolhe o que dispara o toggle…

Escolhe o que dispara o toggle sem duplicar o componente: apenas o botão/ícone ou toda a linha do cabeçalho.
Código gerado

Cole no Claude Code — ele acerta de primeira.

Collapsible (UX) — Design System (Symfony UX Toolkit / shadcn)

BASE CONGELADA (não mude por instância): Congelado: a estrutura div[data-controller=collapsible] > Collapsible:Trigger + Collapsible:Content; o mecanismo de mostrar/ocultar via atributo `hidden`; o contrato de a11y (aria-expanded no gatilho, aria-hidden no conteúdo, data-state open/closed espelhado nos dois); o prop `open` (default false) e o controller Stimulus `collapsible`; e todos os tokens visuais (fonte, cor, cantos, espaçamento) herdados dos componentes compostos (Button, bordas). Nada disso muda por instância — enrichers só adicionam comportamento/conteúdo aditivo em volta desse contrato.

USO: <twig:Collapsible />

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Animação de transição: Como o conteúdo entra/sai ao alternar: instantâneo (hidden) ou animando altura/opacidade, sempre respeitando prefers-reduced-motion. [select → Instantâneo / Deslizar (altura) / Esmaecer / Deslizar + esmaecer]- Indicador de estado com rotação: Vincula um chevron ao data-state do gatilho, girando-o ao abrir/fechar como sinal do disclosure; sem alterar cor/tamanho da base. [toggle]- Lembrar estado (persistência): Guarda aberto/fechado por id no storage do navegador e restaura o mesmo estado na próxima visita. [toggle]- Conteúdo sob demanda (lazy): Só renderiza/carrega o conteúdo interno na primeira abertura, evitando custo quando permanece fechado. [toggle]- Estado inicial responsivo: Decide o aberto/fechado inicial conforme o viewport, sem tocar no prop nem no visual da base. [select → Sempre fechado / Sempre aberto / Aberto no desktop / fechado no mobile / Aberto no mobile / fechado no desktop]- Rótulo dinâmico do gatilho: Troca o texto (e o sr-only) do gatilho conforme o estado, ex.: 'Mostrar detalhes' quando fechado ↔ 'Ocultar detalhes' quando aberto. [text]- Área de clique do gatilho: Escolhe o que dispara o toggle sem duplicar o componente: apenas o botão/ícone ou toda a linha do cabeçalho. [select → Só o botão/ícone / Toda a linha do cabeçalho]- Prévia quando fechado: Mostra um resumo aditivo do conteúdo oculto no cabeçalho enquanto fechado (ex.: contagem '3 itens' ou primeira linha), somindo ao abrir. [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): o contrato de a11y (aria-expanded no gatilho, aria-hidden no conteúdo, data-state open/closed espelhado nos dois)

Via MCP: tool get_component com {"id": "collapsible"} · list_capabilities("collapsible").

Spec crua: config/ds-specs/collapsible.json · Conectar o MCP.

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "collapsible",
    "component": "Collapsible",
    "eixo": "ux",
    "particularidade": "É uma ÚNICA região de disclosure: exatamente um gatilho que controla um único bloco de conteúdo, revelando/ocultando sob demanda. Diferente do Accordion, não tem conjunto de itens nem lógica de único-vs-múltiplo — sua identidade inteira é a transição mostrar/ocultar e o vínculo de a11y entre gatilho e conteúdo.",
    "base_congelada": "Congelado: a estrutura div[data-controller=collapsible] > Collapsible:Trigger + Collapsible:Content; o mecanismo de mostrar/ocultar via atributo `hidden`; o contrato de a11y (aria-expanded no gatilho, aria-hidden no conteúdo, data-state open/closed espelhado nos dois); o prop `open` (default false) e o controller Stimulus `collapsible`; e todos os tokens visuais (fonte, cor, cantos, espaçamento) herdados dos componentes compostos (Button, bordas). Nada disso muda por instância — enrichers só adicionam comportamento/conteúdo aditivo em volta desse contrato.",
    "props": [
        {
            "name": "open",
            "type": "boolean",
            "default": "false",
            "description": "Whether the collapsible is open by default. Defaults to `false` #}"
        }
    ],
    "capacidades": [
        {
            "id": "anima-o-de-transi-o",
            "nome": "Animação de transição",
            "descricao": "Como o conteúdo entra/sai ao alternar: instantâneo (hidden) ou animando altura/opacidade, sempre respeitando prefers-reduced-motion.",
            "controle": "select",
            "opcoes": [
                "Instantâneo",
                "Deslizar (altura)",
                "Esmaecer",
                "Deslizar + esmaecer"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "indicador-de-estado-com-rota-o",
            "nome": "Indicador de estado com rotação",
            "descricao": "Vincula um chevron ao data-state do gatilho, girando-o ao abrir/fechar como sinal do disclosure; sem alterar cor/tamanho da base.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "lembrar-estado-persist-ncia",
            "nome": "Lembrar estado (persistência)",
            "descricao": "Guarda aberto/fechado por id no storage do navegador e restaura o mesmo estado na próxima visita.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "conte-do-sob-demanda-lazy",
            "nome": "Conteúdo sob demanda (lazy)",
            "descricao": "Só renderiza/carrega o conteúdo interno na primeira abertura, evitando custo quando permanece fechado.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "estado-inicial-responsivo",
            "nome": "Estado inicial responsivo",
            "descricao": "Decide o aberto/fechado inicial conforme o viewport, sem tocar no prop nem no visual da base.",
            "controle": "select",
            "opcoes": [
                "Sempre fechado",
                "Sempre aberto",
                "Aberto no desktop / fechado no mobile",
                "Aberto no mobile / fechado no desktop"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "r-tulo-din-mico-do-gatilho",
            "nome": "Rótulo dinâmico do gatilho",
            "descricao": "Troca o texto (e o sr-only) do gatilho conforme o estado, ex.: 'Mostrar detalhes' quando fechado ↔ 'Ocultar detalhes' quando aberto.",
            "controle": "text",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (text). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "rea-de-clique-do-gatilho",
            "nome": "Área de clique do gatilho",
            "descricao": "Escolhe o que dispara o toggle sem duplicar o componente: apenas o botão/ícone ou toda a linha do cabeçalho.",
            "controle": "select",
            "opcoes": [
                "Só o botão/ícone",
                "Toda a linha do cabeçalho"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "pr-via-quando-fechado",
            "nome": "Prévia quando fechado",
            "descricao": "Mostra um resumo aditivo do conteúdo oculto no cabeçalho enquanto fechado (ex.: contagem '3 itens' ou primeira linha), somindo ao abrir.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/collapsible; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:Collapsible />",
    "exemplo_demo": "<twig:Collapsible class=\"flex w-[350px] flex-col gap-2 self-start\">\n    <div class=\"flex items-center justify-between gap-4 px-4\">\n        <h4 class=\"text-sm font-semibold\">Order #4189</h4>\n        <twig:Collapsible:Trigger>\n            <twig:Button variant=\"ghost\" size=\"icon\" class=\"size-8\" {{ ...collapsible_trigger_attrs }}>\n                <twig:ux:icon name=\"lucide:chevrons-up-down\" class=\"size-4\" />\n                <span class=\"sr-only\">Toggle details</span>\n            </twig:Button>\n        </twig:Collapsible:Trigger>\n    </div>\n    <div class=\"flex items-center justify-between rounded-md border px-4 py-2 text-sm\">\n        <span class=\"text-muted-foreground\">Status</span>\n        <span class=\"font-medium\">Shipped</span>\n    </div>\n    <twig:Collapsible:Content class=\"flex flex-col gap-2\">\n        <div class=\"rounded-md border px-4 py-2 text-sm\">\n            <p class=\"font-medium\">Shipping address</p>\n            <p class=\"text-muted-foreground\">100 Market St, San Francisco</p>\n        </div>\n        <div class=\"rounded-md border px-4 py-2 text-sm\">\n            <p class=\"font-medium\">Items</p>\n            <p class=\"text-muted-foreground\">2x Studio Headphones</p>\n        </div>\n    </twig:Collapsible:Content>\n</twig:Collapsible>",
    "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": "o contrato de a11y (aria-expanded no gatilho, aria-hidden no conteúdo, data-state open/closed espelhado nos dois)",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "collapsible"
        }
    }
}