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
Capacidades 7
Conteúdo
Rótulos de estado
Mostra por extenso o estado atual…
Ícone por estado
Coloca um ícone dentro do polegar…
Bloqueio por permissão
Trava a alternância quando o usuário…
Comportamento
Commit assíncrono
Para switches que salvam no servidor:…
Confirmar antes de alternar
Pede confirmação antes de commitar a…
Persistir preferência
Lembra o estado do switch entre…
Atalho de teclado
Vincula uma combinação de teclas que…
Claude Code
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
LLM / MCP
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"
}
}
}