Pular para o conteúdo

Switch

templates/components/Switch

Um controle binário liga/desliga para UM ajuste booleano que, ao contrário do checkbox, normalmente COMMITA na hora e representa um modo/estado (ex.: Modo avião, Notificações). Seus dois estados são semanticamente significativos (ligado/desligado) e ele quase sempre anda ao lado de um Label. Todo enriquecedor tem que respeitar essa natureza binária, de aplicação imediata e portadora-de-estado.

Base congelada

Conteúdo

Rótulos de estado

Mostra por extenso o estado atual…

Mostra por extenso o estado atual ao lado do switch (ex.: Ligado/Desligado, Sim/Não), refletindo on/off em tempo real. Texto adjacente e composável, não mexe na trilha nem no polegar. posicionável

Ícone por estado

Coloca um ícone dentro do polegar…

Coloca um ícone dentro do polegar indicando ligado vs desligado (padrão clássico de troca de tema sol/lua, ou check/x). Conteúdo aditivo via ux_icon, escolhido pelo usuário.

Bloqueio por permissão

Trava a alternância quando o usuário…

Trava a alternância quando o usuário não tem direito de mudar o ajuste (feature-gate/plano), exibindo um cadeado e um tooltip explicativo — distinto de disabled: impede o toque e explica o porquê, sem redefinir o visual da base.

Comportamento

Commit assíncrono

Para switches que salvam no servidor:…

Para switches que salvam no servidor: modo Otimista aplica já e reverte se a chamada falhar (marcando aria-invalid da base); modo Pendente mostra spinner no polegar e bloqueia novo toque até o servidor confirmar. Puro comportamento, overlay transitório.

Confirmar antes de alternar

Pede confirmação antes de commitar a…

Pede confirmação antes de commitar a mudança e reverte se o usuário cancelar. Configurável por direção, porque num switch o lado perigoso costuma ser só um (ex.: desligar 2FA). Comportamental.

Persistir preferência

Lembra o estado do switch entre…

Lembra o estado do switch entre recarregamentos/navegações, gravando o on/off no armazenamento escolhido e reidratando ao carregar. Ideal para preferências (tema, densidade). Comportamental, não toca no visual.

Atalho de teclado

Vincula uma combinação de teclas que…

Vincula uma combinação de teclas que alterna o switch de qualquer lugar da tela (ex.: Cmd+Shift+D para modo escuro), mantendo foco e semântica role=switch. Só comportamento.
Código gerado

Cole no Claude Code — ele acerta de primeira.

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

BASE CONGELADA (não mude por instância): A trilha em pílula (rounded-full, border-transparent) e o polegar circular (thumb) com sua animação translate; os DOIS únicos tamanhos permitidos (default 18.4x32px / sm 14x24px) já expostos pela prop `size` — nenhum outro tamanho existe; as cores de estado (off = bg-input; on = has-checked:bg-primary; thumb = bg-background/primary-foreground); o anel de foco (has-focus-visible:ring-3 ring-ring/50); o estado invalido (has-[input[aria-invalid]] com destructive); a opacidade de disabled (has-disabled:opacity-50); o suporte RTL do deslocamento; e a semantica congelada: `<input type=checkbox role=switch>` nativo por baixo. Cor, fonte, cantos, espacamento e a11y de base sao imutaveis.

USO: <twig:Switch size="default" />

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Rótulos de estado: Mostra por extenso o estado atual ao lado do switch (ex.: Ligado/Desligado, Sim/Não), refletindo on/off em tempo real. Texto adjacente e composável, não mexe na trilha nem no polegar. [select → Ligado / Desligado / Sim / Não / Ativo / Inativo / On / Off / I / O]- Ícone por estado: Coloca um ícone dentro do polegar indicando ligado vs desligado (padrão clássico de troca de tema sol/lua, ou check/x). Conteúdo aditivo via ux_icon, escolhido pelo usuário. [select → Check / X / Sol / Lua / Olho / Olho riscado / Cadeado aberto / fechado / Nenhum]- Commit assíncrono: Para switches que salvam no servidor: modo Otimista aplica já e reverte se a chamada falhar (marcando aria-invalid da base); modo Pendente mostra spinner no polegar e bloqueia novo toque até o servidor confirmar. Puro comportamento, overlay transitório. [select → Otimista (aplica já, reverte se falhar) / Pendente (spinner até confirmar)]- Confirmar antes de alternar: Pede confirmação antes de commitar a mudança e reverte se o usuário cancelar. Configurável por direção, porque num switch o lado perigoso costuma ser só um (ex.: desligar 2FA). Comportamental. [select → Ao desligar / Ao ligar / Sempre]- Persistir preferência: Lembra o estado do switch entre recarregamentos/navegações, gravando o on/off no armazenamento escolhido e reidratando ao carregar. Ideal para preferências (tema, densidade). Comportamental, não toca no visual. [select → localStorage / Cookie / Sessão]- Atalho de teclado: Vincula uma combinação de teclas que alterna o switch de qualquer lugar da tela (ex.: Cmd+Shift+D para modo escuro), mantendo foco e semântica role=switch. Só comportamento. [text]- Bloqueio por permissão: Trava a alternância quando o usuário não tem direito de mudar o ajuste (feature-gate/plano), exibindo um cadeado e um tooltip explicativo — distinto de disabled: impede o toque e explica o porquê, sem redefinir o visual da base. [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 anel de foco (has-focus-visible:ring-3 ring-ring/50). o estado invalido (has-[input[aria-invalid]] com destructive). e a semantica congelada: `<input type=checkbox role=switch>` nativo por baixo

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

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

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "switch",
    "component": "Switch",
    "eixo": "ui",
    "particularidade": "Um controle binário liga/desliga para UM ajuste booleano que, ao contrário do checkbox, normalmente COMMITA na hora e representa um modo/estado (ex.: Modo avião, Notificações). Seus dois estados são semanticamente significativos (ligado/desligado) e ele quase sempre anda ao lado de um Label. Todo enriquecedor tem que respeitar essa natureza binária, de aplicação imediata e portadora-de-estado.",
    "base_congelada": "A trilha em pílula (rounded-full, border-transparent) e o polegar circular (thumb) com sua animação translate; os DOIS únicos tamanhos permitidos (default 18.4x32px / sm 14x24px) já expostos pela prop `size` — nenhum outro tamanho existe; as cores de estado (off = bg-input; on = has-checked:bg-primary; thumb = bg-background/primary-foreground); o anel de foco (has-focus-visible:ring-3 ring-ring/50); o estado invalido (has-[input[aria-invalid]] com destructive); a opacidade de disabled (has-disabled:opacity-50); o suporte RTL do deslocamento; e a semantica congelada: `<input type=checkbox role=switch>` nativo por baixo. Cor, fonte, cantos, espacamento e a11y de base sao imutaveis.",
    "props": [
        {
            "name": "size",
            "type": "'default'|'sm'",
            "default": "default",
            "description": "The switch size. Defaults to `default` #}"
        },
        {
            "name": "checked",
            "type": "boolean",
            "default": "false",
            "description": "Whether the switch is initially checked. Defaults to `false` #}"
        }
    ],
    "capacidades": [
        {
            "id": "r-tulos-de-estado",
            "nome": "Rótulos de estado",
            "descricao": "Mostra por extenso o estado atual ao lado do switch (ex.: Ligado/Desligado, Sim/Não), refletindo on/off em tempo real. Texto adjacente e composável, não mexe na trilha nem no polegar.",
            "controle": "select",
            "opcoes": [
                "Ligado / Desligado",
                "Sim / Não",
                "Ativo / Inativo",
                "On / Off",
                "I / O"
            ],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "cone-por-estado",
            "nome": "Ícone por estado",
            "descricao": "Coloca um ícone dentro do polegar indicando ligado vs desligado (padrão clássico de troca de tema sol/lua, ou check/x). Conteúdo aditivo via ux_icon, escolhido pelo usuário.",
            "controle": "select",
            "opcoes": [
                "Check / X",
                "Sol / Lua",
                "Olho / Olho riscado",
                "Cadeado aberto / fechado",
                "Nenhum"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "commit-ass-ncrono",
            "nome": "Commit assíncrono",
            "descricao": "Para switches que salvam no servidor: modo Otimista aplica já e reverte se a chamada falhar (marcando aria-invalid da base); modo Pendente mostra spinner no polegar e bloqueia novo toque até o servidor confirmar. Puro comportamento, overlay transitório.",
            "controle": "select",
            "opcoes": [
                "Otimista (aplica já, reverte se falhar)",
                "Pendente (spinner até confirmar)"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/switch; 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 commitar a mudança e reverte se o usuário cancelar. Configurável por direção, porque num switch o lado perigoso costuma ser só um (ex.: desligar 2FA). Comportamental.",
            "controle": "select",
            "opcoes": [
                "Ao desligar",
                "Ao ligar",
                "Sempre"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "persistir-prefer-ncia",
            "nome": "Persistir preferência",
            "descricao": "Lembra o estado do switch entre recarregamentos/navegações, gravando o on/off no armazenamento escolhido e reidratando ao carregar. Ideal para preferências (tema, densidade). Comportamental, não toca no visual.",
            "controle": "select",
            "opcoes": [
                "localStorage",
                "Cookie",
                "Sessão"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "atalho-de-teclado",
            "nome": "Atalho de teclado",
            "descricao": "Vincula uma combinação de teclas que alterna o switch de qualquer lugar da tela (ex.: Cmd+Shift+D para modo escuro), mantendo foco e semântica role=switch. Só comportamento.",
            "controle": "text",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (text). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "bloqueio-por-permiss-o",
            "nome": "Bloqueio por permissão",
            "descricao": "Trava a alternância quando o usuário não tem direito de mudar o ajuste (feature-gate/plano), exibindo um cadeado e um tooltip explicativo — distinto de disabled: impede o toque e explica o porquê, sem redefinir o visual da base.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/switch; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:Switch size=\"default\" />",
    "exemplo_demo": "<div class=\"flex items-center space-x-2\">\n    <twig:Switch id=\"airplane-mode\" />\n    <twig:Label for=\"airplane-mode\">Airplane Mode</twig:Label>\n</div>",
    "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 anel de foco (has-focus-visible:ring-3 ring-ring/50). o estado invalido (has-[input[aria-invalid]] com destructive). e a semantica congelada: `<input type=checkbox role=switch>` nativo por baixo",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "switch"
        }
    }
}