@softize/opus 21.0.1 → 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/bin/lib/check.mjs +31 -28
- package/docs/adr/0004-page-content-state-is-composed.md +3 -2
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +3 -0
- package/docs/adr/0006-semantic-context-precedes-visual-variant.md +23 -14
- package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +5 -0
- package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +7 -4
- package/docs/adr/0016-list-collection-header-belongs-to-content.md +13 -8
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +27 -26
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +1 -0
- package/docs/adr/0029-page-header-is-the-single-page-introduction.md +71 -0
- package/docs/adr/0030-framed-content-can-use-sectioned-anatomy.md +89 -0
- package/docs/adr/0031-detail-dividers-own-the-tabular-label-treatment.md +61 -0
- package/docs/adr/0032-tab-baseline-respects-the-container-inset.md +59 -0
- package/docs/adr/0033-drawer-uses-an-inset-structural-frame.md +72 -0
- package/docs/adr/0034-tabs-can-own-a-frame-without-owning-a-surface.md +60 -0
- package/docs/adr/0035-button-group-separates-frame-relationship-and-distribution.md +63 -0
- package/docs/adr/0036-tabs-declares-trigger-distribution.md +58 -0
- package/docs/radius-scale.md +5 -2
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/SKILL.md +9 -6
- package/registry/skills/build-opus-ui/references/evaluations.md +5 -2
- package/registry/skills/build-opus-ui/references/ui-patterns.md +14 -16
- package/src/ui/components/patterns/content-header.tsx +51 -13
- package/src/ui/components/patterns/list.tsx +51 -33
- package/src/ui/components/patterns/page.tsx +139 -251
- package/src/ui/components/patterns/presentation.tsx +2 -6
- package/src/ui/components/primitives/button-group.tsx +80 -8
- package/src/ui/components/primitives/button.tsx +85 -60
- package/src/ui/components/primitives/detail.tsx +63 -57
- package/src/ui/components/primitives/dialog.tsx +8 -11
- package/src/ui/components/primitives/drawer.tsx +16 -12
- package/src/ui/components/primitives/tabs.tsx +123 -54
- package/src/ui/docs/content/button.md +55 -29
- package/src/ui/docs/content/content.md +61 -36
- package/src/ui/docs/content/detail.md +28 -19
- package/src/ui/docs/content/drawer.md +53 -47
- package/src/ui/docs/content/input.md +51 -35
- package/src/ui/docs/content/page.md +84 -95
- package/src/ui/docs/content/pane.md +83 -63
- package/src/ui/docs/content/presentation.md +20 -20
- package/src/ui/docs/content/tabs.md +107 -36
- package/src/ui/meta.ts +5 -5
- package/src/ui/react.tsx +0 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ADR 0030 — Content com moldura pode usar anatomia seccionada
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Substitui parcialmente:** ADR 0023, nas afirmações de que `Content` não expõe `layout` e é
|
|
6
|
+
sempre contínuo.
|
|
7
|
+
- **Complementa:** ADR 0025.
|
|
8
|
+
|
|
9
|
+
## Contexto
|
|
10
|
+
|
|
11
|
+
`Content` já representa uma região nomeada como `section` ou `fieldset`, com header, body e moldura
|
|
12
|
+
opcional. Quando o body é uma estrutura que possui o próprio recuo — como `DetailGroup` com
|
|
13
|
+
divisores, uma lista ou uma tabela — o acabamento contínuo acrescenta padding ao redor dessa
|
|
14
|
+
estrutura e impede que suas linhas alcancem a moldura.
|
|
15
|
+
|
|
16
|
+
O protótipo do detalhe de Veículo no Evento de Pós-vendas confirmou uma composição útil: header com
|
|
17
|
+
mídia, título, recuo de `0.75rem` e divisor; body sem recuo; conteúdo interno responsável por suas
|
|
18
|
+
células. Produzir esse desenho exigiu classes no consumidor para remover o padding da raiz, recortar
|
|
19
|
+
os cantos e reconstruir o header.
|
|
20
|
+
|
|
21
|
+
As forças em tensão são:
|
|
22
|
+
|
|
23
|
+
- reutilizar o vocabulário `continuous | sectioned` de Card, Dialog, Drawer e Pane;
|
|
24
|
+
- preservar `Content` como região semântica interna, sem transformá-lo em Card;
|
|
25
|
+
- manter `frameVariant="legend"` como acabamento da borda, sem misturá-lo à anatomia;
|
|
26
|
+
- impedir combinações em que o body encosta nas bordas sem existir uma moldura para recortá-lo.
|
|
27
|
+
|
|
28
|
+
## Decisão
|
|
29
|
+
|
|
30
|
+
`Content` passa a aceitar `layout="continuous" | "sectioned"`, com `continuous` como default.
|
|
31
|
+
|
|
32
|
+
O layout `sectioned` exige `framed` e forma um pacote indivisível:
|
|
33
|
+
|
|
34
|
+
- A raiz desenha a moldura, recorta os cantos e não aplica padding geral.
|
|
35
|
+
- `ContentHeader` aplica `p-3` e o divisor inferior.
|
|
36
|
+
- `ContentBody` não aplica padding; seu filho direto responde pelo recuo interno.
|
|
37
|
+
- Shorthand e composição explícita produzem a mesma anatomia.
|
|
38
|
+
- `ContentMedia`, título, descrição, contador e ações preservam seus contratos atuais.
|
|
39
|
+
|
|
40
|
+
`frameVariant` continua escolhendo somente o acabamento da moldura contínua. A combinação
|
|
41
|
+
`layout="sectioned"` com `frameVariant="legend"` é inválida: o primeiro coloca o header dentro da
|
|
42
|
+
moldura, enquanto o segundo o integra ao traço externo.
|
|
43
|
+
|
|
44
|
+
O layout não altera a semântica. `as="section"` continua emitindo section com heading e
|
|
45
|
+
`as="fieldset"` continua emitindo fieldset com legend nativa.
|
|
46
|
+
|
|
47
|
+
### Emenda de 2026-09-28 — a moldura usa o raio das superfícies estruturais
|
|
48
|
+
|
|
49
|
+
Quando `framed` está ativo, `Content` desenha uma moldura estrutural completa e usa `rounded-xl`,
|
|
50
|
+
o mesmo degrau de `Card`. A semântica das duas famílias continua diferente, mas essa diferença não
|
|
51
|
+
produz uma geometria externa menor. Em composições aninhadas, evitar a moldura dupla em vez de
|
|
52
|
+
reduzir globalmente o raio de `Content`.
|
|
53
|
+
|
|
54
|
+
## Consequências
|
|
55
|
+
|
|
56
|
+
- DetailGroup, listas e tabelas podem ocupar o body de ponta a ponta sem classes estruturais no
|
|
57
|
+
consumidor.
|
|
58
|
+
- Conteúdo livre que precisa de recuo continua usando o layout `continuous`, ou declara sua própria
|
|
59
|
+
região interna quando o body de ponta a ponta for deliberado.
|
|
60
|
+
- `Content` passa a compartilhar o vocabulário de anatomia das demais superfícies, mas continua sem
|
|
61
|
+
footer e sem assumir o papel de uma superfície autônoma.
|
|
62
|
+
- A API rejeita `sectioned` sem `framed` e rejeita a combinação com `legend`.
|
|
63
|
+
|
|
64
|
+
## Alternativas consideradas
|
|
65
|
+
|
|
66
|
+
### Adicionar `frameVariant="sectioned"`
|
|
67
|
+
|
|
68
|
+
Foi descartado porque misturaria anatomia de regiões com o acabamento da linha externa. Card,
|
|
69
|
+
Dialog, Drawer e Pane já usam `layout` para essa decisão.
|
|
70
|
+
|
|
71
|
+
### Criar propriedades de borda e padding
|
|
72
|
+
|
|
73
|
+
Propriedades como `headerDivider` e `bodyInset` permitiriam combinações arbitrárias e devolveriam a
|
|
74
|
+
anatomia ao consumidor. O resultado validado é um pacote, não dois controles independentes.
|
|
75
|
+
|
|
76
|
+
### Criar DetailCard
|
|
77
|
+
|
|
78
|
+
Foi descartado porque duplicaria a semântica e os slots de `Content` para um único tipo de filho.
|
|
79
|
+
A anatomia também é válida para listas e tabelas.
|
|
80
|
+
|
|
81
|
+
## Verificação
|
|
82
|
+
|
|
83
|
+
- Testes de UI cobrem shorthand, composição explícita, section e fieldset.
|
|
84
|
+
- Testes confirmam header com divisor e `p-3`, body sem padding e raiz com recorte.
|
|
85
|
+
- Testes confirmam `rounded-xl` nas molduras `default`, `legend` e `sectioned`.
|
|
86
|
+
- Testes reprovam `sectioned` sem moldura e a combinação com `frameVariant="legend"`.
|
|
87
|
+
- A documentação demonstra o caso com `DetailGroup` e diferencia anatomia de acabamento.
|
|
88
|
+
- O consumidor de Pós-vendas usa somente props públicas do Opus, sem classes que reconstruam a
|
|
89
|
+
anatomia.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ADR 0031 — Divisórias de DetailGroup definem o tratamento tabular dos rótulos
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
|
|
6
|
+
## Contexto
|
|
7
|
+
|
|
8
|
+
`DetailGroup` separa três decisões: orientação dos pares, divisórias internas e moldura externa.
|
|
9
|
+
Apesar disso, o fundo sutil dos rótulos horizontais era ativado por `variant="framed"`. Assim, a
|
|
10
|
+
mesma grade com divisórias mudava de aparência ao ser hospedada por um `Content` seccionado, onde a
|
|
11
|
+
moldura pertence ao `Content`, e um grupo independente, onde a moldura pertence ao próprio
|
|
12
|
+
`DetailGroup`.
|
|
13
|
+
|
|
14
|
+
O fundo do rótulo é um acabamento da grade tabular: ele ajuda a distinguir chave e valor quando os
|
|
15
|
+
campos formam células conectadas. Não comunica que o grupo possui uma borda externa.
|
|
16
|
+
|
|
17
|
+
## Decisão
|
|
18
|
+
|
|
19
|
+
Na orientação horizontal, `dividers` ativa a composição tabular completa de cada campo:
|
|
20
|
+
|
|
21
|
+
- rótulo e valor recebem padding próprio;
|
|
22
|
+
- a coluna de rótulos usa fundo sutil;
|
|
23
|
+
- a largura padrão dos rótulos passa a `8.5rem`;
|
|
24
|
+
- as divisórias continuam delimitando as células.
|
|
25
|
+
|
|
26
|
+
`variant="framed"` controla somente a moldura externa do grupo. Sem `dividers`, um grupo horizontal
|
|
27
|
+
emoldurado mantém rótulo e valor sobre o mesmo fundo. Na orientação vertical, `dividers` continua
|
|
28
|
+
adicionando somente as separações entre campos, sem pintar o rótulo.
|
|
29
|
+
|
|
30
|
+
## Consequências
|
|
31
|
+
|
|
32
|
+
- Grades horizontais com divisórias têm a mesma aparência dentro de `Content layout="sectioned"`
|
|
33
|
+
e como bloco principal independente.
|
|
34
|
+
- Moldura externa, orientação e acabamento tabular permanecem decisões combináveis.
|
|
35
|
+
- Não é necessária uma nova prop apenas para reproduzir um enfeite que já acompanha a grade.
|
|
36
|
+
- Consumidores que usavam `framed + horizontal` sem divisórias deixam de receber fundo no rótulo.
|
|
37
|
+
|
|
38
|
+
## Alternativas consideradas
|
|
39
|
+
|
|
40
|
+
### Manter o fundo associado a `framed`
|
|
41
|
+
|
|
42
|
+
Preservaria o comportamento atual, mas continuaria fazendo a moldura externa alterar o interior da
|
|
43
|
+
grade e manteria inconsistentes os grupos hospedados por outra superfície.
|
|
44
|
+
|
|
45
|
+
### Criar `labelVariant="muted"`
|
|
46
|
+
|
|
47
|
+
Tornaria o acabamento explícito, mas adicionaria uma decisão a todos os consumidores sem haver um
|
|
48
|
+
caso de produto que precise variar o fundo independentemente das divisórias.
|
|
49
|
+
|
|
50
|
+
### Aplicar o fundo a toda orientação horizontal
|
|
51
|
+
|
|
52
|
+
Uniformizaria os rótulos, mas daria aparência tabular também a grupos simples, sem células ou
|
|
53
|
+
divisórias que sustentem essa leitura.
|
|
54
|
+
|
|
55
|
+
## Verificação
|
|
56
|
+
|
|
57
|
+
- Testes de UI confirmam fundo e padding separados em `horizontal + dividers`, com e sem moldura.
|
|
58
|
+
- Testes de UI confirmam ausência de fundo em `horizontal + framed` sem divisórias.
|
|
59
|
+
- Testes de UI confirmam que divisórias verticais não pintam o rótulo.
|
|
60
|
+
- A documentação de `DetailGroup` descreve `framed`, `dividers` e `orientation` como eixos
|
|
61
|
+
independentes.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ADR 0032 — A linha-base das abas respeita o recuo do contêiner
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Relacionada:** ADR 0023, quanto ao alinhamento horizontal dentro de superfícies.
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
A variante horizontal `line` de `TabsList` estendia a linha-base para fora da própria lista usando
|
|
10
|
+
o recuo de `DrawerBody` e `DialogBody`. Os rótulos permaneciam alinhados ao conteúdo, mas a linha
|
|
11
|
+
alcançava as laterais da superfície e rompia o contorno visual formado pelo corpo.
|
|
12
|
+
|
|
13
|
+
A tela de detalhes do Evento tornou a divergência evidente: cards, detalhes e conteúdo respeitam o
|
|
14
|
+
mesmo recuo lateral, enquanto apenas a navegação por abas terminava nas bordas do drawer.
|
|
15
|
+
|
|
16
|
+
## Decisão
|
|
17
|
+
|
|
18
|
+
A linha-base horizontal ocupa somente a largura de `TabsList`. Ela respeita o recuo aplicado pelo
|
|
19
|
+
contêiner e acompanha o alinhamento do corpo em qualquer superfície.
|
|
20
|
+
|
|
21
|
+
`DrawerBody` e `DialogBody` deixam de publicar uma variável de gutter exclusiva para Tabs. O recuo
|
|
22
|
+
continua pertencendo à anatomia da superfície, e Tabs apenas ocupa a largura que recebe no fluxo.
|
|
23
|
+
|
|
24
|
+
### Emenda de 2026-09-28 — a aba combina uma área de botão e o indicador
|
|
25
|
+
|
|
26
|
+
Na variante horizontal `line`, o gatilho mantém duas camadas: uma área interna com os estados
|
|
27
|
+
interativos e a mesma altura de um Button do tamanho correspondente, e o indicador ancorado na
|
|
28
|
+
linha-base da lista. A área interna permanece transparente e recebe fundo apenas no hover; a aba
|
|
29
|
+
ativa se distingue pelo indicador primário. Um respiro de `0.375rem` separa as duas camadas no uso
|
|
30
|
+
comum; dentro de `Tabs framed`, ele aumenta para `0.5rem` e acompanha o padding superior da moldura.
|
|
31
|
+
|
|
32
|
+
Essa apresentação pertence ao Opus: consumidores escolhem `variant="line"` e não repetem classes
|
|
33
|
+
de fundo, raio, padding ou interação para obter o estado selecionado.
|
|
34
|
+
|
|
35
|
+
## Consequências
|
|
36
|
+
|
|
37
|
+
- A linha e os rótulos das abas compartilham os limites horizontais do corpo.
|
|
38
|
+
- A seleção permanece indicada pela linha inferior, sem manter um segundo fundo persistente.
|
|
39
|
+
- Drawer e Dialog não mantêm uma integração implícita com Tabs.
|
|
40
|
+
- Um consumidor que realmente precise de uma linha até as bordas deve compor essa apresentação de
|
|
41
|
+
forma explícita, pois ela não é mais o comportamento padrão da variante `line`.
|
|
42
|
+
|
|
43
|
+
## Alternativas descartadas
|
|
44
|
+
|
|
45
|
+
### Manter a linha até as bordas
|
|
46
|
+
|
|
47
|
+
Preservaria o comportamento anterior, mas manteria uma exceção visual exclusiva das abas e uma
|
|
48
|
+
dependência oculta entre as primitivas de corpo e Tabs.
|
|
49
|
+
|
|
50
|
+
### Adicionar uma propriedade de full bleed
|
|
51
|
+
|
|
52
|
+
Permitiria escolher os dois comportamentos, mas aumentaria a API para preservar uma apresentação
|
|
53
|
+
sem consumidor conhecido. A composição explícita é suficiente caso essa necessidade apareça.
|
|
54
|
+
|
|
55
|
+
## Verificação
|
|
56
|
+
|
|
57
|
+
O teste da variante `line` verifica que a linha usa `inset-inline: 0`, inclusive quando Tabs está
|
|
58
|
+
dentro de `DrawerBody`. O mesmo teste verifica a separação estrutural entre a superfície interna e
|
|
59
|
+
o indicador da aba ativa. A documentação viva demonstra a variante com os limites do contêiner.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ADR 0033 — Drawer usa uma moldura estrutural recuada
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Substitui:** a decisão da ADR 0005 que prendia a moldura externa do Drawer à borda da viewport.
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
O `Drawer` ocupava toda a altura ou largura da viewport e desenhava somente a borda voltada para o
|
|
10
|
+
conteúdo. No consumidor, o shell principal já delimita as superfícies de trabalho com recuo de
|
|
11
|
+
`0.5rem`, borda completa, fundo base e raio `xl`. Ao abrir um Drawer sobre esse shell, o painel
|
|
12
|
+
voltava à geometria antiga de uma lâmina presa à janela: perdia os cantos, encostava em três bordas
|
|
13
|
+
e não parecia pertencer ao mesmo sistema de molduras.
|
|
14
|
+
|
|
15
|
+
O painel continua precisando comunicar de qual lado entra, preservar sua animação e aproveitar a
|
|
16
|
+
altura disponível. Ao mesmo tempo, a moldura não pode ser reconstruída por cada consumidor, porque
|
|
17
|
+
posição, recorte e animação pertencem à primitive.
|
|
18
|
+
|
|
19
|
+
## Decisão
|
|
20
|
+
|
|
21
|
+
`DrawerContent` passa a ser uma superfície recuada da viewport em todos os lados:
|
|
22
|
+
|
|
23
|
+
- usa `0.25rem` de distância das bordas aplicáveis;
|
|
24
|
+
- usa borda completa, fundo base, `rounded-xl` e `overflow-hidden`;
|
|
25
|
+
- calcula a dimensão que antes ocupava `100%` descontando o recuo dos dois lados;
|
|
26
|
+
- mantém `side` responsável pela direção, dimensão principal e animação de entrada e saída;
|
|
27
|
+
- mantém largura e altura extensíveis por `className`, como antes.
|
|
28
|
+
|
|
29
|
+
O princípio durável é que uma superfície modal lateral possui moldura própria e não se confunde
|
|
30
|
+
com a viewport. O valor do recuo e o degrau de raio continuam materializações substituíveis da
|
|
31
|
+
escala visual vigente.
|
|
32
|
+
|
|
33
|
+
## Consequências
|
|
34
|
+
|
|
35
|
+
- Drawer e shell passam a compartilhar a mesma leitura de moldura estrutural.
|
|
36
|
+
- Header, body e footer full-bleed são recortados pelos cantos do painel.
|
|
37
|
+
- A área útil do Drawer perde `0.5rem` no eixo que antes preenchia a viewport.
|
|
38
|
+
- Consumidores deixam de precisar adicionar borda, raio ou margem para obter essa composição.
|
|
39
|
+
- A mudança visual alcança `top`, `right`, `bottom` e `left`, evitando quatro geometrias distintas
|
|
40
|
+
para a mesma primitive.
|
|
41
|
+
|
|
42
|
+
## Alternativas descartadas
|
|
43
|
+
|
|
44
|
+
### Manter o Drawer preso à viewport
|
|
45
|
+
|
|
46
|
+
Preservaria a geometria histórica, mas manteria a divergência mais visível justamente quando o
|
|
47
|
+
painel aparece sobre um shell emoldurado.
|
|
48
|
+
|
|
49
|
+
### Ajustar somente o Drawer do consumidor
|
|
50
|
+
|
|
51
|
+
Validaria a tela atual com menor alcance, mas espalharia posição e recorte por `className` e
|
|
52
|
+
deixaria outros Drawers fora do mesmo contrato visual.
|
|
53
|
+
|
|
54
|
+
### Arredondar apenas os cantos expostos
|
|
55
|
+
|
|
56
|
+
Manteria o painel preso à borda de entrada, mas produziria uma moldura incompleta e continuaria sem
|
|
57
|
+
o respiro que distingue a superfície da viewport.
|
|
58
|
+
|
|
59
|
+
### Alterar somente o lado direito
|
|
60
|
+
|
|
61
|
+
Resolveria o caso mais frequente, mas faria `side` trocar também o contrato visual da superfície,
|
|
62
|
+
sem uma razão de produto para essa diferença.
|
|
63
|
+
|
|
64
|
+
## Verificação
|
|
65
|
+
|
|
66
|
+
- Testes de `DrawerContent` verificam borda completa, raio, recorte e recuo para os quatro valores
|
|
67
|
+
de `side`.
|
|
68
|
+
- Os testes existentes continuam cobrindo anatomia interna, fechamento e animação.
|
|
69
|
+
- A documentação do Drawer descreve a moldura recuada e continua demonstrando a extensão de
|
|
70
|
+
largura por `className`.
|
|
71
|
+
- Um teste do consumidor confirma que o Drawer de Evento usa o default do Opus, sem reconstruir a
|
|
72
|
+
moldura localmente.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ADR 0034 — Tabs pode ter moldura sem criar uma superfície
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Relacionadas:** ADR 0023, quanto à anatomia das superfícies; ADR 0032, quanto à linha-base das abas.
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
Uma composição em que a navegação por abas e seus painéis formam um único bloco precisa de borda,
|
|
10
|
+
raio e recorte compartilhados. Até aqui, consumidores obtinham essa forma envolvendo `Tabs` em um
|
|
11
|
+
`Card`.
|
|
12
|
+
|
|
13
|
+
O `Card`, porém, não é apenas uma moldura: ele aplica `bg-card text-card-foreground` e cria uma
|
|
14
|
+
superfície semântica própria. Em um `Drawer`, essa superfície pode se distinguir do
|
|
15
|
+
`bg-background`, especialmente no tema escuro, embora a intenção seja somente delimitar as abas
|
|
16
|
+
dentro da superfície já existente.
|
|
17
|
+
|
|
18
|
+
## Decisão
|
|
19
|
+
|
|
20
|
+
`Tabs` aceita a propriedade booleana `framed`, desativada por padrão.
|
|
21
|
+
|
|
22
|
+
Quando ativa, `framed` aplica ao root a moldura estrutural canônica para esse bloco: borda, raio
|
|
23
|
+
`rounded-xl` e recorte do conteúdo. Ela não aplica token de fundo nem de foreground. O conjunto
|
|
24
|
+
herda a superfície do contêiner em que foi composto.
|
|
25
|
+
|
|
26
|
+
Essa propriedade não altera a apresentação de `TabsList`, a seleção, o tamanho nem a orientação. O
|
|
27
|
+
consumidor continua escolhendo esses comportamentos nas APIs existentes, inclusive
|
|
28
|
+
`TabsList variant="line"`.
|
|
29
|
+
|
|
30
|
+
## Consequências
|
|
31
|
+
|
|
32
|
+
- Abas podem formar um bloco único sem serem promovidas a `Card`.
|
|
33
|
+
- Em Drawer, Dialog ou Pane, a moldura acompanha o fundo da superfície externa nos temas claro e
|
|
34
|
+
escuro.
|
|
35
|
+
- `Card` continua correto quando as abas realmente precisarem de uma superfície semântica própria.
|
|
36
|
+
- `framed` permanece independente de `TabsList variant`, que descreve apenas a apresentação da
|
|
37
|
+
lista.
|
|
38
|
+
|
|
39
|
+
## Alternativas descartadas
|
|
40
|
+
|
|
41
|
+
### Manter Card como wrapper obrigatório
|
|
42
|
+
|
|
43
|
+
Preservaria a API atual, mas continuaria acoplando uma necessidade estrutural ao token de superfície
|
|
44
|
+
do Card.
|
|
45
|
+
|
|
46
|
+
### Aplicar classes no consumidor
|
|
47
|
+
|
|
48
|
+
Resolveria uma tela, mas repetiria borda, raio e clipping sem registrar o contrato visual da
|
|
49
|
+
composição.
|
|
50
|
+
|
|
51
|
+
### Fazer toda Tabs ser emoldurada
|
|
52
|
+
|
|
53
|
+
Removeria a escolha, mas quebraria usos em que Tabs participa do fluxo de uma página ou toolbar sem
|
|
54
|
+
formar um bloco autônomo.
|
|
55
|
+
|
|
56
|
+
## Verificação
|
|
57
|
+
|
|
58
|
+
Testes da primitiva verificam que o padrão permanece sem moldura e que `framed` aplica borda, raio e
|
|
59
|
+
recorte sem qualquer classe de fundo. O teste da view de Evento verifica que a composição usa o
|
|
60
|
+
root emoldurado diretamente, sem um `Card` intermediário.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ADR 0035 — ButtonGroup separa moldura, relação e distribuição
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Relacionadas:** ADR 0015, quanto à escala dos controles; ADR 0017, quanto à distribuição igual explícita.
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
Um conjunto de ações do Evento foi composto com `Card`, `ButtonGroup mode="spaced"`, separadores
|
|
10
|
+
manuais e `flex-1` repetido em cada botão. O resultado visual é um único grupo emoldurado, com
|
|
11
|
+
ações de mesma largura e divisores entre elas, mas o consumidor precisa reconstruir essas três
|
|
12
|
+
decisões em componentes e classes diferentes.
|
|
13
|
+
|
|
14
|
+
`ButtonGroup` já distingue a relação entre controles com `mode` e a ocupação da largura com
|
|
15
|
+
`distribution`. Tratar os divisores como uma propriedade booleana criaria combinações sem sentido,
|
|
16
|
+
como controles conectados e divididos ao mesmo tempo. Usar `Card` apenas como borda também cria uma
|
|
17
|
+
superfície semântica e um fundo que não pertencem ao grupo de ações.
|
|
18
|
+
|
|
19
|
+
## Decisão
|
|
20
|
+
|
|
21
|
+
`ButtonGroup` passa a aceitar:
|
|
22
|
+
|
|
23
|
+
- `framed`, booleano independente que aplica borda, raio estrutural e padding sem definir fundo;
|
|
24
|
+
- `mode="divided"`, além de `connected` e `spaced`, para preservar a forma individual dos
|
|
25
|
+
controles e inserir automaticamente um divisor com respiro entre filhos adjacentes;
|
|
26
|
+
- `distribution="equal"`, já existente, para dividir o espaço disponível somente entre os
|
|
27
|
+
controles, sem incluir os divisores no cálculo.
|
|
28
|
+
|
|
29
|
+
O modo `divided` deriva a orientação dos separadores da orientação do grupo. Em grupos horizontais,
|
|
30
|
+
os divisores são verticais; em grupos verticais, são horizontais. `ButtonGroupSeparator` continua
|
|
31
|
+
público para composições conectadas ou divisões seletivas.
|
|
32
|
+
|
|
33
|
+
## Consequências
|
|
34
|
+
|
|
35
|
+
- Consumidores expressam o conjunto em uma única raiz: `<ButtonGroup framed mode="divided"
|
|
36
|
+
distribution="equal">`.
|
|
37
|
+
- A moldura herda a superfície externa e não substitui `Card` quando uma superfície semântica for
|
|
38
|
+
realmente necessária.
|
|
39
|
+
- Os botões não repetem classes de largura, e separadores uniformes não são montados manualmente.
|
|
40
|
+
- `mode` continua sendo o único eixo que descreve a relação visual entre controles.
|
|
41
|
+
|
|
42
|
+
## Alternativas descartadas
|
|
43
|
+
|
|
44
|
+
### Manter Card e separadores explícitos
|
|
45
|
+
|
|
46
|
+
Preservaria a API, mas manteria no consumidor uma composição repetitiva que representa uma única
|
|
47
|
+
decisão visual.
|
|
48
|
+
|
|
49
|
+
### Adicionar `dividers` como booleano
|
|
50
|
+
|
|
51
|
+
Permitiria ativar os traços, mas faria dois mecanismos independentes disputarem a relação entre os
|
|
52
|
+
controles e aceitaria combinações contraditórias.
|
|
53
|
+
|
|
54
|
+
### Criar uma variante visual única
|
|
55
|
+
|
|
56
|
+
Uma variante como `variant="framed-divided-equal"` reduziria a chamada, mas acoplaria três eixos
|
|
57
|
+
que têm usos independentes e impediria novas combinações legítimas.
|
|
58
|
+
|
|
59
|
+
## Verificação
|
|
60
|
+
|
|
61
|
+
Testes da primitiva verificam moldura sem fundo, divisores automáticos nas duas orientações e
|
|
62
|
+
distribuição igual restrita aos controles. O teste da view de Evento verifica que o grupo usa a API
|
|
63
|
+
canônica sem `Card`, `ButtonGroupSeparator` ou classes de distribuição nos filhos.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ADR 0036 — Tabs declara a distribuição dos gatilhos
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita.
|
|
4
|
+
- **Data:** 2026-09-28.
|
|
5
|
+
- **Relacionadas:** ADR 0032, quanto à composição da variante `line`; ADR 0034, quanto à moldura de Tabs; ADR 0035, quanto ao eixo de distribuição em grupos de controles.
|
|
6
|
+
|
|
7
|
+
## Contexto
|
|
8
|
+
|
|
9
|
+
Uma lista de abas pode abraçar a largura dos rótulos ou repartir a largura disponível igualmente
|
|
10
|
+
entre os gatilhos. A view de Evento usa quatro abas no cabeçalho de um bloco emoldurado; larguras
|
|
11
|
+
derivadas do texto deixam esse cabeçalho visualmente desequilibrado, embora todas as seções tenham o
|
|
12
|
+
mesmo nível.
|
|
13
|
+
|
|
14
|
+
Hoje, a variante horizontal `line` força cada gatilho a usar a largura do próprio conteúdo. Um
|
|
15
|
+
consumidor que precise de larguras iguais teria de sobrescrever o `flex` interno ou reconstruir a
|
|
16
|
+
lista com grid, duplicando uma decisão que já existe em `ButtonGroup` como
|
|
17
|
+
`distribution="content" | "equal"`.
|
|
18
|
+
|
|
19
|
+
## Decisão
|
|
20
|
+
|
|
21
|
+
`Tabs` aceita `distribution="content" | "equal"`, com `content` como padrão. A propriedade fica no
|
|
22
|
+
root porque descreve o conjunto de abas e, como `size` e `orientation`, flui por contexto até a
|
|
23
|
+
lista e os gatilhos.
|
|
24
|
+
|
|
25
|
+
Na orientação horizontal, `equal` faz a lista ocupar a largura disponível e atribui a cada gatilho
|
|
26
|
+
a mesma fração flexível, com largura mínima zero. A área interativa interna ocupa toda essa fração,
|
|
27
|
+
incluindo o fundo de hover. `content` preserva o comportamento atual. A distribuição não altera a
|
|
28
|
+
largura nem o layout dos painéis de conteúdo.
|
|
29
|
+
|
|
30
|
+
## Consequências
|
|
31
|
+
|
|
32
|
+
- Consumidores escolhem a mesma linguagem de distribuição usada por `ButtonGroup`.
|
|
33
|
+
- `TabsList variant="line"` continua responsável pela linha e pelos estados visuais; não recebe
|
|
34
|
+
classes de layout no consumidor.
|
|
35
|
+
- A view de Evento declara `distribution="equal"` no `Tabs` sem alterar cada `TabsTrigger`.
|
|
36
|
+
- Usos existentes preservam a distribuição pelo conteúdo.
|
|
37
|
+
|
|
38
|
+
## Alternativas descartadas
|
|
39
|
+
|
|
40
|
+
### Declarar a propriedade em TabsList
|
|
41
|
+
|
|
42
|
+
É a parte que organiza os gatilhos, mas faria a distribuição escapar do mesmo contrato de conjunto
|
|
43
|
+
em que `Tabs` já declara tamanho e orientação.
|
|
44
|
+
|
|
45
|
+
### Usar grid e número de colunas no consumidor
|
|
46
|
+
|
|
47
|
+
Exigiria conhecer a quantidade de abas e repetiria classes de layout em cada tela.
|
|
48
|
+
|
|
49
|
+
### Tornar larguras iguais o padrão
|
|
50
|
+
|
|
51
|
+
Atenderia a view de Evento, mas esticaria navegações curtas e barras que deliberadamente abraçam os
|
|
52
|
+
rótulos.
|
|
53
|
+
|
|
54
|
+
## Verificação
|
|
55
|
+
|
|
56
|
+
Testes da primitiva verificam o default `content`, a largura total da lista e a fração igual dos
|
|
57
|
+
gatilhos quando `equal` está ativo. O teste da view de Evento verifica a propriedade no root e a
|
|
58
|
+
distribuição dos quatro gatilhos sem classes locais equivalentes.
|
package/docs/radius-scale.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> **Status: decidido (ago/2026).** A forma usa uma escala contínua do Tailwind; o nome do
|
|
4
4
|
> componente não cria um segundo conjunto de tokens.
|
|
5
|
+
>
|
|
6
|
+
> **Atualização (2026-09-28).** A ADR 0033 conclui a validação que este documento deixou pendente e
|
|
7
|
+
> aplica `rounded-xl` ao Drawer junto da sua nova moldura recuada.
|
|
5
8
|
|
|
6
9
|
## Contexto
|
|
7
10
|
|
|
@@ -42,8 +45,8 @@ voltaria a acoplar forma e tipo de componente, problema removido na versão 10.0
|
|
|
42
45
|
`--radius` alcançar também os extremos usados pelo Opus. `rounded-3xl` continua nativo do
|
|
43
46
|
Tailwind; aplicações que usam esse degrau precisam revisar a fronteira ao alterar a base.
|
|
44
47
|
- Novos componentes podem escolher uma utility da escala sem inventar vocabulário próprio.
|
|
45
|
-
- Drawer e outras superfícies presas à viewport não
|
|
46
|
-
|
|
48
|
+
- Naquele entregável, Drawer e outras superfícies presas à viewport não mudaram. A ADR 0033 fez a
|
|
49
|
+
validação própria de clipping, scroll e conteúdo full-bleed e passou o Drawer para `rounded-xl`.
|
|
47
50
|
|
|
48
51
|
## Alternativas descartadas
|
|
49
52
|
|
package/package.json
CHANGED
|
@@ -43,18 +43,21 @@ existentes, mantendo navegação observável e componentes reutilizáveis sem la
|
|
|
43
43
|
7. Tratar página, filtro, seleção e modal importante como estado navegável por URL quando
|
|
44
44
|
o produto precisa de deep link, back/forward ou refresh. Em `Presentation`, declarar `route`
|
|
45
45
|
no artefato e deixar o adaptador apenas conectar a spec ao router da aplicação.
|
|
46
|
-
8. Compor superfícies pela gramática estrutural do catálogo: `PageHeader`
|
|
47
|
-
|
|
48
|
-
exatamente um heading principal com `PageTitle` no header
|
|
49
|
-
`PageState` ativo no body
|
|
50
|
-
|
|
51
|
-
`PageHeader` são projetados na barra. `Content` contém `ContentHeader` e `ContentBody`; Card,
|
|
46
|
+
8. Compor superfícies pela gramática estrutural do catálogo: `PageHeader` é a única introdução
|
|
47
|
+
estrutural e reúne navegação contextual opcional, título e ações; `PageBody` contém o conteúdo.
|
|
48
|
+
Na composição explícita, declare exatamente um heading principal com `PageTitle` no header,
|
|
49
|
+
`Content level={1}` ou um `PageState` ativo no body. `PageShell` pode fornecer navegação e ações
|
|
50
|
+
conhecidas pela aplicação, mas não cria uma barra visual. `Content` contém `ContentHeader` e `ContentBody`; Card,
|
|
52
51
|
Drawer e Pane usam seus
|
|
53
52
|
respectivos `*Body`. Para o caso direto, usar a sintaxe abreviada de `Page` (`title`,
|
|
54
53
|
`actions`) ou de `Content` (`title`, `description`, `actions` e `count`); não misturá-la com o
|
|
55
54
|
header explícito. O título da página não carrega contador. Ajustar o
|
|
56
55
|
nível do heading pela hierarquia semântica, não pelo destaque visual. O `Page` mantém seu teto
|
|
57
56
|
centralizado padrão de `80rem`.
|
|
57
|
+
Em `Content framed`, usar `layout="sectioned"` quando o filho direto já controlar o recuo do
|
|
58
|
+
body, como `DetailGroup` com divisores, lista ou tabela; não reconstruir divisor, padding ou
|
|
59
|
+
recorte por classes no consumidor. `frameVariant="legend"` continua sendo um acabamento da
|
|
60
|
+
borda contínua e não combina com o layout seccionado.
|
|
58
61
|
9. Manter margem e posicionamento no consumidor; componente reutilizável controla apenas
|
|
59
62
|
seu interior.
|
|
60
63
|
10. Não definir a fonte raiz em uma biblioteca ou componente. O navegador e a aplicação são
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
- Não dispara: “Ajuste o CSS de um e-mail estático.”
|
|
5
5
|
- Execução: implementar uma lista com modal roteável e provar loading, erro, vazio e back.
|
|
6
6
|
- Execução estrutural: montar uma página de relatório isolada com `Page > PageHeader + PageBody`,
|
|
7
|
-
outra em `PageShell` com `Page > PageHeader
|
|
8
|
-
`PageTitle`
|
|
7
|
+
outra em `PageShell` com navegação contextual acima de `Page > PageHeader + PageBody`, garantindo
|
|
8
|
+
exatamente um `PageTitle` no header, e uma coleção nomeada com
|
|
9
9
|
`Content > ContentHeader + ContentBody > ActionList`. Provar que criação fica em
|
|
10
10
|
`ContentActions`, no extremo oposto ao título, como botão textual `default` sem ícone e com
|
|
11
11
|
`Criar recurso`, enquanto uma operação dependente do recorte usa `toolbarActions`; incluir
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
rejeita a mistura entre props abreviadas e headers explícitos.
|
|
23
23
|
- Execução de forma: compor uma tabela dentro de Card sem moldura duplicada, manter a moldura
|
|
24
24
|
estrutural standalone em `rounded-lg` e os controles internos em `rounded-md`.
|
|
25
|
+
- Execução de Content seccionado: compor `Content framed layout="sectioned"` com mídia e
|
|
26
|
+
`DetailGroup` com divisores; provar header com divisor, body sem recuo e ausência de classes no
|
|
27
|
+
consumidor que reconstruam essa anatomia. Reprovar a combinação com `frameVariant="legend"`.
|
|
25
28
|
- Reprova: criar uma casca `bg-card` que herda o texto global ou introduzir `rounded-widget`
|
|
26
29
|
quando a escala `rounded-*` atende ao papel.
|
|
27
30
|
- Reprova: trocar automaticamente o radius de um controle quando apenas seu tamanho muda ou
|
|
@@ -8,25 +8,21 @@
|
|
|
8
8
|
parâmetro podem declarar `route.record` para hidratar o item a partir de uma Presentation de
|
|
9
9
|
lista; não reconstruir essa decisão com condicionais no componente da página.
|
|
10
10
|
- `Page` fornece o `<main>` e o container centralizado com teto padrão de `80rem`. Sua forma
|
|
11
|
-
explícita exige `PageBody`, exatamente um heading principal e aceita `PageHeader
|
|
12
|
-
|
|
13
|
-
`
|
|
14
|
-
declara chrome; `PageIntro` declara uma introdução opcional no conteúdo. `title` e `actions` no
|
|
11
|
+
explícita exige `PageBody`, exatamente um heading principal e aceita `PageHeader` e `PageFooter`.
|
|
12
|
+
Declare esse heading com `PageTitle` no header, `Content level={1}` ou um `PageState` ativo no
|
|
13
|
+
body. `PageHeader` é a única introdução estrutural e reúne navegação, título e ações. `title` e `actions` no
|
|
15
14
|
próprio `Page` são a abreviação para o caso direto.
|
|
16
15
|
Não misturar as duas formas. Alterar `className` apenas quando a superfície tiver uma necessidade
|
|
17
16
|
real de largura; não reconstruir esse container em cada rota.
|
|
18
|
-
- `
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
uma subpágina simples, `PageBack` recebe o destino pai explícito e aparece
|
|
22
|
-
antes do título como controle somente com ícone. Para mais de um ancestral relevante, use
|
|
17
|
+
- `PageHeader` declara navegação contextual, título e ações. Em uma subpágina simples, `PageBack`
|
|
18
|
+
recebe o destino pai explícito e aparece acima da linha do título como controle somente com
|
|
19
|
+
ícone. Para mais de um ancestral relevante, use
|
|
23
20
|
`Breadcrumb` dentro de `PageNavigation`. Não combine retorno e breadcrumb nem crie um chrome
|
|
24
21
|
paralelo para uma `Page`. `PageActionsTarget` fica reservado a workspaces imersivos que já
|
|
25
22
|
possuam chrome próprio.
|
|
26
23
|
- Quando shell e rota conhecem partes diferentes da mesma página, use `PageShell` ao redor da rota.
|
|
27
|
-
O shell
|
|
28
|
-
no
|
|
29
|
-
Ações com texto na barra usam o tamanho `default`, como as ações de footer de Dialog e Drawer;
|
|
24
|
+
O shell entrega navegação e ações ao `PageHeader` descendente sem criar uma barra visual.
|
|
25
|
+
Ações com texto no cabeçalho usam o tamanho `default`, como as ações de footer de Dialog e Drawer;
|
|
30
26
|
ações somente com ícone de chrome usam `icon`. O tamanho `sm` fica para ações operacionais em
|
|
31
27
|
toolbar, seção ou coleção densa, e `xs`/`icon-xs` para ações internas de linha ou célula. Contexto
|
|
32
28
|
e variante resolvem a hierarquia visual sem alterar essa medida. Em uma coleção, a ação de
|
|
@@ -34,9 +30,8 @@
|
|
|
34
30
|
`PaneHeader`, portal ou seletor global para reconstruir essa composição.
|
|
35
31
|
- `PageState` substitui todo o conteúdo principal quando a página carrega, falha ou está vazia. Na
|
|
36
32
|
forma curta ele pode ser escrito como filho direto de `Page`, que cria o `PageBody`; na forma
|
|
37
|
-
explícita, fica sozinho dentro de `PageBody`. Nos três estados ativos, o cabeçalho da Page
|
|
38
|
-
some inteiro — incluindo
|
|
39
|
-
permanece. O estado ocupa a área disponível e seu título assume o heading
|
|
33
|
+
explícita, fica sozinho dentro de `PageBody`. Nos três estados ativos, o cabeçalho da Page
|
|
34
|
+
some inteiro — incluindo navegação e ações recebidas de `PageShell`. O estado ocupa a área disponível e seu título assume o heading
|
|
40
35
|
principal, inclusive quando um componente intermediário renderiza o estado. Uma subpágina que
|
|
41
36
|
dependa do retorno ao pai oferece essa saída pelo `action` do próprio `PageState`. O erro mantém
|
|
42
37
|
`role="alert"`, usa a mesma composição central e sem moldura dos demais estados e apresenta a
|
|
@@ -48,7 +43,10 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
|
|
|
48
43
|
direto e não podem ser misturados ao header explícito. Na variante `page`, ações da coleção ficam
|
|
49
44
|
no extremo oposto ao título; a criação usa botão textual `default`, sem ícone, com `Criar recurso`.
|
|
50
45
|
O diálogo de criação usa `Criar recurso` e o de edição, `Editar recurso`. `level` preserva a
|
|
51
|
-
hierarquia semântica do heading.
|
|
46
|
+
hierarquia semântica do heading. `Content framed` aceita `layout="sectioned"` quando o filho
|
|
47
|
+
direto já possui recuo interno, como `DetailGroup` com divisores, lista ou tabela: o header recebe
|
|
48
|
+
divisor e o body fica sem padding. `frameVariant="legend"` trata a borda contínua e não combina
|
|
49
|
+
com esse layout.
|
|
52
50
|
- Uma página centrada em coleção compõe `Content + ActionList`. O `ContentHeader` recebe título e
|
|
53
51
|
criação; o `ActionList` mantém busca, filtros, atualização, resultados, estados e paginação.
|
|
54
52
|
`toolbarActions` fica reservado a operações que dependem do recorte atual.
|