Resizable
templates/components/Resizable
O Resizable divide espaço entre painéis vizinhos por meio de um divisor (handle) arrastável e acessível por teclado; sua natureza é realocar proporção (flex-grow) entre um painel e o seguinte. Por isso os enriquecedores atuam sobre COMO o arrasto se comporta — limites, colapso, ancoragem, persistência e feedback — sem tocar na aparência do divisor nem no modelo de layout.
Base congelada
Capacidades 8
Conteúdo
Persistir layout
Salva as proporções escolhidas pelo usuário…
Comportamento
Limites de tamanho (mín/máx)
Define tamanho mínimo e máximo por…
Painel colapsável (snap)
Ao arrastar abaixo de um limiar,…
Botão colapsar/expandir no handle
Adiciona um chevron no divisor para…
Reset por duplo-clique
Duplo-clique no divisor devolve os painéis…
Medida ao arrastar
Exibe o tamanho ao vivo dos…
Pontos de ancoragem (snap)
Durante o arrasto o divisor 'gruda'…
Travar divisor
Fixa um handle específico como não-redimensionável…
Claude Code
Cole no Claude Code — ele acerta de primeira.
Resizable (UX) — Design System (Symfony UX Toolkit / shadcn)
BASE CONGELADA (não mude por instância): Congelado: o modelo de layout flex proporcional (`flex: size 1 0%`), o divisor de 1px (`bg-border`, `ring-offset-background`, `focus-visible:ring-ring`) com sua área de clique (`after:w-1`), o grip opcional (pílula `rounded-lg bg-border` via `withHandle`), os cursores `col-resize`/`row-resize`, os painéis com `overflow-auto`, a orientação horizontal/vertical (flex-direction) e o espelhamento RTL. A11y imutável: `role="separator"`, `tabindex="0"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px). Fonte, cores, cantos e espaçamento vêm dos tokens — nenhum enricher os altera.
USO: <twig:Resizable />
CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Limites de tamanho (mín/máx): Define tamanho mínimo e máximo por painel (px ou %); o arrasto e as setas respeitam os limites em vez do mínimo fixo de 20px. [number]- Painel colapsável (snap): Ao arrastar abaixo de um limiar, o painel encolhe até 0 e fecha; arrastar de volta o reabre no tamanho anterior (padrão sidebar). [toggle]- Botão colapsar/expandir no handle: Adiciona um chevron no divisor para colapsar/expandir o painel vizinho com um clique, além do arrasto. [select → Colapsa painel anterior / Colapsa painel seguinte / Dois botões (ambos)]- Reset por duplo-clique: Duplo-clique no divisor devolve os painéis vizinhos às proporções iniciais (o size declarado). [toggle]- Persistir layout: Salva as proporções escolhidas pelo usuário (localStorage, por id do grupo) e as restaura no próximo carregamento. [toggle]- Medida ao arrastar: Exibe o tamanho ao vivo dos painéis vizinhos enquanto o handle é arrastado; some ao soltar. [select → Porcentagem / Pixels / Porcentagem + pixels]- Pontos de ancoragem (snap): Durante o arrasto o divisor 'gruda' em proporções predefinidas, gerando layouts alinhados sem mira fina. [multi → 25% / 33% / 50% / 67% / 75%]- Travar divisor: Fixa um handle específico como não-redimensionável (divisor rígido), mantendo os demais arrastáveis. [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): A11y imutável: `role="separator"`, `tabindex="0"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px)
LLM / MCP
Via MCP: tool get_component com {"id": "resizable"} · list_capabilities("resizable").
Spec crua: config/ds-specs/resizable.json · Conectar o MCP.
Spec machine-readable (JSON)
{
"$schema_version": "1.0",
"id": "resizable",
"component": "Resizable",
"eixo": "ux",
"particularidade": "O Resizable divide espaço entre painéis vizinhos por meio de um divisor (handle) arrastável e acessível por teclado; sua natureza é realocar proporção (flex-grow) entre um painel e o seguinte. Por isso os enriquecedores atuam sobre COMO o arrasto se comporta — limites, colapso, ancoragem, persistência e feedback — sem tocar na aparência do divisor nem no modelo de layout.",
"base_congelada": "Congelado: o modelo de layout flex proporcional (`flex: size 1 0%`), o divisor de 1px (`bg-border`, `ring-offset-background`, `focus-visible:ring-ring`) com sua área de clique (`after:w-1`), o grip opcional (pílula `rounded-lg bg-border` via `withHandle`), os cursores `col-resize`/`row-resize`, os painéis com `overflow-auto`, a orientação horizontal/vertical (flex-direction) e o espelhamento RTL. A11y imutável: `role=\"separator\"`, `tabindex=\"0\"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px). Fonte, cores, cantos e espaçamento vêm dos tokens — nenhum enricher os altera.",
"props": [
{
"name": "orientation",
"type": "'horizontal'|'vertical'",
"default": "horizontal",
"description": "Layout direction. Defaults to `horizontal` #}"
}
],
"capacidades": [
{
"id": "limites-de-tamanho-m-n-m-x",
"nome": "Limites de tamanho (mín/máx)",
"descricao": "Define tamanho mínimo e máximo por painel (px ou %); o arrasto e as setas respeitam os limites em vez do mínimo fixo de 20px.",
"controle": "number",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (number). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "painel-colaps-vel-snap",
"nome": "Painel colapsável (snap)",
"descricao": "Ao arrastar abaixo de um limiar, o painel encolhe até 0 e fecha; arrastar de volta o reabre no tamanho anterior (padrão sidebar).",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "bot-o-colapsar-expandir-no-handle",
"nome": "Botão colapsar/expandir no handle",
"descricao": "Adiciona um chevron no divisor para colapsar/expandir o painel vizinho com um clique, além do arrasto.",
"controle": "select",
"opcoes": [
"Colapsa painel anterior",
"Colapsa painel seguinte",
"Dois botões (ambos)"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "reset-por-duplo-clique",
"nome": "Reset por duplo-clique",
"descricao": "Duplo-clique no divisor devolve os painéis vizinhos às proporções iniciais (o size declarado).",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "persistir-layout",
"nome": "Persistir layout",
"descricao": "Salva as proporções escolhidas pelo usuário (localStorage, por id do grupo) e as restaura no próximo carregamento.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "medida-ao-arrastar",
"nome": "Medida ao arrastar",
"descricao": "Exibe o tamanho ao vivo dos painéis vizinhos enquanto o handle é arrastado; some ao soltar.",
"controle": "select",
"opcoes": [
"Porcentagem",
"Pixels",
"Porcentagem + pixels"
],
"posicionavel": true,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "pontos-de-ancoragem-snap",
"nome": "Pontos de ancoragem (snap)",
"descricao": "Durante o arrasto o divisor 'gruda' em proporções predefinidas, gerando layouts alinhados sem mira fina.",
"controle": "multi",
"opcoes": [
"25%",
"33%",
"50%",
"67%",
"75%"
],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (multi). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
},
{
"id": "travar-divisor",
"nome": "Travar divisor",
"descricao": "Fixa um handle específico como não-redimensionável (divisor rígido), mantendo os demais arrastáveis.",
"controle": "toggle",
"opcoes": [],
"posicionavel": false,
"e_funcao": true,
"como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
}
],
"snippet_uso": "<twig:Resizable />",
"exemplo_demo": "<twig:Resizable orientation=\"horizontal\" class=\"max-w-sm rounded-lg border h-[200px]\">\n <twig:Resizable:Panel size=\"50\">\n <div class=\"flex h-full items-center justify-center p-6\">\n <span class=\"font-semibold\">One</span>\n </div>\n </twig:Resizable:Panel>\n <twig:Resizable:Handle withHandle />\n <twig:Resizable:Panel size=\"50\">\n <twig:Resizable orientation=\"vertical\">\n <twig:Resizable:Panel size=\"25\">\n <div class=\"flex h-full items-center justify-center p-6\">\n <span class=\"font-semibold\">Two</span>\n </div>\n </twig:Resizable:Panel>\n <twig:Resizable:Handle withHandle />\n <twig:Resizable:Panel size=\"75\">\n <div class=\"flex h-full items-center justify-center p-6\">\n <span class=\"font-semibold\">Three</span>\n </div>\n </twig:Resizable:Panel>\n </twig:Resizable>\n </twig:Resizable:Panel>\n</twig:Resizable>",
"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": "A11y imutável: `role=\"separator\"`, `tabindex=\"0\"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px)",
"mcp": {
"tool": "get_component",
"args": {
"id": "resizable"
}
}
}