@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
@@ -0,0 +1,513 @@
1
+ ## Região nomeada
2
+
3
+ Use `Surface` para delimitar e nomear um trecho da tela: uma seção da página, um grupo de campos, um
4
+ registro, um item de uma grade ou um bloco clicável. Para uma janela modal, use
5
+ [`Dialog`](/ui/dialog); para um painel lateral, [`Drawer`](/ui/drawer); para uma coluna de layout,
6
+ [`Pane`](/ui/pane).
7
+
8
+ O caso mais comum é uma seção com título dentro de uma página. Declare `as="section"` e as
9
+ propriedades do header na própria `Surface`; os filhos formam o corpo. `count` mostra o total de
10
+ itens ao lado do título.
11
+
12
+ ```tsx preview col
13
+ render(
14
+ <Surface
15
+ as="section"
16
+ title="Dispositivos conectados"
17
+ count={3}
18
+ description="Sessões com acesso à sua conta."
19
+ actions={<Button variant="outline">Encerrar outras sessões</Button>}
20
+ >
21
+ <p className="text-sm text-muted-foreground">MacBook Pro · ativo agora</p>
22
+ </Surface>,
23
+ );
24
+ ```
25
+
26
+ Sem moldura e sem fundo, a superfície não aplica recuo: título e corpo ficam alinhados ao conteúdo
27
+ ao redor. A `section` é ligada ao título por `aria-labelledby`, então leitores de tela anunciam a
28
+ região pelo nome.
29
+
30
+ ## Escolher o elemento
31
+
32
+ `as` decide o que a superfície é para o documento. A aparência não muda com o elemento.
33
+
34
+ | `as` | Quando usar | Efeito |
35
+ | -------------- | -------------------------------------------------------------------------------------- | --------------------------------------- |
36
+ | `div` (padrão) | Agrupamento visual sem papel próprio no documento. | Nenhum nome acessível é atribuído. |
37
+ | `section` | Região da página identificada por um título. | `aria-labelledby` aponta para o título. |
38
+ | `article` | Conteúdo que faz sentido sozinho, como um registro ou um item de uma lista de cartões. | `aria-labelledby` aponta para o título. |
39
+ | `fieldset` | Grupo de campos de formulário. | O header vira a `legend` nativa. |
40
+
41
+ Quando `aria-label` é informado, ele substitui a ligação com o título. Para um bloco clicável, use
42
+ `asChild` em vez de `as`, como mostra a seção “Superfície clicável”.
43
+
44
+ ## Moldura, fundo e anatomia
45
+
46
+ Três propriedades independentes definem a aparência:
47
+
48
+ - `framed` acrescenta borda e raio.
49
+ - `elevation="raised"` dá à superfície o fundo de um elemento elevado sobre a página.
50
+ - `layout` escolhe entre o ritmo contínuo e as faixas com divisores.
51
+
52
+ Uma superfície com moldura ou com fundo passa a aplicar o recuo de `1rem` nas suas regiões; você não
53
+ escreve classes de padding no header, no corpo ou no rodapé.
54
+
55
+ ```tsx preview col
56
+ render(
57
+ <div className="grid grid-cols-3 gap-4">
58
+ <Surface as="section" framed title="Com moldura" level={3}>
59
+ <p className="text-sm text-muted-foreground">
60
+ Mostra o fundo de quem a hospeda.
61
+ </p>
62
+ </Surface>
63
+ <Surface as="section" elevation="raised" title="Com fundo" level={3}>
64
+ <p className="text-sm text-muted-foreground">Fundo próprio, sem borda.</p>
65
+ </Surface>
66
+ <Surface
67
+ as="section"
68
+ framed
69
+ elevation="raised"
70
+ title="Com moldura e fundo"
71
+ level={3}
72
+ >
73
+ <p className="text-sm text-muted-foreground">
74
+ O registro destacado sobre a página.
75
+ </p>
76
+ </Surface>
77
+ </div>,
78
+ );
79
+ ```
80
+
81
+ Use a moldura para separar blocos vizinhos e o fundo elevado para destacar um registro ou um item
82
+ sobre a página. Quando a hierarquia pedir sombra, declare a intensidade em `className`, por exemplo
83
+ `shadow-sm`.
84
+
85
+ ## Cabeçalho, corpo e rodapé
86
+
87
+ Quando a superfície precisa de rodapé ou de controle sobre cada região, componha os slots.
88
+ `SurfaceHeader` monta a linha sozinho: mídia, título com contador e descrição de um lado, ações no
89
+ extremo oposto. A ordem em que os slots são escritos não altera a posição.
90
+
91
+ ```tsx preview col
92
+ render(
93
+ <Surface as="article" framed elevation="raised" level={3}>
94
+ <SurfaceHeader>
95
+ <SurfaceTitle>Oficina Centro</SurfaceTitle>
96
+ <SurfaceDescription>
97
+ 4 boxes · 2 consultores de plantão.
98
+ </SurfaceDescription>
99
+ <SurfaceActions>
100
+ <Button size="sm" variant="ghost">
101
+ Editar
102
+ </Button>
103
+ </SurfaceActions>
104
+ </SurfaceHeader>
105
+ <SurfaceBody>
106
+ <p className="text-sm text-muted-foreground">
107
+ Próximo horário livre hoje, às 14h.
108
+ </p>
109
+ </SurfaceBody>
110
+ <SurfaceFooter>
111
+ <Button size="sm" variant="outline">
112
+ Ver agenda
113
+ </Button>
114
+ <Button size="sm">Agendar revisão</Button>
115
+ </SurfaceFooter>
116
+ </Surface>,
117
+ );
118
+ ```
119
+
120
+ Cada região é opcional, e o ritmo acompanha as que estão presentes: a última fecha a superfície com
121
+ o mesmo respiro do topo. O rodapé alinha as ações no fim da linha e vale para qualquer superfície,
122
+ inclusive uma região de página sem moldura.
123
+
124
+ A forma curta e a composição explícita produzem os mesmos elementos. Na forma curta, os filhos
125
+ também podem ser `SurfaceBody` e `SurfaceFooter`, o que permite declarar o header por propriedades e
126
+ ainda ter rodapé:
127
+
128
+ ```tsx preview col
129
+ render(
130
+ <Surface
131
+ as="section"
132
+ framed
133
+ title="Plano atual"
134
+ description="Renova em 12 de maio."
135
+ level={3}
136
+ >
137
+ <SurfaceBody>
138
+ <p className="text-sm text-muted-foreground">
139
+ Até 10 workspaces e 50 agentes.
140
+ </p>
141
+ </SurfaceBody>
142
+ <SurfaceFooter>
143
+ <Button size="sm" variant="outline">
144
+ Alterar plano
145
+ </Button>
146
+ </SurfaceFooter>
147
+ </Surface>,
148
+ );
149
+ ```
150
+
151
+ ### Composições que interrompem a renderização
152
+
153
+ A `Surface` reprova as composições que deixariam a estrutura ambígua:
154
+
155
+ - slots estruturais misturados com filhos livres: envolva o conteúdo em `SurfaceBody`;
156
+ - mais de um `SurfaceHeader`, `SurfaceBody` ou `SurfaceFooter`;
157
+ - propriedades da forma curta junto com `SurfaceHeader`;
158
+ - `media`, `count`, `description` ou `actions` sem `title`;
159
+ - um nó dentro de `SurfaceHeader` que não seja um dos seus slots;
160
+ - um `SurfaceHeader` de `fieldset` sem `SurfaceTitle`, necessário para nomear o grupo.
161
+
162
+ O `opus check` aponta os mesmos problemas antes da execução.
163
+
164
+ ## Contínuo ou seccionado
165
+
166
+ `layout` declara a anatomia da superfície inteira. No padrão, `continuous`, o espaço separa as
167
+ regiões. Em `sectioned`, header e rodapé viram faixas e uma linha separa as regiões vizinhas.
168
+
169
+ ```tsx preview col
170
+ render(
171
+ <div className="grid grid-cols-2 gap-4">
172
+ <Surface as="section" framed elevation="raised" title="Contínuo" level={3}>
173
+ <SurfaceBody>
174
+ <p className="text-sm text-muted-foreground">
175
+ Um assunto, lido de cima a baixo.
176
+ </p>
177
+ </SurfaceBody>
178
+ <SurfaceFooter>
179
+ <Button size="sm" variant="outline">
180
+ Abrir
181
+ </Button>
182
+ </SurfaceFooter>
183
+ </Surface>
184
+ <Surface
185
+ as="section"
186
+ framed
187
+ elevation="raised"
188
+ layout="sectioned"
189
+ title="Seccionado"
190
+ level={3}
191
+ >
192
+ <SurfaceBody>
193
+ <p className="text-sm text-muted-foreground">
194
+ Regiões com pesos diferentes.
195
+ </p>
196
+ </SurfaceBody>
197
+ <SurfaceFooter>
198
+ <Button size="sm" variant="outline">
199
+ Abrir
200
+ </Button>
201
+ </SurfaceFooter>
202
+ </Surface>
203
+ </div>,
204
+ );
205
+ ```
206
+
207
+ Use `continuous` para um bloco de leitura e `sectioned` para uma superfície de operação, com lista
208
+ ou tabela no corpo e ações persistentes no rodapé. `sectioned` exige `framed` ou
209
+ `elevation="raised"`: os divisores precisam de um limite para alcançar.
210
+
211
+ Os divisores são derivados das regiões presentes. Um header sozinho não desenha linha, e um header
212
+ seguido direto de um rodapé produz uma linha só. Por isso, não escreva `border-b` nem `border-t`
213
+ nas regiões, e use `layout="continuous"` para uma superfície sem divisores.
214
+
215
+ ### Corpo de ponta a ponta
216
+
217
+ Quando o filho do corpo já controla o próprio recuo, como `DetailGroup` com divisores, uma lista ou
218
+ uma tabela, declare `flush` em `SurfaceBody`. O corpo entrega a largura inteira ao filho e a moldura
219
+ recorta os cantos.
220
+
221
+ ```tsx preview col
222
+ render(
223
+ <Surface
224
+ as="section"
225
+ framed
226
+ layout="sectioned"
227
+ title="Veículo"
228
+ media={
229
+ <IconTile size="xs">
230
+ <CarFront />
231
+ </IconTile>
232
+ }
233
+ >
234
+ <SurfaceBody flush>
235
+ <DetailGroup dividers columns={2} orientation="horizontal">
236
+ <DetailField label="Marca" value="Toyota" />
237
+ <DetailField label="Modelo" value="Yaris XLS" />
238
+ <DetailField label="Ano/modelo" value="2021" />
239
+ <DetailField label="Placa" value="EVT0012" />
240
+ </DetailGroup>
241
+ </SurfaceBody>
242
+ </Surface>,
243
+ );
244
+ ```
245
+
246
+ ## Mídia
247
+
248
+ `SurfaceMedia`, ou `media` na forma curta, reserva a posição que abre a linha do header. O slot não
249
+ desenha moldura e não altera o que recebe: o conteúdo entra como foi escrito.
250
+
251
+ - Para um ícone com moldura, componha [`IconTile`](/ui/icon-tile) e escolha nele `size`, `context` e
252
+ `variant`.
253
+ - Para uma pessoa ou uma marca, passe [`Avatar`](/ui/avatar) ou `img` diretamente.
254
+ - Um glifo solto aparece sem moldura, no tamanho de `1rem`; uma classe `size-*` no ícone prevalece.
255
+
256
+ ```tsx preview col
257
+ render(
258
+ <div className="grid grid-cols-3 gap-4">
259
+ <Surface as="article" framed elevation="raised" level={3}>
260
+ <SurfaceHeader>
261
+ <SurfaceMedia>
262
+ <IconTile size="sm" context="primary">
263
+ <CarFront />
264
+ </IconTile>
265
+ </SurfaceMedia>
266
+ <SurfaceTitle>Revisão dos 20.000 km</SurfaceTitle>
267
+ <SurfaceDescription>12 de maio, às 9h.</SurfaceDescription>
268
+ </SurfaceHeader>
269
+ </Surface>
270
+ <Surface as="article" framed elevation="raised" level={3}>
271
+ <SurfaceHeader>
272
+ <SurfaceMedia>
273
+ <Avatar>
274
+ <AvatarFallback>MA</AvatarFallback>
275
+ </Avatar>
276
+ </SurfaceMedia>
277
+ <SurfaceTitle>Marina Alves</SurfaceTitle>
278
+ <SurfaceDescription>Consultora responsável.</SurfaceDescription>
279
+ </SurfaceHeader>
280
+ </Surface>
281
+ <Surface as="article" framed elevation="raised" level={3}>
282
+ <SurfaceHeader>
283
+ <SurfaceMedia>
284
+ <ShieldCheck aria-hidden="true" />
285
+ </SurfaceMedia>
286
+ <SurfaceTitle>Garantia ativa</SurfaceTitle>
287
+ <SurfaceDescription>Até março de 2027.</SurfaceDescription>
288
+ </SurfaceHeader>
289
+ </Surface>
290
+ </div>,
291
+ );
292
+ ```
293
+
294
+ `media` com `null` ou `false` não abre o slot.
295
+
296
+ ## Grupo de campos
297
+
298
+ Quando a superfície nomear um conjunto de campos relacionados, use `as="fieldset"`. O título vira a
299
+ `legend` nativa e as propriedades de `fieldset`, como `disabled`, continuam disponíveis. A legenda
300
+ aceita somente título e mídia; contador, descrição e ações não fazem parte do nome do grupo. A
301
+ mídia da legenda é decorativa e fica oculta dos leitores de tela.
302
+
303
+ ```tsx preview col
304
+ render(
305
+ <Surface
306
+ as="fieldset"
307
+ framed
308
+ title="Proprietário"
309
+ media={
310
+ <IconTile size="xs">
311
+ <UserRound />
312
+ </IconTile>
313
+ }
314
+ >
315
+ <FieldGroup>
316
+ <Field>
317
+ <FieldLabel htmlFor="owner-name">Nome</FieldLabel>
318
+ <Input id="owner-name" />
319
+ </Field>
320
+ <Field>
321
+ <FieldLabel htmlFor="owner-document">CPF/CNPJ</FieldLabel>
322
+ <Input id="owner-document" />
323
+ </Field>
324
+ </FieldGroup>
325
+ </Surface>,
326
+ );
327
+ ```
328
+
329
+ Com `framed`, a legenda ocupa a borda do grupo, como em um `fieldset` nativo. Com
330
+ `layout="sectioned"`, ela passa a ser a faixa do header, dentro da moldura. Sem moldura, o grupo
331
+ flui alinhado ao formulário.
332
+
333
+ ## Superfície clicável
334
+
335
+ Quando o bloco inteiro leva a outro lugar ou dispara uma ação, use `asChild`: o filho — um `button`
336
+ ou um link — recebe a moldura e o fundo e mantém a própria semântica. `asChild` não combina com
337
+ `as` nem com a forma curta; componha os slots dentro do filho.
338
+
339
+ ```tsx preview col
340
+ render(
341
+ <Surface
342
+ asChild
343
+ framed
344
+ elevation="raised"
345
+ level={3}
346
+ className="w-full cursor-pointer text-left transition-colors hover:bg-muted"
347
+ >
348
+ <button type="button">
349
+ <SurfaceHeader>
350
+ <SurfaceTitle>Camada de dados</SurfaceTitle>
351
+ <SurfaceDescription>
352
+ Entidades, consultas e migrações.
353
+ </SurfaceDescription>
354
+ <SurfaceActions>
355
+ <ArrowRight
356
+ aria-hidden="true"
357
+ className="size-4 text-muted-foreground"
358
+ />
359
+ </SurfaceActions>
360
+ </SurfaceHeader>
361
+ </button>
362
+ </Surface>,
363
+ );
364
+ ```
365
+
366
+ Os estados de hover e de foco pertencem ao elemento interativo e são declarados em `className`. Não
367
+ coloque outro controle interativo dentro de uma superfície clicável.
368
+
369
+ ## Caixa simples
370
+
371
+ Sem título e sem slots, a `Surface` é uma caixa: aplica somente o que `framed` e `elevation`
372
+ declaram, sem recuo nem espaçamento. O conteúdo e o recuo são seus.
373
+
374
+ ```tsx preview col
375
+ <Surface framed elevation="raised" className="p-4">
376
+ <p className="text-sm text-muted-foreground">
377
+ 3 veículos aguardando retirada.
378
+ </p>
379
+ </Surface>
380
+ ```
381
+
382
+ ## Hierarquia do título
383
+
384
+ `level` escolhe o heading, de `1` a `6`, com `2` como padrão. Ajuste o nível pela estrutura do
385
+ documento: uma superfície dentro de uma seção que já tem `h2` usa `level={3}`. Os níveis de 2 a 6
386
+ têm a mesma aparência.
387
+
388
+ O nível 1 é diferente: identifica o conteúdo principal da página e usa a escala do título de
389
+ [`Page`](/ui/page). É o caso da página centrada em uma coleção.
390
+
391
+ ## Coleção nomeada
392
+
393
+ Quando a região principal da página é uma coleção, a `Surface` nomeia essa coleção e a
394
+ [`ActionList`](/ui/action-list) executa a consulta. Coloque criar, importar e outras ações sobre o
395
+ conjunto completo nas ações da superfície. Busca, filtros, atualização e operações que dependem do
396
+ recorte atual permanecem na toolbar da lista.
397
+
398
+ ```tsx preview col
399
+ render(
400
+ <Surface
401
+ as="section"
402
+ level={1}
403
+ title="Clientes"
404
+ actions={<Button>Criar cliente</Button>}
405
+ >
406
+ <p className="text-sm text-muted-foreground">
407
+ A lista de clientes ocupa o corpo.
408
+ </p>
409
+ </Surface>,
410
+ );
411
+ ```
412
+
413
+ A criação usa um `Button` textual na variante padrão e no tamanho `default`, sem ícone, nomeado
414
+ `Criar {recurso}`; o diálogo aberto por ele repete esse título, e a edição usa `Editar {recurso}`.
415
+
416
+ Em uma [`Page`](/ui/page) com `layout="fill"`, a coleção ocupa a altura da página e rola só os
417
+ resultados. `Surface` e `SurfaceBody` repassam a altura até a `ActionList` sem classes adicionais;
418
+ uma superfície irmã, sem lista, mantém a altura do próprio conteúdo. Uma superfície com
419
+ `elevation="raised"` não repassa a altura: ela mantém a altura do próprio conteúdo. A seção
420
+ “Ocupar a altura disponível” da `ActionList` mostra a composição completa.
421
+
422
+ ## Fundo e aninhamento
423
+
424
+ `elevation="none"` não pinta fundo: a superfície mostra o fundo de quem a hospeda, em qualquer nível
425
+ de aninhamento. `elevation="raised"` pinta o fundo de superfície elevada e o publica na variável CSS
426
+ `--surface-background`, que `Dialog` e `Drawer` também declaram.
427
+
428
+ Um elemento seu que precise de uma cor opaca igual à do fundo visível — um cabeçalho fixo ou uma
429
+ faixa que cobre o conteúdo ao rolar — usa `bg-[var(--surface-background,var(--background))]`. Na
430
+ página, vale `--background`; dentro de uma superfície elevada, o fundo dela. Um contêiner da
431
+ aplicação que pinte outro fundo declara a mesma variável com a cor que usa.
432
+
433
+ ## Propriedades de Surface
434
+
435
+ | Propriedade | Tipo | Padrão | Descrição |
436
+ | ------------- | ----------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------ |
437
+ | `as` | `'div' \| 'section' \| 'article' \| 'fieldset'` | `'div'` | Elemento da superfície. Não combina com `asChild`. |
438
+ | `asChild` | `boolean` | `false` | Entrega a superfície ao único filho, como um `button` ou um link. |
439
+ | `framed` | `boolean` | `false` | Acrescenta borda e raio, e recorta o conteúdo seccionado ou sem recuo. |
440
+ | `elevation` | `'none' \| 'raised'` | `'none'` | `raised` pinta o fundo de superfície elevada e mantém a altura do próprio conteúdo. |
441
+ | `layout` | `'continuous' \| 'sectioned'` | `'continuous'` | Anatomia das regiões. `sectioned` exige `framed` ou `elevation="raised"`. |
442
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Nível do heading do título. `1` identifica o conteúdo principal da página. Não se aplica a `fieldset`. |
443
+ | `title` | `ReactNode` | | Forma curta: título da superfície. |
444
+ | `media` | `ReactNode` | | Forma curta: conteúdo do slot de mídia, antes do título. Exige `title`. |
445
+ | `count` | `number` | | Forma curta: total de itens ao lado do título. Exige `title`; indisponível em `fieldset`. |
446
+ | `description` | `ReactNode` | | Forma curta: frase de apoio sob o título. Exige `title`; indisponível em `fieldset`. |
447
+ | `actions` | `ReactNode` | | Forma curta: ações sobre a superfície inteira. Exige `title`; indisponível em `fieldset`. |
448
+ | `className` | `string` | | Compõe sobre a superfície. Na caixa simples, é onde entra o recuo. |
449
+ | `children` | `ReactNode` | | Filhos livres, que formam o corpo, ou os slots `SurfaceHeader`, `SurfaceBody` e `SurfaceFooter`. |
450
+
451
+ As demais propriedades são as nativas do elemento escolhido em `as`.
452
+
453
+ ## Propriedades de SurfaceHeader
454
+
455
+ | Propriedade | Tipo | Padrão | Descrição |
456
+ | ----------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
457
+ | `children` | `ReactNode` | | No máximo um de cada: `SurfaceMedia`, `SurfaceTitle`, `SurfaceMeta`, `SurfaceDescription` e `SurfaceActions`. Em `fieldset`, o `SurfaceTitle` é obrigatório e somente ele e `SurfaceMedia` são aceitos. |
458
+ | `className` | `string` | | Ajusta a região do header. |
459
+
460
+ `SurfaceHeader` só existe como filho direto de `Surface`.
461
+
462
+ ## Propriedades de SurfaceMedia
463
+
464
+ | Propriedade | Tipo | Padrão | Descrição |
465
+ | ----------- | ----------- | ------ | ----------------------------------------------------------------------- |
466
+ | `children` | `ReactNode` | | `IconTile`, `Avatar`, `img` ou um glifo. O slot não acrescenta moldura. |
467
+ | `className` | `string` | | Ajusta o alinhamento do slot. |
468
+
469
+ ## Propriedades de SurfaceTitle
470
+
471
+ | Propriedade | Tipo | Padrão | Descrição |
472
+ | ----------- | ----------- | ------ | ------------------------------------------------------------------- |
473
+ | `children` | `ReactNode` | | Nome da superfície. O heading usa o `level` declarado em `Surface`. |
474
+ | `className` | `string` | | Ajusta o título. |
475
+
476
+ ## Propriedades de SurfaceMeta
477
+
478
+ | Propriedade | Tipo | Padrão | Descrição |
479
+ | ----------- | ----------- | ------ | -------------------------------------------------------- |
480
+ | `children` | `ReactNode` | | Metadado curto ao lado do título, como o total de itens. |
481
+ | `className` | `string` | | Ajusta o metadado. |
482
+
483
+ ## Propriedades de SurfaceDescription
484
+
485
+ | Propriedade | Tipo | Padrão | Descrição |
486
+ | ----------- | ----------- | ------ | ---------------------------- |
487
+ | `children` | `ReactNode` | | Frase de apoio sob o título. |
488
+ | `className` | `string` | | Ajusta a descrição. |
489
+
490
+ ## Propriedades de SurfaceActions
491
+
492
+ | Propriedade | Tipo | Padrão | Descrição |
493
+ | ----------- | ----------- | ------ | ---------------------------------------------------------------- |
494
+ | `children` | `ReactNode` | | Controles no extremo oposto ao título, como um botão ou um menu. |
495
+ | `className` | `string` | | Ajusta o agrupamento das ações. |
496
+
497
+ ## Propriedades de SurfaceBody
498
+
499
+ Os filhos diretos recebem o ritmo estrutural de `1.5rem`. O layout da `Surface` decide o inset;
500
+ um wrapper filho declara relações locais mais compactas.
501
+
502
+ | Propriedade | Tipo | Padrão | Descrição |
503
+ | ----------- | ----------- | ------- | ---------------------------------------------------------------------- |
504
+ | `flush` | `boolean` | `false` | Remove o recuo do corpo para um filho que já controla o próprio recuo. |
505
+ | `children` | `ReactNode` | | Blocos principais, separados pelo ritmo estrutural. |
506
+ | `className` | `string` | | Ajusta ocupação, overflow, alinhamento ou tipografia. |
507
+
508
+ ## Propriedades de SurfaceFooter
509
+
510
+ | Propriedade | Tipo | Padrão | Descrição |
511
+ | ----------- | ----------- | ------ | ---------------------------------------------------- |
512
+ | `children` | `ReactNode` | | Ações persistentes, alinhadas no fim da linha. |
513
+ | `className` | `string` | | Ajusta o rodapé, inclusive a distribuição das ações. |
@@ -42,12 +42,12 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
42
42
 
43
43
  Use `framed` quando a navegação e seus painéis precisarem formar um único bloco. A propriedade
44
44
  reúne borda, raio e recorte no próprio `Tabs`, mas não aplica fundo: o bloco herda a
45
- superfície em que foi composto. Por isso, não envolva `Tabs` em um `Card` somente para obter essa
45
+ superfície em que foi composto. Por isso, não envolva `Tabs` em um `Surface` somente para obter essa
46
46
  moldura.
47
47
 
48
48
  ```tsx preview col
49
49
  <Tabs defaultValue="overview" framed className="gap-0">
50
- <TabsList className="px-2 pt-2">
50
+ <TabsList>
51
51
  <TabsListItem value="overview">Visão geral</TabsListItem>
52
52
  <TabsListItem value="history">Histórico</TabsListItem>
53
53
  </TabsList>
@@ -92,8 +92,8 @@ essa linha. A linha respeita o recuo do contêiner; dentro de `DrawerBody` e `Di
92
92
  acompanha o alinhamento do restante do corpo sem alcançar as laterais da superfície.
93
93
  Cada item contém uma área interativa com a mesma altura de um Button do tamanho correspondente. A
94
94
  área permanece transparente e recebe fundo apenas no hover; a aba ativa é indicada pela linha
95
- primária inferior. Um respiro de `0.375rem` separa o controle dessa linha; em `Tabs framed`, ele
96
- aumenta para `0.5rem` e acompanha o padding superior da moldura.
95
+ primária inferior. Um respiro de `0.375rem` separa o controle dessa linha. Em `Tabs framed`, a
96
+ lista recebe `0.25rem` nas laterais e no topo, e a mesma medida separa o controle da linha.
97
97
 
98
98
  ```tsx preview col
99
99
  <Tabs defaultValue="agents">
@@ -165,34 +165,34 @@ do gatilho.
165
165
 
166
166
  ## Propriedades de Tabs
167
167
 
168
- | Propriedade | Tipo | Padrão | Descrição |
169
- | --------------- | ---------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
- | `defaultValue` | `string` | | Aba inicial no modo não controlado. |
171
- | `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
172
- | `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
173
- | `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos. |
174
- | `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
175
- | `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
176
- | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
168
+ | Propriedade | Tipo | Padrão | Descrição |
169
+ | --------------- | ---------------------------- | -------------- | --------------------------------------------------------------------------------------------- |
170
+ | `defaultValue` | `string` | | Aba inicial no modo não controlado. |
171
+ | `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
172
+ | `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
173
+ | `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos. |
174
+ | `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
175
+ | `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
176
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
177
177
 
178
178
  ## Propriedades de TabsList
179
179
 
180
- | Propriedade | Tipo | Padrão | Descrição |
181
- | ------------ | -------- | ------ | --------------------------------------------------------------- |
180
+ | Propriedade | Tipo | Padrão | Descrição |
181
+ | ------------ | -------- | ------ | ----------------------------------------------------------------- |
182
182
  | `aria-label` | `string` | | Nomeia a navegação quando não houver um rótulo visível associado. |
183
- | `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação. |
183
+ | `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação. |
184
184
 
185
185
  ## Propriedades de TabsListItem
186
186
 
187
187
  | Propriedade | Tipo | Padrão | Descrição |
188
188
  | ----------- | ----------- | ------- | ----------------------------------------------------------------------------------------------- |
189
- | `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente. |
189
+ | `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente. |
190
190
  | `icon` | `ReactNode` | | Ícone decorativo antes do rótulo, em `1rem`. Em uma aba sem texto, declare também `aria-label`. |
191
191
  | `disabled` | `boolean` | `false` | Impede a seleção da aba. |
192
192
 
193
193
  ## Propriedades de TabsPanel
194
194
 
195
- | Propriedade | Tipo | Padrão | Descrição |
196
- | ------------ | -------- | ------ | ---------------------------------------------------------------------- |
195
+ | Propriedade | Tipo | Padrão | Descrição |
196
+ | ------------ | -------- | ------ | ----------------------------------------------------------------------- |
197
197
  | `value` | `string` | | Identificador do painel, igual ao `value` do `TabsListItem` que o abre. |
198
- | `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
198
+ | `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |