Checkbox
templates/components/Checkbox
O Checkbox é um input nativo binário cuja singularidade é ter um terceiro estado nativo (indeterminado/parcial) e semântica de grupo (pai que controla filhos, "marcar todos"). É a peça central de fluxos de consentimento e conformidade — "Aceito os termos", declarações de EPI/SST — onde o valor precisa ser exigido, confirmado ou lembrado.
Base congelada
By clicking this checkbox, you agree to the terms.
Capacidades 8
Conteúdo
Estado indeterminado (tri-state)
Ativa o terceiro estado nativo do…
Pai marcar todos (hierarquia)
Um checkbox pai comanda um grupo…
Contador do grupo
Exibe quantos itens do grupo estão…
Lembrar escolha
Persiste o valor localmente (localStorage) para…
Formato circular / raio do quadro ⚠️
TENTAÇÃO que NÃO passa no teste:…
Comportamento
Obrigatório com mensagem
Marca o checkbox como exigido (ex.:…
Confirmar antes de alternar
Pede confirmação antes de aplicar a…
Habilitar só após ler (scroll-to-accept)
Mantém o checkbox no estado disabled…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Checkbox (UI) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Ficam imutáveis: o quadro de 16px (size-4), cantos de 4px (rounded-[4px]), a borda border-input, o preenchimento primary quando marcado, o glifo de check (lucide:check size-3.5) como indicador, o anel de foco (focus-visible ring-ring), a opacidade 50% em disabled, o estilo destructive em aria-invalid, o input HTML nativo e toda a a11y (foco visível, semântica, label associável). Nenhum enricher pode tocar fonte, cor, cantos, tamanho ou espaçamento desse quadro — só somar comportamento ou conteúdo ao redor.
USO: <twig:Checkbox />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Estado indeterminado (tri-state): Ativa o terceiro estado nativo do checkbox (parcial): renderiza o glifo de traço no lugar do check, reaproveitando o mesmo quadro/cor/cantos. Usado no pai de um grupo com filhos só parcialmente marcados. [toggle]- Pai marcar todos (hierarquia): Um checkbox pai comanda um grupo de filhos: marca/desmarca todos e assume automaticamente marcado, indeterminado ou vazio conforme o estado dos filhos. Puro comportamento, sem mudar o visual. [toggle]- Contador do grupo: Exibe quantos itens do grupo estão marcados (ex.: '3 de 12'). Conteúdo aditivo posicionável no topo ou no rodapé do grupo, espelhando o princípio de posição da Tabela. [select → N de M selecionados / N selecionados / N/M]- Obrigatório com mensagem: Marca o checkbox como exigido (ex.: 'Aceito os termos') e mostra uma mensagem de validação quando fica vazio no envio, reaproveitando o estilo aria-invalid já congelado na base. Só valida, não muda o visual. [toggle]- Confirmar antes de alternar: Pede confirmação antes de aplicar a mudança de estado — útil para desmarcar uma declaração de conformidade/EPI. Escolha em qual transição a confirmação dispara. [select → Ao marcar / Ao desmarcar / Sempre]- Habilitar só após ler (scroll-to-accept): Mantém o checkbox no estado disabled já congelado na base até o usuário rolar o texto dos termos até o fim, então o libera. Padrão clássico de consentimento/compliance; apenas comportamento de gate. [toggle]- Lembrar escolha: Persiste o valor localmente (localStorage) para padrões como 'não mostrar novamente' ou consentimento de cookies, restaurando o estado na próxima visita. Comportamento aditivo, sem tocar na aparência. [toggle]- Formato circular / raio do quadro: TENTAÇÃO que NÃO passa no teste: tornar o quadro circular altera os 'cantos' congelados da base. É decisão de BASE (muda para todos em app.css), nunca um opt-in por instância — listado aqui como contraexemplo honesto. [select → Quadrado 4px (base) / Circular] (⚠️ borderline: revisar se não é decisão de base)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) como indicador, o anel de foco (focus-visible ring-ring), a opacidade 50% em disabled, o estilo destructive em aria-invalid, o input HTML nativo e toda a a11y (foco visível, semântica, label associável)
LLM / MCP
Via MCP: tool get_component com {"id": "checkbox"} · list_capabilities("checkbox").
Spec crua: config/ds-specs/checkbox.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "checkbox",
"component": "Checkbox",
"eixo": "ui",
"particularidade": "O Checkbox é um input nativo binário cuja singularidade é ter um terceiro estado nativo (indeterminado/parcial) e semântica de grupo (pai que controla filhos, \"marcar todos\"). É a peça central de fluxos de consentimento e conformidade — \"Aceito os termos\", declarações de EPI/SST — onde o valor precisa ser exigido, confirmado ou lembrado.",
"base_congelada": "Ficam imutáveis: o quadro de 16px (size-4), cantos de 4px (rounded-[4px]), a borda border-input, o preenchimento primary quando marcado, o glifo de check (lucide:check size-3.5) como indicador, o anel de foco (focus-visible ring-ring), a opacidade 50% em disabled, o estilo destructive em aria-invalid, o input HTML nativo e toda a a11y (foco visível, semântica, label associável). Nenhum enricher pode tocar fonte, cor, cantos, tamanho ou espaçamento desse quadro — só somar comportamento ou conteúdo ao redor.",
"props": [],
"capacidades": [
{
"id": "estado-indeterminado-tri-state",
"nome": "Estado indeterminado (tri-state)",
"descricao": "Ativa o terceiro estado nativo do checkbox (parcial): renderiza o glifo de traço no lugar do check, reaproveitando o mesmo quadro/cor/cantos. Usado no pai de um grupo com filhos só parcialmente marcados.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "pai-marcar-todos-hierarquia",
"nome": "Pai marcar todos (hierarquia)",
"descricao": "Um checkbox pai comanda um grupo de filhos: marca/desmarca todos e assume automaticamente marcado, indeterminado ou vazio conforme o estado dos filhos. Puro comportamento, sem mudar o visual.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "contador-do-grupo",
"nome": "Contador do grupo",
"descricao": "Exibe quantos itens do grupo estão marcados (ex.: '3 de 12'). Conteúdo aditivo posicionável no topo ou no rodapé do grupo, espelhando o princípio de posição da Tabela.",
"controle": "select",
"opcoes": [
"N de M selecionados",
"N selecionados",
"N/M"
],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "obrigat-rio-com-mensagem",
"nome": "Obrigatório com mensagem",
"descricao": "Marca o checkbox como exigido (ex.: 'Aceito os termos') e mostra uma mensagem de validação quando fica vazio no envio, reaproveitando o estilo aria-invalid já congelado na base. Só valida, não muda o visual.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "confirmar-antes-de-alternar",
"nome": "Confirmar antes de alternar",
"descricao": "Pede confirmação antes de aplicar a mudança de estado — útil para desmarcar uma declaração de conformidade/EPI. Escolha em qual transição a confirmação dispara.",
"controle": "select",
"opcoes": [
"Ao marcar",
"Ao desmarcar",
"Sempre"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "habilitar-s-ap-s-ler-scroll-to-accept",
"nome": "Habilitar só após ler (scroll-to-accept)",
"descricao": "Mantém o checkbox no estado disabled já congelado na base até o usuário rolar o texto dos termos até o fim, então o libera. Padrão clássico de consentimento/compliance; apenas comportamento de gate.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "lembrar-escolha",
"nome": "Lembrar escolha",
"descricao": "Persiste o valor localmente (localStorage) para padrões como 'não mostrar novamente' ou consentimento de cookies, restaurando o estado na próxima visita. Comportamento aditivo, sem tocar na aparência.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/checkbox; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "formato-circular-raio-do-quadro",
"nome": "Formato circular / raio do quadro",
"descricao": "TENTAÇÃO que NÃO passa no teste: tornar o quadro circular altera os 'cantos' congelados da base. É decisão de BASE (muda para todos em app.css), nunca um opt-in por instância — listado aqui como contraexemplo honesto.",
"controle": "select",
"opcoes": [
"Quadrado 4px (base)",
"Circular"
],
"posicionavel": false,
"e_funcao": false,
"como_plugar": "REPROVADO no teste 'é função ou é base' — NÃO plugar como capacidade. É decisão de BASE: mude o token na fonte única assets/styles/app.css, valendo para TODOS os componentes."
}
],
"snippet_uso": "<twig:Checkbox />",
"exemplo_demo": "<twig:Field:Group class=\"max-w-sm\">\n <twig:Field orientation=\"horizontal\">\n <twig:Checkbox id=\"terms-checkbox\" name=\"terms-checkbox\" />\n <twig:Label for=\"terms-checkbox\">Accept terms and conditions</twig:Label>\n </twig:Field>\n <twig:Field orientation=\"horizontal\">\n <twig:Checkbox id=\"terms-checkbox-2\" name=\"terms-checkbox-2\" checked />\n <twig:Field:Content>\n <twig:Field:Label for=\"terms-checkbox-2\">Accept terms and conditions</twig:Field:Label>\n <twig:Field:Description>By clicking this checkbox, you agree to the terms.</twig:Field:Description>\n </twig:Field:Content>\n </twig:Field>\n <twig:Field orientation=\"horizontal\" data-disabled>\n <twig:Checkbox id=\"toggle-checkbox\" name=\"toggle-checkbox\" disabled />\n <twig:Field:Label for=\"toggle-checkbox\">Enable notifications</twig:Field:Label>\n </twig:Field>\n <twig:Field:Label>\n <twig:Field orientation=\"horizontal\">\n <twig:Checkbox id=\"toggle-checkbox-2\" name=\"toggle-checkbox-2\" />\n <twig:Field:Content>\n <twig:Field:Title>Enable notifications</twig:Field:Title>\n <twig:Field:Description>You can enable or disable notifications at any time.</twig:Field:Description>\n </twig:Field:Content>\n </twig:Field>\n </twig:Field:Label>\n</twig:Field:Group>",
"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) como indicador, o anel de foco (focus-visible ring-ring), a opacidade 50% em disabled, o estilo destructive em aria-invalid, o input HTML nativo e toda a a11y (foco visível, semântica, label associável)",
"mcp": {
"tool": "get_component",
"args": {
"id": "checkbox"
}
},
"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."
}