Pular para o conteúdo

Radio Group

templates/components/RadioGroup

Controle de escolha ÚNICA e mutuamente exclusiva entre N opções (role="radiogroup"), cada item um input radio nativo desenhado como círculo + ponto. Diferente do checkbox, o radio nativo não pode ser desmarcado clicando de novo — o que torna certas capacidades (limpar, pré-seleção) exclusivas da sua natureza.

Base congelada

Conteúdo

Título + descrição do grupo

Rótulo acessível do grupo (vira o…

Rótulo acessível do grupo (vira o aria-labelledby do radiogroup) com subtítulo opcional acima das opções.

Descrição por opção

Linha secundária de apoio abaixo do…

Linha secundária de apoio abaixo do label de cada opção (ex.: o que cada plano/escolha inclui).

Ícone por opção

Ícone lucide aditivo junto ao label…

Ícone lucide aditivo junto ao label de cada opção, posicionável no início ou no fim. posicionável

Desabilitar opções

Marca opções específicas como indisponíveis, usando…

Marca opções específicas como indisponíveis, usando o estado disabled já embutido na base.

Comportamento

Opção pré-selecionada

Define qual valor já vem marcado…

Define qual valor já vem marcado ao abrir (checked inicial) — ou nenhum.

Limpar seleção

Botão para desmarcar tudo e voltar…

Botão para desmarcar tudo e voltar ao estado sem escolha — algo que o radio nativo não permite clicando de novo. posicionável

Validação obrigatória

Exige uma escolha antes de enviar;…

Exige uma escolha antes de enviar; ativa o estado inválido (aria-invalid, já estilizado na base) e exibe mensagem de erro.

Orientação

Dispõe as opções em coluna ou…

Dispõe as opções em coluna ou em linha, ajustando aria-orientation e o eixo das setas do teclado.

Opção "Outro" + campo livre

Uma opção que, ao ser escolhida,…

Uma opção que, ao ser escolhida, revela um input de texto para resposta livre; oculto e ignorado quando não selecionada.
Código gerado

Cole no Claude Code — ele acerta de primeira.

RadioGroup (UI) — Design System (Symfony UX Toolkit / shadcn)

BASE CONGELADA (não mude por instância): O desenho do controle: círculo size-4 rounded-full com border-input, cor primary quando marcado, ponto interno primary-foreground que escala ao selecionar, grid com gap-2, anel de foco (peer-focus-visible ring), e os estados disabled (opacity-50) e aria-invalid (destructive) já embutidos, com suporte RTL e semântica role="radiogroup". Fonte, cor, cantos, tamanho do controle e espaçamento são imutáveis.

USO: <twig:RadioGroup />

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Título + descrição do grupo: Rótulo acessível do grupo (vira o aria-labelledby do radiogroup) com subtítulo opcional acima das opções. [select → nenhum / só título / título + descrição]- Descrição por opção: Linha secundária de apoio abaixo do label de cada opção (ex.: o que cada plano/escolha inclui). [toggle]- Ícone por opção: Ícone lucide aditivo junto ao label de cada opção, posicionável no início ou no fim. [toggle]- Opção pré-selecionada: Define qual valor já vem marcado ao abrir (checked inicial) — ou nenhum. [select → nenhuma / primeira opção / opção específica]- Limpar seleção: Botão para desmarcar tudo e voltar ao estado sem escolha — algo que o radio nativo não permite clicando de novo. [toggle]- Validação obrigatória: Exige uma escolha antes de enviar; ativa o estado inválido (aria-invalid, já estilizado na base) e exibe mensagem de erro. [toggle]- Desabilitar opções: Marca opções específicas como indisponíveis, usando o estado disabled já embutido na base. [multi → 1ª opção / 2ª opção / 3ª opção / …]- Orientação: Dispõe as opções em coluna ou em linha, ajustando aria-orientation e o eixo das setas do teclado. [select → vertical / horizontal]- Opção "Outro" + campo livre: Uma opção que, ao ser escolhida, revela um input de texto para resposta livre; oculto e ignorado quando não selecionada. [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 desenho do controle: círculo size-4 rounded-full com border-input, cor primary quando marcado, ponto interno primary-foreground que escala ao selecionar, grid com gap-2, anel de foco (peer-focus-visible ring), e os estados disabled (opacity-50) e aria-invalid (destructive) já embutidos, com suporte RTL e semântica role="radiogroup". Fonte, cor, cantos, tamanho do controle e espaçamento são imutáveis

Via MCP: tool get_component com {"id": "radio-group"} · list_capabilities("radio-group").

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

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "radio-group",
    "component": "RadioGroup",
    "eixo": "ui",
    "particularidade": "Controle de escolha ÚNICA e mutuamente exclusiva entre N opções (role=\"radiogroup\"), cada item um input radio nativo desenhado como círculo + ponto. Diferente do checkbox, o radio nativo não pode ser desmarcado clicando de novo — o que torna certas capacidades (limpar, pré-seleção) exclusivas da sua natureza.",
    "base_congelada": "O desenho do controle: círculo size-4 rounded-full com border-input, cor primary quando marcado, ponto interno primary-foreground que escala ao selecionar, grid com gap-2, anel de foco (peer-focus-visible ring), e os estados disabled (opacity-50) e aria-invalid (destructive) já embutidos, com suporte RTL e semântica role=\"radiogroup\". Fonte, cor, cantos, tamanho do controle e espaçamento são imutáveis.",
    "props": [],
    "capacidades": [
        {
            "id": "t-tulo-descri-o-do-grupo",
            "nome": "Título + descrição do grupo",
            "descricao": "Rótulo acessível do grupo (vira o aria-labelledby do radiogroup) com subtítulo opcional acima das opções.",
            "controle": "select",
            "opcoes": [
                "nenhum",
                "só título",
                "título + descrição"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "descri-o-por-op-o",
            "nome": "Descrição por opção",
            "descricao": "Linha secundária de apoio abaixo do label de cada opção (ex.: o que cada plano/escolha inclui).",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "cone-por-op-o",
            "nome": "Ícone por opção",
            "descricao": "Ícone lucide aditivo junto ao label de cada opção, posicionável no início ou no fim.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "op-o-pr-selecionada",
            "nome": "Opção pré-selecionada",
            "descricao": "Define qual valor já vem marcado ao abrir (checked inicial) — ou nenhum.",
            "controle": "select",
            "opcoes": [
                "nenhuma",
                "primeira opção",
                "opção específica"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "limpar-sele-o",
            "nome": "Limpar seleção",
            "descricao": "Botão para desmarcar tudo e voltar ao estado sem escolha — algo que o radio nativo não permite clicando de novo.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "valida-o-obrigat-ria",
            "nome": "Validação obrigatória",
            "descricao": "Exige uma escolha antes de enviar; ativa o estado inválido (aria-invalid, já estilizado na base) e exibe mensagem de erro.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "desabilitar-op-es",
            "nome": "Desabilitar opções",
            "descricao": "Marca opções específicas como indisponíveis, usando o estado disabled já embutido na base.",
            "controle": "multi",
            "opcoes": [
                "1ª opção",
                "2ª opção",
                "3ª opção",
                "…"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (multi). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "orienta-o",
            "nome": "Orientação",
            "descricao": "Dispõe as opções em coluna ou em linha, ajustando aria-orientation e o eixo das setas do teclado.",
            "controle": "select",
            "opcoes": [
                "vertical",
                "horizontal"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "op-o-outro-campo-livre",
            "nome": "Opção \"Outro\" + campo livre",
            "descricao": "Uma opção que, ao ser escolhida, revela um input de texto para resposta livre; oculto e ignorado quando não selecionada.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/radio-group; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:RadioGroup />",
    "exemplo_demo": "<twig:RadioGroup class=\"w-fit\">\n    <div class=\"flex items-center gap-3\">\n        <twig:RadioGroup:Item id=\"r1\" name=\"spacing\" value=\"default\" />\n        <twig:Label for=\"r1\">Default</twig:Label>\n    </div>\n    <div class=\"flex items-center gap-3\">\n        <twig:RadioGroup:Item id=\"r2\" name=\"spacing\" value=\"comfortable\" checked />\n        <twig:Label for=\"r2\">Comfortable</twig:Label>\n    </div>\n    <div class=\"flex items-center gap-3\">\n        <twig:RadioGroup:Item id=\"r3\" name=\"spacing\" value=\"compact\" />\n        <twig:Label for=\"r3\">Compact</twig:Label>\n    </div>\n</twig:RadioGroup>",
    "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 desenho do controle: círculo size-4 rounded-full com border-input, cor primary quando marcado, ponto interno primary-foreground que escala ao selecionar, grid com gap-2, anel de foco (peer-focus-visible ring), e os estados disabled (opacity-50) e aria-invalid (destructive) já embutidos, com suporte RTL e semântica role=\"radiogroup\". Fonte, cor, cantos, tamanho do controle e espaçamento são imutáveis",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "radio-group"
        }
    },
    "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."
}