@inchurch/matcha-theme 21.4.0 → 22.1.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 (94) hide show
  1. package/abstracts/_colors.scss +236 -236
  2. package/abstracts/_elevation.scss +108 -108
  3. package/abstracts/_functions.scss +321 -321
  4. package/abstracts/_media-breakpoint.scss +26 -26
  5. package/abstracts/_theme-api.scss +219 -219
  6. package/abstracts/_typography.scss +128 -128
  7. package/base/_reset.scss +446 -445
  8. package/components/app-page-header.scss +260 -260
  9. package/components/matcha-accordion.scss +293 -293
  10. package/components/matcha-audio-player.scss +31 -31
  11. package/components/matcha-autocomplete.scss +145 -145
  12. package/components/matcha-avatar.scss +440 -301
  13. package/components/matcha-badge.scss +120 -120
  14. package/components/matcha-breadcrumb.scss +231 -231
  15. package/components/matcha-button-toggle.scss +383 -384
  16. package/components/matcha-buttons.scss +913 -913
  17. package/components/matcha-calendar.scss +218 -218
  18. package/components/matcha-cards.scss +230 -230
  19. package/components/matcha-chart-card.scss +53 -53
  20. package/components/matcha-checkbox.scss +255 -255
  21. package/components/matcha-chip.scss +292 -292
  22. package/components/matcha-color-pick.scss +34 -34
  23. package/components/matcha-command-palette.scss +316 -316
  24. package/components/matcha-contact.scss +35 -0
  25. package/components/matcha-crop.scss +406 -406
  26. package/components/matcha-date.scss +81 -81
  27. package/components/matcha-divider.scss +100 -100
  28. package/components/matcha-draggable.scss +23 -23
  29. package/components/matcha-drawer.scss +376 -376
  30. package/components/matcha-drop-image.scss +14 -14
  31. package/components/matcha-drop-list.scss +430 -430
  32. package/components/matcha-emoji.scss +69 -69
  33. package/components/matcha-empty-state.scss +230 -230
  34. package/components/matcha-file-uploader.scss +471 -471
  35. package/components/matcha-flag.scss +103 -103
  36. package/components/matcha-footer.scss +91 -91
  37. package/components/matcha-form-field.scss +606 -606
  38. package/components/matcha-form-section.scss +100 -100
  39. package/components/matcha-header.scss +135 -135
  40. package/components/matcha-highlight.scss +164 -164
  41. package/components/matcha-hint-text.scss +90 -90
  42. package/components/matcha-horizontal-tree.scss +316 -316
  43. package/components/matcha-icon.scss +98 -98
  44. package/components/matcha-image.scss +61 -61
  45. package/components/matcha-list.scss +211 -211
  46. package/components/matcha-menu.scss +99 -99
  47. package/components/matcha-modal.scss +210 -210
  48. package/components/matcha-nav.scss +581 -581
  49. package/components/matcha-notification.scss +413 -413
  50. package/components/matcha-option.scss +170 -170
  51. package/components/matcha-page-builder.scss +4196 -4196
  52. package/components/matcha-paginator.scss +689 -689
  53. package/components/matcha-panel.scss +194 -194
  54. package/components/matcha-profile-card.scss +50 -50
  55. package/components/matcha-progress-bar.scss +313 -313
  56. package/components/matcha-radio.scss +320 -320
  57. package/components/matcha-ripple.scss +7 -7
  58. package/components/matcha-scrollbar.scss +33 -33
  59. package/components/matcha-scrollbox-shadow.scss +120 -120
  60. package/components/matcha-section.scss +102 -102
  61. package/components/matcha-select-multiple.scss +56 -56
  62. package/components/matcha-select.scss +667 -667
  63. package/components/matcha-skeleton.scss +155 -155
  64. package/components/matcha-slide-toggle.scss +369 -369
  65. package/components/matcha-slider.scss +285 -285
  66. package/components/matcha-snack-bar.scss +259 -259
  67. package/components/matcha-spin.scss +164 -164
  68. package/components/matcha-spinner.scss +97 -97
  69. package/components/matcha-status-pill.scss +38 -18
  70. package/components/matcha-stepper.scss +376 -376
  71. package/components/matcha-table-cell.scss +49 -41
  72. package/components/matcha-table.scss +579 -579
  73. package/components/matcha-tabs.scss +600 -600
  74. package/components/matcha-text-editor.scss +105 -105
  75. package/components/matcha-time.scss +33 -33
  76. package/components/matcha-timepicker.scss +13 -13
  77. package/components/matcha-toolbar.scss +359 -359
  78. package/components/matcha-tooltip.scss +224 -224
  79. package/components/matcha-tree-view.scss +257 -257
  80. package/layout/matcha-page-layout.scss +778 -778
  81. package/main.scss +331 -329
  82. package/package.json +1 -1
  83. package/tokens/_animations-tokens.scss +37 -37
  84. package/tokens/_breakpoints-tokens.scss +35 -35
  85. package/tokens/_color-tokens.scss +274 -274
  86. package/tokens/_elevation-tokens.scss +33 -33
  87. package/tokens/_emit-tokens.scss +324 -324
  88. package/tokens/_material-palette.scss +1149 -1149
  89. package/tokens/_primitives.scss +98 -98
  90. package/tokens/_semantic-aliases.scss +120 -120
  91. package/tokens/_spacing-tokens.scss +94 -94
  92. package/tokens/_static-tokens.scss +137 -137
  93. package/tokens/_typography-tokens.scss +65 -65
  94. package/vendors/charts.scss +90 -90
@@ -1,913 +1,913 @@
1
- @use "../abstracts/media-breakpoint" as *;
2
- @use "../abstracts/functions" as *;
3
- @use "../tokens/breakpoints-tokens" as *;
4
- // =============================================================================
5
- // matcha-buttons.scss — Button & Icon Button (modernized)
6
- // Aligned with Figma: matcha/button + matcha/icon-button
7
- // Sizes: SM(32) MD(40) LG(48) XL(56) + legacy aliases (tiny/small/medium/large/huge)
8
- // Variants: Filled(default) Outline Ghost(basic) Alpha Link
9
- // State layer: ::before opacity pattern (consistent with all DS components)
10
- // =============================================================================
11
-
12
- // ── Disabled mixins ─────────────────────────────────────────────────────────
13
-
14
- @mixin disabled-solid-button($theme) {
15
- background: var(--matcha-color-disabled-bg);
16
- color: var(--matcha-color-disabled-fg);
17
- box-shadow: 0 0 0 0px inset;
18
- pointer-events: none;
19
- }
20
-
21
- @mixin disabled-basic-button($theme) {
22
- background: var(--matcha-color-surface);
23
- color: var(--matcha-color-disabled-bg);
24
- box-shadow: 0 0 0 0px inset;
25
- pointer-events: none;
26
- }
27
-
28
- @mixin disabled-outline-button($theme) {
29
- background: transparent;
30
- color: var(--matcha-color-disabled-bg);
31
- box-shadow: 0 0 0 var(--matcha-border-thin, 2px) inset;
32
- pointer-events: none;
33
- }
34
-
35
- @mixin disabled-alpha-button($theme) {
36
- background: var(--matcha-color-disabled-bg-alpha, var(--matcha-color-interaction-disabled-bg-alpha));
37
- color: var(--matcha-color-disabled-bg);
38
- box-shadow: 0 0 0 0px inset;
39
- pointer-events: none;
40
- }
41
-
42
- @mixin disabled-link-button($theme) {
43
- background: transparent;
44
- color: var(--matcha-color-disabled-bg);
45
- box-shadow: 0 0 0 0px inset;
46
- pointer-events: none;
47
- }
48
-
49
- // ── Size classes (responsive, aligned with Figma XS/SM/MD/LG/XL) ────────────
50
-
51
- @mixin generate-matcha-button-size-classes($helper-breakpoints) {
52
- @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
53
- @include media-breakpoint($materialBreakpoint) {
54
- $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
55
-
56
- // XS — 28px. Figma matcha/button XS: pad 8 · gap 2 · text/caption (11/400/14, letter 0.3)
57
- &[size#{$infix}="xs"] {
58
- padding: 0 var(--matcha-space-100, 8px);
59
- font-size: var(--matcha-text-p-tiny, 11px);
60
- font-weight: var(--matcha-weight-regular, 400);
61
- line-height: var(--matcha-leading-p-tiny, 14px);
62
- letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
63
- height: 28px;
64
- gap: var(--matcha-space-025, 2px);
65
- }
66
-
67
- // SM — 32px. Figma: pad 8 · gap 4 · text/button-sm (13/500/19 letter 0.2)
68
- &[size#{$infix}="sm"],
69
- &[size#{$infix}="tiny"] {
70
- padding: 0 var(--matcha-space-100, 8px);
71
- font-size: var(--matcha-text-label-sm, 13px);
72
- font-weight: var(--matcha-weight-medium, 500);
73
- line-height: var(--matcha-leading-label-sm, 19px);
74
- height: 32px;
75
- gap: var(--matcha-space-050, 4px);
76
- }
77
-
78
- // MD — 40px. Figma: pad 16 · gap 8 · text/button-md (14/500/20)
79
- &[size#{$infix}="md"],
80
- &[size#{$infix}="small"] {
81
- padding: 0 var(--matcha-space-200, 16px);
82
- font-size: var(--matcha-text-md, 14px);
83
- font-weight: var(--matcha-weight-medium, 500);
84
- line-height: var(--matcha-leading-md, 20px);
85
- height: 40px;
86
- gap: var(--matcha-space-100, 8px);
87
- }
88
-
89
- // LG — 48px. Figma: pad 24 · gap 8
90
- &[size#{$infix}="lg"],
91
- &[size#{$infix}="medium"] {
92
- padding: 0 var(--matcha-space-300, 24px);
93
- font-size: var(--matcha-text-lg, 16px);
94
- font-weight: var(--matcha-weight-semibold, 600);
95
- line-height: var(--matcha-leading-label-lg, 22px);
96
- height: 48px;
97
- gap: var(--matcha-space-100, 8px);
98
- }
99
-
100
- // XL — 56px. Figma: pad 32 · gap 8
101
- &[size#{$infix}="xl"],
102
- &[size#{$infix}="large"],
103
- &[size#{$infix}="huge"] {
104
- padding: 0 var(--matcha-space-400, 32px);
105
- font-size: var(--matcha-text-lg, 16px);
106
- font-weight: var(--matcha-weight-semibold, 600);
107
- line-height: var(--matcha-leading-label-lg, 22px);
108
- height: 56px;
109
- gap: var(--matcha-space-100, 8px);
110
- }
111
- }
112
- }
113
- }
114
-
115
- // ── Structural ──────────────────────────────────────────────────────────────
116
-
117
- button[matcha-button],
118
- a[matcha-button],
119
- button[matcha-icon-button],
120
- a[matcha-icon-button],
121
- button[matcha-link],
122
- a[matcha-link] {
123
- overflow: hidden;
124
- position: relative;
125
- font-size: var(--matcha-text-md, 14px);
126
- font-weight: var(--matcha-weight-medium, 500);
127
- font-family: inherit;
128
- height: px-to-rem(40px); // default MD; overridden by size attribute selectors
129
- appearance: auto;
130
- text-rendering: auto;
131
- letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
132
- word-spacing: normal;
133
- line-height: normal;
134
- text-transform: none;
135
- text-indent: 0px;
136
- text-shadow: none;
137
- text-align: center;
138
- text-wrap: nowrap;
139
- display: flex;
140
- justify-content: center;
141
- align-items: center;
142
- cursor: pointer;
143
- box-sizing: border-box;
144
- border: none;
145
- border-radius: var(--matcha-radius-lg, 8px);
146
-
147
- // Figma matcha/button usa DOIS feedbacks combinados:
148
- // 1. Cor do bg muda (accent-default → accent-hover → accent-pressed)
149
- // 2. State-layer overlay por cima (matcha/color/state-layer/fg)
150
- // - Filled: opacity 0.15 hover / 0.30 active
151
- // - Outline/Ghost: opacity 0.10 hover / 0.20 active
152
- // ::before aplica o state-layer; :hover/:active ou .is-state-* ativam.
153
- // NOTA: declarada ANTES das regras aninhadas (evita a deprecação mixed-decls);
154
- // é onde o hoisting do Sass já a colocava — output byte-idêntico.
155
- transition:
156
- background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
157
- box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
158
- color 150ms cubic-bezier(0.4, 0, 0.2, 1);
159
-
160
- * { pointer-events: none; }
161
-
162
- // Figma spec: ícones dentro de button herdam a foreground color do botão
163
- // (matcha-icon tem color-default próprio via tema — forçamos inherit com !important
164
- // pra vencer o tema que aplica em [class^="i-matcha-"] globalmente)
165
- matcha-icon,
166
- [class^="i-matcha-"],
167
- [class*=" i-matcha-"] {
168
- color: inherit !important;
169
- }
170
-
171
- @include generate-matcha-button-size-classes($helper-breakpoints);
172
-
173
- &::before {
174
- content: '';
175
- position: absolute;
176
- inset: 0;
177
- background: var(--matcha-color-state-layer-fg, currentColor);
178
- opacity: 0;
179
- border-radius: inherit;
180
- pointer-events: none;
181
- transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
182
- }
183
-
184
- // Filled (variant default) — state-layer forte
185
- &:hover:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link])::before,
186
- &.is-state-hover:not([outline]):not([basic]):not([ghost]):not([alpha])::before {
187
- opacity: 0.15;
188
- }
189
- &:active:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link])::before,
190
- &.is-state-active:not([outline]):not([basic]):not([ghost]):not([alpha])::before {
191
- opacity: 0.30;
192
- }
193
-
194
- // Outline / Ghost — state-layer sutil
195
- &[outline]:hover:not([disabled]):not([disabled="true"])::before,
196
- &[basic]:hover:not([disabled]):not([disabled="true"])::before,
197
- &[ghost]:hover:not([disabled]):not([disabled="true"])::before,
198
- &[outline].is-state-hover::before,
199
- &[basic].is-state-hover::before,
200
- &[ghost].is-state-hover::before {
201
- opacity: 0.10;
202
- }
203
- &[outline]:active:not([disabled]):not([disabled="true"])::before,
204
- &[basic]:active:not([disabled]):not([disabled="true"])::before,
205
- &[ghost]:active:not([disabled]):not([disabled="true"])::before,
206
- &[outline].is-state-active::before,
207
- &[basic].is-state-active::before,
208
- &[ghost].is-state-active::before {
209
- opacity: 0.20;
210
- }
211
-
212
- &[disabled] {
213
- pointer-events: none;
214
- cursor: default;
215
- }
216
-
217
- // ── Variant modifiers (estrutural — cores vêm do mixin de tema) ──
218
- &[basic]:not([basic="false"]),
219
- &[ghost]:not([ghost="false"]) {
220
- background: transparent;
221
- }
222
-
223
- &[outline]:not([outline="false"]) {
224
- background: transparent;
225
-
226
- &[link]:not([link="false"]) {
227
- &[size="sm"], &[size="tiny"] { padding: 0 var(--matcha-space-100, 8px); gap: var(--matcha-space-050, 4px); }
228
- &[size="md"], &[size="small"] { padding: 0 var(--matcha-space-150, 12px); gap: var(--matcha-space-100, 8px); }
229
- &[size="lg"], &[size="medium"] { padding: 0 var(--matcha-space-200, 16px); gap: var(--matcha-space-150, 12px); }
230
- &[size="xl"], &[size="large"], &[size="huge"] { padding: 0 var(--matcha-space-200, 16px); gap: var(--matcha-space-150, 12px); }
231
- }
232
- }
233
-
234
- &[pill]:not([pill="false"]) {
235
- border-radius: var(--matcha-radius-pill, 9999px);
236
- }
237
-
238
- &[link]:not([link="false"]) {
239
- text-transform: initial;
240
- letter-spacing: 0;
241
- text-decoration: none;
242
- padding: 0;
243
- background: transparent;
244
- }
245
-
246
- // ── matcha-link specific (Figma spec 16914:500) ──
247
- // SM=11/14 Regular letter 0.3 · MD=14/20 Medium · LG=16/22 SemiBold
248
- // Hover/Active → underline automático. Underline=true → permanente.
249
- // Disabled → opacity via disabled-fg color (no underline).
250
- &[matcha-link] {
251
- height: auto;
252
- min-height: 0;
253
- max-height: none;
254
- padding: 0;
255
- background: transparent;
256
- border-radius: 0;
257
- text-decoration: none; // reset do underline default do <a>; hover/class re-aplica
258
-
259
- &[size="sm"], &[size="tiny"] {
260
- font-size: var(--matcha-text-p-tiny, 11px);
261
- line-height: var(--matcha-leading-p-tiny, 14px);
262
- font-weight: var(--matcha-weight-regular, 400);
263
- letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
264
- height: auto;
265
- max-height: none;
266
- }
267
- &[size="md"], &[size="small"] {
268
- font-size: var(--matcha-text-md, 14px);
269
- line-height: var(--matcha-leading-md, 20px);
270
- font-weight: var(--matcha-weight-medium, 500);
271
- letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
272
- height: auto;
273
- max-height: none;
274
- }
275
- &[size="lg"], &[size="medium"] {
276
- font-size: var(--matcha-text-lg, 16px);
277
- line-height: var(--matcha-leading-label-lg, 22px);
278
- font-weight: var(--matcha-weight-semibold, 600);
279
- letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
280
- height: auto;
281
- max-height: none;
282
- }
283
-
284
- // State layer fica off (link não tem overlay/bg)
285
- &::before { display: none; }
286
- // Press scale também off — link é texto
287
- transition: none;
288
- &:active:not([disabled]):not([disabled="true"]) { transform: none; }
289
-
290
- // Hover e active: underline (Figma spec)
291
- &:hover:not([disabled]):not([disabled="true"]),
292
- &:active:not([disabled]):not([disabled="true"]) {
293
- text-decoration: underline;
294
- }
295
-
296
- // Underline permanente via property (classe do HostBinding no component)
297
- &.matcha-link--underline {
298
- text-decoration: underline;
299
- }
300
-
301
- // ── Canonical extended size names (tiny|small|medium|large|huge) ────────
302
- // matcha-link expõe a escala CANÔNICA por extenso, 1:1 com sm/md/lg
303
- // (sm→small · md→medium · lg→large). O bloco acima pareia esses nomes com
304
- // a escala LEGADA do matcha-button (small=md, medium=lg), então
305
- // re-asseguramos aqui — scoped APENAS ao matcha-link — os mesmos pixels do
306
- // tier abreviado correspondente (sem mudança visual). tiny→sm tier (menor
307
- // disponível), huge→lg tier (maior disponível no link).
308
- &[size="tiny"],
309
- &[size="small"] {
310
- font-size: var(--matcha-text-p-tiny, 11px);
311
- line-height: var(--matcha-leading-p-tiny, 14px);
312
- font-weight: var(--matcha-weight-regular, 400);
313
- letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
314
- height: auto;
315
- max-height: none;
316
- }
317
- &[size="medium"] {
318
- font-size: var(--matcha-text-md, 14px);
319
- line-height: var(--matcha-leading-md, 20px);
320
- font-weight: var(--matcha-weight-medium, 500);
321
- letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
322
- height: auto;
323
- max-height: none;
324
- }
325
- &[size="large"],
326
- &[size="huge"] {
327
- font-size: var(--matcha-text-lg, 16px);
328
- line-height: var(--matcha-leading-label-lg, 22px);
329
- font-weight: var(--matcha-weight-semibold, 600);
330
- letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
331
- height: auto;
332
- max-height: none;
333
- }
334
- }
335
-
336
- &[icon]:not([icon="false"]) {
337
- aspect-ratio: 1;
338
- display: flex;
339
- align-items: center;
340
- justify-content: center;
341
-
342
- // Icon-button padding per Figma spec matcha/icon-button (all-direction):
343
- // XS=6 · SM=6 · MD=8 · LG=12 · XL=16 (padding igual em todos os lados)
344
- &[size="xs"] {
345
- padding: var(--matcha-space-075, 6px) !important;
346
- }
347
- &[size="sm"], &[size="tiny"] {
348
- padding: var(--matcha-space-075, 6px) !important;
349
- }
350
- &[size="md"], &[size="small"] {
351
- padding: var(--matcha-space-100, 8px) !important;
352
- }
353
- &[size="lg"], &[size="medium"] {
354
- padding: var(--matcha-space-150, 12px) !important;
355
- }
356
- &[size="xl"], &[size="large"], &[size="huge"] {
357
- padding: var(--matcha-space-200, 16px) !important;
358
- }
359
-
360
- // Auto-size inner matcha-icon conforme Figma matcha/icon-button spec:
361
- // XS→16 · SM→20 · MD→24 · LG→24 · XL→24
362
- &[size="xs"] [class^="i-matcha-"],
363
- &[size="xs"] [class*=" i-matcha-"] {
364
- font-size: var(--matcha-icon-size-sm, 16px);
365
- width: var(--matcha-icon-size-sm, 16px);
366
- height: var(--matcha-icon-size-sm, 16px);
367
- line-height: var(--matcha-icon-size-sm, 16px);
368
- }
369
- &[size="sm"] [class^="i-matcha-"],
370
- &[size="sm"] [class*=" i-matcha-"],
371
- &[size="tiny"] [class^="i-matcha-"],
372
- &[size="tiny"] [class*=" i-matcha-"] {
373
- font-size: 20px;
374
- width: 20px;
375
- height: 20px;
376
- line-height: 20px;
377
- }
378
- &[size="md"] [class^="i-matcha-"],
379
- &[size="md"] [class*=" i-matcha-"],
380
- &[size="small"] [class^="i-matcha-"],
381
- &[size="small"] [class*=" i-matcha-"],
382
- &[size="lg"] [class^="i-matcha-"],
383
- &[size="lg"] [class*=" i-matcha-"],
384
- &[size="medium"] [class^="i-matcha-"],
385
- &[size="medium"] [class*=" i-matcha-"],
386
- &[size="xl"] [class^="i-matcha-"],
387
- &[size="xl"] [class*=" i-matcha-"],
388
- &[size="large"] [class^="i-matcha-"],
389
- &[size="large"] [class*=" i-matcha-"],
390
- &[size="huge"] [class^="i-matcha-"],
391
- &[size="huge"] [class*=" i-matcha-"] {
392
- font-size: var(--matcha-icon-size-md, 24px);
393
- width: var(--matcha-icon-size-md, 24px);
394
- height: var(--matcha-icon-size-md, 24px);
395
- line-height: var(--matcha-icon-size-md, 24px);
396
- }
397
- }
398
-
399
- // ── Badge ──
400
- &[badge]:not([badge="false"]) {
401
- overflow: visible;
402
- position: relative;
403
-
404
- &::after {
405
- content: "";
406
- position: absolute;
407
- top: -0.25rem;
408
- right: -0.25rem;
409
- width: 1.32rem;
410
- height: 1.32rem;
411
- border-radius: var(--matcha-radius-pill, 9999px);
412
- z-index: 2;
413
- }
414
-
415
- &[size="sm"]::after, &[size="tiny"]::after { width: 0.75rem; height: 0.75rem; top: -0.15rem; right: -0.15rem; }
416
- &[size="md"]::after, &[size="small"]::after { width: 1rem; height: 1rem; top: -0.2rem; right: -0.2rem; }
417
- &[size="xl"]::after, &[size="large"]::after, &[size="huge"]::after { width: 1.5rem; height: 1.5rem; top: -0.3rem; right: -0.3rem; }
418
- }
419
-
420
- // ── Focus ring ──
421
- &:focus-visible:not([disabled]) {
422
- outline: var(--matcha-state-focus-ring-width, 2px) solid var(--matcha-color-accent, #6200ee);
423
- outline-offset: var(--matcha-state-focus-ring-offset, 2px);
424
- }
425
-
426
- @media (prefers-reduced-motion: reduce) {
427
- &::before { transition: none; }
428
- &, &:active:not([disabled]):not([disabled="true"]) { transition: none; transform: none; }
429
- }
430
-
431
- // ── Loading state ──
432
- // Spinner inline do lado direito do conteúdo + content dims pra 60%
433
- // Preserva width (não shift) e bloqueia cliques com pointer-events.
434
- &.matcha-button--loading {
435
- pointer-events: none;
436
- cursor: progress;
437
-
438
- // Dim no content interior (tudo exceto o spinner)
439
- > :not(.matcha-button__spinner) {
440
- opacity: 0.6;
441
- }
442
- }
443
-
444
- .matcha-button__spinner {
445
- display: inline-flex;
446
- align-items: center;
447
- justify-content: center;
448
- margin-left: var(--matcha-space-100, 8px);
449
- flex: none;
450
- line-height: 0;
451
-
452
- svg {
453
- display: block;
454
- }
455
-
456
- @media (prefers-reduced-motion: reduce) {
457
- svg circle animateTransform {
458
- // Retarda mas não remove (ainda sinaliza loading)
459
- dur: 2s !important;
460
- }
461
- }
462
- }
463
-
464
- // Visually hidden span pra screen readers (loading announcement)
465
- .matcha-button__sr-only {
466
- position: absolute !important;
467
- width: 1px !important;
468
- height: 1px !important;
469
- padding: 0 !important;
470
- margin: -1px !important;
471
- overflow: hidden !important;
472
- clip: rect(0, 0, 0, 0) !important;
473
- white-space: nowrap !important;
474
- border: 0 !important;
475
- }
476
-
477
- // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ──
478
- // XS (28), SM (32), MD (40) ficam abaixo de 44px — expande hit area invisível.
479
- @media (pointer: coarse) {
480
- &[size="xs"],
481
- &[size="sm"], &[size="tiny"],
482
- &[size="md"], &[size="small"] {
483
- position: relative;
484
-
485
- &::after {
486
- content: '';
487
- position: absolute;
488
- inset: -8px; // expande ~16px em cada eixo → 28+16=44, 32+16=48, 40+16=56 ✓
489
- z-index: 0;
490
- pointer-events: auto; // captura cliques aqui
491
- }
492
-
493
- // ::before já é usado pro state layer, então nosso overlay vai no ::after
494
- // State layer (::before) fica colado ao botão visual, overlay (::after) expande hit.
495
- }
496
- }
497
- }
498
-
499
- // ── Theme mixin ─────────────────────────────────────────────────────────────
500
-
501
- @mixin matcha-button-theme($theme) {
502
- // ═════════════════════════════════════════════════════════════════════════
503
- // CSS var-based theming (regra DS-ARCHITECTURE #7: Tokens over hardcode)
504
- //
505
- // Todo botão usa --matcha-button-color como token interno.
506
- // `[color="X"]` apenas mapeia pra esses tokens — as variantes
507
- // (filled/outline/ghost/alpha/link) interpretam um único set de vars.
508
- //
509
- // Consumidor pode override QUALQUER cor via style:
510
- // <button matcha-button style="--matcha-button-color: #ff00ff">Custom</button>
511
- //
512
- // Adicionar cor nova = adicionar 1 mapping (3 linhas).
513
- // ═════════════════════════════════════════════════════════════════════════
514
-
515
- button[matcha-button],
516
- a[matcha-button],
517
- button[matcha-icon-button],
518
- a[matcha-icon-button],
519
- button[matcha-link],
520
- a[matcha-link] {
521
- // Tokens internos (Figma matcha/button 13913:1088):
522
- // --matcha-button-color = bg do filled (Default)
523
- // --matcha-button-color-hover = bg filled / stroke outline (Hover)
524
- // --matcha-button-color-pressed = bg filled / stroke+text (Active)
525
- // --matcha-button-color-text-on = texto do filled (accent/text-on etc.)
526
- // --matcha-button-color-alpha = alpha modifier / ripple
527
- // --matcha-button-color-edge = stroke do outline (Default)
528
- --matcha-button-color: var(--matcha-color-neutral);
529
- --matcha-button-color-hover: var(--matcha-color-neutral-hover);
530
- --matcha-button-color-pressed: var(--matcha-color-neutral-hover);
531
- --matcha-button-color-text-on: var(--matcha-color-neutral-contrast);
532
- --matcha-button-color-alpha: var(--matcha-color-neutral-alpha);
533
- --matcha-button-color-edge: var(--matcha-color-fg);
534
-
535
- // Badge ring herda o bg do botão (filled) — outline/ghost override pra surface,
536
- // pois o bg deles é transparente e o ring deve casar com a página atrás.
537
- --matcha-color-badge-border: var(--matcha-button-color);
538
-
539
- // ── Color mapping ────────────────────────────────────────────────────────────────
540
- &[color="basic"] {
541
- --matcha-button-color: var(--matcha-color-neutral);
542
- --matcha-button-color-hover: var(--matcha-color-neutral-hover);
543
- --matcha-button-color-pressed: var(--matcha-color-neutral-hover);
544
- --matcha-button-color-text-on: var(--matcha-color-neutral-contrast);
545
- --matcha-button-color-alpha: var(--matcha-color-neutral-alpha);
546
- --matcha-button-color-edge: var(--matcha-color-fg);
547
- }
548
- &[color="primary"] {
549
- --matcha-button-color: var(--matcha-color-primary);
550
- --matcha-button-color-hover: var(--matcha-color-primary-hover);
551
- --matcha-button-color-pressed: var(--matcha-color-primary-pressed);
552
- --matcha-button-color-text-on: var(--matcha-color-primary-contrast);
553
- --matcha-button-color-alpha: var(--matcha-color-primary-alpha);
554
- --matcha-button-color-edge: var(--matcha-color-primary);
555
- }
556
- &[color="accent"] {
557
- --matcha-button-color: var(--matcha-color-accent);
558
- --matcha-button-color-hover: var(--matcha-color-accent-hover);
559
- --matcha-button-color-pressed: var(--matcha-color-accent-pressed);
560
- --matcha-button-color-text-on: var(--matcha-color-accent-text-on);
561
- --matcha-button-color-alpha: var(--matcha-color-accent-alpha);
562
- --matcha-button-color-edge: var(--matcha-color-accent);
563
- }
564
- &[color="success"] {
565
- --matcha-button-color: var(--matcha-color-success);
566
- --matcha-button-color-hover: var(--matcha-color-success-hover);
567
- --matcha-button-color-pressed: var(--matcha-color-success-pressed);
568
- --matcha-button-color-text-on: var(--matcha-color-success-contrast);
569
- --matcha-button-color-alpha: var(--matcha-color-success-alpha);
570
- --matcha-button-color-edge: var(--matcha-color-success);
571
- }
572
- &[color="warn"] {
573
- --matcha-button-color: var(--matcha-color-warn);
574
- --matcha-button-color-hover: var(--matcha-color-warn-hover);
575
- --matcha-button-color-pressed: var(--matcha-color-warn-pressed);
576
- --matcha-button-color-text-on: var(--matcha-color-warn-contrast);
577
- --matcha-button-color-alpha: var(--matcha-color-warn-alpha);
578
- --matcha-button-color-edge: var(--matcha-color-warn);
579
- }
580
- &[color="danger"] {
581
- --matcha-button-color: var(--matcha-color-danger);
582
- --matcha-button-color-hover: var(--matcha-color-danger-hover);
583
- --matcha-button-color-pressed: var(--matcha-color-danger-pressed);
584
- --matcha-button-color-text-on: var(--matcha-color-danger-contrast);
585
- --matcha-button-color-alpha: var(--matcha-color-danger-alpha);
586
- --matcha-button-color-edge: var(--matcha-color-danger);
587
- }
588
- &[color="info"] {
589
- --matcha-button-color: var(--matcha-color-info);
590
- --matcha-button-color-hover: var(--matcha-color-info-hover);
591
- --matcha-button-color-pressed: var(--matcha-color-info-pressed);
592
- --matcha-button-color-text-on: var(--matcha-color-info-contrast);
593
- --matcha-button-color-alpha: var(--matcha-color-info-alpha);
594
- --matcha-button-color-edge: var(--matcha-color-info);
595
- }
596
-
597
- // ── Filled (default variant) ──────────────────────────────────────────────────────
598
- // Figma: Default=accent · Hover=accent-hover · Active=accent-pressed
599
- background: var(--matcha-button-color);
600
- color: var(--matcha-button-color-text-on);
601
-
602
- &:hover:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link]) {
603
- background: var(--matcha-button-color-hover);
604
- }
605
- &:active:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link]) {
606
- background: var(--matcha-button-color-pressed);
607
- }
608
-
609
- // ── Variant: outline ────────────────────────────────────────────────────────────────
610
- // Figma matcha/button (13913:1088) vs matcha/icon-button (13914:482):
611
- // BUTTON outline: texto=text-primary · ícones=edge (accent)
612
- // ICON-BUTTON outline: host color=edge (ícone herda via inherit !important)
613
- // Hover: icons/host = accent-hover (texto button mantém primary)
614
- // Active: tudo = accent-pressed
615
- &[outline]:not([outline="false"]),
616
- &[variant="outline"] {
617
- background: transparent;
618
- box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-edge);
619
- // Color slot: text segue --matcha-button-color-edge (border+text matching),
620
- // com fallback pra text-primary quando consumer não setar a var.
621
- // Antes era hardcoded text-primary → consumer tinha que duelar specificity.
622
- color: var(--matcha-button-color-edge);
623
- // Badge ring transparente — bg é transparente, qualquer cor fixa
624
- // colide com o bg-do-pai-da-página (impossível detectar em CSS).
625
- --matcha-color-badge-border: transparent;
626
-
627
- &:hover:not([disabled]):not([disabled="true"]) {
628
- box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-hover);
629
- }
630
- &:active:not([disabled]):not([disabled="true"]) {
631
- box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-pressed);
632
- color: var(--matcha-button-color-pressed);
633
- }
634
- }
635
-
636
- // Icon-button outline: host color = edge (ícone é o único conteúdo semântico)
637
- &[matcha-icon-button][outline]:not([outline="false"]),
638
- &[matcha-icon-button][variant="outline"] {
639
- color: var(--matcha-button-color-edge);
640
-
641
- &:hover:not([disabled]):not([disabled="true"]) {
642
- color: var(--matcha-button-color-hover);
643
- }
644
- &:active:not([disabled]):not([disabled="true"]) {
645
- color: var(--matcha-button-color-pressed);
646
- }
647
- }
648
-
649
- // Button outline: ícones (Left/Right) acompanham edge — texto mantém primary
650
- &[matcha-button][outline]:not([outline="false"]) matcha-icon,
651
- &[matcha-button][outline]:not([outline="false"]) [class^="i-matcha-"],
652
- &[matcha-button][outline]:not([outline="false"]) [class*=" i-matcha-"],
653
- &[matcha-button][variant="outline"] matcha-icon,
654
- &[matcha-button][variant="outline"] [class^="i-matcha-"],
655
- &[matcha-button][variant="outline"] [class*=" i-matcha-"] {
656
- color: var(--matcha-button-color-edge) !important;
657
- }
658
- &[matcha-button][outline]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
659
- &[matcha-button][outline]:hover:not([disabled]):not([disabled="true"]) [class^="i-matcha-"],
660
- &[matcha-button][outline].is-state-hover matcha-icon {
661
- color: var(--matcha-button-color-hover) !important;
662
- }
663
- &[matcha-button][outline]:active:not([disabled]):not([disabled="true"]) matcha-icon,
664
- &[matcha-button][outline]:active:not([disabled]):not([disabled="true"]) [class^="i-matcha-"],
665
- &[matcha-button][outline].is-state-active matcha-icon {
666
- color: var(--matcha-button-color-pressed) !important;
667
- }
668
-
669
- // ── Variant: ghost (basic = legacy alias) ────────────────────────────────────────
670
- // Figma: Default/Hover=text-primary · Active=accent-pressed (texto + ícones)
671
- // Slot --matcha-button-ghost-text com fallback text-primary (Figma intent
672
- // preservado). Consumer override via parent: --matcha-button-ghost-text: X
673
- &[basic]:not([basic="false"]),
674
- &[ghost]:not([ghost="false"]),
675
- &[variant="ghost"] {
676
- background: transparent;
677
- color: var(--matcha-button-ghost-text, var(--matcha-color-text-primary));
678
- // Badge ring transparente — bg é transparente, qualquer cor fixa
679
- // colide com o bg-do-pai-da-página (impossível detectar em CSS).
680
- --matcha-color-badge-border: transparent;
681
-
682
- &:active:not([disabled]):not([disabled="true"]) {
683
- color: var(--matcha-button-color-pressed);
684
- }
685
- }
686
-
687
- // Button ghost: ícones acompanham edge (accent) — texto mantém primary
688
- &[matcha-button][basic]:not([basic="false"]) matcha-icon,
689
- &[matcha-button][basic]:not([basic="false"]) [class^="i-matcha-"],
690
- &[matcha-button][ghost]:not([ghost="false"]) matcha-icon,
691
- &[matcha-button][ghost]:not([ghost="false"]) [class^="i-matcha-"],
692
- &[matcha-button][variant="ghost"] matcha-icon,
693
- &[matcha-button][variant="ghost"] [class^="i-matcha-"] {
694
- color: var(--matcha-button-color-edge) !important;
695
- }
696
- &[matcha-button][ghost]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
697
- &[matcha-button][basic]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
698
- &[matcha-button][ghost].is-state-hover matcha-icon,
699
- &[matcha-button][basic].is-state-hover matcha-icon {
700
- color: var(--matcha-button-color-hover) !important;
701
- }
702
- &[matcha-button][ghost]:active:not([disabled]):not([disabled="true"]) matcha-icon,
703
- &[matcha-button][basic]:active:not([disabled]):not([disabled="true"]) matcha-icon,
704
- &[matcha-button][ghost].is-state-active matcha-icon,
705
- &[matcha-button][basic].is-state-active matcha-icon {
706
- color: var(--matcha-button-color-pressed) !important;
707
- }
708
-
709
- // ── Icon-button neutral (basic/default) — Figma matcha/icon-button ──
710
- // Outline/Ghost Default (13914:179 / 13914:332) vinculam o glyph — e a
711
- // borda do Outline — a matcha/color/icon/default (#75767a light / #9aa0a6
712
- // dark), o MESMO token do matcha-icon solto. O texto do botão usa fg/
713
- // text-primary; o ícone do icon-button NÃO. Redireciona edge + ghost-text
714
- // só pro icon-button neutro. Cores override (accent/danger/...) seguem a
715
- // própria cor via os blocos &[color="..."] acima.
716
- &[matcha-icon-button]:not([color]),
717
- &[matcha-icon-button][color="basic"] {
718
- --matcha-button-color-edge: var(--matcha-color-icon-default);
719
- --matcha-button-ghost-text: var(--matcha-color-icon-default);
720
- }
721
-
722
- // ── Modifier: alpha ──────────────────────────────────────────────
723
- &[alpha]:not([alpha="false"]) {
724
- background: var(--matcha-button-color-alpha);
725
- color: var(--matcha-button-color);
726
- }
727
-
728
- // ── Modifier: link (legacy, afeta button[link="true"]) ───────────
729
- // matcha-link tem seu próprio bloco (&[matcha-link]) com regras
730
- // próprias de underline (hover + .matcha-link--underline)
731
- &[link]:not([link="false"]):not([matcha-link]) {
732
- background: transparent;
733
- color: var(--matcha-button-color);
734
- text-decoration: underline;
735
- }
736
- // matcha-link: apenas cor do texto (o bloco estrutural [matcha-link]
737
- // define text-decoration: none + hover/active + class underline)
738
- &[matcha-link] {
739
- color: var(--matcha-button-color);
740
- }
741
-
742
- // ── State: disabled ──────────────────────────────────────────────
743
- // Figma (13913:1088) Disabled state:
744
- // Filled: bg=interaction/disabled-bg · label+icons=accent/text-on (mesma cor do filled default)
745
- // Outline: stroke=interaction/disabled-bg (NÃO disabled-border) · label+icons=disabled-fg
746
- // Ghost: sem bg/stroke · label+icons=disabled-fg
747
- // State-layer overlay DESLIGADO em todos disabled.
748
- &[disabled]:not([disabled="false"]),
749
- &[disabled="true"] {
750
- pointer-events: none;
751
- cursor: not-allowed;
752
-
753
- // state-layer overlay desligado
754
- &::before { opacity: 0 !important; }
755
-
756
- // Filled — bg disabled + texto/ícones accent-text-on (Figma spec)
757
- background: var(--matcha-color-interaction-disabled-bg);
758
- color: var(--matcha-color-accent-text-on);
759
- box-shadow: none;
760
-
761
- // Outline — stroke usa mesma cor do filled-bg (Figma usa interaction/disabled-bg)
762
- &[outline]:not([outline="false"]),
763
- &[variant="outline"] {
764
- background: transparent;
765
- color: var(--matcha-color-interaction-disabled-fg);
766
- box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-color-interaction-disabled-bg);
767
- }
768
-
769
- // Ghost / basic / link — só texto dedicado
770
- &[basic]:not([basic="false"]),
771
- &[ghost]:not([ghost="false"]),
772
- &[variant="ghost"],
773
- &[link]:not([link="false"]) {
774
- background: transparent;
775
- color: var(--matcha-color-interaction-disabled-fg);
776
- box-shadow: none;
777
- }
778
-
779
- // Alpha — bg alpha dedicado
780
- &[alpha]:not([alpha="false"]) {
781
- background: var(--matcha-color-interaction-disabled-bg-alpha);
782
- color: var(--matcha-color-interaction-disabled-fg);
783
- }
784
-
785
- // Ícones do button (Left/Right) no disabled:
786
- // Filled: herdam color do host (accent-text-on) ✓
787
- // Outline/Ghost: forçam disabled-fg pra sobrescrever regra !important do edge
788
- &[matcha-button][outline] matcha-icon,
789
- &[matcha-button][outline] [class^="i-matcha-"],
790
- &[matcha-button][outline] [class*=" i-matcha-"],
791
- &[matcha-button][basic] matcha-icon,
792
- &[matcha-button][basic] [class^="i-matcha-"],
793
- &[matcha-button][ghost] matcha-icon,
794
- &[matcha-button][ghost] [class^="i-matcha-"],
795
- &[matcha-button][variant="outline"] matcha-icon,
796
- &[matcha-button][variant="outline"] [class^="i-matcha-"],
797
- &[matcha-button][variant="ghost"] matcha-icon,
798
- &[matcha-button][variant="ghost"] [class^="i-matcha-"] {
799
- color: var(--matcha-color-interaction-disabled-fg) !important;
800
- }
801
- }
802
-
803
- // ── Badge indicator (always accent, pattern fixo) ────────────────
804
- &[badge]:not([badge="false"])::after {
805
- background: var(--matcha-color-accent);
806
- }
807
-
808
- // ── Ripple overlay ───────────────────────────────────────────────
809
- .ripple {
810
- background: var(--matcha-button-color-alpha);
811
- }
812
- }
813
- }
814
-
815
- // ── matcha-icon-button: auto-size inner icons per Figma spec ────────────────
816
- // XS button (28) → icon 16 · SM (32) → 20 · MD (40)/LG (48)/XL (56) → 24
817
- button[matcha-icon-button],
818
- a[matcha-icon-button] {
819
- aspect-ratio: 1;
820
-
821
- matcha-icon {
822
- flex: none; // NÃO expandir com o button
823
- }
824
-
825
- &[size="xs"] matcha-icon,
826
- &[size="tiny"] matcha-icon {
827
- font-size: 16px !important;
828
- width: 16px !important;
829
- height: 16px !important;
830
- line-height: 16px !important;
831
- }
832
- &[size="sm"] matcha-icon,
833
- &[size="small"] matcha-icon {
834
- font-size: 20px !important;
835
- width: 20px !important;
836
- height: 20px !important;
837
- line-height: 20px !important;
838
- }
839
- &[size="md"] matcha-icon,
840
- &[size="medium"] matcha-icon,
841
- &[size="lg"] matcha-icon,
842
- &[size="large"] matcha-icon,
843
- &[size="xl"] matcha-icon,
844
- &[size="huge"] matcha-icon {
845
- font-size: 24px !important;
846
- width: 24px !important;
847
- height: 24px !important;
848
- line-height: 24px !important;
849
- }
850
-
851
- // ── Canonical extended size names (tiny|small|medium|large|huge) ────────────
852
- // matcha-icon-button expõe a escala CANÔNICA por extenso, 1:1 com as
853
- // abreviações xs/sm/md/lg/xl (xs→tiny · sm→small · md→medium · lg→large ·
854
- // xl→huge). O matcha-buttons.scss compartilhado pareia esses nomes com a
855
- // escala LEGADA do matcha-button (small=md, medium=lg, …), então re-asseguramos
856
- // aqui — scoped APENAS ao icon-button — os pixels corretos (sem mudança visual).
857
- &[size="tiny"] { height: 28px; }
858
- &[size="small"] { height: 32px; }
859
- &[size="medium"] { height: 40px; }
860
- &[size="large"] { height: 48px; }
861
- &[size="huge"] { height: 56px; }
862
-
863
- &[size="tiny"][icon] { padding: var(--matcha-space-075, 6px) !important; }
864
- &[size="small"][icon] { padding: var(--matcha-space-075, 6px) !important; }
865
- &[size="medium"][icon] { padding: var(--matcha-space-100, 8px) !important; }
866
- &[size="large"][icon] { padding: var(--matcha-space-150, 12px) !important; }
867
- &[size="huge"][icon] { padding: var(--matcha-space-200, 16px) !important; }
868
- }
869
-
870
- // ── Forced colors (Windows High Contrast) ──
871
- // Buttons precisam ser claramente acionáveis em high-contrast.
872
- @media (forced-colors: active) {
873
- button[matcha-button],
874
- a[matcha-button],
875
- button[matcha-icon-button],
876
- a[matcha-icon-button] {
877
- // Filled: usa botão de sistema (ButtonFace/ButtonText) — afford clicável.
878
- background: ButtonFace !important;
879
- color: ButtonText !important;
880
- border: 1px solid ButtonText !important;
881
- box-shadow: none !important;
882
-
883
- // Outline/ghost/alpha/link — bg transparente, border CanvasText.
884
- &[outline], &[basic], &[alpha], &[link] {
885
- background: transparent !important;
886
- border-color: CanvasText !important;
887
- color: CanvasText !important;
888
- }
889
-
890
- // Disabled — GrayText system token.
891
- &[disabled],
892
- &.is-disabled {
893
- color: GrayText !important;
894
- border-color: GrayText !important;
895
- }
896
-
897
- &:focus-visible {
898
- outline: 2px solid CanvasText !important;
899
- outline-offset: 2px;
900
- }
901
- }
902
- }
903
-
904
- // ── Print ──
905
- // Buttons hide (interativos, não fazem sentido em papel).
906
- @media print {
907
- button[matcha-button],
908
- a[matcha-button],
909
- button[matcha-icon-button],
910
- a[matcha-icon-button] {
911
- display: none !important;
912
- }
913
- }
1
+ @use "../abstracts/media-breakpoint" as *;
2
+ @use "../abstracts/functions" as *;
3
+ @use "../tokens/breakpoints-tokens" as *;
4
+ // =============================================================================
5
+ // matcha-buttons.scss — Button & Icon Button (modernized)
6
+ // Aligned with Figma: matcha/button + matcha/icon-button
7
+ // Sizes: SM(32) MD(40) LG(48) XL(56) + legacy aliases (tiny/small/medium/large/huge)
8
+ // Variants: Filled(default) Outline Ghost(basic) Alpha Link
9
+ // State layer: ::before opacity pattern (consistent with all DS components)
10
+ // =============================================================================
11
+
12
+ // ── Disabled mixins ─────────────────────────────────────────────────────────
13
+
14
+ @mixin disabled-solid-button($theme) {
15
+ background: var(--matcha-color-disabled-bg);
16
+ color: var(--matcha-color-disabled-fg);
17
+ box-shadow: 0 0 0 0px inset;
18
+ pointer-events: none;
19
+ }
20
+
21
+ @mixin disabled-basic-button($theme) {
22
+ background: var(--matcha-color-surface);
23
+ color: var(--matcha-color-disabled-bg);
24
+ box-shadow: 0 0 0 0px inset;
25
+ pointer-events: none;
26
+ }
27
+
28
+ @mixin disabled-outline-button($theme) {
29
+ background: transparent;
30
+ color: var(--matcha-color-disabled-bg);
31
+ box-shadow: 0 0 0 var(--matcha-border-thin, 2px) inset;
32
+ pointer-events: none;
33
+ }
34
+
35
+ @mixin disabled-alpha-button($theme) {
36
+ background: var(--matcha-color-disabled-bg-alpha, var(--matcha-color-interaction-disabled-bg-alpha));
37
+ color: var(--matcha-color-disabled-bg);
38
+ box-shadow: 0 0 0 0px inset;
39
+ pointer-events: none;
40
+ }
41
+
42
+ @mixin disabled-link-button($theme) {
43
+ background: transparent;
44
+ color: var(--matcha-color-disabled-bg);
45
+ box-shadow: 0 0 0 0px inset;
46
+ pointer-events: none;
47
+ }
48
+
49
+ // ── Size classes (responsive, aligned with Figma XS/SM/MD/LG/XL) ────────────
50
+
51
+ @mixin generate-matcha-button-size-classes($helper-breakpoints) {
52
+ @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
53
+ @include media-breakpoint($materialBreakpoint) {
54
+ $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
55
+
56
+ // XS — 28px. Figma matcha/button XS: pad 8 · gap 2 · text/caption (11/400/14, letter 0.3)
57
+ &[size#{$infix}="xs"] {
58
+ padding: 0 var(--matcha-space-100, 8px);
59
+ font-size: var(--matcha-text-p-tiny, 11px);
60
+ font-weight: var(--matcha-weight-regular, 400);
61
+ line-height: var(--matcha-leading-p-tiny, 14px);
62
+ letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
63
+ height: 28px;
64
+ gap: var(--matcha-space-025, 2px);
65
+ }
66
+
67
+ // SM — 32px. Figma: pad 8 · gap 4 · text/button-sm (13/500/19 letter 0.2)
68
+ &[size#{$infix}="sm"],
69
+ &[size#{$infix}="tiny"] {
70
+ padding: 0 var(--matcha-space-100, 8px);
71
+ font-size: var(--matcha-text-label-sm, 13px);
72
+ font-weight: var(--matcha-weight-medium, 500);
73
+ line-height: var(--matcha-leading-label-sm, 19px);
74
+ height: 32px;
75
+ gap: var(--matcha-space-050, 4px);
76
+ }
77
+
78
+ // MD — 40px. Figma: pad 16 · gap 8 · text/button-md (14/500/20)
79
+ &[size#{$infix}="md"],
80
+ &[size#{$infix}="small"] {
81
+ padding: 0 var(--matcha-space-200, 16px);
82
+ font-size: var(--matcha-text-md, 14px);
83
+ font-weight: var(--matcha-weight-medium, 500);
84
+ line-height: var(--matcha-leading-md, 20px);
85
+ height: 40px;
86
+ gap: var(--matcha-space-100, 8px);
87
+ }
88
+
89
+ // LG — 48px. Figma: pad 24 · gap 8
90
+ &[size#{$infix}="lg"],
91
+ &[size#{$infix}="medium"] {
92
+ padding: 0 var(--matcha-space-300, 24px);
93
+ font-size: var(--matcha-text-lg, 16px);
94
+ font-weight: var(--matcha-weight-semibold, 600);
95
+ line-height: var(--matcha-leading-label-lg, 22px);
96
+ height: 48px;
97
+ gap: var(--matcha-space-100, 8px);
98
+ }
99
+
100
+ // XL — 56px. Figma: pad 32 · gap 8
101
+ &[size#{$infix}="xl"],
102
+ &[size#{$infix}="large"],
103
+ &[size#{$infix}="huge"] {
104
+ padding: 0 var(--matcha-space-400, 32px);
105
+ font-size: var(--matcha-text-lg, 16px);
106
+ font-weight: var(--matcha-weight-semibold, 600);
107
+ line-height: var(--matcha-leading-label-lg, 22px);
108
+ height: 56px;
109
+ gap: var(--matcha-space-100, 8px);
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ // ── Structural ──────────────────────────────────────────────────────────────
116
+
117
+ button[matcha-button],
118
+ a[matcha-button],
119
+ button[matcha-icon-button],
120
+ a[matcha-icon-button],
121
+ button[matcha-link],
122
+ a[matcha-link] {
123
+ overflow: hidden;
124
+ position: relative;
125
+ font-size: var(--matcha-text-md, 14px);
126
+ font-weight: var(--matcha-weight-medium, 500);
127
+ font-family: inherit;
128
+ height: px-to-rem(40px); // default MD; overridden by size attribute selectors
129
+ appearance: auto;
130
+ text-rendering: auto;
131
+ letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
132
+ word-spacing: normal;
133
+ line-height: normal;
134
+ text-transform: none;
135
+ text-indent: 0px;
136
+ text-shadow: none;
137
+ text-align: center;
138
+ text-wrap: nowrap;
139
+ display: flex;
140
+ justify-content: center;
141
+ align-items: center;
142
+ cursor: pointer;
143
+ box-sizing: border-box;
144
+ border: none;
145
+ border-radius: var(--matcha-radius-lg, 8px);
146
+
147
+ // Figma matcha/button usa DOIS feedbacks combinados:
148
+ // 1. Cor do bg muda (accent-default → accent-hover → accent-pressed)
149
+ // 2. State-layer overlay por cima (matcha/color/state-layer/fg)
150
+ // - Filled: opacity 0.15 hover / 0.30 active
151
+ // - Outline/Ghost: opacity 0.10 hover / 0.20 active
152
+ // ::before aplica o state-layer; :hover/:active ou .is-state-* ativam.
153
+ // NOTA: declarada ANTES das regras aninhadas (evita a deprecação mixed-decls);
154
+ // é onde o hoisting do Sass já a colocava — output byte-idêntico.
155
+ transition:
156
+ background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
157
+ box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
158
+ color 150ms cubic-bezier(0.4, 0, 0.2, 1);
159
+
160
+ * { pointer-events: none; }
161
+
162
+ // Figma spec: ícones dentro de button herdam a foreground color do botão
163
+ // (matcha-icon tem color-default próprio via tema — forçamos inherit com !important
164
+ // pra vencer o tema que aplica em [class^="i-matcha-"] globalmente)
165
+ matcha-icon,
166
+ [class^="i-matcha-"],
167
+ [class*=" i-matcha-"] {
168
+ color: inherit !important;
169
+ }
170
+
171
+ @include generate-matcha-button-size-classes($helper-breakpoints);
172
+
173
+ &::before {
174
+ content: '';
175
+ position: absolute;
176
+ inset: 0;
177
+ background: var(--matcha-color-state-layer-fg, currentColor);
178
+ opacity: 0;
179
+ border-radius: inherit;
180
+ pointer-events: none;
181
+ transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
182
+ }
183
+
184
+ // Filled (variant default) — state-layer forte
185
+ &:hover:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link])::before,
186
+ &.is-state-hover:not([outline]):not([basic]):not([ghost]):not([alpha])::before {
187
+ opacity: 0.15;
188
+ }
189
+ &:active:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link])::before,
190
+ &.is-state-active:not([outline]):not([basic]):not([ghost]):not([alpha])::before {
191
+ opacity: 0.30;
192
+ }
193
+
194
+ // Outline / Ghost — state-layer sutil
195
+ &[outline]:hover:not([disabled]):not([disabled="true"])::before,
196
+ &[basic]:hover:not([disabled]):not([disabled="true"])::before,
197
+ &[ghost]:hover:not([disabled]):not([disabled="true"])::before,
198
+ &[outline].is-state-hover::before,
199
+ &[basic].is-state-hover::before,
200
+ &[ghost].is-state-hover::before {
201
+ opacity: 0.10;
202
+ }
203
+ &[outline]:active:not([disabled]):not([disabled="true"])::before,
204
+ &[basic]:active:not([disabled]):not([disabled="true"])::before,
205
+ &[ghost]:active:not([disabled]):not([disabled="true"])::before,
206
+ &[outline].is-state-active::before,
207
+ &[basic].is-state-active::before,
208
+ &[ghost].is-state-active::before {
209
+ opacity: 0.20;
210
+ }
211
+
212
+ &[disabled] {
213
+ pointer-events: none;
214
+ cursor: default;
215
+ }
216
+
217
+ // ── Variant modifiers (estrutural — cores vêm do mixin de tema) ──
218
+ &[basic]:not([basic="false"]),
219
+ &[ghost]:not([ghost="false"]) {
220
+ background: transparent;
221
+ }
222
+
223
+ &[outline]:not([outline="false"]) {
224
+ background: transparent;
225
+
226
+ &[link]:not([link="false"]) {
227
+ &[size="sm"], &[size="tiny"] { padding: 0 var(--matcha-space-100, 8px); gap: var(--matcha-space-050, 4px); }
228
+ &[size="md"], &[size="small"] { padding: 0 var(--matcha-space-150, 12px); gap: var(--matcha-space-100, 8px); }
229
+ &[size="lg"], &[size="medium"] { padding: 0 var(--matcha-space-200, 16px); gap: var(--matcha-space-150, 12px); }
230
+ &[size="xl"], &[size="large"], &[size="huge"] { padding: 0 var(--matcha-space-200, 16px); gap: var(--matcha-space-150, 12px); }
231
+ }
232
+ }
233
+
234
+ &[pill]:not([pill="false"]) {
235
+ border-radius: var(--matcha-radius-pill, 9999px);
236
+ }
237
+
238
+ &[link]:not([link="false"]) {
239
+ text-transform: initial;
240
+ letter-spacing: 0;
241
+ text-decoration: none;
242
+ padding: 0;
243
+ background: transparent;
244
+ }
245
+
246
+ // ── matcha-link specific (Figma spec 16914:500) ──
247
+ // SM=11/14 Regular letter 0.3 · MD=14/20 Medium · LG=16/22 SemiBold
248
+ // Hover/Active → underline automático. Underline=true → permanente.
249
+ // Disabled → opacity via disabled-fg color (no underline).
250
+ &[matcha-link] {
251
+ height: auto;
252
+ min-height: 0;
253
+ max-height: none;
254
+ padding: 0;
255
+ background: transparent;
256
+ border-radius: 0;
257
+ text-decoration: none; // reset do underline default do <a>; hover/class re-aplica
258
+
259
+ &[size="sm"], &[size="tiny"] {
260
+ font-size: var(--matcha-text-p-tiny, 11px);
261
+ line-height: var(--matcha-leading-p-tiny, 14px);
262
+ font-weight: var(--matcha-weight-regular, 400);
263
+ letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
264
+ height: auto;
265
+ max-height: none;
266
+ }
267
+ &[size="md"], &[size="small"] {
268
+ font-size: var(--matcha-text-md, 14px);
269
+ line-height: var(--matcha-leading-md, 20px);
270
+ font-weight: var(--matcha-weight-medium, 500);
271
+ letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
272
+ height: auto;
273
+ max-height: none;
274
+ }
275
+ &[size="lg"], &[size="medium"] {
276
+ font-size: var(--matcha-text-lg, 16px);
277
+ line-height: var(--matcha-leading-label-lg, 22px);
278
+ font-weight: var(--matcha-weight-semibold, 600);
279
+ letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
280
+ height: auto;
281
+ max-height: none;
282
+ }
283
+
284
+ // State layer fica off (link não tem overlay/bg)
285
+ &::before { display: none; }
286
+ // Press scale também off — link é texto
287
+ transition: none;
288
+ &:active:not([disabled]):not([disabled="true"]) { transform: none; }
289
+
290
+ // Hover e active: underline (Figma spec)
291
+ &:hover:not([disabled]):not([disabled="true"]),
292
+ &:active:not([disabled]):not([disabled="true"]) {
293
+ text-decoration: underline;
294
+ }
295
+
296
+ // Underline permanente via property (classe do HostBinding no component)
297
+ &.matcha-link--underline {
298
+ text-decoration: underline;
299
+ }
300
+
301
+ // ── Canonical extended size names (tiny|small|medium|large|huge) ────────
302
+ // matcha-link expõe a escala CANÔNICA por extenso, 1:1 com sm/md/lg
303
+ // (sm→small · md→medium · lg→large). O bloco acima pareia esses nomes com
304
+ // a escala LEGADA do matcha-button (small=md, medium=lg), então
305
+ // re-asseguramos aqui — scoped APENAS ao matcha-link — os mesmos pixels do
306
+ // tier abreviado correspondente (sem mudança visual). tiny→sm tier (menor
307
+ // disponível), huge→lg tier (maior disponível no link).
308
+ &[size="tiny"],
309
+ &[size="small"] {
310
+ font-size: var(--matcha-text-p-tiny, 11px);
311
+ line-height: var(--matcha-leading-p-tiny, 14px);
312
+ font-weight: var(--matcha-weight-regular, 400);
313
+ letter-spacing: var(--matcha-letter-spacing-caption, 0.3px);
314
+ height: auto;
315
+ max-height: none;
316
+ }
317
+ &[size="medium"] {
318
+ font-size: var(--matcha-text-md, 14px);
319
+ line-height: var(--matcha-leading-md, 20px);
320
+ font-weight: var(--matcha-weight-medium, 500);
321
+ letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
322
+ height: auto;
323
+ max-height: none;
324
+ }
325
+ &[size="large"],
326
+ &[size="huge"] {
327
+ font-size: var(--matcha-text-lg, 16px);
328
+ line-height: var(--matcha-leading-label-lg, 22px);
329
+ font-weight: var(--matcha-weight-semibold, 600);
330
+ letter-spacing: var(--matcha-letter-spacing-button, 0.2px);
331
+ height: auto;
332
+ max-height: none;
333
+ }
334
+ }
335
+
336
+ &[icon]:not([icon="false"]) {
337
+ aspect-ratio: 1;
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+
342
+ // Icon-button padding per Figma spec matcha/icon-button (all-direction):
343
+ // XS=6 · SM=6 · MD=8 · LG=12 · XL=16 (padding igual em todos os lados)
344
+ &[size="xs"] {
345
+ padding: var(--matcha-space-075, 6px) !important;
346
+ }
347
+ &[size="sm"], &[size="tiny"] {
348
+ padding: var(--matcha-space-075, 6px) !important;
349
+ }
350
+ &[size="md"], &[size="small"] {
351
+ padding: var(--matcha-space-100, 8px) !important;
352
+ }
353
+ &[size="lg"], &[size="medium"] {
354
+ padding: var(--matcha-space-150, 12px) !important;
355
+ }
356
+ &[size="xl"], &[size="large"], &[size="huge"] {
357
+ padding: var(--matcha-space-200, 16px) !important;
358
+ }
359
+
360
+ // Auto-size inner matcha-icon conforme Figma matcha/icon-button spec:
361
+ // XS→16 · SM→20 · MD→24 · LG→24 · XL→24
362
+ &[size="xs"] [class^="i-matcha-"],
363
+ &[size="xs"] [class*=" i-matcha-"] {
364
+ font-size: var(--matcha-icon-size-sm, 16px);
365
+ width: var(--matcha-icon-size-sm, 16px);
366
+ height: var(--matcha-icon-size-sm, 16px);
367
+ line-height: var(--matcha-icon-size-sm, 16px);
368
+ }
369
+ &[size="sm"] [class^="i-matcha-"],
370
+ &[size="sm"] [class*=" i-matcha-"],
371
+ &[size="tiny"] [class^="i-matcha-"],
372
+ &[size="tiny"] [class*=" i-matcha-"] {
373
+ font-size: 20px;
374
+ width: 20px;
375
+ height: 20px;
376
+ line-height: 20px;
377
+ }
378
+ &[size="md"] [class^="i-matcha-"],
379
+ &[size="md"] [class*=" i-matcha-"],
380
+ &[size="small"] [class^="i-matcha-"],
381
+ &[size="small"] [class*=" i-matcha-"],
382
+ &[size="lg"] [class^="i-matcha-"],
383
+ &[size="lg"] [class*=" i-matcha-"],
384
+ &[size="medium"] [class^="i-matcha-"],
385
+ &[size="medium"] [class*=" i-matcha-"],
386
+ &[size="xl"] [class^="i-matcha-"],
387
+ &[size="xl"] [class*=" i-matcha-"],
388
+ &[size="large"] [class^="i-matcha-"],
389
+ &[size="large"] [class*=" i-matcha-"],
390
+ &[size="huge"] [class^="i-matcha-"],
391
+ &[size="huge"] [class*=" i-matcha-"] {
392
+ font-size: var(--matcha-icon-size-md, 24px);
393
+ width: var(--matcha-icon-size-md, 24px);
394
+ height: var(--matcha-icon-size-md, 24px);
395
+ line-height: var(--matcha-icon-size-md, 24px);
396
+ }
397
+ }
398
+
399
+ // ── Badge ──
400
+ &[badge]:not([badge="false"]) {
401
+ overflow: visible;
402
+ position: relative;
403
+
404
+ &::after {
405
+ content: "";
406
+ position: absolute;
407
+ top: -0.25rem;
408
+ right: -0.25rem;
409
+ width: 1.32rem;
410
+ height: 1.32rem;
411
+ border-radius: var(--matcha-radius-pill, 9999px);
412
+ z-index: 2;
413
+ }
414
+
415
+ &[size="sm"]::after, &[size="tiny"]::after { width: 0.75rem; height: 0.75rem; top: -0.15rem; right: -0.15rem; }
416
+ &[size="md"]::after, &[size="small"]::after { width: 1rem; height: 1rem; top: -0.2rem; right: -0.2rem; }
417
+ &[size="xl"]::after, &[size="large"]::after, &[size="huge"]::after { width: 1.5rem; height: 1.5rem; top: -0.3rem; right: -0.3rem; }
418
+ }
419
+
420
+ // ── Focus ring ──
421
+ &:focus-visible:not([disabled]) {
422
+ outline: var(--matcha-state-focus-ring-width, 2px) solid var(--matcha-color-accent, #6200ee);
423
+ outline-offset: var(--matcha-state-focus-ring-offset, 2px);
424
+ }
425
+
426
+ @media (prefers-reduced-motion: reduce) {
427
+ &::before { transition: none; }
428
+ &, &:active:not([disabled]):not([disabled="true"]) { transition: none; transform: none; }
429
+ }
430
+
431
+ // ── Loading state ──
432
+ // Spinner inline do lado direito do conteúdo + content dims pra 60%
433
+ // Preserva width (não shift) e bloqueia cliques com pointer-events.
434
+ &.matcha-button--loading {
435
+ pointer-events: none;
436
+ cursor: progress;
437
+
438
+ // Dim no content interior (tudo exceto o spinner)
439
+ > :not(.matcha-button__spinner) {
440
+ opacity: 0.6;
441
+ }
442
+ }
443
+
444
+ .matcha-button__spinner {
445
+ display: inline-flex;
446
+ align-items: center;
447
+ justify-content: center;
448
+ margin-left: var(--matcha-space-100, 8px);
449
+ flex: none;
450
+ line-height: 0;
451
+
452
+ svg {
453
+ display: block;
454
+ }
455
+
456
+ @media (prefers-reduced-motion: reduce) {
457
+ svg circle animateTransform {
458
+ // Retarda mas não remove (ainda sinaliza loading)
459
+ dur: 2s !important;
460
+ }
461
+ }
462
+ }
463
+
464
+ // Visually hidden span pra screen readers (loading announcement)
465
+ .matcha-button__sr-only {
466
+ position: absolute !important;
467
+ width: 1px !important;
468
+ height: 1px !important;
469
+ padding: 0 !important;
470
+ margin: -1px !important;
471
+ overflow: hidden !important;
472
+ clip: rect(0, 0, 0, 0) !important;
473
+ white-space: nowrap !important;
474
+ border: 0 !important;
475
+ }
476
+
477
+ // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ──
478
+ // XS (28), SM (32), MD (40) ficam abaixo de 44px — expande hit area invisível.
479
+ @media (pointer: coarse) {
480
+ &[size="xs"],
481
+ &[size="sm"], &[size="tiny"],
482
+ &[size="md"], &[size="small"] {
483
+ position: relative;
484
+
485
+ &::after {
486
+ content: '';
487
+ position: absolute;
488
+ inset: -8px; // expande ~16px em cada eixo → 28+16=44, 32+16=48, 40+16=56 ✓
489
+ z-index: 0;
490
+ pointer-events: auto; // captura cliques aqui
491
+ }
492
+
493
+ // ::before já é usado pro state layer, então nosso overlay vai no ::after
494
+ // State layer (::before) fica colado ao botão visual, overlay (::after) expande hit.
495
+ }
496
+ }
497
+ }
498
+
499
+ // ── Theme mixin ─────────────────────────────────────────────────────────────
500
+
501
+ @mixin matcha-button-theme($theme) {
502
+ // ═════════════════════════════════════════════════════════════════════════
503
+ // CSS var-based theming (regra DS-ARCHITECTURE #7: Tokens over hardcode)
504
+ //
505
+ // Todo botão usa --matcha-button-color como token interno.
506
+ // `[color="X"]` apenas mapeia pra esses tokens — as variantes
507
+ // (filled/outline/ghost/alpha/link) interpretam um único set de vars.
508
+ //
509
+ // Consumidor pode override QUALQUER cor via style:
510
+ // <button matcha-button style="--matcha-button-color: #ff00ff">Custom</button>
511
+ //
512
+ // Adicionar cor nova = adicionar 1 mapping (3 linhas).
513
+ // ═════════════════════════════════════════════════════════════════════════
514
+
515
+ button[matcha-button],
516
+ a[matcha-button],
517
+ button[matcha-icon-button],
518
+ a[matcha-icon-button],
519
+ button[matcha-link],
520
+ a[matcha-link] {
521
+ // Tokens internos (Figma matcha/button 13913:1088):
522
+ // --matcha-button-color = bg do filled (Default)
523
+ // --matcha-button-color-hover = bg filled / stroke outline (Hover)
524
+ // --matcha-button-color-pressed = bg filled / stroke+text (Active)
525
+ // --matcha-button-color-text-on = texto do filled (accent/text-on etc.)
526
+ // --matcha-button-color-alpha = alpha modifier / ripple
527
+ // --matcha-button-color-edge = stroke do outline (Default)
528
+ --matcha-button-color: var(--matcha-color-neutral);
529
+ --matcha-button-color-hover: var(--matcha-color-neutral-hover);
530
+ --matcha-button-color-pressed: var(--matcha-color-neutral-hover);
531
+ --matcha-button-color-text-on: var(--matcha-color-neutral-contrast);
532
+ --matcha-button-color-alpha: var(--matcha-color-neutral-alpha);
533
+ --matcha-button-color-edge: var(--matcha-color-fg);
534
+
535
+ // Badge ring herda o bg do botão (filled) — outline/ghost override pra surface,
536
+ // pois o bg deles é transparente e o ring deve casar com a página atrás.
537
+ --matcha-color-badge-border: var(--matcha-button-color);
538
+
539
+ // ── Color mapping ────────────────────────────────────────────────────────────────
540
+ &[color="basic"] {
541
+ --matcha-button-color: var(--matcha-color-neutral);
542
+ --matcha-button-color-hover: var(--matcha-color-neutral-hover);
543
+ --matcha-button-color-pressed: var(--matcha-color-neutral-hover);
544
+ --matcha-button-color-text-on: var(--matcha-color-neutral-contrast);
545
+ --matcha-button-color-alpha: var(--matcha-color-neutral-alpha);
546
+ --matcha-button-color-edge: var(--matcha-color-fg);
547
+ }
548
+ &[color="primary"] {
549
+ --matcha-button-color: var(--matcha-color-primary);
550
+ --matcha-button-color-hover: var(--matcha-color-primary-hover);
551
+ --matcha-button-color-pressed: var(--matcha-color-primary-pressed);
552
+ --matcha-button-color-text-on: var(--matcha-color-primary-contrast);
553
+ --matcha-button-color-alpha: var(--matcha-color-primary-alpha);
554
+ --matcha-button-color-edge: var(--matcha-color-primary);
555
+ }
556
+ &[color="accent"] {
557
+ --matcha-button-color: var(--matcha-color-accent);
558
+ --matcha-button-color-hover: var(--matcha-color-accent-hover);
559
+ --matcha-button-color-pressed: var(--matcha-color-accent-pressed);
560
+ --matcha-button-color-text-on: var(--matcha-color-accent-text-on);
561
+ --matcha-button-color-alpha: var(--matcha-color-accent-alpha);
562
+ --matcha-button-color-edge: var(--matcha-color-accent);
563
+ }
564
+ &[color="success"] {
565
+ --matcha-button-color: var(--matcha-color-success);
566
+ --matcha-button-color-hover: var(--matcha-color-success-hover);
567
+ --matcha-button-color-pressed: var(--matcha-color-success-pressed);
568
+ --matcha-button-color-text-on: var(--matcha-color-success-contrast);
569
+ --matcha-button-color-alpha: var(--matcha-color-success-alpha);
570
+ --matcha-button-color-edge: var(--matcha-color-success);
571
+ }
572
+ &[color="warn"] {
573
+ --matcha-button-color: var(--matcha-color-warn);
574
+ --matcha-button-color-hover: var(--matcha-color-warn-hover);
575
+ --matcha-button-color-pressed: var(--matcha-color-warn-pressed);
576
+ --matcha-button-color-text-on: var(--matcha-color-warn-contrast);
577
+ --matcha-button-color-alpha: var(--matcha-color-warn-alpha);
578
+ --matcha-button-color-edge: var(--matcha-color-warn);
579
+ }
580
+ &[color="danger"] {
581
+ --matcha-button-color: var(--matcha-color-danger);
582
+ --matcha-button-color-hover: var(--matcha-color-danger-hover);
583
+ --matcha-button-color-pressed: var(--matcha-color-danger-pressed);
584
+ --matcha-button-color-text-on: var(--matcha-color-danger-contrast);
585
+ --matcha-button-color-alpha: var(--matcha-color-danger-alpha);
586
+ --matcha-button-color-edge: var(--matcha-color-danger);
587
+ }
588
+ &[color="info"] {
589
+ --matcha-button-color: var(--matcha-color-info);
590
+ --matcha-button-color-hover: var(--matcha-color-info-hover);
591
+ --matcha-button-color-pressed: var(--matcha-color-info-pressed);
592
+ --matcha-button-color-text-on: var(--matcha-color-info-contrast);
593
+ --matcha-button-color-alpha: var(--matcha-color-info-alpha);
594
+ --matcha-button-color-edge: var(--matcha-color-info);
595
+ }
596
+
597
+ // ── Filled (default variant) ──────────────────────────────────────────────────────
598
+ // Figma: Default=accent · Hover=accent-hover · Active=accent-pressed
599
+ background: var(--matcha-button-color);
600
+ color: var(--matcha-button-color-text-on);
601
+
602
+ &:hover:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link]) {
603
+ background: var(--matcha-button-color-hover);
604
+ }
605
+ &:active:not([disabled]):not([disabled="true"]):not([outline]):not([basic]):not([ghost]):not([link]):not([alpha]):not([matcha-link]) {
606
+ background: var(--matcha-button-color-pressed);
607
+ }
608
+
609
+ // ── Variant: outline ────────────────────────────────────────────────────────────────
610
+ // Figma matcha/button (13913:1088) vs matcha/icon-button (13914:482):
611
+ // BUTTON outline: texto=text-primary · ícones=edge (accent)
612
+ // ICON-BUTTON outline: host color=edge (ícone herda via inherit !important)
613
+ // Hover: icons/host = accent-hover (texto button mantém primary)
614
+ // Active: tudo = accent-pressed
615
+ &[outline]:not([outline="false"]),
616
+ &[variant="outline"] {
617
+ background: transparent;
618
+ box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-edge);
619
+ // Color slot: text segue --matcha-button-color-edge (border+text matching),
620
+ // com fallback pra text-primary quando consumer não setar a var.
621
+ // Antes era hardcoded text-primary → consumer tinha que duelar specificity.
622
+ color: var(--matcha-button-color-edge);
623
+ // Badge ring transparente — bg é transparente, qualquer cor fixa
624
+ // colide com o bg-do-pai-da-página (impossível detectar em CSS).
625
+ --matcha-color-badge-border: transparent;
626
+
627
+ &:hover:not([disabled]):not([disabled="true"]) {
628
+ box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-hover);
629
+ }
630
+ &:active:not([disabled]):not([disabled="true"]) {
631
+ box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-button-color-pressed);
632
+ color: var(--matcha-button-color-pressed);
633
+ }
634
+ }
635
+
636
+ // Icon-button outline: host color = edge (ícone é o único conteúdo semântico)
637
+ &[matcha-icon-button][outline]:not([outline="false"]),
638
+ &[matcha-icon-button][variant="outline"] {
639
+ color: var(--matcha-button-color-edge);
640
+
641
+ &:hover:not([disabled]):not([disabled="true"]) {
642
+ color: var(--matcha-button-color-hover);
643
+ }
644
+ &:active:not([disabled]):not([disabled="true"]) {
645
+ color: var(--matcha-button-color-pressed);
646
+ }
647
+ }
648
+
649
+ // Button outline: ícones (Left/Right) acompanham edge — texto mantém primary
650
+ &[matcha-button][outline]:not([outline="false"]) matcha-icon,
651
+ &[matcha-button][outline]:not([outline="false"]) [class^="i-matcha-"],
652
+ &[matcha-button][outline]:not([outline="false"]) [class*=" i-matcha-"],
653
+ &[matcha-button][variant="outline"] matcha-icon,
654
+ &[matcha-button][variant="outline"] [class^="i-matcha-"],
655
+ &[matcha-button][variant="outline"] [class*=" i-matcha-"] {
656
+ color: var(--matcha-button-color-edge) !important;
657
+ }
658
+ &[matcha-button][outline]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
659
+ &[matcha-button][outline]:hover:not([disabled]):not([disabled="true"]) [class^="i-matcha-"],
660
+ &[matcha-button][outline].is-state-hover matcha-icon {
661
+ color: var(--matcha-button-color-hover) !important;
662
+ }
663
+ &[matcha-button][outline]:active:not([disabled]):not([disabled="true"]) matcha-icon,
664
+ &[matcha-button][outline]:active:not([disabled]):not([disabled="true"]) [class^="i-matcha-"],
665
+ &[matcha-button][outline].is-state-active matcha-icon {
666
+ color: var(--matcha-button-color-pressed) !important;
667
+ }
668
+
669
+ // ── Variant: ghost (basic = legacy alias) ────────────────────────────────────────
670
+ // Figma: Default/Hover=text-primary · Active=accent-pressed (texto + ícones)
671
+ // Slot --matcha-button-ghost-text com fallback text-primary (Figma intent
672
+ // preservado). Consumer override via parent: --matcha-button-ghost-text: X
673
+ &[basic]:not([basic="false"]),
674
+ &[ghost]:not([ghost="false"]),
675
+ &[variant="ghost"] {
676
+ background: transparent;
677
+ color: var(--matcha-button-ghost-text, var(--matcha-color-text-primary));
678
+ // Badge ring transparente — bg é transparente, qualquer cor fixa
679
+ // colide com o bg-do-pai-da-página (impossível detectar em CSS).
680
+ --matcha-color-badge-border: transparent;
681
+
682
+ &:active:not([disabled]):not([disabled="true"]) {
683
+ color: var(--matcha-button-color-pressed);
684
+ }
685
+ }
686
+
687
+ // Button ghost: ícones acompanham edge (accent) — texto mantém primary
688
+ &[matcha-button][basic]:not([basic="false"]) matcha-icon,
689
+ &[matcha-button][basic]:not([basic="false"]) [class^="i-matcha-"],
690
+ &[matcha-button][ghost]:not([ghost="false"]) matcha-icon,
691
+ &[matcha-button][ghost]:not([ghost="false"]) [class^="i-matcha-"],
692
+ &[matcha-button][variant="ghost"] matcha-icon,
693
+ &[matcha-button][variant="ghost"] [class^="i-matcha-"] {
694
+ color: var(--matcha-button-color-edge) !important;
695
+ }
696
+ &[matcha-button][ghost]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
697
+ &[matcha-button][basic]:hover:not([disabled]):not([disabled="true"]) matcha-icon,
698
+ &[matcha-button][ghost].is-state-hover matcha-icon,
699
+ &[matcha-button][basic].is-state-hover matcha-icon {
700
+ color: var(--matcha-button-color-hover) !important;
701
+ }
702
+ &[matcha-button][ghost]:active:not([disabled]):not([disabled="true"]) matcha-icon,
703
+ &[matcha-button][basic]:active:not([disabled]):not([disabled="true"]) matcha-icon,
704
+ &[matcha-button][ghost].is-state-active matcha-icon,
705
+ &[matcha-button][basic].is-state-active matcha-icon {
706
+ color: var(--matcha-button-color-pressed) !important;
707
+ }
708
+
709
+ // ── Icon-button neutral (basic/default) — Figma matcha/icon-button ──
710
+ // Outline/Ghost Default (13914:179 / 13914:332) vinculam o glyph — e a
711
+ // borda do Outline — a matcha/color/icon/default (#75767a light / #9aa0a6
712
+ // dark), o MESMO token do matcha-icon solto. O texto do botão usa fg/
713
+ // text-primary; o ícone do icon-button NÃO. Redireciona edge + ghost-text
714
+ // só pro icon-button neutro. Cores override (accent/danger/...) seguem a
715
+ // própria cor via os blocos &[color="..."] acima.
716
+ &[matcha-icon-button]:not([color]),
717
+ &[matcha-icon-button][color="basic"] {
718
+ --matcha-button-color-edge: var(--matcha-color-icon-default);
719
+ --matcha-button-ghost-text: var(--matcha-color-icon-default);
720
+ }
721
+
722
+ // ── Modifier: alpha ──────────────────────────────────────────────
723
+ &[alpha]:not([alpha="false"]) {
724
+ background: var(--matcha-button-color-alpha);
725
+ color: var(--matcha-button-color);
726
+ }
727
+
728
+ // ── Modifier: link (legacy, afeta button[link="true"]) ───────────
729
+ // matcha-link tem seu próprio bloco (&[matcha-link]) com regras
730
+ // próprias de underline (hover + .matcha-link--underline)
731
+ &[link]:not([link="false"]):not([matcha-link]) {
732
+ background: transparent;
733
+ color: var(--matcha-button-color);
734
+ text-decoration: underline;
735
+ }
736
+ // matcha-link: apenas cor do texto (o bloco estrutural [matcha-link]
737
+ // define text-decoration: none + hover/active + class underline)
738
+ &[matcha-link] {
739
+ color: var(--matcha-button-color);
740
+ }
741
+
742
+ // ── State: disabled ──────────────────────────────────────────────
743
+ // Figma (13913:1088) Disabled state:
744
+ // Filled: bg=interaction/disabled-bg · label+icons=accent/text-on (mesma cor do filled default)
745
+ // Outline: stroke=interaction/disabled-bg (NÃO disabled-border) · label+icons=disabled-fg
746
+ // Ghost: sem bg/stroke · label+icons=disabled-fg
747
+ // State-layer overlay DESLIGADO em todos disabled.
748
+ &[disabled]:not([disabled="false"]),
749
+ &[disabled="true"] {
750
+ pointer-events: none;
751
+ cursor: not-allowed;
752
+
753
+ // state-layer overlay desligado
754
+ &::before { opacity: 0 !important; }
755
+
756
+ // Filled — bg disabled + texto/ícones accent-text-on (Figma spec)
757
+ background: var(--matcha-color-interaction-disabled-bg);
758
+ color: var(--matcha-color-accent-text-on);
759
+ box-shadow: none;
760
+
761
+ // Outline — stroke usa mesma cor do filled-bg (Figma usa interaction/disabled-bg)
762
+ &[outline]:not([outline="false"]),
763
+ &[variant="outline"] {
764
+ background: transparent;
765
+ color: var(--matcha-color-interaction-disabled-fg);
766
+ box-shadow: inset 0 0 0 var(--matcha-border-thin, 2px) var(--matcha-color-interaction-disabled-bg);
767
+ }
768
+
769
+ // Ghost / basic / link — só texto dedicado
770
+ &[basic]:not([basic="false"]),
771
+ &[ghost]:not([ghost="false"]),
772
+ &[variant="ghost"],
773
+ &[link]:not([link="false"]) {
774
+ background: transparent;
775
+ color: var(--matcha-color-interaction-disabled-fg);
776
+ box-shadow: none;
777
+ }
778
+
779
+ // Alpha — bg alpha dedicado
780
+ &[alpha]:not([alpha="false"]) {
781
+ background: var(--matcha-color-interaction-disabled-bg-alpha);
782
+ color: var(--matcha-color-interaction-disabled-fg);
783
+ }
784
+
785
+ // Ícones do button (Left/Right) no disabled:
786
+ // Filled: herdam color do host (accent-text-on) ✓
787
+ // Outline/Ghost: forçam disabled-fg pra sobrescrever regra !important do edge
788
+ &[matcha-button][outline] matcha-icon,
789
+ &[matcha-button][outline] [class^="i-matcha-"],
790
+ &[matcha-button][outline] [class*=" i-matcha-"],
791
+ &[matcha-button][basic] matcha-icon,
792
+ &[matcha-button][basic] [class^="i-matcha-"],
793
+ &[matcha-button][ghost] matcha-icon,
794
+ &[matcha-button][ghost] [class^="i-matcha-"],
795
+ &[matcha-button][variant="outline"] matcha-icon,
796
+ &[matcha-button][variant="outline"] [class^="i-matcha-"],
797
+ &[matcha-button][variant="ghost"] matcha-icon,
798
+ &[matcha-button][variant="ghost"] [class^="i-matcha-"] {
799
+ color: var(--matcha-color-interaction-disabled-fg) !important;
800
+ }
801
+ }
802
+
803
+ // ── Badge indicator (always accent, pattern fixo) ────────────────
804
+ &[badge]:not([badge="false"])::after {
805
+ background: var(--matcha-color-accent);
806
+ }
807
+
808
+ // ── Ripple overlay ───────────────────────────────────────────────
809
+ .ripple {
810
+ background: var(--matcha-button-color-alpha);
811
+ }
812
+ }
813
+ }
814
+
815
+ // ── matcha-icon-button: auto-size inner icons per Figma spec ────────────────
816
+ // XS button (28) → icon 16 · SM (32) → 20 · MD (40)/LG (48)/XL (56) → 24
817
+ button[matcha-icon-button],
818
+ a[matcha-icon-button] {
819
+ aspect-ratio: 1;
820
+
821
+ matcha-icon {
822
+ flex: none; // NÃO expandir com o button
823
+ }
824
+
825
+ &[size="xs"] matcha-icon,
826
+ &[size="tiny"] matcha-icon {
827
+ font-size: 16px !important;
828
+ width: 16px !important;
829
+ height: 16px !important;
830
+ line-height: 16px !important;
831
+ }
832
+ &[size="sm"] matcha-icon,
833
+ &[size="small"] matcha-icon {
834
+ font-size: 20px !important;
835
+ width: 20px !important;
836
+ height: 20px !important;
837
+ line-height: 20px !important;
838
+ }
839
+ &[size="md"] matcha-icon,
840
+ &[size="medium"] matcha-icon,
841
+ &[size="lg"] matcha-icon,
842
+ &[size="large"] matcha-icon,
843
+ &[size="xl"] matcha-icon,
844
+ &[size="huge"] matcha-icon {
845
+ font-size: 24px !important;
846
+ width: 24px !important;
847
+ height: 24px !important;
848
+ line-height: 24px !important;
849
+ }
850
+
851
+ // ── Canonical extended size names (tiny|small|medium|large|huge) ────────────
852
+ // matcha-icon-button expõe a escala CANÔNICA por extenso, 1:1 com as
853
+ // abreviações xs/sm/md/lg/xl (xs→tiny · sm→small · md→medium · lg→large ·
854
+ // xl→huge). O matcha-buttons.scss compartilhado pareia esses nomes com a
855
+ // escala LEGADA do matcha-button (small=md, medium=lg, …), então re-asseguramos
856
+ // aqui — scoped APENAS ao icon-button — os pixels corretos (sem mudança visual).
857
+ &[size="tiny"] { height: 28px; }
858
+ &[size="small"] { height: 32px; }
859
+ &[size="medium"] { height: 40px; }
860
+ &[size="large"] { height: 48px; }
861
+ &[size="huge"] { height: 56px; }
862
+
863
+ &[size="tiny"][icon] { padding: var(--matcha-space-075, 6px) !important; }
864
+ &[size="small"][icon] { padding: var(--matcha-space-075, 6px) !important; }
865
+ &[size="medium"][icon] { padding: var(--matcha-space-100, 8px) !important; }
866
+ &[size="large"][icon] { padding: var(--matcha-space-150, 12px) !important; }
867
+ &[size="huge"][icon] { padding: var(--matcha-space-200, 16px) !important; }
868
+ }
869
+
870
+ // ── Forced colors (Windows High Contrast) ──
871
+ // Buttons precisam ser claramente acionáveis em high-contrast.
872
+ @media (forced-colors: active) {
873
+ button[matcha-button],
874
+ a[matcha-button],
875
+ button[matcha-icon-button],
876
+ a[matcha-icon-button] {
877
+ // Filled: usa botão de sistema (ButtonFace/ButtonText) — afford clicável.
878
+ background: ButtonFace !important;
879
+ color: ButtonText !important;
880
+ border: 1px solid ButtonText !important;
881
+ box-shadow: none !important;
882
+
883
+ // Outline/ghost/alpha/link — bg transparente, border CanvasText.
884
+ &[outline], &[basic], &[alpha], &[link] {
885
+ background: transparent !important;
886
+ border-color: CanvasText !important;
887
+ color: CanvasText !important;
888
+ }
889
+
890
+ // Disabled — GrayText system token.
891
+ &[disabled],
892
+ &.is-disabled {
893
+ color: GrayText !important;
894
+ border-color: GrayText !important;
895
+ }
896
+
897
+ &:focus-visible {
898
+ outline: 2px solid CanvasText !important;
899
+ outline-offset: 2px;
900
+ }
901
+ }
902
+ }
903
+
904
+ // ── Print ──
905
+ // Buttons hide (interativos, não fazem sentido em papel).
906
+ @media print {
907
+ button[matcha-button],
908
+ a[matcha-button],
909
+ button[matcha-icon-button],
910
+ a[matcha-icon-button] {
911
+ display: none !important;
912
+ }
913
+ }