@softize/opus 23.0.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 (95) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/bin/cli.mjs +2 -0
  3. package/bin/lib/check.mjs +241 -42
  4. package/bin/lib/copy.mjs +16 -34
  5. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +6 -0
  6. package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +3 -3
  7. package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +5 -6
  8. package/docs/adr/0042-toggle-group-shares-the-grouped-control-grammar.md +79 -0
  9. package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +73 -0
  10. package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +133 -0
  11. package/docs/adr/0045-surface-replaces-content-and-card.md +230 -0
  12. package/docs/adr/0046-structural-body-owns-content-rhythm.md +103 -0
  13. package/docs/adr/0047-pane-is-only-a-split-layout-unit.md +71 -0
  14. package/docs/adr/0048-modal-intro-separates-content-from-chrome.md +34 -0
  15. package/package.json +5 -1
  16. package/registry/skills/build-opus-ui/SKILL.md +11 -11
  17. package/registry/skills/build-opus-ui/references/evaluations.md +10 -9
  18. package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -16
  19. package/registry/templates/app/src/App.tsx +24 -12
  20. package/src/ui/appearance.ts +149 -0
  21. package/src/ui/components/patterns/action-form-card.tsx +46 -33
  22. package/src/ui/components/patterns/action-list-dialog.tsx +2 -4
  23. package/src/ui/components/patterns/confirm.tsx +3 -1
  24. package/src/ui/components/patterns/fill-region.ts +8 -7
  25. package/src/ui/components/patterns/form-dialog.tsx +1 -3
  26. package/src/ui/components/patterns/form.tsx +8 -7
  27. package/src/ui/components/patterns/list.tsx +15 -19
  28. package/src/ui/components/patterns/page.tsx +15 -11
  29. package/src/ui/components/patterns/pane.tsx +9 -240
  30. package/src/ui/components/patterns/presentation.tsx +14 -14
  31. package/src/ui/components/patterns/sidebar.tsx +7 -17
  32. package/src/ui/components/patterns/split.tsx +111 -59
  33. package/src/ui/components/patterns/{surface-header.tsx → structural-header.tsx} +50 -19
  34. package/src/ui/components/patterns/structural-media.tsx +26 -0
  35. package/src/ui/components/patterns/surface-anatomy.ts +71 -61
  36. package/src/ui/components/patterns/surface-assistant.tsx +2 -4
  37. package/src/ui/components/patterns/surface.tsx +620 -0
  38. package/src/ui/components/patterns/trigger.tsx +11 -9
  39. package/src/ui/components/primitives/ask.tsx +155 -83
  40. package/src/ui/components/primitives/button.tsx +11 -9
  41. package/src/ui/components/primitives/checkbox.tsx +8 -8
  42. package/src/ui/components/primitives/command.tsx +1 -1
  43. package/src/ui/components/primitives/composer.tsx +47 -45
  44. package/src/ui/components/primitives/detail.tsx +9 -2
  45. package/src/ui/components/primitives/dialog.tsx +60 -26
  46. package/src/ui/components/primitives/drawer.tsx +58 -12
  47. package/src/ui/components/primitives/icon-picker.tsx +90 -73
  48. package/src/ui/components/primitives/input-group.tsx +35 -28
  49. package/src/ui/components/primitives/input-otp.tsx +16 -16
  50. package/src/ui/components/primitives/input.tsx +20 -10
  51. package/src/ui/components/primitives/item.tsx +9 -12
  52. package/src/ui/components/primitives/menu.tsx +44 -65
  53. package/src/ui/components/primitives/metric-card.tsx +75 -59
  54. package/src/ui/components/primitives/radio-group.tsx +9 -9
  55. package/src/ui/components/primitives/select.tsx +344 -209
  56. package/src/ui/components/primitives/switch.tsx +10 -10
  57. package/src/ui/components/primitives/table.tsx +29 -24
  58. package/src/ui/components/primitives/tabs.tsx +4 -2
  59. package/src/ui/components/primitives/textarea.tsx +6 -6
  60. package/src/ui/components/primitives/toggle-group-context.ts +24 -0
  61. package/src/ui/components/primitives/toggle-group.tsx +116 -74
  62. package/src/ui/components/primitives/toggle.tsx +82 -16
  63. package/src/ui/docs/DocBrowser.tsx +77 -69
  64. package/src/ui/docs/content/action-form-card.md +9 -9
  65. package/src/ui/docs/content/action-list.md +76 -70
  66. package/src/ui/docs/content/action-view.md +33 -18
  67. package/src/ui/docs/content/button.md +1 -1
  68. package/src/ui/docs/content/communication.md +1 -1
  69. package/src/ui/docs/content/customization.md +64 -11
  70. package/src/ui/docs/content/detail.md +8 -6
  71. package/src/ui/docs/content/dialog.md +100 -58
  72. package/src/ui/docs/content/drawer.md +63 -19
  73. package/src/ui/docs/content/field.md +14 -15
  74. package/src/ui/docs/content/icon-tile.md +14 -13
  75. package/src/ui/docs/content/page.md +34 -26
  76. package/src/ui/docs/content/pane.md +41 -290
  77. package/src/ui/docs/content/presentation.md +3 -3
  78. package/src/ui/docs/content/separator.md +6 -6
  79. package/src/ui/docs/content/sidebar.md +68 -266
  80. package/src/ui/docs/content/skeleton.md +20 -18
  81. package/src/ui/docs/content/split.md +14 -14
  82. package/src/ui/docs/content/surface.md +513 -0
  83. package/src/ui/docs/content/tabs.md +20 -20
  84. package/src/ui/docs/content/toggle.md +117 -65
  85. package/src/ui/docs/content/tokens.md +124 -40
  86. package/src/ui/docs/registry.tsx +4 -6
  87. package/src/ui/index.ts +30 -27
  88. package/src/ui/meta.ts +15 -27
  89. package/src/ui/react.tsx +40 -43
  90. package/src/ui/theme.css +311 -64
  91. package/src/ui/components/patterns/content-header.tsx +0 -528
  92. package/src/ui/components/patterns/surface-media.tsx +0 -69
  93. package/src/ui/components/primitives/card.tsx +0 -191
  94. package/src/ui/docs/content/card.md +0 -292
  95. package/src/ui/docs/content/content.md +0 -275
@@ -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
@@ -6,13 +6,13 @@ centralizado e ocupa a largura disponível até `80rem` (`max-w-7xl`). Use `clas
6
6
  a composição pedir outro teto ou largura total.
7
7
 
8
8
  A forma curta é o padrão para páginas comuns e cria `PageHeader` e `PageBody`. A forma explícita
9
- exige exatamente um heading principal. Use `PageTitle` no header, `Content level={1}` no body, ou
9
+ exige exatamente um heading principal. Use `PageTitle` no header, `Surface level={1}` no body, ou
10
10
  deixe um `PageState` ativo assumir esse papel. O header pode ser omitido quando o próprio conteúdo
11
11
  nomeia a página. Contadores e outros indicadores pertencem ao conteúdo que os explica.
12
12
 
13
13
  A `Page` é contínua por definição e não há divisores entre suas regiões. O `layout` da `Page` não
14
14
  é a anatomia de superfície de
15
- [`Card`](/ui/card), [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer) e [`Pane`](/ui/pane) — aqui ele
15
+ [`Surface`](/ui/surface), [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer) e [`Pane`](/ui/pane) — aqui ele
16
16
  decide a ocupação de altura. `flow`, o padrão, deixa a página crescer e a rolagem acontecer na
17
17
  janela; `fill` limita a página à altura do shell para que uma região interna, como uma tabela longa
18
18
  ou um split, assuma a rolagem.
@@ -85,7 +85,7 @@ ocupa a mesma posição introdutória sem transformar a trilha em ação.
85
85
  ## Contexto conhecido pelo shell
86
86
 
87
87
  Use `PageShell` quando o shell conhece o breadcrumb e a rota conhece ações globais ou uma
88
- introdução. Não crie um `PaneHeader` paralelo nem esconda slots de `Page` com CSS. O Opus compõe a
88
+ introdução. Não crie um cabeçalho paralelo no layout nem esconda slots de `Page` com CSS. O Opus compõe a
89
89
  navegação e as ações do shell no `PageHeader` rolável da página ativa.
90
90
 
91
91
  ```tsx preview col
@@ -158,9 +158,9 @@ render(
158
158
  );
159
159
  ```
160
160
 
161
- Em uma página de listagem, [`Content`](/ui/content) pode fornecer o título e as ações da coleção,
161
+ Em uma página de listagem, [`Surface`](/ui/surface) pode fornecer o título e as ações da coleção,
162
162
  deixando `Page` apenas com `PageBody`. Se `PageShell` fornecer navegação ou ações nesse caso, `Page`
163
- materializa somente o `PageHeader` necessário para recebê-las, sem duplicar o heading de `Content`.
163
+ materializa somente o `PageHeader` necessário para recebê-las, sem duplicar o heading de `Surface`.
164
164
 
165
165
  ## Ações de etapa no rodapé
166
166
 
@@ -197,20 +197,25 @@ render(
197
197
  <PageHeader>
198
198
  <PageTitle>Ordens de serviço</PageTitle>
199
199
  </PageHeader>
200
- <PageBody className="min-h-0 flex-1 space-y-2 overflow-y-auto">
201
- {[
202
- "OS-1842",
203
- "OS-1839",
204
- "OS-1835",
205
- "OS-1830",
206
- "OS-1827",
207
- "OS-1821",
208
- "OS-1818",
209
- ].map((id) => (
210
- <div key={id} className="rounded-md border border-border p-3 text-sm">
211
- {id}
212
- </div>
213
- ))}
200
+ <PageBody className="min-h-0 flex-1 overflow-y-auto">
201
+ <div className="space-y-2">
202
+ {[
203
+ "OS-1842",
204
+ "OS-1839",
205
+ "OS-1835",
206
+ "OS-1830",
207
+ "OS-1827",
208
+ "OS-1821",
209
+ "OS-1818",
210
+ ].map((id) => (
211
+ <div
212
+ key={id}
213
+ className="rounded-md border border-border p-3 text-sm"
214
+ >
215
+ {id}
216
+ </div>
217
+ ))}
218
+ </div>
214
219
  </PageBody>
215
220
  </Page>
216
221
  </div>,
@@ -218,8 +223,8 @@ render(
218
223
  ```
219
224
 
220
225
  Uma [`ActionList`](/ui/action-list) não precisa dessas classes. Filha de `PageBody`, ou de um
221
- [`Content`](/ui/content) dentro dele, ela recebe a altura da página e assume `layout="fill"`
222
- sozinha: `PageBody`, `Content` e `ContentBody` passam a repassar a altura até ela, o cabeçalho da
226
+ [`Surface`](/ui/surface) dentro dele, ela recebe a altura da página e assume `layout="fill"`
227
+ sozinha: `PageBody`, `Surface` e `SurfaceBody` passam a repassar a altura até ela, o cabeçalho da
223
228
  tabela fica fixo e só os resultados rolam. As demais seções do `PageBody` mantêm a altura do
224
229
  próprio conteúdo, e uma página `fill` sem lista não muda.
225
230
 
@@ -373,11 +378,14 @@ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui
373
378
 
374
379
  ## Propriedades de PageBody
375
380
 
376
- | Propriedade | Tipo | Descrição |
377
- | ----------- | ----------------------------- | ------------------------------------------ |
378
- | `children` | `ReactNode` | Conteúdo principal da página. |
379
- | `className` | `string` | Classes adicionais da região principal. |
380
- | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados ao container. |
381
+ `PageBody` separa seus blocos diretos por `1.5rem`; o inset da página continua pertencendo a
382
+ `Page`. Uma grade, lista compacta ou linha horizontal fica dentro de um wrapper próprio.
383
+
384
+ | Propriedade | Tipo | Descrição |
385
+ | ----------- | ----------------------------- | --------------------------------------------------------- |
386
+ | `children` | `ReactNode` | Blocos principais, separados pelo ritmo estrutural. |
387
+ | `className` | `string` | Classes de ocupação, overflow, alinhamento ou tipografia. |
388
+ | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados ao container. |
381
389
 
382
390
  Em uma `Page` com `layout="fill"`, o `PageBody` vira coluna flexível sozinho quando contém uma `ActionList` que
383
391
  ocupa a altura.