@softize/opus 22.2.0 → 24.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 (115) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/bin/cli.mjs +3 -9
  3. package/bin/lib/check.mjs +242 -49
  4. package/bin/lib/copy.mjs +17 -35
  5. package/bin/lib/gen-runner.mjs +2 -0
  6. package/bin/lib/gen-stubs.mjs +3 -1
  7. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +1 -1
  8. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +6 -0
  9. package/docs/adr/0032-tab-baseline-respects-the-container-inset.md +16 -12
  10. package/docs/adr/0034-tabs-can-own-a-frame-without-owning-a-surface.md +8 -6
  11. package/docs/adr/0036-tabs-declares-trigger-distribution.md +11 -8
  12. package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
  13. package/docs/adr/0038-content-level-defines-primary-presentation.md +62 -0
  14. package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +62 -0
  15. package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +59 -0
  16. package/docs/adr/0042-toggle-group-shares-the-grouped-control-grammar.md +79 -0
  17. package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +73 -0
  18. package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +133 -0
  19. package/docs/adr/0045-surface-replaces-content-and-card.md +230 -0
  20. package/docs/adr/0046-structural-body-owns-content-rhythm.md +103 -0
  21. package/docs/adr/0047-pane-is-only-a-split-layout-unit.md +71 -0
  22. package/docs/adr/0048-modal-intro-separates-content-from-chrome.md +34 -0
  23. package/docs/code-style.md +1 -1
  24. package/package.json +5 -1
  25. package/registry/skills/build-opus-ui/SKILL.md +11 -11
  26. package/registry/skills/build-opus-ui/references/evaluations.md +10 -9
  27. package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -16
  28. package/registry/skills/create-opus-action/references/contract-and-binding.md +3 -3
  29. package/registry/templates/app/src/App.tsx +24 -12
  30. package/src/core/contracts.ts +1 -1
  31. package/src/core/runtime.ts +4 -37
  32. package/src/core/types.ts +5 -23
  33. package/src/design/index.ts +14 -3
  34. package/src/testing/index.ts +6 -26
  35. package/src/ui/appearance.ts +149 -0
  36. package/src/ui/components/patterns/action-form-card.tsx +46 -33
  37. package/src/ui/components/patterns/action-list-dialog.tsx +2 -4
  38. package/src/ui/components/patterns/confirm.tsx +3 -1
  39. package/src/ui/components/patterns/fill-region.ts +8 -7
  40. package/src/ui/components/patterns/form-dialog.tsx +1 -3
  41. package/src/ui/components/patterns/form.tsx +8 -7
  42. package/src/ui/components/patterns/list.tsx +64 -14
  43. package/src/ui/components/patterns/page.tsx +15 -11
  44. package/src/ui/components/patterns/pane.tsx +9 -240
  45. package/src/ui/components/patterns/presentation.tsx +14 -14
  46. package/src/ui/components/patterns/sidebar.tsx +7 -17
  47. package/src/ui/components/patterns/split.tsx +111 -59
  48. package/src/ui/components/patterns/{surface-header.tsx → structural-header.tsx} +50 -19
  49. package/src/ui/components/patterns/structural-media.tsx +26 -0
  50. package/src/ui/components/patterns/surface-anatomy.ts +71 -61
  51. package/src/ui/components/patterns/surface-assistant.tsx +2 -4
  52. package/src/ui/components/patterns/surface.tsx +620 -0
  53. package/src/ui/components/patterns/trigger.tsx +11 -9
  54. package/src/ui/components/primitives/ask.tsx +155 -83
  55. package/src/ui/components/primitives/button.tsx +11 -9
  56. package/src/ui/components/primitives/checkbox.tsx +8 -8
  57. package/src/ui/components/primitives/command.tsx +1 -1
  58. package/src/ui/components/primitives/composer.tsx +47 -45
  59. package/src/ui/components/primitives/detail.tsx +9 -2
  60. package/src/ui/components/primitives/dialog.tsx +60 -26
  61. package/src/ui/components/primitives/drawer.tsx +58 -12
  62. package/src/ui/components/primitives/icon-picker.tsx +93 -70
  63. package/src/ui/components/primitives/input-group.tsx +35 -28
  64. package/src/ui/components/primitives/input-otp.tsx +16 -16
  65. package/src/ui/components/primitives/input.tsx +20 -10
  66. package/src/ui/components/primitives/item.tsx +9 -12
  67. package/src/ui/components/primitives/menu.tsx +44 -65
  68. package/src/ui/components/primitives/metric-card.tsx +75 -59
  69. package/src/ui/components/primitives/radio-group.tsx +9 -9
  70. package/src/ui/components/primitives/select.tsx +344 -209
  71. package/src/ui/components/primitives/switch.tsx +10 -10
  72. package/src/ui/components/primitives/table.tsx +29 -24
  73. package/src/ui/components/primitives/tabs.tsx +58 -88
  74. package/src/ui/components/primitives/textarea.tsx +6 -6
  75. package/src/ui/components/primitives/toggle-group-context.ts +24 -0
  76. package/src/ui/components/primitives/toggle-group.tsx +116 -74
  77. package/src/ui/components/primitives/toggle.tsx +82 -16
  78. package/src/ui/docs/DocBrowser.tsx +77 -69
  79. package/src/ui/docs/content/action-form-card.md +9 -9
  80. package/src/ui/docs/content/action-list.md +85 -70
  81. package/src/ui/docs/content/action-view.md +33 -18
  82. package/src/ui/docs/content/actions.md +6 -7
  83. package/src/ui/docs/content/button.md +1 -1
  84. package/src/ui/docs/content/communication.md +1 -1
  85. package/src/ui/docs/content/customization.md +64 -11
  86. package/src/ui/docs/content/detail.md +8 -6
  87. package/src/ui/docs/content/dialog.md +100 -58
  88. package/src/ui/docs/content/drawer.md +63 -19
  89. package/src/ui/docs/content/field.md +14 -15
  90. package/src/ui/docs/content/icon-tile.md +14 -13
  91. package/src/ui/docs/content/page.md +34 -26
  92. package/src/ui/docs/content/pane.md +41 -290
  93. package/src/ui/docs/content/presentation.md +3 -3
  94. package/src/ui/docs/content/semantic-context.md +0 -1
  95. package/src/ui/docs/content/separator.md +6 -6
  96. package/src/ui/docs/content/sidebar.md +68 -266
  97. package/src/ui/docs/content/skeleton.md +20 -18
  98. package/src/ui/docs/content/split.md +14 -14
  99. package/src/ui/docs/content/surface.md +513 -0
  100. package/src/ui/docs/content/tabs.md +82 -83
  101. package/src/ui/docs/content/testing.md +2 -3
  102. package/src/ui/docs/content/toggle.md +120 -63
  103. package/src/ui/docs/content/tokens.md +124 -40
  104. package/src/ui/docs/registry.tsx +4 -6
  105. package/src/ui/drivers/react.tsx +4 -3
  106. package/src/ui/index.ts +30 -27
  107. package/src/ui/meta.ts +15 -27
  108. package/src/ui/react.tsx +42 -46
  109. package/src/ui/theme.css +311 -64
  110. package/src/vite/design.ts +23 -21
  111. package/src/ui/components/patterns/content-header.tsx +0 -532
  112. package/src/ui/components/patterns/surface-media.tsx +0 -69
  113. package/src/ui/components/primitives/card.tsx +0 -191
  114. package/src/ui/docs/content/card.md +0 -292
  115. package/src/ui/docs/content/content.md +0 -276
@@ -29,9 +29,11 @@ distinta da página.
29
29
  <DialogHeader>
30
30
  <DialogTitle>Criar workspace</DialogTitle>
31
31
  </DialogHeader>
32
- <DialogBody className="space-y-2">
33
- <Label htmlFor="workspace-name">Nome</Label>
34
- <Input id="workspace-name" placeholder="Ex.: Empresa X" />
32
+ <DialogBody>
33
+ <div className="space-y-2">
34
+ <Label htmlFor="workspace-name">Nome</Label>
35
+ <Input id="workspace-name" placeholder="Ex.: Empresa X" />
36
+ </div>
35
37
  </DialogBody>
36
38
  <DialogFooter>
37
39
  <ButtonGroup mode="spaced">
@@ -46,6 +48,9 @@ distinta da página.
46
48
  ```
47
49
 
48
50
  O corpo cresce até o limite da janela e passa a rolar; cabeçalho e rodapé permanecem visíveis.
51
+ Seus filhos diretos recebem o ritmo estrutural de `1.5rem`. Um grupo compacto, uma grade ou uma
52
+ linha declara a própria relação em um wrapper filho; `flush` fica reservado ao conteúdo que
53
+ controla todo o inset.
49
54
  O backdrop escurece a página sem aplicar desfoque.
50
55
  O fechamento do cabeçalho usa uma ação circular neutra e sutil. `closeSize` escolhe entre `xs` e
51
56
  `sm`; o padrão é `sm`.
@@ -131,7 +136,9 @@ fluxo modal encadeado, em que a superfície permanece aberta e troca de etapa.
131
136
  </DialogActions>
132
137
  </DialogHeader>
133
138
  <DialogBody>
134
- <p className="text-sm text-muted-foreground">Detalhe do evento selecionado.</p>
139
+ <p className="text-sm text-muted-foreground">
140
+ Detalhe do evento selecionado.
141
+ </p>
135
142
  </DialogBody>
136
143
  </DialogContent>
137
144
  </Dialog>
@@ -147,9 +154,9 @@ decidir aparece no começo do corpo, como texto ou `Alert`.
147
154
  ## Identificar a tarefa com um ícone
148
155
 
149
156
  Use `DialogMedia` quando um ícone ajuda a reconhecer a tarefa antes da leitura do título. No modo
150
- padrão, ele abre a linha do cabeçalho com a mesma moldura de [`IconTile`](/ui/icon-tile) das demais
151
- superfícies. No `mode="alert"`, a moldura cresce e fica centralizada acima do título, porque ali o
152
- ícone é o principal sinal de reconhecimento da resposta.
157
+ padrão, ele apenas reserva e alinha o espaço antes do título. Componha [`IconTile`](/ui/icon-tile)
158
+ explicitamente quando o ícone precisar de moldura; passe o glifo diretamente quando não precisar.
159
+ O mesmo contrato vale no `mode="alert"`: a mídia não ganha tamanho, fundo ou borda automaticamente.
153
160
 
154
161
  ```tsx preview
155
162
  render(
@@ -161,13 +168,17 @@ render(
161
168
  <DialogContent>
162
169
  <DialogHeader>
163
170
  <DialogMedia>
164
- <Share2 />
171
+ <IconTile size="sm">
172
+ <Share2 />
173
+ </IconTile>
165
174
  </DialogMedia>
166
175
  <DialogTitle>Compartilhar contrato</DialogTitle>
167
176
  </DialogHeader>
168
- <DialogBody className="space-y-2">
169
- <Label htmlFor="share-email">E-mail</Label>
170
- <Input id="share-email" placeholder="nome@empresa.com.br" />
177
+ <DialogBody>
178
+ <div className="space-y-2">
179
+ <Label htmlFor="share-email">E-mail</Label>
180
+ <Input id="share-email" placeholder="nome@empresa.com.br" />
181
+ </div>
171
182
  </DialogBody>
172
183
  <DialogFooter>
173
184
  <ButtonGroup mode="spaced">
@@ -185,8 +196,10 @@ render(
185
196
  </DialogTrigger>
186
197
  <DialogContent>
187
198
  <DialogHeader>
188
- <DialogMedia context="danger">
189
- <ShieldOff />
199
+ <DialogMedia>
200
+ <IconTile size="lg" context="danger">
201
+ <ShieldOff />
202
+ </IconTile>
190
203
  </DialogMedia>
191
204
  <DialogTitle>Revogar acesso de Marina?</DialogTitle>
192
205
  </DialogHeader>
@@ -211,7 +224,8 @@ render(
211
224
  );
212
225
  ```
213
226
 
214
- Na API imperativa, a opção `media` recebe o ícone e o apresenta na mesma moldura do alerta.
227
+ Na API imperativa, a opção `media` preserva o nó recebido. Passe um `IconTile` quando quiser
228
+ moldura.
215
229
 
216
230
  ## Pedir uma resposta pela API imperativa
217
231
 
@@ -245,7 +259,11 @@ function Demo() {
245
259
  const accepted = await dialog.confirm({
246
260
  title: "Excluir rascunho?",
247
261
  body: "O rascunho e os comentários dele serão removidos.",
248
- media: <Trash2 />,
262
+ media: (
263
+ <IconTile context="danger">
264
+ <Trash2 />
265
+ </IconTile>
266
+ ),
249
267
  action: "Excluir",
250
268
  context: "danger",
251
269
  });
@@ -443,42 +461,43 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Arqu
443
461
 
444
462
  ## Propriedades de DialogContent
445
463
 
446
- | Propriedade | Tipo | Padrão | Descrição |
447
- | ----------------- | ----------- | ------ | --------------------------------------------------------------------------------------------------------- |
448
- | `showCloseButton` | `boolean` | `true` | No modo padrão, inclui a ação de fechamento no header; o modo de alerta exige uma resposta identificável. |
449
- | `closeDisabled` | `boolean` | `false` | Mantém o botão de fechar visível e desabilitado e impede que Esc e o clique externo fechem a superfície. Handlers como `onEscapeKeyDown` e `onPointerDownOutside` continuam sendo chamados antes do bloqueio. |
450
- | `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
451
- | `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. Ignorado em `mode="alert"`. |
452
- | `className` | `string` | | Ajusta a superfície. A largura máxima é `32rem` no modo padrão e `20rem` no alerta; amplie o alerta com `max-w-lg` quando o rodapé tiver três ações. |
453
- | `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou conteúdo próprio. |
464
+ | Propriedade | Tipo | Padrão | Descrição |
465
+ | ----------------- | ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
466
+ | `showCloseButton` | `boolean` | `true` | No modo padrão, inclui a ação de fechamento no header; o modo de alerta exige uma resposta identificável. |
467
+ | `closeDisabled` | `boolean` | `false` | Mantém o botão de fechar visível e desabilitado e impede que Esc e o clique externo fechem a superfície. Handlers como `onEscapeKeyDown` e `onPointerDownOutside` continuam sendo chamados antes do bloqueio. |
468
+ | `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
469
+ | `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. Ignorado em `mode="alert"`. |
470
+ | `className` | `string` | | Ajusta a superfície. A largura máxima é `32rem` no modo padrão e `20rem` no alerta; amplie o alerta com `max-w-lg` quando o rodapé tiver três ações. |
471
+ | `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou conteúdo próprio. |
454
472
 
455
473
  ## Propriedades de DialogHeader
456
474
 
457
- | Propriedade | Tipo | Padrão | Descrição |
458
- | ----------- | ----------- | ------ | ---------------------------------------------------------- |
459
- | `className` | `string` | | Ajusta a faixa superior sobre o recuo do layout. |
475
+ | Propriedade | Tipo | Padrão | Descrição |
476
+ | ----------- | ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
477
+ | `className` | `string` | | Ajusta a faixa superior sobre o recuo do layout. |
460
478
  | `children` | `ReactNode` | | Somente `DialogNavigation`, `DialogMedia`, `DialogTitle` e `DialogActions`, no máximo um de cada; o fechamento padrão entra depois deles. |
461
479
 
462
480
  ## Propriedades de DialogNavigation
463
481
 
464
- | Propriedade | Tipo | Padrão | Descrição |
465
- | ----------- | ----------- | ------ | -------------------------------------------------------------- |
466
- | `className` | `string` | | Ajusta a região introdutória da linha. |
467
- | `children` | `ReactNode` | | Retorno de um fluxo modal encadeado, antes do título. |
482
+ | Propriedade | Tipo | Padrão | Descrição |
483
+ | ----------- | ----------- | ------ | ----------------------------------------------------- |
484
+ | `className` | `string` | | Ajusta a região introdutória da linha. |
485
+ | `children` | `ReactNode` | | Retorno de um fluxo modal encadeado, antes do título. |
468
486
 
469
487
  ## Propriedades de DialogActions
470
488
 
471
- | Propriedade | Tipo | Padrão | Descrição |
472
- | ----------- | ----------- | ------ | ------------------------------------------------------------------ |
473
- | `className` | `string` | | Ajusta o agrupamento das ações. |
474
- | `children` | `ReactNode` | | Ações da tela no cabeçalho, antes do botão de fechar. |
489
+ | Propriedade | Tipo | Padrão | Descrição |
490
+ | ----------- | ----------- | ------ | ----------------------------------------------------- |
491
+ | `className` | `string` | | Ajusta o agrupamento das ações. |
492
+ | `children` | `ReactNode` | | Ações da tela no cabeçalho, antes do botão de fechar. |
475
493
 
476
494
  ## Propriedades de DialogBody
477
495
 
478
- | Propriedade | Tipo | Padrão | Descrição |
479
- | ----------- | ----------- | ------ | ---------------------------------------------------------------------- |
480
- | `className` | `string` | | Ajusta o corpo rolável do modo padrão ou o conteúdo próprio do alerta. |
481
- | `children` | `ReactNode` | | Conteúdo principal da tarefa. |
496
+ | Propriedade | Tipo | Padrão | Descrição |
497
+ | ----------- | ----------- | ------- | ---------------------------------------------------------------------- |
498
+ | `flush` | `boolean` | `false` | Remove o inset para conteúdo que controla o próprio recuo. |
499
+ | `className` | `string` | | Ajusta o corpo rolável do modo padrão ou o conteúdo próprio do alerta. |
500
+ | `children` | `ReactNode` | | Conteúdo principal da tarefa. |
482
501
 
483
502
  ## Propriedades de DialogFooter
484
503
 
@@ -489,25 +508,18 @@ botão. O texto informa o efeito real, como `Excluir`, `Revogar acesso` ou `Arqu
489
508
 
490
509
  ## Propriedades de DialogTitle
491
510
 
492
- | Propriedade | Tipo | Padrão | Descrição |
493
- | ----------- | ----------- | ------ | ------------------------------------ |
494
- | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `h2` da primitiva | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
495
- | `className` | `string` | | Ajusta o título. No modo padrão, ele trunca quando não cabe na linha; no alerta, fica centralizado e pode ocupar mais de uma linha. |
496
- | `children` | `ReactNode` | | Nome da tarefa ou do conteúdo modal. |
511
+ | Propriedade | Tipo | Padrão | Descrição |
512
+ | ----------- | ---------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
513
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `h2` da primitiva | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
514
+ | `className` | `string` | | Ajusta o título. No modo padrão, ele trunca quando não cabe na linha; no alerta, fica centralizado e pode ocupar mais de uma linha. |
515
+ | `children` | `ReactNode` | | Nome da tarefa ou do conteúdo modal. |
497
516
 
498
517
  ## Propriedades de DialogMedia
499
518
 
500
- A moldura vem do `IconTile`, como nas demais famílias. No `mode="alert"` ela cresce e centraliza:
501
- ali a mídia não acompanha uma linha de título, ela é o que faz a pessoa reconhecer a resposta.
502
-
503
- | Propriedade | Tipo | Padrão | Descrição |
504
- | ----------- | ----------------------------------------------------------- | --------- | -------------------------------------------------- |
505
- | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `sm` | Escala da moldura; no alerta vale a moldura maior. |
506
- | `context` | `UiContext` | `neutral` | Cor semântica da moldura. |
507
- | `variant` | `'subtle' \| 'outline'` | `subtle` | Tratamento visual da moldura. |
508
- | `className` | `string` | | Ajustes pontuais da moldura. |
509
- | `children` | `ReactNode` | | Ícone que reforça o contexto da resposta. |
510
- | `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
519
+ | Propriedade | Tipo | Padrão | Descrição |
520
+ | ----------- | ----------- | ------ | ----------------------------------------------------------------------- |
521
+ | `className` | `string` | | Ajusta o alinhamento do slot. |
522
+ | `children` | `ReactNode` | | `IconTile`, marca, imagem ou glifo. O slot não acrescenta apresentação. |
511
523
 
512
524
  ## Propriedades de DialogClose
513
525
 
@@ -540,7 +552,7 @@ imperativa.
540
552
  | -------- | ----------- | ------ | ------------------------------------------------ |
541
553
  | `title` | `ReactNode` | | Nomeia o aviso que exige reconhecimento. |
542
554
  | `body` | `ReactNode` | | Apresenta contexto relevante no início do corpo. |
543
- | `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
555
+ | `media` | `ReactNode` | | Exibe o nó recebido em `DialogMedia`. |
544
556
  | `action` | `string` | `'OK'` | Define o rótulo de reconhecimento. |
545
557
 
546
558
  ## Opções de dialog.confirm
@@ -549,7 +561,7 @@ imperativa.
549
561
  | --------- | ----------------------- | ------------- | ------------------------------------------------------------------- |
550
562
  | `title` | `ReactNode` | | Nomeia a decisão. |
551
563
  | `body` | `ReactNode` | | Apresenta a consequência ou outro contexto necessário para decidir. |
552
- | `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
564
+ | `media` | `ReactNode` | | Exibe o nó recebido em `DialogMedia`. |
553
565
  | `action` | `string` | `'Confirmar'` | Define o rótulo do resultado afirmativo. |
554
566
  | `cancel` | `string` | `'Cancelar'` | Define o rótulo da resposta negativa. |
555
567
  | `context` | `'primary' \| 'danger'` | `'primary'` | Define o contexto da ação afirmativa. |
@@ -560,7 +572,7 @@ imperativa.
560
572
  | -------------- | ----------------------- | ------------- | ------------------------------------------------------------------ |
561
573
  | `title` | `ReactNode` | | Nomeia a informação solicitada. |
562
574
  | `body` | `ReactNode` | | Explica como a informação será usada quando isso mudar a resposta. |
563
- | `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
575
+ | `media` | `ReactNode` | | Exibe o nó recebido em `DialogMedia`. |
564
576
  | `action` | `string` | `'Confirmar'` | Define o rótulo do resultado afirmativo. |
565
577
  | `cancel` | `string` | `'Cancelar'` | Define o rótulo da resposta negativa. |
566
578
  | `context` | `'primary' \| 'danger'` | `'primary'` | Define o contexto da ação afirmativa. |
@@ -573,7 +585,7 @@ imperativa.
573
585
  | --------- | ------------------------- | ------ | ------------------------------------------------ |
574
586
  | `title` | `ReactNode` | | Nomeia a escolha. |
575
587
  | `body` | `ReactNode` | | Apresenta o contexto necessário antes das ações. |
576
- | `media` | `ReactNode` | | Exibe um ícone na moldura de `DialogMedia`. |
588
+ | `media` | `ReactNode` | | Exibe o nó recebido em `DialogMedia`. |
577
589
  | `actions` | `readonly DialogChoice[]` | | Declara os resultados disponíveis. |
578
590
 
579
591
  ## Propriedades de DialogChoice
@@ -586,3 +598,33 @@ imperativa.
586
598
  | `context` | `ButtonContext` | Última ação: `primary`; demais: `neutral` | Define o significado semântico. |
587
599
  | `variant` | `ButtonVariant` | Última ação: `solid`; demais: `outline` | Define o tratamento visual. |
588
600
  | `disabled` | `boolean` | `false` | Impede a escolha desta ação. |
601
+
602
+ ## Intro separada do chrome
603
+
604
+ Quando o header contém apenas navegação, ações globais e o fechamento, coloque o título do
605
+ conteúdo no primeiro bloco de `DialogBody`. `DialogIntro` exige um `DialogTitle` e aceita um
606
+ `DialogIntroActions`; o body continua responsável pelo espaço antes, entre e depois dos blocos.
607
+
608
+ ```tsx
609
+ <Dialog>
610
+ <DialogContent>
611
+ <DialogHeader>
612
+ <DialogActions>
613
+ <Button variant="ghost">Ajuda</Button>
614
+ </DialogActions>
615
+ </DialogHeader>
616
+ <DialogBody>
617
+ <DialogIntro>
618
+ <DialogTitle>Editar evento</DialogTitle>
619
+ <DialogIntroActions>
620
+ <Button variant="outline">Revisar</Button>
621
+ </DialogIntroActions>
622
+ </DialogIntro>
623
+ <form>…</form>
624
+ </DialogBody>
625
+ <DialogFooter>…</DialogFooter>
626
+ </DialogContent>
627
+ </Dialog>
628
+ ```
629
+
630
+ No formato compacto, mantenha `DialogTitle` e `DialogActions` no próprio `DialogHeader`.
@@ -18,9 +18,11 @@ cantos, em qualquer valor de `side`.
18
18
  <DrawerHeader>
19
19
  <DrawerTitle>ABC-1D23</DrawerTitle>
20
20
  </DrawerHeader>
21
- <DrawerBody className="space-y-1 text-sm text-muted-foreground">
22
- <p>Corolla XEi 2022 · 38.400 km</p>
23
- <p>Última revisão em 12 de maio.</p>
21
+ <DrawerBody className="text-sm text-muted-foreground">
22
+ <div className="space-y-1">
23
+ <p>Corolla XEi 2022 · 38.400 km</p>
24
+ <p>Última revisão em 12 de maio.</p>
25
+ </div>
24
26
  </DrawerBody>
25
27
  </DrawerContent>
26
28
  </Drawer>
@@ -42,9 +44,11 @@ neutra e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
42
44
  <DrawerHeader>
43
45
  <DrawerTitle>Editar veículo</DrawerTitle>
44
46
  </DrawerHeader>
45
- <DrawerBody className="space-y-2">
46
- <Label htmlFor="vehicle-owner">Responsável</Label>
47
- <Input id="vehicle-owner" defaultValue="Marina Alves" />
47
+ <DrawerBody>
48
+ <div className="space-y-2">
49
+ <Label htmlFor="vehicle-owner">Responsável</Label>
50
+ <Input id="vehicle-owner" defaultValue="Marina Alves" />
51
+ </div>
48
52
  </DrawerBody>
49
53
  <DrawerFooter>
50
54
  <ButtonGroup mode="spaced">
@@ -102,9 +106,9 @@ título ocupa a folga, as ações vão para o extremo oposto e o botão de fecha
102
106
  nó que não seja um desses slots interrompe a renderização com um erro; contexto e conteúdo
103
107
  pertencem ao `DrawerBody`.
104
108
 
105
- `DrawerMedia` é a moldura de ícone do Opus — o mesmo [`IconTile`](/ui/icon-tile) —, útil para
106
- identificar o tipo do recurso aberto. `DrawerNavigation` carrega o retorno de um fluxo encadeado,
107
- em que o painel permanece aberto e troca de etapa.
109
+ `DrawerMedia` reserva o espaço que identifica o tipo do recurso aberto, sem acrescentar moldura.
110
+ Componha [`IconTile`](/ui/icon-tile) quando ela ajudar na hierarquia. `DrawerNavigation` carrega o
111
+ retorno de um fluxo encadeado, em que o painel permanece aberto e troca de etapa.
108
112
 
109
113
  ```tsx preview
110
114
  <Drawer>
@@ -118,8 +122,10 @@ em que o painel permanece aberto e troca de etapa.
118
122
  <ArrowLeft />
119
123
  </Button>
120
124
  </DrawerNavigation>
121
- <DrawerMedia context="primary">
122
- <CarFront />
125
+ <DrawerMedia>
126
+ <IconTile size="sm" context="primary">
127
+ <CarFront />
128
+ </IconTile>
123
129
  </DrawerMedia>
124
130
  <DrawerTitle>ABC-1D23</DrawerTitle>
125
131
  <DrawerActions>
@@ -210,13 +216,10 @@ de continuar, use [`Dialog mode="alert"`](/ui/dialog), que não fecha pelo cliqu
210
216
 
211
217
  ## Propriedades de DrawerMedia
212
218
 
213
- | Propriedade | Tipo | Padrão | Descrição |
214
- | ----------- | ------------------------------------------------------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------- |
215
- | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'sm'` | Tamanho da moldura e do glifo na escala compartilhada. |
216
- | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
217
- | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
218
- | `className` | `string` | | Ajusta a moldura sem substituir a escala. |
219
- | `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
219
+ | Propriedade | Tipo | Padrão | Descrição |
220
+ | ----------- | ----------- | ------ | ----------------------------------------------------------------------- |
221
+ | `className` | `string` | | Ajusta o alinhamento do slot. |
222
+ | `children` | `ReactNode` | | `IconTile`, marca, imagem ou glifo. O slot não acrescenta apresentação. |
220
223
 
221
224
  ## Propriedades de DrawerNavigation
222
225
 
@@ -244,7 +247,14 @@ de continuar, use [`Dialog mode="alert"`](/ui/dialog), que não fecha pelo cliqu
244
247
 
245
248
  `DrawerBody` aceita os atributos de `HTMLDivElement` e ocupa a região flexível e rolável entre
246
249
  `DrawerHeader` e `DrawerFooter`. O cabeçalho nomeia o painel; contexto relevante entra no começo
247
- do corpo como texto ou `Alert`.
250
+ do corpo como texto ou `Alert`. Seus filhos diretos recebem o ritmo estrutural; use `flush` quando
251
+ o conteúdo controlar o próprio inset e um wrapper filho para relações internas mais compactas.
252
+
253
+ | Propriedade | Tipo | Padrão | Descrição |
254
+ | ----------- | ----------- | ------- | ---------------------------------------------------------- |
255
+ | `flush` | `boolean` | `false` | Remove o inset para conteúdo que controla o próprio recuo. |
256
+ | `className` | `string` | | Ajusta ocupação, overflow, alinhamento ou tipografia. |
257
+ | `children` | `ReactNode` | | Blocos principais do painel. |
248
258
 
249
259
  ## Propriedades de DrawerFooter
250
260
 
@@ -252,3 +262,37 @@ do corpo como texto ou `Alert`.
252
262
  | ----------- | ----------- | ------ | ------------------------------------------------------------- |
253
263
  | `className` | `string` | | Ajusta a faixa do rodapé, inclusive a distribuição das ações. |
254
264
  | `children` | `ReactNode` | | Ações persistentes, normalmente dentro de um `ButtonGroup`. |
265
+
266
+ ## Intro separada do chrome
267
+
268
+ Quando o header contém apenas o recolhimento, ações globais e o fechamento, use `DrawerIntro`
269
+ como primeiro bloco de `DrawerBody`. Ele exige um `DrawerTitle` e aceita `DrawerIntroActions`.
270
+ Assim o título acompanha o conteúdo rolável enquanto o chrome permanece estável.
271
+
272
+ ```tsx
273
+ <Drawer>
274
+ <DrawerContent showCloseButton={false}>
275
+ <DrawerHeader>
276
+ <DrawerNavigation>
277
+ <DrawerClose asChild>
278
+ <Button>Recolher</Button>
279
+ </DrawerClose>
280
+ </DrawerNavigation>
281
+ <DrawerActions>
282
+ <Button variant="ghost">Conversa</Button>
283
+ </DrawerActions>
284
+ </DrawerHeader>
285
+ <DrawerBody>
286
+ <DrawerIntro>
287
+ <DrawerTitle>Editar evento</DrawerTitle>
288
+ <DrawerIntroActions>
289
+ <Button variant="outline">Revisar</Button>
290
+ </DrawerIntroActions>
291
+ </DrawerIntro>
292
+ <form>…</form>
293
+ </DrawerBody>
294
+ </DrawerContent>
295
+ </Drawer>
296
+ ```
297
+
298
+ No formato compacto, mantenha `DrawerTitle` e `DrawerActions` no próprio `DrawerHeader`.
@@ -37,14 +37,14 @@ vincula ao controle, ligue-o ao controle por `aria-labelledby` para manter o nom
37
37
 
38
38
  ## Conjunto com legenda e erro
39
39
 
40
- Use [`Content`](/ui/content) com `as="fieldset"` para nomear um conjunto de campos relacionados. O título vira a legenda
40
+ Use [`Surface`](/ui/surface) com `as="fieldset"` para nomear um conjunto de campos relacionados. O título vira a legenda
41
41
  nativa, enquanto `FieldGroup` define o espaçamento e `FieldSeparator` marca uma divisão.
42
42
  `FieldError` aparece somente quando existe mensagem de erro. Declare `data-invalid="true"` no
43
43
  `Field` para que o rótulo acompanhe o contexto de erro; `aria-invalid` marca apenas o controle. O
44
44
  `ActionForm` define os dois atributos sozinho.
45
45
 
46
46
  ```tsx preview col md
47
- <Content as="fieldset" title="Novo agente">
47
+ <Surface as="fieldset" title="Novo agente">
48
48
  <FieldGroup>
49
49
  <Field data-invalid="true">
50
50
  <FieldLabel htmlFor="agent-name">Nome</FieldLabel>
@@ -69,7 +69,7 @@ nativa, enquanto `FieldGroup` define o espaçamento e `FieldSeparator` marca uma
69
69
  />
70
70
  </Field>
71
71
  </FieldGroup>
72
- </Content>
72
+ </Surface>
73
73
  ```
74
74
 
75
75
  ## Campo desabilitado
@@ -86,15 +86,14 @@ declare também `data-disabled="true"` no `Field`: `FieldLabel` e `FieldTitle` e
86
86
 
87
87
  ## Conjunto com moldura
88
88
 
89
- Use a moldura para distinguir grupos vizinhos que continuem pertencendo ao mesmo formulário. O
90
- título permanece uma legenda semântica. A moldura é contínua por padrão; quando o agrupamento pedir
91
- o acabamento compacto com a legenda integrada à borda, declare `frameVariant="legend"`.
89
+ Use a moldura para distinguir grupos vizinhos que continuem pertencendo ao mesmo formulário.
90
+ Declare `as="fieldset"`: a `Surface` transforma o header em `legend` nativa e preserva o título
91
+ como legenda semântica.
92
92
 
93
93
  ```tsx preview col md
94
- <Content
94
+ <Surface
95
95
  as="fieldset"
96
96
  framed
97
- frameVariant="legend"
98
97
  title={
99
98
  <span className="flex items-center gap-2">
100
99
  <IconTile size="xs">
@@ -110,13 +109,13 @@ o acabamento compacto com a legenda integrada à borda, declare `frameVariant="l
110
109
  <Input id="vehicle-plate" defaultValue="GBR1A03" />
111
110
  </Field>
112
111
  </FieldGroup>
113
- </Content>
112
+ </Surface>
114
113
  ```
115
114
 
116
115
  ## Compatibilidade de FieldSet
117
116
 
118
117
  `FieldSet` e `FieldLegend` continuam disponíveis para composições existentes. Em código novo,
119
- prefira `Content as="fieldset"`: ele reúne legenda, corpo e moldura na mesma anatomia usada pelas
118
+ prefira `Surface as="fieldset"`: ele reúne legenda, corpo e moldura na mesma anatomia usada pelas
120
119
  demais regiões nomeadas. A migração pode ser gradual porque os controles e o `FieldGroup` não mudam.
121
120
 
122
121
  ## Propriedades de FieldSet
@@ -127,11 +126,11 @@ demais regiões nomeadas. A migração pode ser gradual porque os controles e o
127
126
 
128
127
  ## Propriedades de Field
129
128
 
130
- | Propriedade | Tipo | Padrão | Descrição |
131
- | -------------- | -------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
132
- | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Direção fixa do campo. `responsive` permanece como alias compatível de `horizontal`. |
133
- | `data-invalid` | `boolean \| 'true' \| 'false'` | | Com `true`, aplica o contexto de erro ao campo e ao rótulo. O `ActionForm` define o atributo sozinho. |
134
- | `data-disabled` | `'true' \| 'false'` | | Com `true`, esmaece `FieldLabel` e `FieldTitle`. Declare `disabled` também no controle. |
129
+ | Propriedade | Tipo | Padrão | Descrição |
130
+ | --------------- | -------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
131
+ | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Direção fixa do campo. `responsive` permanece como alias compatível de `horizontal`. |
132
+ | `data-invalid` | `boolean \| 'true' \| 'false'` | | Com `true`, aplica o contexto de erro ao campo e ao rótulo. O `ActionForm` define o atributo sozinho. |
133
+ | `data-disabled` | `'true' \| 'false'` | | Com `true`, esmaece `FieldLabel` e `FieldTitle`. Declare `disabled` também no controle. |
135
134
 
136
135
  ## Propriedades de FieldLegend
137
136
 
@@ -18,19 +18,20 @@ uma classe `size-*` preserva a própria medida.
18
18
  Slots genéricos de ícone, como `FieldLegend.icon`, não criam a moldura: passe o glifo quando quiser
19
19
  só o ícone e componha `IconTile` quando a moldura reforçar a hierarquia.
20
20
 
21
- Os slots de mídia das superfícies já são um `IconTile`: `CardMedia`, `ContentMedia`, `DialogMedia`,
22
- `DrawerMedia` e `PaneMedia` desenham a moldura sozinhos. Passe só o glifo e escolha `size`,
23
- `context` e `variant` no próprio slot. Em [`Content`](/ui/content), um `IconTile` passado em
24
- `media` é adotado pelo slot, que herda o que ele declara, em vez de desenhar uma moldura dentro da
25
- outra.
21
+ Os slots de mídia estruturais não criam decoração. `SurfaceMedia`, `DialogMedia` e `DrawerMedia`
22
+ reservam posição e alinhamento, mas preservam o nó recebido. Passe o glifo diretamente
23
+ quando ele deve ficar solto; componha `IconTile` de forma explícita quando quiser moldura.
26
24
 
27
25
  ```tsx preview col md
28
- <Content
26
+ <Surface
29
27
  as="fieldset"
30
28
  framed
31
- frameVariant="legend"
32
29
  title="Veículo"
33
- media={<CarFront aria-hidden="true" />}
30
+ media={
31
+ <IconTile size="xs">
32
+ <CarFront aria-hidden="true" />
33
+ </IconTile>
34
+ }
34
35
  >
35
36
  <FieldGroup>
36
37
  <Field>
@@ -38,7 +39,7 @@ outra.
38
39
  <Input id="icon-tile-plate" />
39
40
  </Field>
40
41
  </FieldGroup>
41
- </Content>
42
+ </Surface>
42
43
  ```
43
44
 
44
45
  ## Propriedades de IconTile
@@ -59,10 +60,10 @@ não é renderizado pelo `IconTile`. Os padrões são os mesmos do componente: `
59
60
 
60
61
  ```ts
61
62
  function iconTileVariants(options?: {
62
- context?: IconTileContext | null
63
- variant?: IconTileVariant | null
64
- size?: IconTileSize | null
65
- }): string
63
+ context?: IconTileContext | null;
64
+ variant?: IconTileVariant | null;
65
+ size?: IconTileSize | null;
66
+ }): string;
66
67
  ```
67
68
 
68
69
  `resolveIconTileStyle` continua exportado, mas está depreciado e sai em uma próxima versão maior. Ele