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