@softize/opus 12.10.0 → 13.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.
Files changed (154) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/bin/lib/check.mjs +1098 -310
  3. package/bin/lib/copy.mjs +12 -5
  4. package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +65 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
  7. package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
  8. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  9. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  10. package/docs/radius-scale.md +1 -1
  11. package/package.json +1 -1
  12. package/registry/instructions/opus.md +5 -0
  13. package/registry/skills/build-opus-ui/SKILL.md +27 -16
  14. package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
  15. package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
  16. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  17. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  18. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  19. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  20. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  21. package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
  22. package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
  23. package/registry/templates/app/src/App.tsx +1 -1
  24. package/src/core/dictionary.ts +52 -14
  25. package/src/core/index.ts +10 -0
  26. package/src/core/ui-context.ts +29 -0
  27. package/src/schema/drivers/zod.ts +17 -8
  28. package/src/ui/components/patterns/action-form-card.tsx +18 -12
  29. package/src/ui/components/patterns/confirm.tsx +163 -40
  30. package/src/ui/components/patterns/content-header.tsx +335 -61
  31. package/src/ui/components/patterns/data-state.tsx +23 -10
  32. package/src/ui/components/patterns/list.tsx +1097 -783
  33. package/src/ui/components/patterns/page-state.tsx +115 -0
  34. package/src/ui/components/patterns/page.tsx +231 -41
  35. package/src/ui/components/patterns/sidebar.tsx +357 -83
  36. package/src/ui/components/patterns/trigger.tsx +37 -30
  37. package/src/ui/components/patterns/view.tsx +7 -11
  38. package/src/ui/components/primitives/alert.tsx +298 -110
  39. package/src/ui/components/primitives/ask.tsx +2 -1
  40. package/src/ui/components/primitives/badge.tsx +91 -30
  41. package/src/ui/components/primitives/button.tsx +99 -60
  42. package/src/ui/components/primitives/calendar.tsx +39 -39
  43. package/src/ui/components/primitives/card.tsx +96 -23
  44. package/src/ui/components/primitives/detail.tsx +2 -2
  45. package/src/ui/components/primitives/dialog.tsx +196 -39
  46. package/src/ui/components/primitives/dictionary-value.tsx +9 -14
  47. package/src/ui/components/primitives/dot.tsx +74 -21
  48. package/src/ui/components/primitives/drawer.tsx +40 -24
  49. package/src/ui/components/primitives/empty.tsx +3 -3
  50. package/src/ui/components/primitives/item.tsx +135 -79
  51. package/src/ui/components/primitives/menu.tsx +11 -3
  52. package/src/ui/components/primitives/metric-card.tsx +133 -0
  53. package/src/ui/components/primitives/sonner.tsx +187 -8
  54. package/src/ui/components/primitives/table.tsx +2 -2
  55. package/src/ui/docs/DocBrowser.tsx +104 -25
  56. package/src/ui/docs/changelog.tsx +1 -1
  57. package/src/ui/docs/content/accordion.md +22 -16
  58. package/src/ui/docs/content/action-form-card.md +8 -8
  59. package/src/ui/docs/content/action-form-dialog.md +9 -9
  60. package/src/ui/docs/content/action-form.md +28 -34
  61. package/src/ui/docs/content/action-list-dialog.md +11 -6
  62. package/src/ui/docs/content/action-list.md +64 -39
  63. package/src/ui/docs/content/action-trigger.md +21 -14
  64. package/src/ui/docs/content/action-view.md +8 -8
  65. package/src/ui/docs/content/actions.md +9 -9
  66. package/src/ui/docs/content/ai.md +3 -3
  67. package/src/ui/docs/content/alert.md +54 -28
  68. package/src/ui/docs/content/aspect-ratio.md +4 -4
  69. package/src/ui/docs/content/audit.md +2 -2
  70. package/src/ui/docs/content/auth.md +3 -3
  71. package/src/ui/docs/content/avatar.md +34 -14
  72. package/src/ui/docs/content/badge.md +21 -22
  73. package/src/ui/docs/content/breadcrumb.md +13 -8
  74. package/src/ui/docs/content/button.md +93 -15
  75. package/src/ui/docs/content/calendar.md +5 -5
  76. package/src/ui/docs/content/card.md +6 -6
  77. package/src/ui/docs/content/carousel.md +16 -11
  78. package/src/ui/docs/content/chat.md +3 -3
  79. package/src/ui/docs/content/checkbox.md +7 -7
  80. package/src/ui/docs/content/cli.md +5 -5
  81. package/src/ui/docs/content/collapsible.md +8 -8
  82. package/src/ui/docs/content/command.md +16 -8
  83. package/src/ui/docs/content/composer.md +2 -2
  84. package/src/ui/docs/content/content.md +44 -0
  85. package/src/ui/docs/content/copyable.md +4 -3
  86. package/src/ui/docs/content/customization.md +7 -7
  87. package/src/ui/docs/content/cycle.md +3 -3
  88. package/src/ui/docs/content/data-state.md +11 -12
  89. package/src/ui/docs/content/data.md +26 -33
  90. package/src/ui/docs/content/detail.md +8 -5
  91. package/src/ui/docs/content/dialog.md +339 -31
  92. package/src/ui/docs/content/dictionary-value.md +19 -18
  93. package/src/ui/docs/content/dock.md +3 -3
  94. package/src/ui/docs/content/dot.md +7 -7
  95. package/src/ui/docs/content/drawer.md +32 -16
  96. package/src/ui/docs/content/empty-value.md +2 -2
  97. package/src/ui/docs/content/empty.md +19 -12
  98. package/src/ui/docs/content/events.md +4 -4
  99. package/src/ui/docs/content/field.md +34 -12
  100. package/src/ui/docs/content/getting-started.md +1 -1
  101. package/src/ui/docs/content/icon-picker.md +8 -4
  102. package/src/ui/docs/content/input-otp.md +20 -12
  103. package/src/ui/docs/content/input.md +121 -9
  104. package/src/ui/docs/content/item.md +64 -24
  105. package/src/ui/docs/content/kbd.md +19 -11
  106. package/src/ui/docs/content/label.md +5 -3
  107. package/src/ui/docs/content/log.md +4 -4
  108. package/src/ui/docs/content/markdown.md +7 -6
  109. package/src/ui/docs/content/mcp.md +13 -15
  110. package/src/ui/docs/content/menu.md +36 -17
  111. package/src/ui/docs/content/metric-card.md +41 -0
  112. package/src/ui/docs/content/observability.md +2 -2
  113. package/src/ui/docs/content/page.md +93 -10
  114. package/src/ui/docs/content/pagination.md +22 -17
  115. package/src/ui/docs/content/popover.md +16 -8
  116. package/src/ui/docs/content/progress.md +7 -5
  117. package/src/ui/docs/content/queue.md +5 -5
  118. package/src/ui/docs/content/radio-group.md +20 -12
  119. package/src/ui/docs/content/router.md +11 -6
  120. package/src/ui/docs/content/scheduler.md +4 -5
  121. package/src/ui/docs/content/scroll-area.md +12 -7
  122. package/src/ui/docs/content/select.md +42 -29
  123. package/src/ui/docs/content/semantic-context.md +63 -0
  124. package/src/ui/docs/content/separator.md +5 -5
  125. package/src/ui/docs/content/sidebar.md +325 -56
  126. package/src/ui/docs/content/skeleton.md +5 -4
  127. package/src/ui/docs/content/slider.md +8 -7
  128. package/src/ui/docs/content/spinner.md +8 -8
  129. package/src/ui/docs/content/split.md +8 -5
  130. package/src/ui/docs/content/storage.md +6 -8
  131. package/src/ui/docs/content/switch.md +8 -7
  132. package/src/ui/docs/content/table.md +16 -6
  133. package/src/ui/docs/content/tabs.md +28 -14
  134. package/src/ui/docs/content/testing.md +9 -11
  135. package/src/ui/docs/content/textarea.md +5 -4
  136. package/src/ui/docs/content/toast.md +47 -13
  137. package/src/ui/docs/content/toggle.md +75 -7
  138. package/src/ui/docs/content/tokens.md +31 -3
  139. package/src/ui/docs/content/tooltip.md +19 -11
  140. package/src/ui/docs/content/truncate.md +7 -8
  141. package/src/ui/docs/content/ui.md +10 -9
  142. package/src/ui/docs/content/upgrading.md +7 -8
  143. package/src/ui/docs/doc-client.tsx +2 -2
  144. package/src/ui/docs/registry.tsx +580 -229
  145. package/src/ui/lib/semantic-context.ts +30 -0
  146. package/src/ui/meta.ts +278 -286
  147. package/src/ui/react.tsx +377 -111
  148. package/src/ui/theme.css +116 -0
  149. package/src/ui/components/primitives/alert-dialog.tsx +0 -190
  150. package/src/ui/docs/content/alert-dialog.md +0 -73
  151. package/src/ui/docs/content/button-group.md +0 -71
  152. package/src/ui/docs/content/confirm.md +0 -120
  153. package/src/ui/docs/content/input-group.md +0 -78
  154. package/src/ui/docs/content/toggle-group.md +0 -81
package/bin/lib/copy.mjs CHANGED
@@ -71,10 +71,6 @@ const JSX_CHILD_ROLES = new Map([
71
71
  ['ActionFormCard', 'message'],
72
72
  ['ActionFormDialog', 'message'],
73
73
  ['AlertDescription', 'message'],
74
- ['AlertDialogAction', 'button'],
75
- ['AlertDialogCancel', 'button'],
76
- ['AlertDialogDescription', 'dialog-body'],
77
- ['AlertDialogTitle', 'title'],
78
74
  ['AlertTitle', 'title'],
79
75
  ['Badge', 'badge'],
80
76
  ['BreadcrumbLink', 'breadcrumb'],
@@ -84,6 +80,8 @@ const JSX_CHILD_ROLES = new Map([
84
80
  ['CardTitle', 'title'],
85
81
  ['CommandEmpty', 'empty-state'],
86
82
  ['CommandItem', 'menu-item'],
83
+ ['ContentDescription', 'description'],
84
+ ['ContentTitle', 'title'],
87
85
  ['DialogDescription', 'dialog-body'],
88
86
  ['DialogTitle', 'title'],
89
87
  ['DrawerDescription', 'dialog-body'],
@@ -104,6 +102,8 @@ const JSX_CHILD_ROLES = new Map([
104
102
  ['MenuRadioItem', 'menu-item'],
105
103
  ['PopoverDescription', 'description'],
106
104
  ['PopoverTitle', 'title'],
105
+ ['PageDescription', 'description'],
106
+ ['PageTitle', 'title'],
107
107
  ['TableCaption', 'description'],
108
108
  ['TableHead', 'heading'],
109
109
  ['TabsTrigger', 'tab'],
@@ -130,6 +130,8 @@ const JSX_PROP_ROLES = new Map([
130
130
  ['ActionTrigger', new Map([['label', 'button']])],
131
131
  ['Alert', new Map([['title', 'title'], ['description', 'message']])],
132
132
  ['CommandInput', new Map([['placeholder', 'placeholder']])],
133
+ ['Content', new Map([['title', 'title'], ['description', 'description']])],
134
+ ['ContentHeader', new Map([['title', 'title'], ['description', 'description']])],
133
135
  ['DataState', new Map([['emptyText', 'empty-state'], ['errorText', 'error']])],
134
136
  // A Dock nomeia a barra e cada ação por prop. Sem estas linhas, a copy sairia do inventário
135
137
  // exatamente quando uma superfície migra de <Button aria-label> para <DockAction label>.
@@ -138,6 +140,7 @@ const JSX_PROP_ROLES = new Map([
138
140
  ['Input', new Map([['placeholder', 'placeholder']])],
139
141
  ['InputGroupInput', new Map([['placeholder', 'placeholder']])],
140
142
  ['InputGroupTextarea', new Map([['placeholder', 'placeholder']])],
143
+ ['MetricCard', new Map([['label', 'label'], ['description', 'description']])],
141
144
  ['Page', new Map([['title', 'title'], ['description', 'description']])],
142
145
  ['Select', new Map([
143
146
  ['placeholder', 'placeholder'],
@@ -183,9 +186,12 @@ const JSX_PROP_CLASS = new Map([
183
186
  ['ActionTrigger', new Map([['label', 'className']])],
184
187
  ['Alert', new Map([['title', 'className'], ['description', 'className']])],
185
188
  ['CommandInput', new Map([['placeholder', 'className']])],
189
+ ['Content', new Map([['title', 'className'], ['description', 'className']])],
190
+ ['ContentHeader', new Map([['title', 'className'], ['description', 'className']])],
186
191
  ['Input', new Map([['placeholder', 'className']])],
187
192
  ['InputGroupInput', new Map([['placeholder', 'className']])],
188
193
  ['InputGroupTextarea', new Map([['placeholder', 'className']])],
194
+ ['MetricCard', new Map([['label', 'className'], ['description', 'className']])],
189
195
  ['Page', new Map([['title', 'className'], ['description', 'className']])],
190
196
  ['Select', new Map([['placeholder', 'className']])],
191
197
  ['Textarea', new Map([['placeholder', 'className']])],
@@ -200,11 +206,12 @@ const JSX_PROP_STYLE = new Map([
200
206
  ['Input', new Map([['placeholder', 'style']])],
201
207
  ['InputGroupInput', new Map([['placeholder', 'style']])],
202
208
  ['InputGroupTextarea', new Map([['placeholder', 'style']])],
209
+ ['MetricCard', new Map([['label', 'style'], ['description', 'style']])],
203
210
  ['Textarea', new Map([['placeholder', 'style']])],
204
211
  ])
205
212
 
206
213
  const JSX_PORTAL_BOUNDARIES = new Set([
207
- 'AlertDialogContent', 'DialogContent', 'DrawerContent', 'MenuContent', 'MenuSubContent',
214
+ 'DialogContent', 'DrawerContent', 'MenuContent', 'MenuSubContent',
208
215
  'PopoverContent', 'TooltipContent',
209
216
  ])
210
217
 
@@ -3,6 +3,9 @@
3
3
  - Status: aceita
4
4
  - Data: 2026-09-02
5
5
 
6
+ > A ADR 0006 substitui o nome `tone` por `context` para a família semântica e define sua migração
7
+ > compatível. O princípio desta ADR — apresentação declarada e não inferida — permanece vigente.
8
+
6
9
  ## Contexto e forças
7
10
 
8
11
  `t.dict` declara vocabulário fechado uma única vez: código estável, rótulo, documentação de
@@ -0,0 +1,65 @@
1
+ # ADR 0004 — O estado integral do conteúdo é composto dentro de Page
2
+
3
+ ## Contexto
4
+
5
+ `Page` padroniza o `<main>`, o cabeçalho e o container de uma página. Hoje, consumidores que ainda
6
+ não têm conteúdo para mostrar repetem spinners, alertas e vazios diretamente em `children`. Essas
7
+ composições divergem visualmente e nem sempre distinguem carregamento, ausência e falha ou oferecem
8
+ uma ação de recuperação.
9
+
10
+ Ao mesmo tempo, uma página pode agregar várias fontes independentes. Fazer `Page` receber flags de
11
+ carregamento ou erro faria o esqueleto decidir quando toda a página deve desaparecer por causa de
12
+ uma única fonte.
13
+
14
+ ## Decisão
15
+
16
+ `Page` continua presentacional e sem conhecimento de dados. O Opus fornece `PageState` como pattern
17
+ composto para o estado integral da área de conteúdo. Na forma curta ele pode ser escrito como filho
18
+ direto de `Page`, que materializa `PageBody`; na composição explícita, `PageState` fica dentro de
19
+ `PageBody`. Ele é usado quando o conteúdo principal inteiro está carregando, falhou ou está vazio.
20
+
21
+ `PageState`:
22
+
23
+ - recebe um estado discriminado entre `loading`, `error`, `empty` e `ready`;
24
+ - preserva o cabeçalho da página em todos os estados;
25
+ - compõe `Spinner`, `Alert` e `Empty` em vez de recriar suas superfícies;
26
+ - aceita título, descrição, ícone e ação contextual sem exibir erro técnico;
27
+ - expõe `data-slot="page-state"` e `data-status` para testes e análise estrutural;
28
+ - renderiza o conteúdo sem moldura adicional em `ready`.
29
+
30
+ Estados parciais continuam pertencendo a `DataState`, `ActionView`, `ActionList` ou `Alert`, conforme
31
+ a fronteira afetada. Uma coleção vazia continua dentro da estrutura da coleção; `Empty` representa
32
+ uma região disponível, uma escolha pendente ou uma próxima ação.
33
+
34
+ ## Alternativas consideradas
35
+
36
+ ### Flags diretamente em Page
37
+
38
+ Rejeitada porque mistura layout com aquisição de dados e torna ambígua a precedência entre várias
39
+ fontes da mesma página.
40
+
41
+ ### Usar somente DataState
42
+
43
+ Rejeitada como contrato único porque o modo bloco de `DataState` também atende seções menores e não
44
+ expressa que o conteúdo principal inteiro está indisponível. `PageState` pode compartilhar as mesmas
45
+ primitivas sem confundir as duas escalas.
46
+
47
+ ### Manter composições locais
48
+
49
+ Rejeitada porque mantém diferenças de altura, borda, copy, recuperação e semântica acessível entre
50
+ telas equivalentes.
51
+
52
+ ## Consequências
53
+
54
+ - Consumidores ganham uma composição uniforme sem acoplar `Page` a hooks ou actions.
55
+ - A copy específica do fluxo permanece no consumidor; defaults seguros cobrem usos simples.
56
+ - Migrações precisam distinguir estado integral de estado parcial antes de substituir a composição.
57
+ - O gate estrutural do consumidor deve impedir novos estados integrais montados manualmente.
58
+
59
+ ## Verificação
60
+
61
+ - Testes do Opus cobrem os quatro estados, slots, precedência do conteúdo e ação contextual.
62
+ - A documentação do catálogo demonstra o uso na forma curta e sua posição dentro de `PageBody` na
63
+ composição explícita.
64
+ - Consumidores verificam `data-slot="page-state"` nos estados integrais e mantêm testes próprios para
65
+ recuperação e distinção entre erro e vazio.
@@ -0,0 +1,180 @@
1
+ # ADR 0005 — Superfícies estruturais compartilham uma anatomia explícita
2
+
3
+ - **Status:** aceita.
4
+ - **Data:** 2026-09-04.
5
+
6
+ ## Contexto
7
+
8
+ Os componentes estruturais da UI descrevem regiões equivalentes com APIs diferentes. `Dialog`
9
+ expõe header, título, descrição, body e footer; `Card` chama o corpo de `Content`; e `Page`
10
+ recebe título, descrição e ações somente por propriedades, sem expor seus elementos estruturais.
11
+ `ContentHeader`, por sua vez, pode aparecer solto, embora seus nomes sugiram uma família `Content`.
12
+
13
+ Essa variação obriga quem consome a biblioteca a reaprender a composição em cada superfície e
14
+ impede que análise estática verifique relações como “um título pertence ao header da sua família”.
15
+ Ao mesmo tempo, a forma curta de `Page` e `ContentHeader` atende bem ao caso comum e não precisa ser
16
+ perdida para obter uma estrutura explícita.
17
+
18
+ ## Decisão
19
+
20
+ As superfícies mantidas pela casa adotam a gramática `Root > Header + Body + Footer`, com
21
+ `Title`, `Description`, `Meta` e `Actions` pertencendo ao `Header` da mesma família.
22
+
23
+ - `Page` oferece `PageHeader`, `PageTitle`, `PageDescription`, `PageMeta`, `PageActions` e
24
+ `PageBody`.
25
+ - `Content` representa uma região de conteúdo semanticamente nomeada e oferece `ContentHeader`,
26
+ `ContentTitle`, `ContentDescription`, `ContentMeta`, `ContentActions` e `ContentBody`.
27
+ - `CardContent` passa a ter `CardBody` como nome canônico.
28
+ - `Drawer` recebe `DrawerBody`.
29
+ - `PaneContent` passa a ter `PaneBody` como nome canônico.
30
+ - `Dialog` permanece como referência porque já possui `DialogHeader` e `DialogBody`.
31
+ - `Alert` oferece `AlertMedia`, `AlertHeader`, `AlertTitle`, `AlertDescription` e
32
+ `AlertActions`; a forma curta por propriedades materializa esses mesmos slots.
33
+ - `Empty` oferece `EmptyHeader`, `EmptyMedia`, `EmptyTitle`, `EmptyDescription` e
34
+ `EmptyActions`. `EmptyContent` permaneceu como alias legado durante a versão 12 e foi removido na
35
+ versão 13.
36
+ - `Item` oferece `ItemMedia`, `ItemHeader`, `ItemTitle`, `ItemDescription`, `ItemBody`,
37
+ `ItemActions` e `ItemFooter`. `ItemContent` permanece temporariamente como alias legado de
38
+ corpo, mas deixa de envolver título e descrição no código novo.
39
+
40
+ `Page` e `Content` aceitam também uma forma curta com `title`, `description`, `meta` e `actions`.
41
+ Essa forma é açúcar sintático: produz a mesma árvore semântica, os mesmos estilos e os mesmos
42
+ `data-slot` da composição explícita. Um consumidor não pode misturar as duas formas na mesma raiz.
43
+
44
+ `ContentHeader` deixa de ser uma região solta e passa a pertencer a `Content`. A forma histórica
45
+ por propriedades continua disponível temporariamente dentro de `Content`, mas a composição
46
+ explícita usa os slots da família.
47
+
48
+ `Content` técnico mantém seu nome quando representa o contêiner montado por uma primitiva ou um
49
+ painel controlado, como `DialogContent`, `PopoverContent`, `TabsContent` e `AccordionContent`.
50
+ `CarouselContent` também permanece porque representa o trilho técnico da primitiva, não o body de
51
+ uma superfície estrutural. No `Empty`, a antiga região `EmptyContent` não é um body: como contém
52
+ somente comandos, seu nome canônico passa a ser `EmptyActions`.
53
+
54
+ As superfícies modais centralizadas pertencem a uma única família pública, `Dialog`. O modo padrão
55
+ permite tarefas e conteúdo dispensáveis; `mode="alert"` exige uma resposta explícita e seleciona
56
+ internamente a primitiva que emite `role="alertdialog"`, prende o foco e impede o fechamento pelo
57
+ clique externo. O comportamento determina o papel de acessibilidade: `role` não é usado como chave
58
+ de configuração. A família pública `AlertDialog` deixa de existir, enquanto os métodos imperativos
59
+ `dialog.alert`, `dialog.confirm`, `dialog.prompt` e `dialog.choose` usam `Dialog mode="alert"`.
60
+
61
+ Os dois modos usam o fundo base e o texto de primeiro plano, delimitados por borda semântica, raio
62
+ `xl` e elevação. A anatomia interna continua responsável pelo espaçamento de cabeçalho, corpo e
63
+ rodapé. O fundo base preserva o contraste com faixas internas, enquanto a borda torna o limite do
64
+ modal reconhecível mesmo quando a página usa o mesmo fundo. No modo de alerta, `DialogMedia`
65
+ oferece a moldura quadrada de mídia e o header e o footer adotam a composição compacta da resposta.
66
+
67
+ `Drawer` preserva a moldura externa ancorada à borda da janela, mas compartilha com `Dialog` as
68
+ faixas internas: o header recebe divisor inferior, o footer recebe divisor superior sobre fundo
69
+ sutil e o body mantém o mesmo alinhamento horizontal. Essa aproximação não adiciona raio nem borda
70
+ ao lado preso à janela.
71
+
72
+ Primitives comportamentais não escolhem a aparência do controle. `DialogClose` e `DrawerClose`
73
+ aplicam fechamento ao filho; `Button` define contexto, variante, tamanho e o efeito do clique. Em
74
+ `Dialog mode="alert"`, `DialogClose initialFocus` marca a saída segura sem dividir fechamentos entre
75
+ action e cancel. `result` identifica uma resposta para `Dialog onResult` quando o consumidor precisa
76
+ observá-la. A composição com `asChild` mantém o elemento interativo único e torna a mesma regra
77
+ previsível em diálogos comuns, drawers e diálogos que exigem resposta.
78
+
79
+ A API imperativa mantém contratos semânticos curtos para reconhecimento, confirmação binária e
80
+ entrada textual. Para escolhas simples com mais resultados, `dialog.choose` recebe uma coleção de
81
+ ações e devolve a união dos valores de `result`, ou `null` quando a superfície fecha sem escolha.
82
+ A coleção exige um único `initialFocus` habilitado e resultados únicos. Composição JSX permanece
83
+ para fluxos cujo carregamento, validação, erro ou estado controlado precisa existir dentro do modal.
84
+
85
+ Nas superfícies horizontais, `Media` e `Header` formam a mesma linha estrutural. Quando `Media`
86
+ desenha uma moldura para ícone ou imagem, ela mantém largura e altura iguais e fica alinhada ao
87
+ topo. O conteúdo textual determina naturalmente a altura da linha sem transformar a mídia em um
88
+ retângulo quando a descrição ocupa mais linhas.
89
+
90
+ Nas superfícies de feedback, `Actions` se alinha ao fim lógico da área útil e permanece centralizado
91
+ verticalmente na mesma linha do conteúdo. `Alert` e `Toast` compartilham essa posição; descrições
92
+ longas ocupam o espaço restante e quebram linha sem deslocar as ações para o rodapé.
93
+
94
+ ## Consequências
95
+
96
+ - A API comum fica previsível sem obrigar o caso simples a escrever todos os slots.
97
+ - A forma explícita permite composição e extensão sem reconstruir o layout da biblioteca.
98
+ - Em `Page` e `Content`, tipos separam as props das duas formas, contexto em runtime protege os
99
+ slots e `opus check` verifica a anatomia JSX completa. Nas famílias históricas, o lint reconhece
100
+ wrappers transparentes e render props: reprova uma família visivelmente errada sem proibir que
101
+ um slot seja encapsulado por um componente reutilizável.
102
+ - Aliases históricos podem permanecer durante uma janela de migração e ser removidos numa versão
103
+ major; a versão 13 remove `EmptyContent` e mantém os demais aliases até uma decisão específica.
104
+ - Consumidores precisam migrar `ContentHeader` solto para `Content` e podem migrar as demais formas
105
+ gradualmente enquanto os aliases existirem.
106
+ - Alertas e itens simples ganham uma hierarquia igual sem perder suas semânticas distintas:
107
+ `Alert` comunica estado e `Item` representa uma entidade ou opção numa coleção.
108
+ - Diálogos comuns e diálogos que exigem resposta compartilham nome, anatomia e moldura visual;
109
+ consumidores escolhem a diferença comportamental com `mode`, sem reconstruir a superfície.
110
+
111
+ ## Alternativas consideradas
112
+
113
+ ### Manter APIs diferentes por componente
114
+
115
+ Preservaria compatibilidade total, mas manteria a carga cognitiva e impediria uma regra estrutural
116
+ comum. Foi descartada porque as diferenças não representam comportamentos distintos.
117
+
118
+ ### Exigir somente composição explícita
119
+
120
+ Produziria uma API uniforme, mas tornaria páginas e regiões simples desnecessariamente verbosas.
121
+ Foi descartada porque shorthand e slots podem convergir para uma única implementação.
122
+
123
+ ### Renomear `ContentHeader` sem criar `Content`
124
+
125
+ Nomes como `SectionHeader` pressupõem outro pai; nomes como `GenericHeader` descrevem ausência de
126
+ semântica; e `HeadingBlock` abandona a gramática das demais famílias. Foi descartada em favor de
127
+ dar a `ContentHeader` um pai estrutural real.
128
+
129
+ ### Manter `DialogContent` como card sem borda
130
+
131
+ Preservaria a diferença histórica entre `Dialog` e o antigo `AlertDialog`, mas faria superfícies com a mesma
132
+ função modal responderem a fundos e limites diferentes. Foi descartada porque essa diferença não
133
+ representa comportamento distinto e pode reduzir a percepção do contorno sobre páginas com fundo
134
+ semelhante.
135
+
136
+ ### Manter `AlertDialog` como família pública separada
137
+
138
+ Espelharia diretamente as duas primitives do Radix, mas duplicaria nomes estruturais e faria a API
139
+ pública expressar um detalhe de implementação. Foi descartada porque `mode="alert"` preserva a
140
+ semântica e as garantias comportamentais sem obrigar quem consome a reaprender outra família.
141
+
142
+ ### Usar `role="alertdialog"` para selecionar o comportamento
143
+
144
+ Seguiria a API de alguns design systems, mas transformaria um atributo de acessibilidade em chave de
145
+ comportamento, algo que o restante do Opus não faz. Foi descartada para que o `mode` selecione as
146
+ garantias e o componente derive o `role` correto.
147
+
148
+ ### Expor action e cancel como primitives distintos no diálogo de alerta
149
+
150
+ Preservaria os nomes do Radix, mas apresentaria um fechamento como “action” embora cancelar também
151
+ seja uma ação. Os dois caminhos fecham a superfície; a única diferença técnica relevante é o
152
+ registro do destino inicial do foco. Foi descartada em favor de `DialogClose`, cuja propriedade
153
+ `initialFocus` expressa essa diferença sem criar duas categorias públicas.
154
+
155
+ ### Exigir composição JSX para toda escolha com mais de duas ações
156
+
157
+ Manteria a API imperativa menor, mas obrigaria cada consumidor a reconstruir fila, fechamento,
158
+ foco seguro e resolução para uma escolha sem estado próprio. Foi descartada porque `dialog.choose`
159
+ consegue preservar essas garantias e devolver um resultado tipado sem absorver formulários ou ações
160
+ assíncronas que precisam manter o modal aberto.
161
+
162
+ ## Verificação
163
+
164
+ - Testes de UI comparam DOM, acessibilidade e `data-slot` das formas curta e explícita.
165
+ - Testes de runtime proíbem a mistura das formas e os slots de `Page`/`Content` usados fora da
166
+ família correspondente; essa relação entre elementos JSX não é representável somente pelo tipo
167
+ de `children` do React.
168
+ - `opus check` reprova relações JSX estruturais inválidas nos consumidores.
169
+ - Testes de layout verificam que `AlertMedia` e `ItemMedia` mantêm molduras quadradas alinhadas ao
170
+ topo, sem fixar a altura do conteúdo textual.
171
+ - Testes de estrutura verificam que os dois modos de `DialogContent` usam fundo base, texto de
172
+ primeiro plano, borda semântica, raio e elevação, sem retornar ao fundo de card.
173
+ - Testes do `Drawer` verificam divisores, fundo do footer e alinhamento de padding entre header,
174
+ body e footer.
175
+ - Testes de estrutura verificam que `DialogClose` não materializa `Button`, usa `result` somente como
176
+ resposta e aplica `initialFocus` à primitive que registra a saída segura no modo de alerta.
177
+ - Testes do host verificam inferência dos resultados de `dialog.choose`, foco seguro, defaults
178
+ visuais, escolha explícita, fechamento sem escolha e entradas inválidas.
179
+ - Documentação e metadados apresentam a forma curta como caminho comum e a composição explícita
180
+ como caminho de extensão.
@@ -0,0 +1,182 @@
1
+ # ADR 0006 — Contexto semântico precede variante visual
2
+
3
+ - Status: aceita
4
+ - Data: 2026-09-04
5
+
6
+ ## Contexto e forças
7
+
8
+ Os componentes do Opus usam `variant` para eixos diferentes. Em Button, Badge, Alert e Dot, a
9
+ prop mistura hierarquia (`default`, `secondary`), contexto semântico (`success`, `warning`,
10
+ `destructive`) e tratamento visual (`outline`, `ghost`, `link`). Em Table, Detail, Tabs e Item,
11
+ `variant` descreve apenas uma alternativa estrutural ou visual local (`plain`, `framed`, `line`).
12
+
13
+ Ao mesmo tempo, entradas de `t.dict` usam `tone` para selecionar a família semântica de status e
14
+ estágios. Essa família não coincide com os componentes: o estado vermelho é `danger` no dicionário,
15
+ `destructive` em alguns primitives, e `MetricCard tone="warning"` atualmente usa tokens vermelhos.
16
+ Um agente ou consumidor precisa conhecer cada exceção para obter a mesma linguagem visual.
17
+
18
+ As forças em tensão são:
19
+
20
+ - o significado precisa permanecer independente da forma concreta com que cada componente o
21
+ apresenta;
22
+ - a API deve ser previsível entre primitives sem transformar todas as combinações em opções
23
+ válidas para todos os componentes;
24
+ - `destructive` precisa continuar descrevendo o risco comportamental de uma ação, sem se tornar o
25
+ nome da família visual vermelha;
26
+ - dicionários e componentes existentes precisam de uma migração explícita e verificável;
27
+ - agentes precisam aprender a regra por contratos, documentação e avaliações, não por memória ou
28
+ inferência a partir de exemplos isolados;
29
+ - light mode e dark mode são temas, não significados de produto.
30
+
31
+ ## Alternativas consideradas
32
+
33
+ ### Manter `tone` nos dicionários e `variant` nos componentes
34
+
35
+ Preserva compatibilidade imediata, mas mantém dois nomes para o mesmo eixo e deixa `variant`
36
+ misturar significado com apresentação. Cada novo componente precisaria repetir mapas locais.
37
+ Rejeitada.
38
+
39
+ ### Copiar literalmente as variantes contextuais do Bootstrap
40
+
41
+ O vocabulário `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light` e `dark` é
42
+ conhecido e cobre grande parte dos casos. Porém, o Bootstrap chama de variante tanto o contexto
43
+ quanto sua materialização (`btn-danger`, `btn-outline-danger`), mistura hierarquia com semântica e
44
+ inclui `light`/`dark`, que pertencem ao tema. Copiar a API manteria a ambiguidade que queremos
45
+ remover. Rejeitada como contrato literal e aceita como referência de vocabulário.
46
+
47
+ ### Declarar contexto e variante como eixos independentes
48
+
49
+ `context` escolhe a família de tokens e responde por que existe o realce. `variant` escolhe como a
50
+ família aparece naquele componente. Cada primitive aceita somente o subconjunto coerente com seu
51
+ papel e fornece defaults. É uma mudança maior, mas torna combinações e exceções explícitas e permite
52
+ que domínio, UI e agentes compartilhem o mesmo modelo. Aceita.
53
+
54
+ ## Decisão
55
+
56
+ ### Vocabulário contextual
57
+
58
+ O Opus define o vocabulário canônico:
59
+
60
+ ```ts
61
+ type UiContext =
62
+ | 'neutral'
63
+ | 'primary'
64
+ | 'info'
65
+ | 'success'
66
+ | 'warning'
67
+ | 'danger'
68
+ ```
69
+
70
+ - `neutral`: estado normal, inativo ou sem julgamento positivo/negativo;
71
+ - `primary`: ação ou elemento de maior destaque no contexto atual;
72
+ - `info`: informação ou processo em andamento sem alerta;
73
+ - `success`: resultado positivo ou estado saudável;
74
+ - `warning`: condição que pede atenção, mas não representa falha;
75
+ - `danger`: falha, impedimento ou consequência perigosa.
76
+
77
+ `secondary` não é contexto universal: ação secundária é hierarquia, enquanto estado neutro é
78
+ semântica. `light` e `dark` permanecem modos de cor. Nenhum dos três entra em `UiContext`.
79
+
80
+ Entradas de dicionário aceitam `DictContext`, o subconjunto sem `primary`, porque um estado de
81
+ domínio não se torna a ação principal da interface. A metadata canônica passa de `tone` para
82
+ `context`.
83
+
84
+ ### Variante visual
85
+
86
+ Para componentes semânticos, `variant` descreve somente o tratamento visual:
87
+
88
+ ```ts
89
+ type SemanticVariant = 'solid' | 'subtle' | 'outline' | 'ghost' | 'link'
90
+ ```
91
+
92
+ Cada componente aceita apenas as variantes que consegue materializar com coerência. Os defaults
93
+ iniciais são:
94
+
95
+ | Componente | Contexto padrão | Variante padrão | Contextos permitidos |
96
+ | --- | --- | --- | --- |
97
+ | Button | `primary` | `solid` | `neutral`, `primary`, `danger` |
98
+ | Badge | `neutral` | `subtle` | todos |
99
+ | Alert | `neutral` | `subtle` | `neutral`, `info`, `success`, `warning`, `danger` |
100
+ | Dot | `neutral` | `solid` | `neutral`, `primary`, `info`, `success`, `warning`, `danger` |
101
+ | MetricCard | `neutral` | `subtle` no ícone | `neutral`, `info`, `success`, `warning`, `danger` |
102
+
103
+ `solid`, `subtle` e `outline` podem compartilhar um contexto sem compartilhar classes. `ghost` e
104
+ `link` ficam restritos a controles interativos. Cor e ícone continuam reforços: texto ou nome
105
+ acessível comunica o significado.
106
+
107
+ Componentes cuja `variant` é exclusivamente estrutural ou local, como `plain | framed` e
108
+ `default | line`, podem mantê-la. Ao criar API nova, preferir uma prop específica quando o nome do
109
+ eixo for mais claro (`frame`, `layout`, `appearance`); não renomear primitives existentes sem ganho
110
+ observável.
111
+
112
+ ### Ações destrutivas
113
+
114
+ `destructive` permanece em contratos de ação e confirmação para declarar risco, confirmação e
115
+ comportamento. A projeção visual desse risco usa `context="danger"`. Portanto, `destructive` não é
116
+ um `UiContext` nem uma variante visual canônica.
117
+
118
+ ### Compatibilidade
119
+
120
+ A migração ocorre em uma janela explícita:
121
+
122
+ 1. componentes e `t.dict` passam a aceitar a API canônica e os nomes antigos como aliases
123
+ depreciados;
124
+ 2. informar os dois eixos antigo e novo ao mesmo tempo é inválido quando houver ambiguidade;
125
+ 3. renderers normalizam aliases antes de escolher tokens;
126
+ 4. manifest, documentação e exemplos gerados projetam somente `context` como forma canônica;
127
+ 5. consumidores são migrados e um gate impede novos usos semânticos de `tone` e de variantes como
128
+ `success`, `warning`, `danger` ou `destructive`;
129
+ 6. os aliases são removidos somente em uma versão major posterior, depois de o inventário chegar a
130
+ zero.
131
+
132
+ Usos locais de `tone` que não representam contexto semântico não são convertidos automaticamente.
133
+ Por exemplo, relações de diagrama com valores `optional`, `same` e `return` devem receber um nome
134
+ de domínio como `kind` ou `relation`, não `UiContext`.
135
+
136
+ ## Orientação para agentes
137
+
138
+ A regra precisa alcançar uma IA por fontes complementares e verificáveis:
139
+
140
+ 1. **Contrato compilável:** `UiContext`, `DictContext` e os tipos de props limitam as combinações
141
+ possíveis e são a fonte primária.
142
+ 2. **Catálogo do Opus:** documentação e metadata de cada componente explicam contexto, variante,
143
+ defaults e exceções com exemplos canônicos.
144
+ 3. **Skills:** `build-opus-ui` ensina a matriz `context × variant`; `model-opus-dictionary` exige
145
+ `context` em status e estágios e proíbe inferência pela chave ou pelo nome do dicionário.
146
+ 4. **Avaliações:** casos positivos, negativos e de execução reprovam `variant="success"`,
147
+ `tone="warning"` sem compatibilidade justificada, `destructive` como cor e comunicação somente
148
+ por cor.
149
+ 5. **Instrução permanente:** a instrução materializada do Opus resume a regra e encaminha às
150
+ skills; não replica toda a tabela.
151
+ 6. **Gate estático:** o check do Opus detecta novas ocorrências legadas fora de arquivos de
152
+ compatibilidade, testes de migração e exemplos negativos explicitamente marcados.
153
+ 7. **Materialização:** mudanças no registry são propagadas por `opus setup`; o projeto consumidor
154
+ valida que `.agents` e os adapters suportados não divergiram da versão declarada.
155
+
156
+ Essa redundância é intencional: tipos impedem combinações inválidas no código, documentação apoia
157
+ decisões humanas, skills orientam o fluxo e o gate detecta regressão mesmo quando uma instrução não
158
+ for consultada.
159
+
160
+ ## Consequências
161
+
162
+ - Primitives semânticos passam a compartilhar um vocabulário e tokens contextuais.
163
+ - Defaults podem variar por componente, mas a mesma palavra nunca muda de significado.
164
+ - A API fica mais explícita em chamadas que precisam dos dois eixos.
165
+ - A transição aumenta temporariamente tipos, testes e normalização por causa dos aliases.
166
+ - Temas precisam oferecer tokens de superfície, borda, texto de ênfase e sólido para cada contexto,
167
+ em light e dark mode.
168
+ - Consumidores com classes Tailwind semânticas literais não são automaticamente corretos; a
169
+ migração precisa classificar se representam contexto, visualização de dados ou linguagem própria
170
+ de um diagrama.
171
+
172
+ ## Verificação
173
+
174
+ - testes puros cobrem o vocabulário, aliases e combinações inválidas;
175
+ - testes de cada primitive cobrem a matriz aceita e seus defaults;
176
+ - testes de `t.dict`, descriptor e manifest comprovam `context` e a compatibilidade de leitura;
177
+ - testes de renderização provam que `DictionaryValue` usa `context` sem a tela escolher classes;
178
+ - avaliações das skills cobrem seleção, execução correta e exemplos que devem ser recusados;
179
+ - o validador de skills passa no registry e nas cópias materializadas;
180
+ - o gate estático reprova novas variantes semânticas e novos `tone` públicos;
181
+ - `opus check`, testes, typecheck, build, `opus copy --check` e gates do consumidor permanecem
182
+ verdes.
@@ -0,0 +1,63 @@
1
+ # ADR 0007 — Ações de Toast formam uma coleção ordenada
2
+
3
+ - Status: aceita
4
+ - Data: 2026-09-05
5
+
6
+ ## Contexto
7
+
8
+ O Opus exportava diretamente a API do Sonner, que distingue uma ação principal (`action`) de uma
9
+ ação especializada de cancelamento (`cancel`). Essa divisão expõe uma decisão da dependência na API
10
+ da casa e força uma semântica que não existe em toda notificação. Os demais componentes do Opus
11
+ tratam ações como uma região ordenada, enquanto contexto e variante descrevem a hierarquia visual.
12
+
13
+ A compatibilidade importa porque consumidores da versão 12 já podem usar os campos do Sonner. Ao
14
+ mesmo tempo, manter os dois formatos indefinidamente deixaria a API ambígua e impediria que o Opus
15
+ controlasse composição, estilos e comportamento de forma consistente.
16
+
17
+ ## Decisão
18
+
19
+ O contrato canônico de Toast passa a aceitar `actions`, uma coleção ordenada de `ToastAction`.
20
+ Cada entrada declara `label`, `onClick` e, quando necessário, `context`, `variant`, `disabled` e uma
21
+ chave React. As ações são renderizadas com `Button size="sm"`: a última usa `primary + solid` por
22
+ padrão e as anteriores usam `neutral + ghost`.
23
+
24
+ Depois do handler, o Toast fecha, salvo quando o evento chama `preventDefault()`. `actions` não
25
+ pode ser combinado com os campos antigos `action` ou `cancel`.
26
+
27
+ `action` e `cancel` permaneceram aceitos e marcados como obsoletos durante a versão 12. A versão 13
28
+ remove esses campos e a estilização de compatibilidade. Os consumidores dependem do contrato do
29
+ Opus, sem expor a divisão especializada do Sonner.
30
+
31
+ ## Consequências
32
+
33
+ - Uma notificação pode ter zero, uma ou mais ações sem inventar um papel de cancelamento.
34
+ - A ordem da coleção comunica hierarquia e mantém o destaque principal no final, como nas demais
35
+ regiões de ação.
36
+ - Consumidores da versão 12 tiveram uma janela de migração antes da remoção dos campos obsoletos.
37
+ - `toast.promise` continua seguindo o contrato próprio do Sonner nesta etapa; a coleção canônica se
38
+ aplica aos disparos simples e tipados por estado.
39
+
40
+ ## Alternativas consideradas
41
+
42
+ ### Manter `action` e `cancel`
43
+
44
+ Preservaria a API da dependência, mas manteria uma distinção semântica artificial e impediria uma
45
+ composição uniforme. Foi descartada como contrato canônico.
46
+
47
+ ### Remover os campos antigos imediatamente
48
+
49
+ Produziria uma API menor, mas quebraria consumidores dentro da mesma versão principal. Foi
50
+ descartada em favor de uma janela explícita de migração.
51
+
52
+ ### Aceitar somente elementos React prontos
53
+
54
+ Daria liberdade total de composição, mas transferiria geometria, hierarquia e fechamento para cada
55
+ consumidor. Foi descartada porque ações declarativas cobrem o fluxo comum sem impedir personalização
56
+ por contexto e variante.
57
+
58
+ ## Verificação
59
+
60
+ - Testes de DOM verificam que `actions` produz uma região única com `Button` do Opus.
61
+ - Testes de interação verificam ordem, defaults visuais, execução e fechamento.
62
+ - Testes de contrato reprovam a mistura de `actions` com `action` ou `cancel`.
63
+ - Documentação e metadata apresentam somente `actions` como caminho canônico.
@@ -0,0 +1,71 @@
1
+ # ADR 0008 — Navegação hierárquica é composta na fronteira do consumidor
2
+
3
+ - **Status:** aceita.
4
+ - **Data:** 2026-09-06.
5
+
6
+ ## Contexto
7
+
8
+ `SidebarNav` oferece `groups`, `items` e `subgroups`. O terceiro nível foi introduzido para
9
+ preservar a estrutura seção → grupo → página do `DocBrowser`, mas não possui outro consumidor no
10
+ Opus nem nos aplicativos conhecidos. A API geral passou a carregar uma topologia rígida criada por
11
+ uma única tela.
12
+
13
+ Ao mesmo tempo, `SidebarItem` já representa os comportamentos necessários para árvores: um destino
14
+ principal, um controle irmão de expansão por `onToggle` e `expanded`, ações por item e indicadores
15
+ de drop. O estado da árvore e a origem de seus nós variam por consumidor. Porém, o recuo e a guia
16
+ vertical que comunicam descendência se repetiam nos aplicativos como a mesma combinação de classes,
17
+ embora sejam parte da linguagem visual da sidebar.
18
+
19
+ ## Decisão
20
+
21
+ `SidebarNav` mantém somente a estrutura plana `groups → items`. `SidebarNavSubgroup` e a
22
+ propriedade `subgroups` deixam a API pública.
23
+
24
+ Hierarquias são compostas pelo consumidor com `SidebarGroupLabel`, `SidebarItem` e
25
+ `SidebarTreeGroup`. O consumidor controla expansão, ordem, profundidade e carregamento dos nós;
26
+ `SidebarTreeGroup` aplica a guia e o recuo dos descendentes, enquanto `SidebarItem` mantém a
27
+ apresentação e a acessibilidade de cada linha.
28
+
29
+ O `DocBrowser` materializa sua árvore própria a partir de `DocSection[]`. Seções aparecem como
30
+ rótulos, grupos nomeados viram nós expansíveis e páginas viram folhas. Grupos começam abertos para
31
+ preservar a descoberta atual, podem ser recolhidos e voltam a abrir quando passam a conter a página
32
+ ativa.
33
+
34
+ ## Consequências
35
+
36
+ - A API geral deixa de prometer uma árvore limitada a exatamente três níveis.
37
+ - A documentação continua preservando seção, grupo e página sem usar um contrato específico no
38
+ componente compartilhado.
39
+ - Outros consumidores podem montar árvores rasas, recursivas, assíncronas ou arrastáveis com os
40
+ mesmos elementos, sem novas propriedades na `SidebarNav` nem classes locais para a guia.
41
+ - Remover `SidebarNavSubgroup` e `subgroups` é uma mudança incompatível e exige uma versão major na
42
+ próxima publicação do pacote.
43
+ - `DocBrowser` passa a possuir o estado de expansão de seus grupos.
44
+
45
+ ## Alternativas consideradas
46
+
47
+ ### Manter `subgroups`
48
+
49
+ Preservaria compatibilidade, mas manteria na API geral uma topologia usada somente pela
50
+ documentação. Foi descartada porque a composição existente já cobre o comportamento sem acoplar o
51
+ componente à estrutura de uma tela.
52
+
53
+ ### Substituir por uma árvore recursiva em `SidebarNav`
54
+
55
+ Aceitar `children` recursivos permitiria profundidade arbitrária, mas também exigiria que o
56
+ componente decidisse expansão, carregamento, seleção e semântica de pastas para todos os produtos.
57
+ Foi descartada até que consumidores independentes revelem um contrato comum.
58
+
59
+ ### Achatar a documentação
60
+
61
+ Converter grupos em uma lista única simplificaria o renderer, mas perderia a classificação que
62
+ também é produzida por pastas e pelo frontmatter das docs de projetos. Foi descartada porque a
63
+ hierarquia tem significado para descoberta e localização.
64
+
65
+ ## Verificação
66
+
67
+ - Um teste de API impede que `SidebarNavSubgroup` volte a ser exportado.
68
+ - Testes de `SidebarNav` cobrem grupos planos, seleção e omissão de grupos vazios.
69
+ - Um teste de `SidebarTreeGroup` protege a guia, o recuo e o espaçamento entre descendentes.
70
+ - Testes de `DocBrowser` cobrem os três níveis, expansão e reabertura do grupo da página ativa.
71
+ - A documentação do Sidebar demonstra árvore com `SidebarTreeGroup` e não menciona `subgroups`.
@@ -27,7 +27,7 @@ voltaria a acoplar forma e tipo de componente, problema removido na versão 10.0
27
27
  token semântico por componente.
28
28
  - Superfícies aninhadas evitam moldura dupla e normalmente usam um degrau menor que o contêiner.
29
29
  Elementos conectados removem os raios nas arestas internas.
30
- - Card, Dialog e AlertDialog permanecem em `xl`. Molduras estruturais com cantos, como a tabela
30
+ - Card e os dois modos de Dialog permanecem em `xl`. Molduras estruturais com cantos, como a tabela
31
31
  standalone, permanecem em `lg`; Page, Split e Sidebar não ganham uma moldura por essa regra.
32
32
  - Calendar e Command preservam a geometria embutível. Um consumidor standalone pode ajustar o
33
33
  contêiner por `className` quando a composição pedir mais presença.