@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,606 +1,606 @@
1
- @use "../abstracts/media-breakpoint" as *;
2
- @use "../tokens/breakpoints-tokens" as *;
3
- // =============================================================================
4
- // matcha-form-field.scss — Theme mixin (stacked pattern, Figma matcha/field/_base)
5
- // Border colors vêm do data-color (component SCSS). Theme mixin cuida de:
6
- // - tipografia do input (size por breakpoint)
7
- // - placeholder color
8
- // - disabled text color
9
- // =============================================================================
10
-
11
- // ── Structural (inputs) — sem border, sem bg (box wraps) ────────────────────
12
-
13
- // ─── Filhos projetáveis do form-field ────────────────────────────────────
14
- // Todos os tipos de input que podem ser projetados DEVEM preencher o espaço
15
- // disponível e herdar padding/border/bg do wrapper externo (.matcha-form-field).
16
- // O componente filho NÃO deve ter padding/border próprio quando dentro do field.
17
- .matcha-form-field {
18
- input,
19
- input[type="text"],
20
- input[type="email"],
21
- input[type="tel"],
22
- input[type="url"],
23
- input[type="password"],
24
- input[type="number"],
25
- input[type="search"],
26
- input[type="date"],
27
- input[type="time"],
28
- textarea {
29
- flex: 1 1 auto;
30
- width: 100%;
31
- min-width: 0;
32
- min-height: 0; // evita que altura natural do input expanda o container
33
- background-color: transparent;
34
- outline: none;
35
- box-shadow: none;
36
- border: none;
37
- padding: 0; // wrapper fornece padding horizontal
38
- margin: 0;
39
- font-family: var(--matcha-font-family);
40
- font-size: var(--matcha-font-size-sm, 14px); // Figma MD placeholder/value 14/400/20
41
- font-weight: var(--matcha-weight-regular, 400);
42
- line-height: var(--matcha-leading-body-md, 20px);
43
- color: var(--matcha-color-text-primary); // Figma filled value: #E8EAED dark / #0E1116 light
44
- resize: none;
45
-
46
- &:disabled {
47
- color: var(--matcha-color-interaction-disabled-fg);
48
- -webkit-text-fill-color: var(--matcha-color-interaction-disabled-fg); // Safari override
49
- }
50
- }
51
-
52
- // Textarea ocupa TODA a altura disponível do wrapper (que é column mode).
53
- // Quando `matchaAutoGrow` está ligado, a directive aplica `.matcha-autogrow`
54
- // e controla altura via JS — o override abaixo solta o 100% forçado.
55
- textarea {
56
- height: 100%;
57
- min-height: 100%;
58
- align-self: stretch;
59
-
60
- &.matcha-autogrow {
61
- height: auto; // directive calcula e seta inline
62
- min-height: 0; // flex: 1 já garante piso via wrapper min-height
63
- }
64
- }
65
-
66
- // ── Scrollbar estilizada no textarea (Webkit + Firefox) ──────────────────
67
- // Usa tokens do DS em vez do scrollbar nativo cinza feio do Chrome/Windows.
68
- // Aplica tanto no textarea "rows fixo" (se user passar rows pequeno + muito texto)
69
- // quanto no `.matcha-autogrow` depois de atingir `maxRows`.
70
- textarea {
71
- scrollbar-width: thin; // Firefox
72
- scrollbar-color: var(--matcha-color-text-placeholder)
73
- transparent; // thumb + track
74
-
75
- &::-webkit-scrollbar { width: 6px; height: 6px; }
76
- &::-webkit-scrollbar-track { background: transparent; }
77
- &::-webkit-scrollbar-thumb {
78
- background: var(--matcha-color-text-placeholder);
79
- border-radius: var(--matcha-radius-pill, 999px);
80
- }
81
- &::-webkit-scrollbar-thumb:hover { background: var(--matcha-color-text-secondary); }
82
- }
83
-
84
- input:focus,
85
- textarea:focus {
86
- outline: none;
87
- }
88
-
89
- // Truncate: quando o campo é menor que placeholder/value, corta com ellipsis
90
- // Evita overflow feio em sidebars, colunas estreitas, responsive mobile.
91
- input {
92
- text-overflow: ellipsis;
93
- overflow: hidden;
94
- white-space: nowrap;
95
-
96
- // Placeholder também trunca (Chrome/Safari precisam do ::placeholder explícito)
97
- &::placeholder {
98
- text-overflow: ellipsis;
99
- overflow: hidden;
100
- white-space: nowrap;
101
- }
102
- }
103
-
104
- // ── Autofill (Chrome/Safari) — mata o background amarelo/azul e usa os tokens ──
105
- // Browsers injetam background-color inline no -webkit-autofill (prioridade sobre CSS normal).
106
- // O workaround padrão: box-shadow inset transparente (não sobreposto) + text-fill-color pro texto.
107
- // `transition: ... 5000s` é o "truque dos 5000 segundos" — impede o browser de re-aplicar o bg.
108
- input:-webkit-autofill,
109
- input:-webkit-autofill:hover,
110
- input:-webkit-autofill:focus,
111
- input:-webkit-autofill:active,
112
- textarea:-webkit-autofill {
113
- -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
114
- -webkit-text-fill-color: var(--matcha-color-text-primary) !important;
115
- caret-color: var(--matcha-color-primary) !important;
116
- transition: background-color 5000s ease-in-out 0s, color 5000s ease-in-out 0s;
117
- }
118
-
119
- // Caret color (cursor piscando) = primary pra match com focus border
120
- input,
121
- textarea {
122
- caret-color: var(--matcha-color-primary);
123
- }
124
-
125
- // Selection highlight (quando user seleciona texto) — usa token de primary com alpha
126
- input::selection,
127
- textarea::selection {
128
- background: var(--matcha-color-primary-alpha);
129
- color: var(--matcha-color-text-primary);
130
- }
131
-
132
- // Componentes projetados — todos preenchem a largura e não adicionam borda/padding próprio
133
- matcha-select,
134
- matcha-autocomplete,
135
- matcha-date,
136
- matcha-field-range,
137
- matcha-time,
138
- matcha-phone {
139
- flex: 1 1 auto;
140
- width: 100%;
141
- min-width: 0;
142
- display: flex;
143
- align-items: center;
144
- background: transparent;
145
- border: none;
146
- padding: 0;
147
- }
148
-
149
- // matcha-phone: padding/bg próprios do componente quebram o layout dentro do field
150
- matcha-phone .matcha-phone {
151
- padding: 0;
152
- background: transparent;
153
- font-size: inherit;
154
- width: 100%;
155
- }
156
-
157
- // matcha-date/time: container do componente preenche o espaço sem padding/border próprios
158
- matcha-date .matcha-date-container,
159
- matcha-time .matcha-time-container {
160
- width: 100%;
161
- background: transparent;
162
- border: none;
163
- padding: 0;
164
- min-width: 0;
165
- gap: var(--matcha-space-100, 8px);
166
- }
167
-
168
- // ── matchaPasswordToggle: botão do olho injetado ao lado de input[type="password"] ──
169
- // Figma matcha/field/password: ícone 16/20/24 por size, cor text-placeholder (#9AA0A6)
170
- .matcha-password-toggle-button {
171
- flex: none;
172
- display: inline-flex;
173
- align-items: center;
174
- justify-content: center;
175
- width: 16px; // SM default
176
- height: 16px;
177
- padding: 0;
178
- margin: 0;
179
- background: transparent;
180
- border: none;
181
- cursor: pointer;
182
- color: var(--matcha-color-text-placeholder); // #9AA0A6
183
- transition: color var(--matcha-duration-fast, 200ms) ease;
184
-
185
- &:hover:not(:disabled) {
186
- color: var(--matcha-color-text-primary);
187
- }
188
-
189
- &:disabled {
190
- cursor: not-allowed;
191
- color: var(--matcha-color-interaction-disabled-fg);
192
- }
193
-
194
- &:focus { outline: none; }
195
- &:focus-visible {
196
- outline: 2px solid var(--matcha-color-primary);
197
- outline-offset: 2px;
198
- border-radius: var(--matcha-radius-md, 4px);
199
- }
200
-
201
- > [class^="i-matcha-"],
202
- > [class*=" i-matcha-"] {
203
- font-size: inherit !important;
204
- width: 1em !important;
205
- height: 1em !important;
206
- line-height: 1 !important;
207
- color: inherit !important;
208
- }
209
- }
210
-
211
- // ── matchaQuantityStepper: wrapper flex-row + dois botões (−/+) ──────
212
- // Figma matcha/field/quantity: gap 4, ícones 16px constante, cor #9AA0A6
213
- .matcha-quantity-stepper {
214
- flex: none;
215
- display: inline-flex;
216
- align-items: center;
217
- gap: var(--matcha-space-050, 4px);
218
- }
219
-
220
- .matcha-quantity-stepper-button {
221
- flex: none;
222
- display: inline-flex;
223
- align-items: center;
224
- justify-content: center;
225
- width: 16px; // Figma: constante 16px em todos os sizes
226
- height: 16px;
227
- padding: 0;
228
- margin: 0;
229
- background: transparent;
230
- border: none;
231
- cursor: pointer;
232
- color: var(--matcha-color-text-placeholder); // #9AA0A6
233
- transition: color var(--matcha-duration-fast, 200ms) ease;
234
-
235
- &:hover:not(:disabled) {
236
- color: var(--matcha-color-text-primary);
237
- }
238
-
239
- &:disabled {
240
- cursor: not-allowed;
241
- color: var(--matcha-color-interaction-disabled-fg);
242
- }
243
-
244
- &:focus { outline: none; }
245
- &:focus-visible {
246
- outline: 2px solid var(--matcha-color-primary);
247
- outline-offset: 2px;
248
- border-radius: var(--matcha-radius-md, 4px);
249
- }
250
-
251
- > [class^="i-matcha-"],
252
- > [class*=" i-matcha-"] {
253
- font-size: 16px !important;
254
- width: 16px !important;
255
- height: 16px !important;
256
- line-height: 1 !important;
257
- color: inherit !important;
258
- }
259
- }
260
-
261
- // Esconde spinner nativo (Figma usa botões customizados)
262
- input[type="number"][matchaQuantityStepper] {
263
- -moz-appearance: textfield;
264
- appearance: textfield;
265
-
266
- &::-webkit-outer-spin-button,
267
- &::-webkit-inner-spin-button {
268
- -webkit-appearance: none;
269
- appearance: none;
270
- margin: 0;
271
- }
272
- }
273
-
274
- // Esconde botão × nativo do Chrome/Safari em input[type="search"]
275
- // (o matchaClearButton directive injeta nosso próprio — não duplicar)
276
- input[type="search"] {
277
- &::-webkit-search-cancel-button,
278
- &::-webkit-search-decoration,
279
- &::-webkit-search-results-button,
280
- &::-webkit-search-results-decoration {
281
- -webkit-appearance: none;
282
- appearance: none;
283
- display: none;
284
- }
285
- }
286
-
287
- // ── matchaClearButton: botão × ao lado de search/text ────────────────
288
- // Figma-consistent: 16/20/24 por size, cor text-placeholder
289
- .matcha-clear-button {
290
- flex: none;
291
- display: inline-flex;
292
- align-items: center;
293
- justify-content: center;
294
- width: 16px; height: 16px;
295
- padding: 0;
296
- // margin-left: auto empurra o × pro final do flex row, imune a irmãos
297
- // invisíveis (ng-content placeholders, matcha-autocomplete host que
298
- // acaba como sibling quando projetado via matcha-field, etc).
299
- margin: 0 0 0 auto;
300
- background: transparent;
301
- border: none;
302
- cursor: pointer;
303
- color: var(--matcha-color-text-placeholder);
304
- transition: color var(--matcha-duration-fast, 200ms) ease;
305
- position: relative;
306
-
307
- &:hover:not(:disabled) { color: var(--matcha-color-text-primary); }
308
-
309
- &:focus { outline: none; }
310
- &:focus-visible {
311
- outline: 2px solid var(--matcha-color-primary);
312
- outline-offset: 2px;
313
- border-radius: var(--matcha-radius-md, 4px);
314
- }
315
-
316
- > [class^="i-matcha-"],
317
- > [class*=" i-matcha-"] {
318
- font-size: 16px !important;
319
- width: 16px !important; height: 16px !important;
320
- line-height: 1 !important;
321
- color: inherit !important;
322
- }
323
- }
324
-
325
- // matchaCharCounter removido daqui — agora é irmão do .matcha-form-field
326
- // (vive no wrapper), estilizado fora deste bloco pra funcionar em qualquer size.
327
- // Nota: estilo precisa ser GLOBAL (este arquivo) porque o span é injetado via
328
- // Renderer2 sem _ngcontent — CSS scoped do Emulated não matcha.
329
-
330
- // ── Loading spinner no suffix do field ────────────────────────────────
331
- .matcha-form-field-spinner {
332
- flex: none;
333
- display: inline-block;
334
- width: 16px; height: 16px;
335
- border: 2px solid var(--matcha-color-surface-border);
336
- border-top-color: var(--matcha-color-primary);
337
- border-radius: 50%;
338
- animation: matcha-spin 800ms linear infinite;
339
-
340
- @media (prefers-reduced-motion: reduce) {
341
- animation-duration: 2000ms;
342
- }
343
- }
344
-
345
- // Select trigger ocupa altura total do form-field (clique abre dropdown em qualquer ponto)
346
- matcha-select,
347
- .matcha-select-container,
348
- .matcha-select-trigger {
349
- height: 100%;
350
- }
351
-
352
- .matcha-suffix {
353
- display: flex;
354
- flex-wrap: nowrap;
355
- align-items: center;
356
- }
357
- }
358
-
359
- // ─── Password toggle: tamanho do ícone por size do form-field ──────────────
360
- // (Figma matcha/field/password: SM 16 · MD 20 · LG 24)
361
- matcha-form-field[size="md"] .matcha-password-toggle-button,
362
- matcha-form-field[size="medium"] .matcha-password-toggle-button {
363
- width: 20px;
364
- height: 20px;
365
- font-size: 20px;
366
- }
367
-
368
- matcha-form-field[size="lg"] .matcha-password-toggle-button,
369
- matcha-form-field[size="large"] .matcha-password-toggle-button,
370
- matcha-form-field[size="huge"] .matcha-password-toggle-button {
371
- width: 24px;
372
- height: 24px;
373
- font-size: 24px;
374
- }
375
-
376
- // ── Spinner keyframes ────────────────────────────────────────────────────
377
- @keyframes matcha-spin {
378
- to { transform: rotate(360deg); }
379
- }
380
-
381
- // ── Readonly state ───────────────────────────────────────────────────────
382
- // Visual mais sutil que disabled: texto legível, selecionável, copiável.
383
- // Diferença vs disabled: cor normal do texto (não muted) + cursor `text` no input
384
- // (sinaliza "você pode selecionar e copiar"), enquanto disabled = not-allowed.
385
- matcha-form-field[readonly="true"] .matcha-form-field {
386
- background: var(--matcha-color-surface-bg);
387
-
388
- input, textarea {
389
- cursor: text; // nativo do input — reforça que texto é selecionável
390
- user-select: text; // explícito pra evitar reset acidental
391
- }
392
-
393
- // Remove hover border change (não é editável, hover não deve sugerir edição)
394
- &:hover { border-color: var(--matcha-color-surface-border); }
395
- }
396
-
397
- // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ─────────
398
- // Expande hit area sem mudar visual (pseudo-element overlay).
399
- @media (pointer: coarse) {
400
- .matcha-password-toggle-button,
401
- .matcha-quantity-stepper-button,
402
- .matcha-clear-button,
403
- matcha-date .matcha-date-picker-button,
404
- matcha-time .matcha-time-picker-button,
405
- matcha-field-range .matcha-field-range-button,
406
- matcha-phone .matcha-phone-country {
407
- position: relative;
408
-
409
- &::before {
410
- content: '';
411
- position: absolute;
412
- inset: -14px; // expande 14px em cada lado = 44x44 total (16 + 14*2)
413
- // invisível, mas capturavel — só expande o hit area
414
- }
415
- }
416
- }
417
-
418
- // ── Char counter (injetado via Renderer2 pelo matchaCharCounter directive) ──
419
- // Figma matcha/field/textarea: body-sm 12/16, text-secondary, align right.
420
- // Precisa ser global (sem ngcontent) porque o span vem de fora da view do form-field.
421
- .matcha-form-field-wrapper > .matcha-char-counter {
422
- align-self: flex-end;
423
- font-family: var(--matcha-font-family);
424
- font-size: var(--matcha-text-body-sm, 12px);
425
- font-weight: var(--matcha-weight-regular, 400);
426
- line-height: var(--matcha-leading-body-sm, 16px);
427
- color: var(--matcha-color-text-secondary);
428
- text-align: right;
429
- margin: 0;
430
- padding: 0;
431
- user-select: none;
432
- transition: color var(--matcha-duration-fast, 200ms) ease;
433
-
434
- // 3 níveis visuais (0-89% = neutro, 90-99% = warn, 100%+ = danger).
435
- // `is-near-limit` é aplicado a partir de 90%; `is-at-limit` sobrepõe a partir de 100%.
436
- &.is-near-limit:not(.is-at-limit) {
437
- color: var(--matcha-color-warn);
438
- }
439
-
440
- &.is-at-limit {
441
- color: var(--matcha-color-danger);
442
- font-weight: var(--matcha-weight-semibold, 600);
443
- }
444
- }
445
-
446
- // ── Caps Lock hint (injetado pelo matchaCapsLockHint em input[type="password"]) ──
447
- // Renderiza abaixo do field, mesmo nível do char counter. Usa cor warn (amarelo/laranja)
448
- // pra sinalizar risco sem ser erro (user pode estar digitando intencionalmente).
449
- .matcha-form-field-wrapper > .matcha-caps-lock-hint {
450
- display: inline-flex;
451
- align-items: center;
452
- gap: var(--matcha-space-050, 4px);
453
- align-self: flex-start;
454
- font-family: var(--matcha-font-family);
455
- font-size: var(--matcha-text-body-sm, 12px);
456
- font-weight: var(--matcha-weight-regular, 400);
457
- line-height: var(--matcha-leading-body-sm, 16px);
458
- color: var(--matcha-color-warn);
459
- margin-top: var(--matcha-space-050, 4px);
460
- user-select: none;
461
-
462
- > [class^="i-matcha-"] {
463
- font-size: 14px;
464
- line-height: 14px;
465
- }
466
- }
467
-
468
- // ── Responsive size classes (aligned with Figma SM/MD/LG) ───────────────────
469
-
470
- @mixin generate-matcha-form-field-size-classes($helper-breakpoints) {
471
- @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
472
- @include media-breakpoint($materialBreakpoint) {
473
- $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
474
-
475
- &[size#{$infix}="sm"],
476
- &[size#{$infix}="tiny"],
477
- &[size#{$infix}="small"] {
478
- .matcha-form-field input,
479
- .matcha-form-field textarea {
480
- font-size: var(--matcha-text-body-sm, 12px);
481
- line-height: var(--matcha-leading-body-sm, 16px);
482
- }
483
- }
484
-
485
- &[size#{$infix}="md"],
486
- &[size#{$infix}="medium"] {
487
- .matcha-form-field input,
488
- .matcha-form-field textarea {
489
- font-size: var(--matcha-font-size-sm, 14px);
490
- line-height: var(--matcha-leading-body-md, 20px);
491
- }
492
- }
493
-
494
- &[size#{$infix}="lg"],
495
- &[size#{$infix}="large"],
496
- &[size#{$infix}="huge"] {
497
- .matcha-form-field input,
498
- .matcha-form-field textarea {
499
- font-size: var(--matcha-font-size-md, 16px);
500
- line-height: var(--matcha-leading-body-lg, 24px);
501
- }
502
- }
503
- }
504
- }
505
- }
506
-
507
- // ── Theme mixin ─────────────────────────────────────────────────────────────
508
-
509
- @mixin matcha-form-field($theme) {
510
- .matcha-form-field {
511
- input[type="text"],
512
- input[type="email"],
513
- input[type="tel"],
514
- input[type="url"],
515
- input[type="password"],
516
- input[type="number"],
517
- input[type="search"],
518
- input[type="date"],
519
- input[type="time"],
520
- textarea {
521
- color: var(--matcha-color-text-primary);
522
-
523
- &::placeholder {
524
- color: var(--matcha-color-text-placeholder);
525
- }
526
- }
527
-
528
- input:disabled,
529
- textarea:disabled {
530
- color: var(--matcha-color-interaction-disabled-fg);
531
- }
532
-
533
- // Disabled: muted text, cursor not-allowed (sinalização inequívoca de "não pode interagir").
534
- // `cursor: not-allowed` vai no wrapper; `pointer-events: none` fica só no input/textarea
535
- // pra herdar o cursor do parent e não bloquear eventos no wrapper (senão cursor não aparece).
536
- &.matcha-form-field-disabled {
537
- cursor: not-allowed;
538
-
539
- input, textarea {
540
- pointer-events: none;
541
- color: var(--matcha-color-interaction-disabled-fg);
542
-
543
- &::placeholder {
544
- color: var(--matcha-color-interaction-disabled-fg);
545
- opacity: 0.7;
546
- }
547
- }
548
- }
549
- }
550
-
551
- matcha-form-field {
552
- @include generate-matcha-form-field-size-classes($helper-breakpoints);
553
- }
554
- }
555
-
556
- // ── Forced colors ──
557
- // Field precisa border + bg explícitos pra ser visível como input.
558
- @media (forced-colors: active) {
559
- matcha-form-field {
560
- .matcha-field-control,
561
- .matcha-form-field__control {
562
- border: 1px solid CanvasText !important;
563
- background: Canvas !important;
564
- color: CanvasText !important;
565
- }
566
-
567
- // Estado focused
568
- &.is-focused .matcha-field-control,
569
- &.is-focused .matcha-form-field__control,
570
- .matcha-field-control:focus-within,
571
- .matcha-form-field__control:focus-within {
572
- outline: 2px solid CanvasText !important;
573
- outline-offset: 2px;
574
- }
575
-
576
- // Estado erro/invalid
577
- &.is-invalid .matcha-field-control,
578
- &.has-error .matcha-form-field__control {
579
- border-color: CanvasText !important;
580
- border-width: 2px !important;
581
- }
582
-
583
- &[disabled],
584
- &.is-disabled {
585
- opacity: 0.5;
586
-
587
- .matcha-field-control,
588
- .matcha-form-field__control {
589
- border-color: GrayText !important;
590
- color: GrayText !important;
591
- }
592
- }
593
- }
594
- }
595
-
596
- // ── Print ──
597
- // Form fields preservam estado (form values). Sem fundo decorativo.
598
- @media print {
599
- matcha-form-field {
600
- .matcha-field-control,
601
- .matcha-form-field__control {
602
- border: 1px solid currentColor !important;
603
- background: transparent !important;
604
- }
605
- }
606
- }
1
+ @use "../abstracts/media-breakpoint" as *;
2
+ @use "../tokens/breakpoints-tokens" as *;
3
+ // =============================================================================
4
+ // matcha-form-field.scss — Theme mixin (stacked pattern, Figma matcha/field/_base)
5
+ // Border colors vêm do data-color (component SCSS). Theme mixin cuida de:
6
+ // - tipografia do input (size por breakpoint)
7
+ // - placeholder color
8
+ // - disabled text color
9
+ // =============================================================================
10
+
11
+ // ── Structural (inputs) — sem border, sem bg (box wraps) ────────────────────
12
+
13
+ // ─── Filhos projetáveis do form-field ────────────────────────────────────
14
+ // Todos os tipos de input que podem ser projetados DEVEM preencher o espaço
15
+ // disponível e herdar padding/border/bg do wrapper externo (.matcha-form-field).
16
+ // O componente filho NÃO deve ter padding/border próprio quando dentro do field.
17
+ .matcha-form-field {
18
+ input,
19
+ input[type="text"],
20
+ input[type="email"],
21
+ input[type="tel"],
22
+ input[type="url"],
23
+ input[type="password"],
24
+ input[type="number"],
25
+ input[type="search"],
26
+ input[type="date"],
27
+ input[type="time"],
28
+ textarea {
29
+ flex: 1 1 auto;
30
+ width: 100%;
31
+ min-width: 0;
32
+ min-height: 0; // evita que altura natural do input expanda o container
33
+ background-color: transparent;
34
+ outline: none;
35
+ box-shadow: none;
36
+ border: none;
37
+ padding: 0; // wrapper fornece padding horizontal
38
+ margin: 0;
39
+ font-family: var(--matcha-font-family);
40
+ font-size: var(--matcha-font-size-sm, 14px); // Figma MD placeholder/value 14/400/20
41
+ font-weight: var(--matcha-weight-regular, 400);
42
+ line-height: var(--matcha-leading-body-md, 20px);
43
+ color: var(--matcha-color-text-primary); // Figma filled value: #E8EAED dark / #0E1116 light
44
+ resize: none;
45
+
46
+ &:disabled {
47
+ color: var(--matcha-color-interaction-disabled-fg);
48
+ -webkit-text-fill-color: var(--matcha-color-interaction-disabled-fg); // Safari override
49
+ }
50
+ }
51
+
52
+ // Textarea ocupa TODA a altura disponível do wrapper (que é column mode).
53
+ // Quando `matchaAutoGrow` está ligado, a directive aplica `.matcha-autogrow`
54
+ // e controla altura via JS — o override abaixo solta o 100% forçado.
55
+ textarea {
56
+ height: 100%;
57
+ min-height: 100%;
58
+ align-self: stretch;
59
+
60
+ &.matcha-autogrow {
61
+ height: auto; // directive calcula e seta inline
62
+ min-height: 0; // flex: 1 já garante piso via wrapper min-height
63
+ }
64
+ }
65
+
66
+ // ── Scrollbar estilizada no textarea (Webkit + Firefox) ──────────────────
67
+ // Usa tokens do DS em vez do scrollbar nativo cinza feio do Chrome/Windows.
68
+ // Aplica tanto no textarea "rows fixo" (se user passar rows pequeno + muito texto)
69
+ // quanto no `.matcha-autogrow` depois de atingir `maxRows`.
70
+ textarea {
71
+ scrollbar-width: thin; // Firefox
72
+ scrollbar-color: var(--matcha-color-text-placeholder)
73
+ transparent; // thumb + track
74
+
75
+ &::-webkit-scrollbar { width: 6px; height: 6px; }
76
+ &::-webkit-scrollbar-track { background: transparent; }
77
+ &::-webkit-scrollbar-thumb {
78
+ background: var(--matcha-color-text-placeholder);
79
+ border-radius: var(--matcha-radius-pill, 999px);
80
+ }
81
+ &::-webkit-scrollbar-thumb:hover { background: var(--matcha-color-text-secondary); }
82
+ }
83
+
84
+ input:focus,
85
+ textarea:focus {
86
+ outline: none;
87
+ }
88
+
89
+ // Truncate: quando o campo é menor que placeholder/value, corta com ellipsis
90
+ // Evita overflow feio em sidebars, colunas estreitas, responsive mobile.
91
+ input {
92
+ text-overflow: ellipsis;
93
+ overflow: hidden;
94
+ white-space: nowrap;
95
+
96
+ // Placeholder também trunca (Chrome/Safari precisam do ::placeholder explícito)
97
+ &::placeholder {
98
+ text-overflow: ellipsis;
99
+ overflow: hidden;
100
+ white-space: nowrap;
101
+ }
102
+ }
103
+
104
+ // ── Autofill (Chrome/Safari) — mata o background amarelo/azul e usa os tokens ──
105
+ // Browsers injetam background-color inline no -webkit-autofill (prioridade sobre CSS normal).
106
+ // O workaround padrão: box-shadow inset transparente (não sobreposto) + text-fill-color pro texto.
107
+ // `transition: ... 5000s` é o "truque dos 5000 segundos" — impede o browser de re-aplicar o bg.
108
+ input:-webkit-autofill,
109
+ input:-webkit-autofill:hover,
110
+ input:-webkit-autofill:focus,
111
+ input:-webkit-autofill:active,
112
+ textarea:-webkit-autofill {
113
+ -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
114
+ -webkit-text-fill-color: var(--matcha-color-text-primary) !important;
115
+ caret-color: var(--matcha-color-primary) !important;
116
+ transition: background-color 5000s ease-in-out 0s, color 5000s ease-in-out 0s;
117
+ }
118
+
119
+ // Caret color (cursor piscando) = primary pra match com focus border
120
+ input,
121
+ textarea {
122
+ caret-color: var(--matcha-color-primary);
123
+ }
124
+
125
+ // Selection highlight (quando user seleciona texto) — usa token de primary com alpha
126
+ input::selection,
127
+ textarea::selection {
128
+ background: var(--matcha-color-primary-alpha);
129
+ color: var(--matcha-color-text-primary);
130
+ }
131
+
132
+ // Componentes projetados — todos preenchem a largura e não adicionam borda/padding próprio
133
+ matcha-select,
134
+ matcha-autocomplete,
135
+ matcha-date,
136
+ matcha-field-range,
137
+ matcha-time,
138
+ matcha-phone {
139
+ flex: 1 1 auto;
140
+ width: 100%;
141
+ min-width: 0;
142
+ display: flex;
143
+ align-items: center;
144
+ background: transparent;
145
+ border: none;
146
+ padding: 0;
147
+ }
148
+
149
+ // matcha-phone: padding/bg próprios do componente quebram o layout dentro do field
150
+ matcha-phone .matcha-phone {
151
+ padding: 0;
152
+ background: transparent;
153
+ font-size: inherit;
154
+ width: 100%;
155
+ }
156
+
157
+ // matcha-date/time: container do componente preenche o espaço sem padding/border próprios
158
+ matcha-date .matcha-date-container,
159
+ matcha-time .matcha-time-container {
160
+ width: 100%;
161
+ background: transparent;
162
+ border: none;
163
+ padding: 0;
164
+ min-width: 0;
165
+ gap: var(--matcha-space-100, 8px);
166
+ }
167
+
168
+ // ── matchaPasswordToggle: botão do olho injetado ao lado de input[type="password"] ──
169
+ // Figma matcha/field/password: ícone 16/20/24 por size, cor text-placeholder (#9AA0A6)
170
+ .matcha-password-toggle-button {
171
+ flex: none;
172
+ display: inline-flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ width: 16px; // SM default
176
+ height: 16px;
177
+ padding: 0;
178
+ margin: 0;
179
+ background: transparent;
180
+ border: none;
181
+ cursor: pointer;
182
+ color: var(--matcha-color-text-placeholder); // #9AA0A6
183
+ transition: color var(--matcha-duration-fast, 200ms) ease;
184
+
185
+ &:hover:not(:disabled) {
186
+ color: var(--matcha-color-text-primary);
187
+ }
188
+
189
+ &:disabled {
190
+ cursor: not-allowed;
191
+ color: var(--matcha-color-interaction-disabled-fg);
192
+ }
193
+
194
+ &:focus { outline: none; }
195
+ &:focus-visible {
196
+ outline: 2px solid var(--matcha-color-primary);
197
+ outline-offset: 2px;
198
+ border-radius: var(--matcha-radius-md, 4px);
199
+ }
200
+
201
+ > [class^="i-matcha-"],
202
+ > [class*=" i-matcha-"] {
203
+ font-size: inherit !important;
204
+ width: 1em !important;
205
+ height: 1em !important;
206
+ line-height: 1 !important;
207
+ color: inherit !important;
208
+ }
209
+ }
210
+
211
+ // ── matchaQuantityStepper: wrapper flex-row + dois botões (−/+) ──────
212
+ // Figma matcha/field/quantity: gap 4, ícones 16px constante, cor #9AA0A6
213
+ .matcha-quantity-stepper {
214
+ flex: none;
215
+ display: inline-flex;
216
+ align-items: center;
217
+ gap: var(--matcha-space-050, 4px);
218
+ }
219
+
220
+ .matcha-quantity-stepper-button {
221
+ flex: none;
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ width: 16px; // Figma: constante 16px em todos os sizes
226
+ height: 16px;
227
+ padding: 0;
228
+ margin: 0;
229
+ background: transparent;
230
+ border: none;
231
+ cursor: pointer;
232
+ color: var(--matcha-color-text-placeholder); // #9AA0A6
233
+ transition: color var(--matcha-duration-fast, 200ms) ease;
234
+
235
+ &:hover:not(:disabled) {
236
+ color: var(--matcha-color-text-primary);
237
+ }
238
+
239
+ &:disabled {
240
+ cursor: not-allowed;
241
+ color: var(--matcha-color-interaction-disabled-fg);
242
+ }
243
+
244
+ &:focus { outline: none; }
245
+ &:focus-visible {
246
+ outline: 2px solid var(--matcha-color-primary);
247
+ outline-offset: 2px;
248
+ border-radius: var(--matcha-radius-md, 4px);
249
+ }
250
+
251
+ > [class^="i-matcha-"],
252
+ > [class*=" i-matcha-"] {
253
+ font-size: 16px !important;
254
+ width: 16px !important;
255
+ height: 16px !important;
256
+ line-height: 1 !important;
257
+ color: inherit !important;
258
+ }
259
+ }
260
+
261
+ // Esconde spinner nativo (Figma usa botões customizados)
262
+ input[type="number"][matchaQuantityStepper] {
263
+ -moz-appearance: textfield;
264
+ appearance: textfield;
265
+
266
+ &::-webkit-outer-spin-button,
267
+ &::-webkit-inner-spin-button {
268
+ -webkit-appearance: none;
269
+ appearance: none;
270
+ margin: 0;
271
+ }
272
+ }
273
+
274
+ // Esconde botão × nativo do Chrome/Safari em input[type="search"]
275
+ // (o matchaClearButton directive injeta nosso próprio — não duplicar)
276
+ input[type="search"] {
277
+ &::-webkit-search-cancel-button,
278
+ &::-webkit-search-decoration,
279
+ &::-webkit-search-results-button,
280
+ &::-webkit-search-results-decoration {
281
+ -webkit-appearance: none;
282
+ appearance: none;
283
+ display: none;
284
+ }
285
+ }
286
+
287
+ // ── matchaClearButton: botão × ao lado de search/text ────────────────
288
+ // Figma-consistent: 16/20/24 por size, cor text-placeholder
289
+ .matcha-clear-button {
290
+ flex: none;
291
+ display: inline-flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ width: 16px; height: 16px;
295
+ padding: 0;
296
+ // margin-left: auto empurra o × pro final do flex row, imune a irmãos
297
+ // invisíveis (ng-content placeholders, matcha-autocomplete host que
298
+ // acaba como sibling quando projetado via matcha-field, etc).
299
+ margin: 0 0 0 auto;
300
+ background: transparent;
301
+ border: none;
302
+ cursor: pointer;
303
+ color: var(--matcha-color-text-placeholder);
304
+ transition: color var(--matcha-duration-fast, 200ms) ease;
305
+ position: relative;
306
+
307
+ &:hover:not(:disabled) { color: var(--matcha-color-text-primary); }
308
+
309
+ &:focus { outline: none; }
310
+ &:focus-visible {
311
+ outline: 2px solid var(--matcha-color-primary);
312
+ outline-offset: 2px;
313
+ border-radius: var(--matcha-radius-md, 4px);
314
+ }
315
+
316
+ > [class^="i-matcha-"],
317
+ > [class*=" i-matcha-"] {
318
+ font-size: 16px !important;
319
+ width: 16px !important; height: 16px !important;
320
+ line-height: 1 !important;
321
+ color: inherit !important;
322
+ }
323
+ }
324
+
325
+ // matchaCharCounter removido daqui — agora é irmão do .matcha-form-field
326
+ // (vive no wrapper), estilizado fora deste bloco pra funcionar em qualquer size.
327
+ // Nota: estilo precisa ser GLOBAL (este arquivo) porque o span é injetado via
328
+ // Renderer2 sem _ngcontent — CSS scoped do Emulated não matcha.
329
+
330
+ // ── Loading spinner no suffix do field ────────────────────────────────
331
+ .matcha-form-field-spinner {
332
+ flex: none;
333
+ display: inline-block;
334
+ width: 16px; height: 16px;
335
+ border: 2px solid var(--matcha-color-surface-border);
336
+ border-top-color: var(--matcha-color-primary);
337
+ border-radius: 50%;
338
+ animation: matcha-spin 800ms linear infinite;
339
+
340
+ @media (prefers-reduced-motion: reduce) {
341
+ animation-duration: 2000ms;
342
+ }
343
+ }
344
+
345
+ // Select trigger ocupa altura total do form-field (clique abre dropdown em qualquer ponto)
346
+ matcha-select,
347
+ .matcha-select-container,
348
+ .matcha-select-trigger {
349
+ height: 100%;
350
+ }
351
+
352
+ .matcha-suffix {
353
+ display: flex;
354
+ flex-wrap: nowrap;
355
+ align-items: center;
356
+ }
357
+ }
358
+
359
+ // ─── Password toggle: tamanho do ícone por size do form-field ──────────────
360
+ // (Figma matcha/field/password: SM 16 · MD 20 · LG 24)
361
+ matcha-form-field[size="md"] .matcha-password-toggle-button,
362
+ matcha-form-field[size="medium"] .matcha-password-toggle-button {
363
+ width: 20px;
364
+ height: 20px;
365
+ font-size: 20px;
366
+ }
367
+
368
+ matcha-form-field[size="lg"] .matcha-password-toggle-button,
369
+ matcha-form-field[size="large"] .matcha-password-toggle-button,
370
+ matcha-form-field[size="huge"] .matcha-password-toggle-button {
371
+ width: 24px;
372
+ height: 24px;
373
+ font-size: 24px;
374
+ }
375
+
376
+ // ── Spinner keyframes ────────────────────────────────────────────────────
377
+ @keyframes matcha-spin {
378
+ to { transform: rotate(360deg); }
379
+ }
380
+
381
+ // ── Readonly state ───────────────────────────────────────────────────────
382
+ // Visual mais sutil que disabled: texto legível, selecionável, copiável.
383
+ // Diferença vs disabled: cor normal do texto (não muted) + cursor `text` no input
384
+ // (sinaliza "você pode selecionar e copiar"), enquanto disabled = not-allowed.
385
+ matcha-form-field[readonly="true"] .matcha-form-field {
386
+ background: var(--matcha-color-surface-bg);
387
+
388
+ input, textarea {
389
+ cursor: text; // nativo do input — reforça que texto é selecionável
390
+ user-select: text; // explícito pra evitar reset acidental
391
+ }
392
+
393
+ // Remove hover border change (não é editável, hover não deve sugerir edição)
394
+ &:hover { border-color: var(--matcha-color-surface-border); }
395
+ }
396
+
397
+ // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ─────────
398
+ // Expande hit area sem mudar visual (pseudo-element overlay).
399
+ @media (pointer: coarse) {
400
+ .matcha-password-toggle-button,
401
+ .matcha-quantity-stepper-button,
402
+ .matcha-clear-button,
403
+ matcha-date .matcha-date-picker-button,
404
+ matcha-time .matcha-time-picker-button,
405
+ matcha-field-range .matcha-field-range-button,
406
+ matcha-phone .matcha-phone-country {
407
+ position: relative;
408
+
409
+ &::before {
410
+ content: '';
411
+ position: absolute;
412
+ inset: -14px; // expande 14px em cada lado = 44x44 total (16 + 14*2)
413
+ // invisível, mas capturavel — só expande o hit area
414
+ }
415
+ }
416
+ }
417
+
418
+ // ── Char counter (injetado via Renderer2 pelo matchaCharCounter directive) ──
419
+ // Figma matcha/field/textarea: body-sm 12/16, text-secondary, align right.
420
+ // Precisa ser global (sem ngcontent) porque o span vem de fora da view do form-field.
421
+ .matcha-form-field-wrapper > .matcha-char-counter {
422
+ align-self: flex-end;
423
+ font-family: var(--matcha-font-family);
424
+ font-size: var(--matcha-text-body-sm, 12px);
425
+ font-weight: var(--matcha-weight-regular, 400);
426
+ line-height: var(--matcha-leading-body-sm, 16px);
427
+ color: var(--matcha-color-text-secondary);
428
+ text-align: right;
429
+ margin: 0;
430
+ padding: 0;
431
+ user-select: none;
432
+ transition: color var(--matcha-duration-fast, 200ms) ease;
433
+
434
+ // 3 níveis visuais (0-89% = neutro, 90-99% = warn, 100%+ = danger).
435
+ // `is-near-limit` é aplicado a partir de 90%; `is-at-limit` sobrepõe a partir de 100%.
436
+ &.is-near-limit:not(.is-at-limit) {
437
+ color: var(--matcha-color-warn);
438
+ }
439
+
440
+ &.is-at-limit {
441
+ color: var(--matcha-color-danger);
442
+ font-weight: var(--matcha-weight-semibold, 600);
443
+ }
444
+ }
445
+
446
+ // ── Caps Lock hint (injetado pelo matchaCapsLockHint em input[type="password"]) ──
447
+ // Renderiza abaixo do field, mesmo nível do char counter. Usa cor warn (amarelo/laranja)
448
+ // pra sinalizar risco sem ser erro (user pode estar digitando intencionalmente).
449
+ .matcha-form-field-wrapper > .matcha-caps-lock-hint {
450
+ display: inline-flex;
451
+ align-items: center;
452
+ gap: var(--matcha-space-050, 4px);
453
+ align-self: flex-start;
454
+ font-family: var(--matcha-font-family);
455
+ font-size: var(--matcha-text-body-sm, 12px);
456
+ font-weight: var(--matcha-weight-regular, 400);
457
+ line-height: var(--matcha-leading-body-sm, 16px);
458
+ color: var(--matcha-color-warn);
459
+ margin-top: var(--matcha-space-050, 4px);
460
+ user-select: none;
461
+
462
+ > [class^="i-matcha-"] {
463
+ font-size: 14px;
464
+ line-height: 14px;
465
+ }
466
+ }
467
+
468
+ // ── Responsive size classes (aligned with Figma SM/MD/LG) ───────────────────
469
+
470
+ @mixin generate-matcha-form-field-size-classes($helper-breakpoints) {
471
+ @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
472
+ @include media-breakpoint($materialBreakpoint) {
473
+ $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
474
+
475
+ &[size#{$infix}="sm"],
476
+ &[size#{$infix}="tiny"],
477
+ &[size#{$infix}="small"] {
478
+ .matcha-form-field input,
479
+ .matcha-form-field textarea {
480
+ font-size: var(--matcha-text-body-sm, 12px);
481
+ line-height: var(--matcha-leading-body-sm, 16px);
482
+ }
483
+ }
484
+
485
+ &[size#{$infix}="md"],
486
+ &[size#{$infix}="medium"] {
487
+ .matcha-form-field input,
488
+ .matcha-form-field textarea {
489
+ font-size: var(--matcha-font-size-sm, 14px);
490
+ line-height: var(--matcha-leading-body-md, 20px);
491
+ }
492
+ }
493
+
494
+ &[size#{$infix}="lg"],
495
+ &[size#{$infix}="large"],
496
+ &[size#{$infix}="huge"] {
497
+ .matcha-form-field input,
498
+ .matcha-form-field textarea {
499
+ font-size: var(--matcha-font-size-md, 16px);
500
+ line-height: var(--matcha-leading-body-lg, 24px);
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+
507
+ // ── Theme mixin ─────────────────────────────────────────────────────────────
508
+
509
+ @mixin matcha-form-field($theme) {
510
+ .matcha-form-field {
511
+ input[type="text"],
512
+ input[type="email"],
513
+ input[type="tel"],
514
+ input[type="url"],
515
+ input[type="password"],
516
+ input[type="number"],
517
+ input[type="search"],
518
+ input[type="date"],
519
+ input[type="time"],
520
+ textarea {
521
+ color: var(--matcha-color-text-primary);
522
+
523
+ &::placeholder {
524
+ color: var(--matcha-color-text-placeholder);
525
+ }
526
+ }
527
+
528
+ input:disabled,
529
+ textarea:disabled {
530
+ color: var(--matcha-color-interaction-disabled-fg);
531
+ }
532
+
533
+ // Disabled: muted text, cursor not-allowed (sinalização inequívoca de "não pode interagir").
534
+ // `cursor: not-allowed` vai no wrapper; `pointer-events: none` fica só no input/textarea
535
+ // pra herdar o cursor do parent e não bloquear eventos no wrapper (senão cursor não aparece).
536
+ &.matcha-form-field-disabled {
537
+ cursor: not-allowed;
538
+
539
+ input, textarea {
540
+ pointer-events: none;
541
+ color: var(--matcha-color-interaction-disabled-fg);
542
+
543
+ &::placeholder {
544
+ color: var(--matcha-color-interaction-disabled-fg);
545
+ opacity: 0.7;
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ matcha-form-field {
552
+ @include generate-matcha-form-field-size-classes($helper-breakpoints);
553
+ }
554
+ }
555
+
556
+ // ── Forced colors ──
557
+ // Field precisa border + bg explícitos pra ser visível como input.
558
+ @media (forced-colors: active) {
559
+ matcha-form-field {
560
+ .matcha-field-control,
561
+ .matcha-form-field__control {
562
+ border: 1px solid CanvasText !important;
563
+ background: Canvas !important;
564
+ color: CanvasText !important;
565
+ }
566
+
567
+ // Estado focused
568
+ &.is-focused .matcha-field-control,
569
+ &.is-focused .matcha-form-field__control,
570
+ .matcha-field-control:focus-within,
571
+ .matcha-form-field__control:focus-within {
572
+ outline: 2px solid CanvasText !important;
573
+ outline-offset: 2px;
574
+ }
575
+
576
+ // Estado erro/invalid
577
+ &.is-invalid .matcha-field-control,
578
+ &.has-error .matcha-form-field__control {
579
+ border-color: CanvasText !important;
580
+ border-width: 2px !important;
581
+ }
582
+
583
+ &[disabled],
584
+ &.is-disabled {
585
+ opacity: 0.5;
586
+
587
+ .matcha-field-control,
588
+ .matcha-form-field__control {
589
+ border-color: GrayText !important;
590
+ color: GrayText !important;
591
+ }
592
+ }
593
+ }
594
+ }
595
+
596
+ // ── Print ──
597
+ // Form fields preservam estado (form values). Sem fundo decorativo.
598
+ @media print {
599
+ matcha-form-field {
600
+ .matcha-field-control,
601
+ .matcha-form-field__control {
602
+ border: 1px solid currentColor !important;
603
+ background: transparent !important;
604
+ }
605
+ }
606
+ }