@softize/opus 18.5.0 → 20.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 (151) hide show
  1. package/CHANGELOG.md +275 -0
  2. package/bin/cli.mjs +5 -0
  3. package/bin/lib/check.mjs +198 -10
  4. package/bin/lib/copy.mjs +29 -0
  5. package/bin/lib/mcp.mjs +1 -1
  6. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +298 -0
  7. package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
  8. package/docs/data-products.md +15 -9
  9. package/docs/protocol.md +3 -4
  10. package/docs/releasing.md +2 -2
  11. package/package.json +2 -2
  12. package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
  13. package/registry/templates/app/package.json +1 -1
  14. package/src/core/runtime.ts +96 -8
  15. package/src/core/types.ts +5 -2
  16. package/src/ui/components/patterns/action-form-card.tsx +11 -1
  17. package/src/ui/components/patterns/content-header.tsx +16 -12
  18. package/src/ui/components/patterns/fill-region.ts +150 -0
  19. package/src/ui/components/patterns/heading-level.ts +16 -0
  20. package/src/ui/components/patterns/list.tsx +103 -73
  21. package/src/ui/components/patterns/page.tsx +20 -4
  22. package/src/ui/components/patterns/pane.tsx +294 -0
  23. package/src/ui/components/patterns/presentation.tsx +81 -54
  24. package/src/ui/components/patterns/sidebar.tsx +13 -72
  25. package/src/ui/components/patterns/split.tsx +51 -38
  26. package/src/ui/components/patterns/surface-anatomy.ts +246 -0
  27. package/src/ui/components/patterns/surface-header.tsx +51 -6
  28. package/src/ui/components/patterns/surface-media.tsx +69 -0
  29. package/src/ui/components/patterns/trigger.tsx +12 -2
  30. package/src/ui/components/primitives/alert.tsx +9 -1
  31. package/src/ui/components/primitives/avatar.tsx +7 -3
  32. package/src/ui/components/primitives/card.tsx +91 -29
  33. package/src/ui/components/primitives/carousel.tsx +34 -5
  34. package/src/ui/components/primitives/checkbox.tsx +11 -3
  35. package/src/ui/components/primitives/command.tsx +14 -5
  36. package/src/ui/components/primitives/copyable.tsx +120 -10
  37. package/src/ui/components/primitives/detail.tsx +10 -13
  38. package/src/ui/components/primitives/dialog.tsx +179 -45
  39. package/src/ui/components/primitives/drawer.tsx +131 -24
  40. package/src/ui/components/primitives/empty.tsx +1 -1
  41. package/src/ui/components/primitives/icon-picker.tsx +6 -2
  42. package/src/ui/components/primitives/icon-tile.tsx +7 -0
  43. package/src/ui/components/primitives/input-group.tsx +9 -2
  44. package/src/ui/components/primitives/input-otp.tsx +1 -1
  45. package/src/ui/components/primitives/kbd.tsx +1 -1
  46. package/src/ui/components/primitives/label.tsx +3 -0
  47. package/src/ui/components/primitives/menu.tsx +5 -0
  48. package/src/ui/components/primitives/metric-card.tsx +98 -39
  49. package/src/ui/components/primitives/popover.tsx +1 -1
  50. package/src/ui/components/primitives/progress.tsx +9 -1
  51. package/src/ui/components/primitives/slider.tsx +62 -11
  52. package/src/ui/components/primitives/truncate.tsx +10 -1
  53. package/src/ui/docs/content/accordion.md +74 -27
  54. package/src/ui/docs/content/action-form-card.md +24 -15
  55. package/src/ui/docs/content/action-form-dialog.md +17 -7
  56. package/src/ui/docs/content/action-form.md +92 -77
  57. package/src/ui/docs/content/action-list-dialog.md +75 -38
  58. package/src/ui/docs/content/action-list.md +134 -52
  59. package/src/ui/docs/content/action-trigger.md +27 -10
  60. package/src/ui/docs/content/action-view.md +14 -14
  61. package/src/ui/docs/content/actions.md +161 -66
  62. package/src/ui/docs/content/ai.md +43 -44
  63. package/src/ui/docs/content/alert.md +83 -29
  64. package/src/ui/docs/content/ask.md +51 -26
  65. package/src/ui/docs/content/aspect-ratio.md +22 -24
  66. package/src/ui/docs/content/audit.md +23 -19
  67. package/src/ui/docs/content/auth.md +38 -19
  68. package/src/ui/docs/content/avatar.md +54 -5
  69. package/src/ui/docs/content/badge.md +29 -9
  70. package/src/ui/docs/content/breadcrumb.md +21 -8
  71. package/src/ui/docs/content/button.md +73 -43
  72. package/src/ui/docs/content/calendar.md +134 -26
  73. package/src/ui/docs/content/card.md +252 -35
  74. package/src/ui/docs/content/carousel.md +37 -6
  75. package/src/ui/docs/content/chat.md +118 -37
  76. package/src/ui/docs/content/checkbox.md +73 -7
  77. package/src/ui/docs/content/cli.md +94 -55
  78. package/src/ui/docs/content/collapsible.md +43 -19
  79. package/src/ui/docs/content/command.md +30 -8
  80. package/src/ui/docs/content/composer.md +23 -8
  81. package/src/ui/docs/content/content.md +37 -7
  82. package/src/ui/docs/content/copyable.md +39 -8
  83. package/src/ui/docs/content/customization.md +55 -46
  84. package/src/ui/docs/content/cycle.md +33 -18
  85. package/src/ui/docs/content/data-products.md +115 -0
  86. package/src/ui/docs/content/data-state.md +20 -6
  87. package/src/ui/docs/content/data.md +20 -19
  88. package/src/ui/docs/content/detail.md +12 -5
  89. package/src/ui/docs/content/dialog.md +219 -28
  90. package/src/ui/docs/content/dictionary-value.md +4 -4
  91. package/src/ui/docs/content/dock.md +54 -19
  92. package/src/ui/docs/content/dot.md +35 -5
  93. package/src/ui/docs/content/drawer.md +193 -22
  94. package/src/ui/docs/content/empty-value.md +20 -6
  95. package/src/ui/docs/content/empty.md +24 -3
  96. package/src/ui/docs/content/events.md +76 -16
  97. package/src/ui/docs/content/executions.md +9 -9
  98. package/src/ui/docs/content/field.md +21 -6
  99. package/src/ui/docs/content/getting-started.md +44 -34
  100. package/src/ui/docs/content/icon-picker.md +19 -13
  101. package/src/ui/docs/content/icon-tile.md +28 -5
  102. package/src/ui/docs/content/input-otp.md +58 -7
  103. package/src/ui/docs/content/input.md +22 -3
  104. package/src/ui/docs/content/item.md +53 -6
  105. package/src/ui/docs/content/kbd.md +5 -1
  106. package/src/ui/docs/content/label.md +13 -4
  107. package/src/ui/docs/content/log.md +5 -5
  108. package/src/ui/docs/content/markdown.md +5 -4
  109. package/src/ui/docs/content/mcp.md +49 -23
  110. package/src/ui/docs/content/menu.md +95 -10
  111. package/src/ui/docs/content/metric-card.md +68 -21
  112. package/src/ui/docs/content/observability.md +10 -9
  113. package/src/ui/docs/content/page.md +125 -11
  114. package/src/ui/docs/content/pagination.md +13 -5
  115. package/src/ui/docs/content/pane.md +300 -0
  116. package/src/ui/docs/content/popover.md +42 -5
  117. package/src/ui/docs/content/presentation.md +91 -47
  118. package/src/ui/docs/content/progress.md +27 -14
  119. package/src/ui/docs/content/queue.md +39 -21
  120. package/src/ui/docs/content/radio-group.md +26 -4
  121. package/src/ui/docs/content/router.md +29 -10
  122. package/src/ui/docs/content/runtime.md +90 -54
  123. package/src/ui/docs/content/scheduler.md +53 -21
  124. package/src/ui/docs/content/scroll-area.md +42 -35
  125. package/src/ui/docs/content/select.md +78 -33
  126. package/src/ui/docs/content/semantic-context.md +21 -2
  127. package/src/ui/docs/content/separator.md +15 -9
  128. package/src/ui/docs/content/sidebar.md +25 -45
  129. package/src/ui/docs/content/skeleton.md +13 -4
  130. package/src/ui/docs/content/slider.md +37 -9
  131. package/src/ui/docs/content/spinner.md +4 -3
  132. package/src/ui/docs/content/split.md +34 -20
  133. package/src/ui/docs/content/storage.md +8 -9
  134. package/src/ui/docs/content/switch.md +10 -7
  135. package/src/ui/docs/content/table.md +46 -8
  136. package/src/ui/docs/content/tabs.md +14 -4
  137. package/src/ui/docs/content/testing.md +76 -31
  138. package/src/ui/docs/content/textarea.md +5 -4
  139. package/src/ui/docs/content/toast.md +48 -7
  140. package/src/ui/docs/content/toggle.md +3 -3
  141. package/src/ui/docs/content/tokens.md +15 -26
  142. package/src/ui/docs/content/tooltip.md +18 -2
  143. package/src/ui/docs/content/truncate.md +36 -6
  144. package/src/ui/docs/content/ui.md +74 -22
  145. package/src/ui/docs/content/upgrading.md +45 -36
  146. package/src/ui/docs/plugin.ts +36 -2
  147. package/src/ui/docs/registry.tsx +14 -2
  148. package/src/ui/drivers/react.tsx +16 -8
  149. package/src/ui/meta.ts +38 -32
  150. package/src/ui/react.tsx +14 -0
  151. package/src/vite/design.ts +16 -4
@@ -35,33 +35,73 @@ acompanham o tamanho.
35
35
  ```tsx preview
36
36
  <Avatar size="xs">
37
37
  <AvatarFallback>AL</AvatarFallback>
38
+ <AvatarBadge className="bg-context-success" />
38
39
  </Avatar>
39
40
  <Avatar size="sm">
40
41
  <AvatarFallback>AL</AvatarFallback>
42
+ <AvatarBadge className="bg-context-success" />
41
43
  </Avatar>
42
44
  <Avatar size="default">
43
45
  <AvatarFallback>AL</AvatarFallback>
46
+ <AvatarBadge className="bg-context-success" />
44
47
  </Avatar>
45
48
  <Avatar size="lg">
46
49
  <AvatarFallback>AL</AvatarFallback>
50
+ <AvatarBadge className="bg-context-success" />
47
51
  </Avatar>
48
52
  ```
49
53
 
50
54
  ## Com selo de status
51
55
 
52
- AvatarBadge é o ponto no canto inferior — verde para o agente developer rodando uma sessão, cinza
53
- para o ocioso. Tinja com className.
56
+ AvatarBadge mostra o estado de quem o avatar representa: verde para a pessoa online ou para o
57
+ agente developer rodando uma sessão, cinza para quem está ocioso. Sem classe, o selo usa a cor
58
+ primária; tinja-o com className. Ele fica no canto inferior direito, sobre a borda do círculo.
59
+
60
+ O selo comunica o estado só pela cor. Quando o estado importa e não aparece em texto ao lado, dê um
61
+ nome ao selo com `role="img"` e `aria-label`, como o `label` do [`Dot`](/ui/dot) faz.
54
62
 
55
63
  ```tsx preview
64
+ <Avatar>
65
+ <AvatarImage src="https://github.com/joaocampos.png" alt="João Campos" />
66
+ <AvatarFallback>JC</AvatarFallback>
67
+ <AvatarBadge className="bg-context-success" role="img" aria-label="Online" />
68
+ </Avatar>
56
69
  <Avatar>
57
70
  <AvatarFallback>
58
71
  <Bot className="size-4" />
59
72
  </AvatarFallback>
60
- <AvatarBadge className="bg-context-success" />
73
+ <AvatarBadge className="bg-context-success" role="img" aria-label="Em sessão" />
61
74
  </Avatar>
62
75
  <Avatar>
63
76
  <AvatarFallback>RV</AvatarFallback>
64
- <AvatarBadge className="bg-muted-foreground" />
77
+ <AvatarBadge className="bg-context-neutral-emphasis" role="img" aria-label="Ocioso" />
78
+ </Avatar>
79
+ ```
80
+
81
+ Um ícone filho aparece dentro do selo, em `0.5rem`. No tamanho `xs`, o selo é pequeno demais para o
82
+ ícone e mostra só a cor.
83
+
84
+ ```tsx preview
85
+ <Avatar size="lg">
86
+ <AvatarFallback>AL</AvatarFallback>
87
+ <AvatarBadge role="img" aria-label="Verificado">
88
+ <Check />
89
+ </AvatarBadge>
90
+ </Avatar>
91
+ ```
92
+
93
+ ## Forma do avatar
94
+
95
+ O avatar é circular por padrão. Para logo de organização ou workspace, declare outra forma no
96
+ próprio Avatar, como `rounded-lg`: a imagem e o fallback a acompanham. O Avatar não recorta o que
97
+ você coloca direto nele; para imagem ou iniciais, use AvatarImage e AvatarFallback.
98
+
99
+ ```tsx preview
100
+ <Avatar className="rounded-lg">
101
+ <AvatarFallback>EX</AvatarFallback>
102
+ </Avatar>
103
+ <Avatar size="lg" className="rounded-lg">
104
+ <AvatarFallback>EX</AvatarFallback>
65
105
  </Avatar>
66
106
  ```
67
107
 
@@ -91,6 +131,7 @@ excedente — os membros do workspace Empresa X.
91
131
  | Propriedade | Tipo | Padrão | Descrição |
92
132
  |---|---|---|---|
93
133
  | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato na escala única dos controles (1.5 · 2 · 2.25 · 2.5rem); o fallback e o selo acompanham. |
134
+ | `className` | `string` | | Classes do root. Uma forma declarada aqui, como `rounded-lg`, vale também para AvatarImage e AvatarFallback. |
94
135
 
95
136
  ## Propriedades de AvatarImage
96
137
 
@@ -109,7 +150,15 @@ excedente — os membros do workspace Empresa X.
109
150
 
110
151
  | Propriedade | Tipo | Padrão | Descrição |
111
152
  |---|---|---|---|
112
- | `className` | `string` | | Classes usadas para comunicar visualmente o status no selo. |
153
+ | `className` | `string` | | Classes usadas para comunicar visualmente o status no selo. Sem cor própria, o fundo é `bg-primary`. |
154
+ | `children` | `React.ReactNode` | | Ícone opcional dentro do selo; fica oculto no tamanho `xs`. |
155
+
156
+ Os demais atributos, como `role` e `aria-label`, vão para o elemento `span`.
157
+
158
+ ## Propriedades de AvatarGroup
159
+
160
+ `AvatarGroup` sobrepõe os `Avatar` filhos e desenha o anel de fundo que os separa. Não tem props
161
+ próprias; aceita as de um elemento `div`, como `className`.
113
162
 
114
163
  ## Propriedades de AvatarGroupCount
115
164
 
@@ -1,7 +1,11 @@
1
1
  ## Contexto e variante
2
2
 
3
- Badge é não-interativo — para clique, use Button. `context` comunica o significado; `variant`
4
- escolhe entre preenchimento sólido, superfície sutil ou contorno.
3
+ Use `Badge` para um rótulo curto de estado ou categoria que precisa continuar legível. O Badge não é
4
+ interativo; para clique, use [`Button`](/ui/button). Quando só a cor basta, ao lado de um texto que
5
+ já nomeia o item, use [`Dot`](/ui/dot).
6
+
7
+ `context` comunica o significado; `variant` escolhe entre preenchimento sólido, superfície sutil ou
8
+ contorno.
5
9
 
6
10
  ```tsx preview
7
11
  <Badge context="neutral">Agente</Badge>
@@ -11,9 +15,9 @@ escolhe entre preenchimento sólido, superfície sutil ou contorno.
11
15
 
12
16
  ## Contextos de status
13
17
 
14
- `success`, `warning`, `info` e `danger` usam `subtle` por padrão. Valor de dicionário com papel
15
- declarado usa `DictionaryValue`, que escolhe o contexto pela metadata em vez de repetir esta
16
- decisão em cada tela.
18
+ Todos os contextos usam `subtle` por padrão, incluindo `success`, `warning`, `info` e `danger`.
19
+ Valor de dicionário com papel declarado usa [`DictionaryValue`](/ui/dictionary-value), que escolhe o
20
+ contexto pelo próprio dicionário em vez de repetir esta decisão em cada tela.
17
21
 
18
22
  ```tsx preview
19
23
  <Badge context="success">Ativa</Badge>
@@ -24,7 +28,7 @@ decisão em cada tela.
24
28
 
25
29
  ## Com ícone
26
30
 
27
- Um svg filho ganha size-3 automaticamente — bom para reforçar o estado sem crescer o rótulo.
31
+ Um ícone filho recebe `size-3` automaticamente e reforça o estado sem aumentar a altura do rótulo.
28
32
 
29
33
  ```tsx preview
30
34
  <Badge context="success"><CircleCheck /> Regressão verde</Badge>
@@ -32,8 +36,9 @@ Um svg filho ganha size-3 automaticamente — bom para reforçar o estado sem cr
32
36
 
33
37
  ## Como link (asChild)
34
38
 
35
- asChild renderiza o filho (Radix Slot) — um `<a>` com cara de badge. O Badge não aplica estado de
36
- hover; acrescente-o por `className` quando o link precisar de retorno visual.
39
+ Com `asChild`, o Badge entrega a aparência ao filho em vez de renderizar o próprio `<span>`: um
40
+ `<a>` passa a ter a forma de badge e mantém a navegação. O Badge não aplica estado de hover;
41
+ acrescente-o por `className` quando o link precisar de retorno visual.
37
42
 
38
43
  ```tsx preview
39
44
  <Badge asChild context="neutral" variant="solid">
@@ -47,4 +52,19 @@ hover; acrescente-o por `className` quando o link precisar de retorno visual.
47
52
  |---|---|---|---|
48
53
  | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado ou destaque contextual. |
49
54
  | `variant` | `'solid' \| 'subtle' \| 'outline'` | `'subtle'` | O tratamento visual aplicado ao contexto. |
50
- | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<span>` — ex.: um `<a>` com cara de badge. |
55
+ | `asChild` | `boolean` | `false` | Renderiza o filho, como um `<a>`, com a aparência de badge, em vez de um `<span>`. |
56
+
57
+ Os demais atributos vão para o `<span>` ou, com `asChild`, para o filho.
58
+
59
+ ## Função badgeVariants
60
+
61
+ `badgeVariants({ context, variant })` retorna as classes do Badge para um componente que só aceita
62
+ `className` e não pode ser envolvido com `asChild`. Os padrões são os mesmos do Badge: `neutral` e
63
+ `subtle`.
64
+
65
+ ```ts
66
+ function badgeVariants(options?: {
67
+ context?: BadgeContext | null
68
+ variant?: BadgeVariant | null
69
+ }): string
70
+ ```
@@ -1,7 +1,13 @@
1
1
  ## Caminho atual
2
2
 
3
- A composição é manual: BreadcrumbLink nos níveis navegáveis, BreadcrumbPage no atual (não
4
- clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
3
+ Use `Breadcrumb` quando a página estiver vários níveis abaixo da entrada da seção e a pessoa
4
+ precisar ver onde está e voltar a um nível intermediário. Em uma página, a trilha ocupa a região de
5
+ navegação do cabeçalho, como descreve [`Page`](/ui/page); quando basta voltar ao nível imediatamente
6
+ acima, use o retorno de `PageBack`.
7
+
8
+ Monte a trilha item a item: `BreadcrumbLink` nos níveis navegáveis, `BreadcrumbPage` no nível atual
9
+ e um `BreadcrumbSeparator` entre cada item. `BreadcrumbPage` não é clicável e marca a página atual
10
+ com `aria-current="page"`.
5
11
 
6
12
  ```tsx preview col-start
7
13
  <Breadcrumb>
@@ -21,10 +27,10 @@ clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
21
27
  </Breadcrumb>
22
28
  ```
23
29
 
24
- ## Com ícone e separador custom
30
+ ## Com ícone e separador próprio
25
31
 
26
- O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children para trocar o
27
- chevron padrão por outro glifo (aqui, uma barra).
32
+ O primeiro nível pode levar um ícone para reforçar a origem da trilha. `BreadcrumbSeparator` aceita
33
+ `children` para trocar o chevron padrão por outro ícone, como a barra do exemplo.
28
34
 
29
35
  ```tsx preview col-start
30
36
  <Breadcrumb>
@@ -53,9 +59,10 @@ chevron padrão por outro glifo (aqui, uma barra).
53
59
 
54
60
  ## Colapsado
55
61
 
56
- Trilha funda demais para o espaço: BreadcrumbEllipsis marca os níveis do meio omitidos e mantém só a
57
- raiz e o destino. A elipse é decorativa (`aria-hidden`), não um menu: os níveis ocultos deixam de ser
58
- alcançáveis pela trilha. Ofereça esse caminho por outro meio, como a navegação da seção.
62
+ Quando a trilha tiver mais níveis do que o espaço comporta, `BreadcrumbEllipsis` substitui os níveis
63
+ intermediários e mantém visíveis a raiz e os níveis mais próximos da página atual. A elipse é
64
+ decorativa (`aria-hidden`), e não um menu: os níveis omitidos deixam de ser alcançáveis pela trilha.
65
+ Ofereça esse caminho por outro meio, como a navegação da seção.
59
66
 
60
67
  ```tsx preview col-start
61
68
  <Breadcrumb>
@@ -91,3 +98,9 @@ alcançáveis pela trilha. Ofereça esse caminho por outro meio, como a navegaç
91
98
  | Propriedade | Tipo | Padrão | Descrição |
92
99
  |---|---|---|---|
93
100
  | `children` | `React.ReactNode` | `<ChevronRight />` | Elemento entre os itens. Omita para usar o chevron padrão ou passe outro ícone. |
101
+
102
+ `Breadcrumb` renderiza um `<nav>` com o nome acessível “Navegação estrutural”; `BreadcrumbList`, um
103
+ `<ol>`; `BreadcrumbItem`, um `<li>`; `BreadcrumbPage`, um `<span>`; e `BreadcrumbEllipsis`, um
104
+ `<span>`. Nenhum deles tem propriedades próprias: todos aceitam as props nativas do respectivo
105
+ elemento, incluindo `className`. Passe `aria-label` ao `Breadcrumb` quando a página tiver mais de
106
+ uma trilha.
@@ -1,13 +1,16 @@
1
1
  ## Contexto e variante
2
2
 
3
- `context` declara a hierarquia ou o risco da ação; `variant` escolhe o tratamento visual. Use
4
- `primary` para a ação principal, `neutral` para ações de apoio e `danger` quando a ação tiver uma
5
- consequência perigosa. A variante `outline` mantém o fundo transparente em repouso e usa uma
6
- superfície apenas no hover, preservando o fundo da região onde o botão está inserido.
3
+ Use `Button` para iniciar uma ação. `context` declara a hierarquia ou o risco da ação; `variant`
4
+ escolhe o tratamento visual. Use `primary` para a ação principal, `neutral` para ações de apoio e
5
+ `danger` quando a ação tiver uma consequência perigosa. A variante `outline` mantém o fundo
6
+ transparente em repouso e usa uma superfície apenas no hover, preservando o fundo da região onde o
7
+ botão está inserido. `subtle` preenche o botão com um tom suave do contexto, para ações que precisam
8
+ de presença sem competir com a principal.
7
9
 
8
10
  ```tsx preview
9
11
  <Button>Criar workspace</Button>
10
12
  <Button context="neutral" variant="solid">Duplicar</Button>
13
+ <Button variant="subtle">Convidar pessoa</Button>
11
14
  <Button context="neutral" variant="outline">Ver prévia</Button>
12
15
  <Button context="neutral" variant="ghost">Cancelar</Button>
13
16
  <Button context="danger" variant="solid">Excluir</Button>
@@ -16,8 +19,9 @@ superfície apenas no hover, preservando o fundo da região onde o botão está
16
19
 
17
20
  ## Tamanhos
18
21
 
19
- `size` usa a escala única dos controles: o mesmo nome tem a mesma medida em `Button`, `Select`,
20
- `Tabs`, `Toggle`, `Switch`, `Avatar`, `Spinner` e nos botões embutidos. Os tamanhos de texto dão a
22
+ `size` usa a escala única dos controles: o mesmo nome tem a mesma altura em `Button`, `Select`,
23
+ `Tabs`, `Toggle`, `Avatar` e nos botões embutidos. `Switch` e `Spinner` usam os mesmos nomes, mas
24
+ acompanham o tamanho do ícone de cada degrau, e não a altura da linha. Os tamanhos de texto dão a
21
25
  altura da linha; os `icon-*` são quadrados para botões só de ícone, que exigem `aria-label` porque não
22
26
  há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em `xs`, `sm`,
23
27
  `icon-xs`, `icon-sm` e `icon`; 1rem em `default`; 1.25rem em `lg` e `icon-lg`), a menos que o
@@ -26,8 +30,8 @@ há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em
26
30
  | Nome | Medida | Uso |
27
31
  | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
28
32
  | `xs` | 1.5rem | Ação dentro de um campo (`InputGroupButton`). |
29
- | `sm` | 2rem | Ação operacional em toolbar, seção ou coleção densa, ao lado de `Select` e `Tabs` `sm`. |
30
- | `default` | 2.25rem | Decisão da superfície em header de Page ou footer de Dialog/Drawer; é também a linha padrão de `Input`. |
33
+ | `sm` | 2rem | Operação densa em toolbar ou dentro de uma seção, ao lado de `Select` e `Tabs` `sm`. |
34
+ | `default` | 2.25rem | Decisão da superfície: header de Page, criação no cabeçalho de uma coleção e footer de Dialog ou Drawer; é também a linha de `Input`. |
31
35
  | `lg` | 2.5rem | Chamada principal com mais área de toque. |
32
36
  | `icon-xs` | 1.5rem | Ação só de ícone dentro de um campo ou de uma linha densa; é o quadrado de `ActionTrigger` com `icon`. |
33
37
  | `icon-sm` | 1.75rem | Ação só de ícone em composições compactas que precisam de mais presença; também é o quadrado das setas do pager de `ActionList`. |
@@ -35,10 +39,11 @@ há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em
35
39
  | `icon-lg` | 2.5rem | Ação só de ícone ao lado de um `lg`. |
36
40
 
37
41
  Escolha o tamanho pela região, não pela importância visual: `variant` e `context` resolvem a
38
- hierarquia da ação. Headers de Page e footers de Dialog/Drawer usam `default`; ações operacionais
39
- de seção, toolbar e coleção usam `sm`; ações dentro de linha ou célula usam `xs` ou `icon-xs`.
40
- Assim a mesma decisão mantém a mesma altura mesmo quando uma superfície troca uma ação secundária
41
- por uma primária.
42
+ hierarquia da ação. Headers de Page, a criação no cabeçalho de uma coleção e footers de
43
+ [`Dialog`](/ui/dialog) e [`Drawer`](/ui/drawer) usam `default`; a criação de uma coleção é um botão textual, sem
44
+ ícone, como mostra [`Content`](/ui/content). Operações densas de toolbar ou de seção usam `sm`;
45
+ ações dentro de linha ou célula usam `xs` ou `icon-xs`. Assim a mesma decisão mantém a mesma altura
46
+ mesmo quando uma superfície troca uma ação secundária por uma primária.
42
47
 
43
48
  O retorno de `PageBack` usa `icon`. O fechamento de Dialog e Drawer é um `CloseButton` circular
44
49
  (`shape="pill"`), `neutral` e `subtle`, no quadrado `icon-sm` por padrão, ou `icon-xs` com
@@ -49,29 +54,43 @@ O retorno de `PageBack` usa `icon`. O fechamento de Dialog e Drawer é um `Close
49
54
  <Button size="sm">Pequeno</Button>
50
55
  <Button>Padrão</Button>
51
56
  <Button size="lg">Grande</Button>
52
- <Button size="icon-xs" variant="ghost" aria-label="Novo"><Plus /></Button>
53
- <Button size="icon-sm" variant="ghost" aria-label="Novo"><Plus /></Button>
54
- <Button size="icon" aria-label="Novo"><Plus /></Button>
55
- <Button size="icon-lg" aria-label="Novo"><Plus /></Button>
57
+ <Button size="icon-xs" variant="ghost" aria-label="Configurações"><Settings /></Button>
58
+ <Button size="icon-sm" variant="ghost" aria-label="Configurações"><Settings /></Button>
59
+ <Button size="icon" aria-label="Configurações"><Settings /></Button>
60
+ <Button size="icon-lg" aria-label="Configurações"><Settings /></Button>
61
+ ```
62
+
63
+ ## Forma
64
+
65
+ `shape="pill"` arredonda as extremidades por completo. Use-a quando o botão acompanhar outros
66
+ controles arredondados, como a busca da toolbar de listas ou um grupo de filtros em pílula.
67
+
68
+ ```tsx preview
69
+ <Button shape="pill" variant="outline" context="neutral">Todos os status</Button>
70
+ <Button shape="pill" size="icon" variant="ghost" aria-label="Filtros"><Filter /></Button>
56
71
  ```
57
72
 
58
73
  ## Estados
59
74
 
60
- busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão. `icon` recebe o
61
- elemento do ícone e o dimensiona pelo `size`; com icon, o Spinner TROCA o ícone (não soma). Não
62
- confunda com carregar conteúdo (ANTES) — isso é `DataState` ou `Skeleton` em um nível de página.
75
+ `busy` indica que a ação já foi acionada e ainda está em andamento: o botão mostra um
76
+ [`Spinner`](/ui/spinner) e fica desabilitado até a conclusão. `icon` recebe o elemento do ícone e o
77
+ dimensiona pelo `size`; com `busy`, o spinner ocupa o lugar do ícone, sem alterar a largura. Para o
78
+ carregamento de conteúdo antes de qualquer ação, use [`DataState`](/ui/data-state) ou
79
+ [`Skeleton`](/ui/skeleton).
63
80
 
64
81
  ```tsx preview
65
82
  <Button disabled>Desabilitado</Button>
66
83
  <Button busy>Salvando…</Button>
67
- <Button icon={<Plus />}>Novo</Button>
68
- <Button icon={<Plus />} busy>Novo</Button>
84
+ <Button icon={<Download />}>Exportar</Button>
85
+ <Button icon={<Download />} busy>Exportar</Button>
69
86
  ```
70
87
 
71
88
  ## Como outro elemento (asChild)
72
89
 
73
- Âncora com cara de botão: asChild renderiza o filho (Radix Slot) — sem forkar estilo.
74
- buttonVariants serve para o caso sem filho único.
90
+ Quando a ação navega para outro endereço, o elemento correto é um link, mesmo que ele precise da
91
+ aparência de botão. Com `asChild`, o `Button` aplica seus estilos ao filho, como um `<a>` ou o link
92
+ do roteador, sem criar uma cópia das classes. Quando não houver um único filho para receber os
93
+ estilos, use `buttonVariants` para obter as classes.
75
94
 
76
95
  ```tsx preview
77
96
  <Button asChild variant="outline">
@@ -90,20 +109,20 @@ buttonVariants serve para o caso sem filho único.
90
109
  | `size` | `'xs' \| 'sm' \| 'default' \| 'lg' \| 'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'default'` | A medida na escala única dos controles (tabela acima). Os `icon-*` são quadrados para botões só de ícone, com `aria-label`. |
91
110
  | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do botão; `pill` arredonda as extremidades por completo. |
92
111
  | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
93
- | `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton em um nível de página). |
94
- | `icon` | `React.ReactNode` | | Ícone à esquerda, como nó (ex.: `icon={<Plus />}`); o glifo segue o `size`. No busy é trocado pelo Spinner — não soma. |
112
+ | `busy` | `boolean` | `false` | Ação em andamento, depois do clique: mostra o `Spinner` e desabilita o botão. Não indica carregamento de conteúdo; para isso, use `DataState` ou `Skeleton`. |
113
+ | `icon` | `React.ReactNode` | | Ícone à esquerda, como nó (ex.: `icon={<Plus />}`); o glifo segue o `size`. Com `busy`, dá lugar ao `Spinner`; os dois não aparecem juntos. |
95
114
 
96
115
  ## ButtonGroup
97
116
 
98
- Use `ButtonGroup` para manter ações relacionadas juntas. No modo `connected`, que permanece como
99
- padrão, as bordas internas colapsam e somente as pontas externas ficam arredondadas. Mantenha a
117
+ Use `ButtonGroup` para manter ações relacionadas juntas. No modo `connected`, o padrão, as
118
+ bordas internas colapsam e somente as pontas externas ficam arredondadas. Mantenha a
100
119
  mesma variante nos filhos para preservar a unidade visual.
101
120
 
102
121
  ```tsx preview
103
122
  <ButtonGroup>
104
123
  <Button variant="outline">Visão geral</Button>
105
- <Button variant="outline">Sessões</Button>
106
- <Button variant="outline">Habilidades</Button>
124
+ <Button variant="outline">Histórico</Button>
125
+ <Button variant="outline">Anexos</Button>
107
126
  </ButtonGroup>
108
127
  ```
109
128
 
@@ -111,7 +130,7 @@ mesma variante nos filhos para preservar a unidade visual.
111
130
 
112
131
  Use `mode="spaced"` quando os controles pertencem ao mesmo conjunto, mas cada um precisa preservar
113
132
  sua própria forma e superfície de hover. O grupo aplica o intervalo compacto de `0.25rem` sem
114
- conectar bordas. Esse modo atende ações icon-only em barras e linhas de listagem.
133
+ conectar bordas. Esse modo atende ações só com ícone em barras e linhas de listagem.
115
134
 
116
135
  ```tsx preview
117
136
  <ButtonGroup mode="spaced">
@@ -131,8 +150,7 @@ conectar bordas. Esse modo atende ações icon-only em barras e linhas de listag
131
150
 
132
151
  `DialogFooter` e `DrawerFooter` cuidam somente da faixa; envolva as decisões em um
133
152
  `ButtonGroup mode="spaced"`, que responde pelo agrupamento e pela distribuição. Por padrão, as
134
- ações preservam a largura do conteúdo e `Cancelar` usa `ghost`. Use `distribution="equal"` somente
135
- quando as duas decisões tiverem peso equivalente; nesse caso, `Cancelar` usa `outline`.
153
+ ações preservam a largura do conteúdo e `Cancelar` usa `ghost`.
136
154
 
137
155
  ```tsx
138
156
  <DialogFooter>
@@ -145,6 +163,18 @@ quando as duas decisões tiverem peso equivalente; nesse caso, `Cancelar` usa `o
145
163
  </DialogFooter>
146
164
  ```
147
165
 
166
+ Use `distribution="equal"` somente quando as duas decisões tiverem peso equivalente, como em uma
167
+ confirmação. As ações dividem a largura disponível; use `outline` no `Cancelar`.
168
+
169
+ ```tsx preview col
170
+ <div className="w-80">
171
+ <ButtonGroup mode="spaced" distribution="equal">
172
+ <Button context="neutral" variant="outline">Cancelar</Button>
173
+ <Button context="danger">Excluir</Button>
174
+ </ButtonGroup>
175
+ </div>
176
+ ```
177
+
148
178
  ### Ação dividida
149
179
 
150
180
  Combine a ação principal, um separador e um botão de ícone quando o mesmo comando oferecer
@@ -152,9 +182,9 @@ variações.
152
182
 
153
183
  ```tsx preview
154
184
  <ButtonGroup>
155
- <Button icon={<Play />}>Rodar agente developer</Button>
185
+ <Button icon={<Download />}>Exportar relatório</Button>
156
186
  <ButtonGroupSeparator />
157
- <Button size="icon" aria-label="Mais opções">
187
+ <Button size="icon" aria-label="Mais formatos de exportação">
158
188
  <ChevronDown />
159
189
  </Button>
160
190
  </ButtonGroup>
@@ -168,11 +198,11 @@ semântica de outro elemento, como `label`.
168
198
  ```tsx preview
169
199
  <ButtonGroup>
170
200
  <ButtonGroupText>
171
- <GitBranch />
172
- empresa-x-api
201
+ <CalendarDays />
202
+ Junho de 2026
173
203
  </ButtonGroupText>
174
204
  <Button variant="outline" icon={<RotateCw />}>
175
- Sincronizar
205
+ Atualizar
176
206
  </Button>
177
207
  </ButtonGroup>
178
208
  ```
@@ -183,14 +213,14 @@ semântica de outro elemento, como `label`.
183
213
 
184
214
  ```tsx preview col-start
185
215
  <ButtonGroup orientation="vertical">
186
- <Button variant="outline" size="icon" aria-label="Rodar sessão">
187
- <Play />
216
+ <Button variant="outline" size="icon" aria-label="Ampliar">
217
+ <ZoomIn />
188
218
  </Button>
189
- <Button variant="outline" size="icon" aria-label="Pausar sessão">
190
- <Pause />
219
+ <Button variant="outline" size="icon" aria-label="Reduzir">
220
+ <ZoomOut />
191
221
  </Button>
192
- <Button variant="outline" size="icon" aria-label="Reiniciar sessão">
193
- <RotateCw />
222
+ <Button variant="outline" size="icon" aria-label="Restaurar zoom">
223
+ <RotateCcw />
194
224
  </Button>
195
225
  </ButtonGroup>
196
226
  ```