@inchurch/matcha-theme 22.38.2 → 22.38.3

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 (93) hide show
  1. package/abstracts/_colors.scss +291 -291
  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 +224 -224
  6. package/abstracts/_typography.scss +128 -128
  7. package/base/_reset.scss +487 -487
  8. package/components/app-page-header.scss +260 -260
  9. package/components/matcha-accordion.scss +297 -297
  10. package/components/matcha-audio-player.scss +31 -31
  11. package/components/matcha-autocomplete.scss +145 -145
  12. package/components/matcha-avatar.scss +426 -426
  13. package/components/matcha-badge.scss +120 -120
  14. package/components/matcha-breadcrumb.scss +231 -231
  15. package/components/matcha-button-toggle.scss +484 -484
  16. package/components/matcha-buttons.scss +973 -973
  17. package/components/matcha-calendar.scss +253 -253
  18. package/components/matcha-cards.scss +212 -212
  19. package/components/matcha-chart-card.scss +53 -53
  20. package/components/matcha-checkbox.scss +266 -266
  21. package/components/matcha-chip.scss +311 -311
  22. package/components/matcha-color-pick.scss +34 -34
  23. package/components/matcha-command-palette.scss +316 -316
  24. package/components/matcha-crop.scss +419 -419
  25. package/components/matcha-date.scss +81 -81
  26. package/components/matcha-divider.scss +100 -100
  27. package/components/matcha-draggable.scss +23 -23
  28. package/components/matcha-drawer.scss +376 -376
  29. package/components/matcha-drop-image.scss +14 -14
  30. package/components/matcha-drop-list.scss +430 -430
  31. package/components/matcha-emoji.scss +69 -69
  32. package/components/matcha-empty-state.scss +230 -230
  33. package/components/matcha-file-uploader.scss +650 -637
  34. package/components/matcha-flag.scss +103 -103
  35. package/components/matcha-footer.scss +91 -91
  36. package/components/matcha-form-field.scss +883 -883
  37. package/components/matcha-form-section.scss +100 -100
  38. package/components/matcha-header.scss +135 -135
  39. package/components/matcha-highlight.scss +164 -164
  40. package/components/matcha-hint-text.scss +90 -90
  41. package/components/matcha-horizontal-tree.scss +247 -247
  42. package/components/matcha-icon.scss +111 -111
  43. package/components/matcha-image.scss +60 -60
  44. package/components/matcha-list.scss +211 -211
  45. package/components/matcha-menu.scss +99 -99
  46. package/components/matcha-modal.scss +210 -210
  47. package/components/matcha-nav.scss +770 -770
  48. package/components/matcha-notification.scss +417 -417
  49. package/components/matcha-option.scss +170 -170
  50. package/components/matcha-page-builder.scss +4196 -4196
  51. package/components/matcha-paginator.scss +699 -699
  52. package/components/matcha-panel.scss +216 -216
  53. package/components/matcha-period.scss +174 -174
  54. package/components/matcha-profile-card.scss +50 -50
  55. package/components/matcha-progress-bar.scss +355 -355
  56. package/components/matcha-radio.scss +325 -325
  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 +629 -629
  63. package/components/matcha-skeleton.scss +182 -182
  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-stepper.scss +376 -376
  70. package/components/matcha-table-cell.scss +107 -107
  71. package/components/matcha-table.scss +722 -722
  72. package/components/matcha-tabs.scss +642 -642
  73. package/components/matcha-text-editor.scss +111 -111
  74. package/components/matcha-time.scss +33 -33
  75. package/components/matcha-timepicker.scss +13 -13
  76. package/components/matcha-toolbar.scss +366 -366
  77. package/components/matcha-tooltip.scss +235 -235
  78. package/components/matcha-tree-view.scss +636 -636
  79. package/layout/matcha-page-layout.scss +778 -778
  80. package/main.scss +334 -334
  81. package/package.json +1 -1
  82. package/tokens/_animations-tokens.scss +37 -37
  83. package/tokens/_breakpoints-tokens.scss +35 -35
  84. package/tokens/_color-tokens.scss +288 -288
  85. package/tokens/_elevation-tokens.scss +33 -33
  86. package/tokens/_emit-tokens.scss +329 -329
  87. package/tokens/_material-palette.scss +1149 -1149
  88. package/tokens/_primitives.scss +98 -98
  89. package/tokens/_semantic-aliases.scss +120 -120
  90. package/tokens/_spacing-tokens.scss +94 -94
  91. package/tokens/_static-tokens.scss +137 -137
  92. package/tokens/_typography-tokens.scss +65 -65
  93. package/vendors/charts.scss +90 -90
@@ -1,883 +1,883 @@
1
- @use "../abstracts/media-breakpoint" as *;
2
- @use "../tokens/breakpoints-tokens" as *;
3
- // =============================================================================
4
- // matcha-form-field.scss — FONTE ÚNICA de estilo do matcha-form-field
5
- // (estrutura, states, sizes, tipografia). O component SCSS é comment-only.
6
- // =============================================================================
7
-
8
- // ═══ Estrutura e states (migrado do matcha-form-field.component.scss) ═══════
9
- // Histórico: estas regras viviam no SCSS do componente (ViewEncapsulation.
10
- // Emulated). Seletor de host escrito como `matcha-form-field[...]` ganhava
11
- // sufixo [_ngcontent-…] e virava CSS morto — caso do disabled, cuja opacity
12
- // nunca aplicou. Global no tema, o que está escrito é o que vale; os antigos
13
- // ::ng-deep viram seletores diretos.
14
-
15
- // Wrapper stacked: Label → gap → Box → gap → Hint (Figma matcha/field/_base)
16
- .matcha-form-field-wrapper {
17
- display: flex;
18
- flex-direction: column;
19
- align-items: flex-start;
20
- gap: var(--matcha-space-075, 6px); // Figma matcha/field/text: gap 6px entre label/box/hint
21
- width: 100%;
22
- }
23
-
24
- // Box do input — onde fica o input/select/textarea
25
- // Figma: align-self: stretch → ocupa toda a largura do wrapper
26
- .matcha-form-field {
27
- display: flex;
28
- flex-direction: row;
29
- align-items: center;
30
- gap: var(--matcha-space-100, 8px);
31
- width: 100%; // stretch pro wrapper
32
- min-height: 44px; // MD default
33
- padding: 0 var(--matcha-space-150, 12px); // pad horizontal 12
34
- border: var(--matcha-border-thin, 2px) solid var(--matcha-color-surface-border, #E1E7E2);
35
- border-radius: var(--matcha-radius-lg, 8px);
36
- background: transparent;
37
- box-sizing: border-box;
38
- transition: border-color var(--matcha-duration-fast, 200ms);
39
- }
40
-
41
- // Slots prefix/suffix — elementos decorativos antes/depois do input
42
- .matcha-form-field .matcha-form-field-prefix,
43
- .matcha-form-field .matcha-form-field-suffix {
44
- display: inline-flex;
45
- align-items: center;
46
- flex: none;
47
- color: var(--matcha-color-text-label);
48
- font-family: var(--matcha-font-family);
49
- font-size: var(--matcha-font-size-sm, 14px);
50
- font-weight: var(--matcha-weight-regular, 400);
51
- line-height: var(--matcha-leading-body-md, 20px);
52
- user-select: none;
53
- }
54
-
55
- .matcha-form-field .matcha-form-field-prefix:not(:empty) + * {
56
- padding-left: 0;
57
- }
58
-
59
- // Hover / focus-within (Figma matcha/field/_base State=Focus).
60
- // Disabled não chega aqui (pointer-events: none nas regras de disabled abaixo);
61
- // readonly neutraliza o hover na regra própria de readonly; error e data-color
62
- // vencem por especificidade/ordem.
63
- .matcha-form-field:hover {
64
- border-color: var(--matcha-color-surface-border-strong);
65
- }
66
-
67
- .matcha-form-field:focus-within {
68
- border-color: var(--matcha-color-primary, #1664C2);
69
- }
70
-
71
- // Color variants via data-color — override border-color
72
- .matcha-form-field[data-color="accent"] { border-color: var(--matcha-color-accent); }
73
- .matcha-form-field[data-color="primary"] { border-color: var(--matcha-color-primary); }
74
- .matcha-form-field[data-color="danger"] { border-color: var(--matcha-color-danger); }
75
- .matcha-form-field[data-color="warn"] { border-color: var(--matcha-color-warn); }
76
- .matcha-form-field[data-color="success"] { border-color: var(--matcha-color-success); }
77
- .matcha-form-field[data-color="info"] { border-color: var(--matcha-color-info); }
78
-
79
- // Sizes (Figma heights): SM 36, MD 44, LG 52.
80
- // Alturas não são responsivas — a tipografia responsiva fica no mixin abaixo.
81
- matcha-form-field[size="sm"] .matcha-form-field,
82
- matcha-form-field[size="tiny"] .matcha-form-field,
83
- matcha-form-field[size="small"] .matcha-form-field {
84
- min-height: 36px;
85
- padding: 0 var(--matcha-space-150, 12px);
86
- }
87
- matcha-form-field[size="md"] .matcha-form-field,
88
- matcha-form-field[size="medium"] .matcha-form-field {
89
- min-height: 44px;
90
- padding: 0 var(--matcha-space-150, 12px);
91
- }
92
- matcha-form-field[size="lg"] .matcha-form-field,
93
- matcha-form-field[size="large"] .matcha-form-field,
94
- matcha-form-field[size="huge"] .matcha-form-field {
95
- min-height: 52px;
96
- padding: 0 var(--matcha-space-200, 16px);
97
- }
98
-
99
- // Disabled — Figma aplica opacity 0.38 no WRAPPER inteiro
100
- // (label, field-box e hint ficam apagados de uma vez).
101
- // EXCEÇÃO: tooltip do label preserva opacity=1 e pointer-events pra manter a
102
- // pergunta "por que desabilitado?" respondível (UX crítico pra forms complexos).
103
- // Sem border-color especial: o token interaction-disabled-border tem MAIS
104
- // contraste que a borda normal e anulava o esmaecimento — a borda fica a normal
105
- // e a opacity apaga o conjunto uniformemente.
106
- matcha-form-field[disabled]:not([disabled="false"]) {
107
- opacity: var(--matcha-opacity-disabled, 0.38);
108
-
109
- .matcha-form-field {
110
- pointer-events: none;
111
- cursor: default;
112
- }
113
-
114
- // Tooltip icon continua interativo e legível mesmo com wrapper disabled
115
- .matcha-label-tooltip {
116
- opacity: calc(1 / var(--matcha-opacity-disabled, 0.38));
117
- pointer-events: auto;
118
- }
119
- }
120
-
121
- // Fallback legado (classe aplicada via JS quando o input interno tem disabled);
122
- // cursor/cores de texto dessa classe vivem no theme mixin mais abaixo.
123
- .matcha-form-field-disabled {
124
- pointer-events: none;
125
- }
126
-
127
- // Error state (Figma: border danger) — via @Input() error ou data-color="danger"
128
- matcha-form-field[error]:not([error="false"]) .matcha-form-field {
129
- border-color: var(--matcha-color-danger);
130
- }
131
-
132
- // ─── Simulação de states em docs/playgrounds ────────────────────────────
133
- matcha-form-field.is-state-focus .matcha-form-field {
134
- border-color: var(--matcha-color-primary) !important;
135
- }
136
-
137
- matcha-form-field.is-state-disabled {
138
- opacity: var(--matcha-opacity-disabled, 0.38);
139
- pointer-events: none;
140
- }
141
-
142
- // ─── Field-range override (Figma matcha/field/range) ────────────────────
143
- // Field-range renderiza DUAS caixas bordadas próprias (start/end com separador).
144
- // O form-field externo NÃO deve desenhar sua box — apenas empilha label + range + hint.
145
- .matcha-form-field:has(matcha-field-range) {
146
- border: none;
147
- background: transparent;
148
- padding: 0;
149
- min-height: 0;
150
- gap: 0;
151
- }
152
-
153
- matcha-form-field[size="sm"] .matcha-form-field:has(matcha-field-range),
154
- matcha-form-field[size="tiny"] .matcha-form-field:has(matcha-field-range),
155
- matcha-form-field[size="small"] .matcha-form-field:has(matcha-field-range),
156
- matcha-form-field[size="md"] .matcha-form-field:has(matcha-field-range),
157
- matcha-form-field[size="medium"] .matcha-form-field:has(matcha-field-range),
158
- matcha-form-field[size="lg"] .matcha-form-field:has(matcha-field-range),
159
- matcha-form-field[size="large"] .matcha-form-field:has(matcha-field-range),
160
- matcha-form-field[size="huge"] .matcha-form-field:has(matcha-field-range) {
161
- min-height: 0;
162
- padding: 0;
163
- border: none;
164
- }
165
-
166
- // Erro: não mexe na borda do wrapper (já é none), a cor do erro vai para as halves
167
- matcha-form-field[error]:not([error="false"]) .matcha-form-field:has(matcha-field-range) {
168
- border: none;
169
- }
170
-
171
- // ─── Textarea override (Figma matcha/field/textarea) ────────────────────
172
- // Quando o field contém textarea, o wrapper vira column + padding all-sides
173
- // Heights por size: SM 80px · MD 100px · LG 120px
174
- .matcha-form-field:has(textarea) {
175
- flex-direction: column;
176
- align-items: flex-start;
177
- padding: var(--matcha-space-150, 12px);
178
- min-height: 80px; // fallback SM
179
- }
180
-
181
- matcha-form-field[size="sm"] .matcha-form-field:has(textarea),
182
- matcha-form-field[size="tiny"] .matcha-form-field:has(textarea),
183
- matcha-form-field[size="small"] .matcha-form-field:has(textarea) {
184
- min-height: 80px;
185
- padding: var(--matcha-space-150, 12px);
186
- }
187
- matcha-form-field[size="md"] .matcha-form-field:has(textarea),
188
- matcha-form-field[size="medium"] .matcha-form-field:has(textarea) {
189
- min-height: 100px;
190
- padding: var(--matcha-space-150, 12px);
191
- }
192
- matcha-form-field[size="lg"] .matcha-form-field:has(textarea),
193
- matcha-form-field[size="large"] .matcha-form-field:has(textarea),
194
- matcha-form-field[size="huge"] .matcha-form-field:has(textarea) {
195
- min-height: 120px;
196
- padding: var(--matcha-space-150, 12px) var(--matcha-space-200, 16px);
197
- }
198
-
199
- // ─── Search: ícone lupa automático após o input[type=search] (Figma suffix icon) ──
200
- // Mesmo padding horizontal dos outros fields. Por size: SM 16 · MD 20 · LG 24.
201
- .matcha-form-field:has(input[type="search"])::after {
202
- content: '\e90c'; // codepoint "search" em MatchaIcons
203
- font-family: 'MatchaIcons' !important; // fonte registrada no projects/matcha-icons/matcha-icons.css
204
- font-weight: normal;
205
- font-style: normal;
206
- font-variant: normal;
207
- speak: none;
208
- text-transform: none;
209
- -webkit-font-smoothing: antialiased;
210
- color: var(--matcha-color-text-placeholder);
211
- font-size: 16px;
212
- line-height: 16px;
213
- flex: none;
214
- pointer-events: none;
215
- display: inline-flex;
216
- align-items: center;
217
- justify-content: center;
218
- }
219
- matcha-form-field[size="md"] .matcha-form-field:has(input[type="search"])::after,
220
- matcha-form-field[size="medium"] .matcha-form-field:has(input[type="search"])::after {
221
- font-size: 20px;
222
- line-height: 20px;
223
- }
224
- matcha-form-field[size="lg"] .matcha-form-field:has(input[type="search"])::after,
225
- matcha-form-field[size="large"] .matcha-form-field:has(input[type="search"])::after,
226
- matcha-form-field[size="huge"] .matcha-form-field:has(input[type="search"])::after {
227
- font-size: 24px;
228
- line-height: 24px;
229
- }
230
-
231
- // Hierarquia de trailing icons: loading > clear × > suffix default.
232
- // Cada nível oculta os abaixo pra não empilhar ícones redundantes.
233
-
234
- // Loading: spinner assume o suffix; oculta lupa + × juntos.
235
- .matcha-form-field:has(input[type="search"]):has(.matcha-form-field-spinner)::after,
236
- .matcha-form-field:has(.matcha-form-field-spinner) .matcha-clear-button {
237
- display: none !important;
238
- }
239
-
240
- // Clear visível (input com valor): × toma o lugar da lupa default.
241
- .matcha-form-field:has(input[type="search"]):has(.matcha-clear-button--visible)::after {
242
- display: none;
243
- }
244
-
245
- // ── Structural (inputs) — sem border, sem bg (box wraps) ────────────────────
246
-
247
- // ─── Filhos projetáveis do form-field ────────────────────────────────────
248
- // Todos os tipos de input que podem ser projetados DEVEM preencher o espaço
249
- // disponível e herdar padding/border/bg do wrapper externo (.matcha-form-field).
250
- // O componente filho NÃO deve ter padding/border próprio quando dentro do field.
251
- .matcha-form-field {
252
- input,
253
- input[type="text"],
254
- input[type="email"],
255
- input[type="tel"],
256
- input[type="url"],
257
- input[type="password"],
258
- input[type="number"],
259
- input[type="search"],
260
- input[type="date"],
261
- input[type="time"],
262
- textarea {
263
- flex: 1 1 auto;
264
- width: 100%;
265
- min-width: 0;
266
- min-height: 0; // evita que altura natural do input expanda o container
267
- background-color: transparent;
268
- outline: none;
269
- box-shadow: none;
270
- border: none;
271
- padding: 0; // wrapper fornece padding horizontal
272
- margin: 0;
273
- font-family: var(--matcha-font-family);
274
- font-size: var(--matcha-font-size-sm, 14px); // Figma MD placeholder/value 14/400/20
275
- font-weight: var(--matcha-weight-regular, 400);
276
- line-height: var(--matcha-leading-body-md, 20px);
277
- color: var(--matcha-color-text-primary); // Figma filled value: #E8EAED dark / #0E1116 light
278
- resize: none;
279
-
280
- // Figma: text-placeholder (#6E747A dark / #9AA0A6 light); opacity 1
281
- // porque o Firefox aplica opacity < 1 no ::placeholder por default
282
- &::placeholder {
283
- color: var(--matcha-color-text-placeholder);
284
- opacity: 1;
285
- }
286
-
287
- &:disabled {
288
- color: var(--matcha-color-interaction-disabled-fg);
289
- -webkit-text-fill-color: var(--matcha-color-interaction-disabled-fg); // Safari override
290
- }
291
- }
292
-
293
- // Textarea ocupa TODA a altura disponível do wrapper (que é column mode).
294
- // Quando `matchaAutoGrow` está ligado, a directive aplica `.matcha-autogrow`
295
- // e controla altura via JS — o override abaixo solta o 100% forçado.
296
- textarea {
297
- height: 100%;
298
- min-height: 100%;
299
- align-self: stretch;
300
-
301
- &.matcha-autogrow {
302
- height: auto; // directive calcula e seta inline
303
- min-height: 0; // flex: 1 já garante piso via wrapper min-height
304
- }
305
- }
306
-
307
- // ── Scrollbar estilizada no textarea (Webkit + Firefox) ──────────────────
308
- // Usa tokens do DS em vez do scrollbar nativo cinza feio do Chrome/Windows.
309
- // Aplica tanto no textarea "rows fixo" (se user passar rows pequeno + muito texto)
310
- // quanto no `.matcha-autogrow` depois de atingir `maxRows`.
311
- textarea {
312
- scrollbar-width: thin; // Firefox
313
- scrollbar-color: var(--matcha-color-text-placeholder)
314
- transparent; // thumb + track
315
-
316
- &::-webkit-scrollbar { width: 6px; height: 6px; }
317
- &::-webkit-scrollbar-track { background: transparent; }
318
- &::-webkit-scrollbar-thumb {
319
- background: var(--matcha-color-text-placeholder);
320
- border-radius: var(--matcha-radius-pill, 999px);
321
- }
322
- &::-webkit-scrollbar-thumb:hover { background: var(--matcha-color-text-secondary); }
323
- }
324
-
325
- input:focus,
326
- textarea:focus {
327
- outline: none;
328
- }
329
-
330
- // Truncate: quando o campo é menor que placeholder/value, corta com ellipsis
331
- // Evita overflow feio em sidebars, colunas estreitas, responsive mobile.
332
- input {
333
- text-overflow: ellipsis;
334
- overflow: hidden;
335
- white-space: nowrap;
336
-
337
- // Placeholder também trunca (Chrome/Safari precisam do ::placeholder explícito)
338
- &::placeholder {
339
- text-overflow: ellipsis;
340
- overflow: hidden;
341
- white-space: nowrap;
342
- }
343
- }
344
-
345
- // ── Autofill (Chrome/Safari) — mata o azul/amarelo nativo e usa os tokens ──
346
- // Browsers pintam autofill com um background-color INTERNO que ignora
347
- // `background-color` normal via CSS (prioridade de UA style). O workaround
348
- // padrão cobre esse fundo com um box-shadow inset opaco do tamanho da caixa —
349
- // e por isso a cor do box-shadow tem que ser OPACA e igual ao fundo real por
350
- // trás do input, não `transparent` (bug anterior: transparent não cobre nada,
351
- // o azul nativo do Chrome continuava visível por baixo -- ilegível no tema
352
- // escuro, onde o contraste com o texto claro inverte). O input em si é
353
- // `background-color: transparent` (linha ~33): o que aparece por trás é o
354
- // matcha-card que envolve o form-field na esmagadora maioria dos usos deste
355
- // painel -- por isso surface-card, não surface-bg (fundo da PÁGINA, um tom
356
- // mais escuro/mais claro que o card e que deixaria um contorno visível).
357
- // `transition: ... 5000s` é o "truque dos 5000 segundos" — impede o browser de
358
- // reaplicar o bg nativo antes do box-shadow cobrir.
359
- input:-webkit-autofill,
360
- input:-webkit-autofill:hover,
361
- input:-webkit-autofill:focus,
362
- input:-webkit-autofill:active,
363
- textarea:-webkit-autofill {
364
- -webkit-box-shadow: 0 0 0 1000px var(--matcha-color-surface-card) inset !important;
365
- -webkit-text-fill-color: var(--matcha-color-text-primary) !important;
366
- caret-color: var(--matcha-color-primary) !important;
367
- transition: background-color 5000s ease-in-out 0s, color 5000s ease-in-out 0s;
368
- }
369
-
370
- // Caret color (cursor piscando) = primary pra match com focus border
371
- input,
372
- textarea {
373
- caret-color: var(--matcha-color-primary);
374
- }
375
-
376
- // Selection highlight (quando user seleciona texto) — mesmo par accent do
377
- // ::selection global (base/_reset.scss). Era primary-alpha aqui e azul do SO no
378
- // resto da app: duas cores de seleção diferentes na mesma tela.
379
- input::selection,
380
- textarea::selection {
381
- background-color: var(--matcha-color-accent);
382
- color: var(--matcha-color-accent-text-on);
383
- }
384
-
385
- // Componentes projetados — todos preenchem a largura e não adicionam borda/padding próprio
386
- matcha-select,
387
- matcha-autocomplete,
388
- matcha-date,
389
- matcha-field-range,
390
- matcha-time,
391
- matcha-phone {
392
- flex: 1 1 auto;
393
- width: 100%;
394
- min-width: 0;
395
- display: flex;
396
- align-items: center;
397
- background: transparent;
398
- border: none;
399
- padding: 0;
400
- }
401
-
402
- // matcha-phone: padding/bg próprios do componente quebram o layout dentro do field
403
- matcha-phone .matcha-phone {
404
- padding: 0;
405
- background: transparent;
406
- font-size: inherit;
407
- width: 100%;
408
- }
409
-
410
- // matcha-date/time: container do componente preenche o espaço sem padding/border próprios
411
- matcha-date .matcha-date-container,
412
- matcha-time .matcha-time-container {
413
- width: 100%;
414
- background: transparent;
415
- border: none;
416
- padding: 0;
417
- min-width: 0;
418
- gap: var(--matcha-space-100, 8px);
419
- }
420
-
421
- // ── matchaPasswordToggle: botão do olho injetado ao lado de input[type="password"] ──
422
- // Figma matcha/field/password: ícone 16/20/24 por size, cor text-placeholder (#9AA0A6)
423
- .matcha-password-toggle-button {
424
- flex: none;
425
- display: inline-flex;
426
- align-items: center;
427
- justify-content: center;
428
- width: 16px; // SM default
429
- height: 16px;
430
- padding: 0;
431
- margin: 0;
432
- background: transparent;
433
- border: none;
434
- cursor: pointer;
435
- color: var(--matcha-color-text-placeholder); // #9AA0A6
436
- transition: color var(--matcha-duration-fast, 200ms) ease;
437
-
438
- &:hover:not(:disabled) {
439
- color: var(--matcha-color-text-primary);
440
- }
441
-
442
- &:disabled {
443
- cursor: not-allowed;
444
- color: var(--matcha-color-interaction-disabled-fg);
445
- }
446
-
447
- &:focus { outline: none; }
448
- &:focus-visible {
449
- outline: 2px solid var(--matcha-color-primary);
450
- outline-offset: 2px;
451
- border-radius: var(--matcha-radius-md, 4px);
452
- }
453
-
454
- > [class^="i-matcha-"],
455
- > [class*=" i-matcha-"] {
456
- font-size: inherit !important;
457
- width: 1em !important;
458
- height: 1em !important;
459
- line-height: 1 !important;
460
- color: inherit !important;
461
- }
462
- }
463
-
464
- // ── matchaQuantityStepper: wrapper flex-row + dois botões (−/+) ──────
465
- // Figma matcha/field/quantity: gap 4, ícones 16px constante, cor #9AA0A6
466
- .matcha-quantity-stepper {
467
- flex: none;
468
- display: inline-flex;
469
- align-items: center;
470
- gap: var(--matcha-space-050, 4px);
471
- }
472
-
473
- .matcha-quantity-stepper-button {
474
- flex: none;
475
- display: inline-flex;
476
- align-items: center;
477
- justify-content: center;
478
- width: 16px; // Figma: constante 16px em todos os sizes
479
- height: 16px;
480
- padding: 0;
481
- margin: 0;
482
- background: transparent;
483
- border: none;
484
- cursor: pointer;
485
- color: var(--matcha-color-text-placeholder); // #9AA0A6
486
- transition: color var(--matcha-duration-fast, 200ms) ease;
487
-
488
- &:hover:not(:disabled) {
489
- color: var(--matcha-color-text-primary);
490
- }
491
-
492
- &:disabled {
493
- cursor: not-allowed;
494
- color: var(--matcha-color-interaction-disabled-fg);
495
- }
496
-
497
- &:focus { outline: none; }
498
- &:focus-visible {
499
- outline: 2px solid var(--matcha-color-primary);
500
- outline-offset: 2px;
501
- border-radius: var(--matcha-radius-md, 4px);
502
- }
503
-
504
- > [class^="i-matcha-"],
505
- > [class*=" i-matcha-"] {
506
- font-size: 16px !important;
507
- width: 16px !important;
508
- height: 16px !important;
509
- line-height: 1 !important;
510
- color: inherit !important;
511
- }
512
- }
513
-
514
- // Esconde spinner nativo (Figma usa botões customizados)
515
- input[type="number"][matchaQuantityStepper] {
516
- -moz-appearance: textfield;
517
- appearance: textfield;
518
-
519
- &::-webkit-outer-spin-button,
520
- &::-webkit-inner-spin-button {
521
- -webkit-appearance: none;
522
- appearance: none;
523
- margin: 0;
524
- }
525
- }
526
-
527
- // Esconde botão × nativo do Chrome/Safari em input[type="search"]
528
- // (o matchaClearButton directive injeta nosso próprio — não duplicar)
529
- input[type="search"] {
530
- &::-webkit-search-cancel-button,
531
- &::-webkit-search-decoration,
532
- &::-webkit-search-results-button,
533
- &::-webkit-search-results-decoration {
534
- -webkit-appearance: none;
535
- appearance: none;
536
- display: none;
537
- }
538
- }
539
-
540
- // ── matchaClearButton: botão × ao lado de search/text ────────────────
541
- // Figma-consistent: 16/20/24 por size, cor text-placeholder
542
- .matcha-clear-button {
543
- flex: none;
544
- display: inline-flex;
545
- align-items: center;
546
- justify-content: center;
547
- width: 16px; height: 16px;
548
- padding: 0;
549
- // margin-left: auto empurra o × pro final do flex row, imune a irmãos
550
- // invisíveis (ng-content placeholders, matcha-autocomplete host que
551
- // acaba como sibling quando projetado via matcha-field, etc).
552
- margin: 0 0 0 auto;
553
- background: transparent;
554
- border: none;
555
- cursor: pointer;
556
- color: var(--matcha-color-text-placeholder);
557
- transition: color var(--matcha-duration-fast, 200ms) ease;
558
- position: relative;
559
-
560
- &:hover:not(:disabled) { color: var(--matcha-color-text-primary); }
561
-
562
- &:focus { outline: none; }
563
- &:focus-visible {
564
- outline: 2px solid var(--matcha-color-primary);
565
- outline-offset: 2px;
566
- border-radius: var(--matcha-radius-md, 4px);
567
- }
568
-
569
- > [class^="i-matcha-"],
570
- > [class*=" i-matcha-"] {
571
- font-size: 16px !important;
572
- width: 16px !important; height: 16px !important;
573
- line-height: 1 !important;
574
- color: inherit !important;
575
- }
576
- }
577
-
578
- // matchaCharCounter removido daqui — agora é irmão do .matcha-form-field
579
- // (vive no wrapper), estilizado fora deste bloco pra funcionar em qualquer size.
580
- // Nota: estilo precisa ser GLOBAL (este arquivo) porque o span é injetado via
581
- // Renderer2 sem _ngcontent — CSS scoped do Emulated não matcha.
582
-
583
- // ── Loading spinner no suffix do field ────────────────────────────────
584
- .matcha-form-field-spinner {
585
- flex: none;
586
- display: inline-block;
587
- width: 16px; height: 16px;
588
- border: 2px solid var(--matcha-color-surface-border);
589
- border-top-color: var(--matcha-color-primary);
590
- border-radius: 50%;
591
- animation: matcha-spin 800ms linear infinite;
592
-
593
- @media (prefers-reduced-motion: reduce) {
594
- animation-duration: 2000ms;
595
- }
596
- }
597
-
598
- // Select trigger ocupa altura E largura totais do form-field (clique abre
599
- // dropdown em qualquer ponto da caixa)
600
- matcha-select,
601
- .matcha-select-container,
602
- .matcha-select-trigger {
603
- height: 100%;
604
- }
605
-
606
- .matcha-select-container,
607
- .matcha-select-trigger {
608
- width: 100%;
609
- }
610
-
611
- // Suffix costuma agrupar mais de uma info (contador de caracteres + ícone de
612
- // status/erro) — 8px de respiro entre elas. Regra global (sem ::ng-deep): o
613
- // host do <matcha-suffix> tem class .matcha-form-field-suffix e vive dentro do
614
- // matcha-form-field. O display:inline-flex vem do component scss.
615
- .matcha-form-field-suffix {
616
- gap: var(--matcha-space-100, 8px);
617
- }
618
-
619
- // @deprecated `.matcha-suffix` (div avulsa) — use o componente <matcha-suffix>
620
- // (host `.matcha-form-field-suffix`), que já traz o gap de 8px entre múltiplas
621
- // infos. Mantido só por retrocompat; migrar consumidores e remover numa próxima major.
622
- .matcha-suffix {
623
- display: flex;
624
- flex-wrap: nowrap;
625
- align-items: center;
626
- gap: var(--matcha-space-100, 8px);
627
- }
628
- }
629
-
630
- // ─── Password toggle: tamanho do ícone por size do form-field ──────────────
631
- // (Figma matcha/field/password: SM 16 · MD 20 · LG 24)
632
- matcha-form-field[size="md"] .matcha-password-toggle-button,
633
- matcha-form-field[size="medium"] .matcha-password-toggle-button {
634
- width: 20px;
635
- height: 20px;
636
- font-size: 20px;
637
- }
638
-
639
- matcha-form-field[size="lg"] .matcha-password-toggle-button,
640
- matcha-form-field[size="large"] .matcha-password-toggle-button,
641
- matcha-form-field[size="huge"] .matcha-password-toggle-button {
642
- width: 24px;
643
- height: 24px;
644
- font-size: 24px;
645
- }
646
-
647
- // ── Spinner keyframes ────────────────────────────────────────────────────
648
- @keyframes matcha-spin {
649
- to { transform: rotate(360deg); }
650
- }
651
-
652
- // ── Readonly state ───────────────────────────────────────────────────────
653
- // Visual mais sutil que disabled: texto legível, selecionável, copiável.
654
- // Diferença vs disabled: cor normal do texto (não muted) + cursor `text` no input
655
- // (sinaliza "você pode selecionar e copiar"), enquanto disabled = not-allowed.
656
- matcha-form-field[readonly="true"] .matcha-form-field {
657
- background: var(--matcha-color-surface-bg);
658
-
659
- input, textarea {
660
- cursor: text; // nativo do input — reforça que texto é selecionável
661
- user-select: text; // explícito pra evitar reset acidental
662
- }
663
-
664
- // Remove hover border change (não é editável, hover não deve sugerir edição)
665
- &:hover { border-color: var(--matcha-color-surface-border); }
666
- }
667
-
668
- // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ─────────
669
- // Expande hit area sem mudar visual (pseudo-element overlay).
670
- @media (pointer: coarse) {
671
- .matcha-password-toggle-button,
672
- .matcha-quantity-stepper-button,
673
- .matcha-clear-button,
674
- matcha-date .matcha-date-picker-button,
675
- matcha-time .matcha-time-picker-button,
676
- matcha-field-range .matcha-field-range-button,
677
- matcha-phone .matcha-phone-country {
678
- position: relative;
679
-
680
- &::before {
681
- content: '';
682
- position: absolute;
683
- inset: -14px; // expande 14px em cada lado = 44x44 total (16 + 14*2)
684
- // invisível, mas capturavel — só expande o hit area
685
- }
686
- }
687
- }
688
-
689
- // ── Char counter (injetado via Renderer2 pelo matchaCharCounter directive) ──
690
- // Figma matcha/field/textarea: body-sm 12/16, text-secondary, align right.
691
- // Precisa ser global (sem ngcontent) porque o span vem de fora da view do form-field.
692
- // IN-40430 (painel): a tipografia vale pra QUALQUER host do counter — quando o span
693
- // caía fora do .matcha-form-field-wrapper (textarea/usos legados), ficava sem estilo
694
- // e herdava fonte maior que o próprio texto do campo.
695
- .matcha-char-counter {
696
- font-family: var(--matcha-font-family);
697
- font-size: var(--matcha-text-body-sm, 12px);
698
- font-weight: var(--matcha-weight-regular, 400);
699
- line-height: var(--matcha-leading-body-sm, 16px);
700
- color: var(--matcha-color-text-secondary);
701
- text-align: right;
702
- margin: 0;
703
- padding: 0;
704
- user-select: none;
705
- }
706
-
707
- .matcha-form-field-wrapper > .matcha-char-counter {
708
- align-self: flex-end;
709
- transition: color var(--matcha-duration-fast, 200ms) ease;
710
-
711
- // 3 níveis visuais (0-89% = neutro, 90-99% = warn, 100%+ = danger).
712
- // `is-near-limit` é aplicado a partir de 90%; `is-at-limit` sobrepõe a partir de 100%.
713
- &.is-near-limit:not(.is-at-limit) {
714
- color: var(--matcha-color-warn);
715
- }
716
-
717
- &.is-at-limit {
718
- color: var(--matcha-color-danger);
719
- font-weight: var(--matcha-weight-semibold, 600);
720
- }
721
- }
722
-
723
- // ── Caps Lock hint (injetado pelo matchaCapsLockHint em input[type="password"]) ──
724
- // Renderiza abaixo do field, mesmo nível do char counter. Usa cor warn (amarelo/laranja)
725
- // pra sinalizar risco sem ser erro (user pode estar digitando intencionalmente).
726
- .matcha-form-field-wrapper > .matcha-caps-lock-hint {
727
- display: inline-flex;
728
- align-items: center;
729
- gap: var(--matcha-space-050, 4px);
730
- align-self: flex-start;
731
- font-family: var(--matcha-font-family);
732
- font-size: var(--matcha-text-body-sm, 12px);
733
- font-weight: var(--matcha-weight-regular, 400);
734
- line-height: var(--matcha-leading-body-sm, 16px);
735
- color: var(--matcha-color-warn);
736
- margin-top: var(--matcha-space-050, 4px);
737
- user-select: none;
738
-
739
- > [class^="i-matcha-"] {
740
- font-size: 14px;
741
- line-height: 14px;
742
- }
743
- }
744
-
745
- // ── Responsive size classes (aligned with Figma SM/MD/LG) ───────────────────
746
-
747
- @mixin generate-matcha-form-field-size-classes($helper-breakpoints) {
748
- @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
749
- @include media-breakpoint($materialBreakpoint) {
750
- $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
751
-
752
- &[size#{$infix}="sm"],
753
- &[size#{$infix}="tiny"],
754
- &[size#{$infix}="small"] {
755
- .matcha-form-field input,
756
- .matcha-form-field textarea {
757
- font-size: var(--matcha-text-body-sm, 12px);
758
- line-height: var(--matcha-leading-body-sm, 16px);
759
- }
760
- }
761
-
762
- &[size#{$infix}="md"],
763
- &[size#{$infix}="medium"] {
764
- .matcha-form-field input,
765
- .matcha-form-field textarea {
766
- font-size: var(--matcha-font-size-sm, 14px);
767
- line-height: var(--matcha-leading-body-md, 20px);
768
- }
769
- }
770
-
771
- &[size#{$infix}="lg"],
772
- &[size#{$infix}="large"],
773
- &[size#{$infix}="huge"] {
774
- .matcha-form-field input,
775
- .matcha-form-field textarea {
776
- font-size: var(--matcha-font-size-md, 16px);
777
- line-height: var(--matcha-leading-body-lg, 24px);
778
- }
779
- }
780
- }
781
- }
782
- }
783
-
784
- // ── Theme mixin ─────────────────────────────────────────────────────────────
785
-
786
- @mixin matcha-form-field($theme) {
787
- .matcha-form-field {
788
- input[type="text"],
789
- input[type="email"],
790
- input[type="tel"],
791
- input[type="url"],
792
- input[type="password"],
793
- input[type="number"],
794
- input[type="search"],
795
- input[type="date"],
796
- input[type="time"],
797
- textarea {
798
- color: var(--matcha-color-text-primary);
799
-
800
- &::placeholder {
801
- color: var(--matcha-color-text-placeholder);
802
- }
803
- }
804
-
805
- input:disabled,
806
- textarea:disabled {
807
- color: var(--matcha-color-interaction-disabled-fg);
808
- }
809
-
810
- // Disabled: muted text, cursor not-allowed (sinalização inequívoca de "não pode interagir").
811
- // `cursor: not-allowed` vai no wrapper; `pointer-events: none` fica só no input/textarea
812
- // pra herdar o cursor do parent e não bloquear eventos no wrapper (senão cursor não aparece).
813
- &.matcha-form-field-disabled {
814
- cursor: not-allowed;
815
-
816
- input, textarea {
817
- pointer-events: none;
818
- color: var(--matcha-color-interaction-disabled-fg);
819
-
820
- &::placeholder {
821
- color: var(--matcha-color-interaction-disabled-fg);
822
- opacity: 0.7;
823
- }
824
- }
825
- }
826
- }
827
-
828
- matcha-form-field {
829
- @include generate-matcha-form-field-size-classes($helper-breakpoints);
830
- }
831
- }
832
-
833
- // ── Forced colors ──
834
- // Field precisa border + bg explícitos pra ser visível como input.
835
- @media (forced-colors: active) {
836
- matcha-form-field {
837
- .matcha-field-control,
838
- .matcha-form-field__control {
839
- border: 1px solid CanvasText !important;
840
- background: Canvas !important;
841
- color: CanvasText !important;
842
- }
843
-
844
- // Estado focused
845
- &.is-focused .matcha-field-control,
846
- &.is-focused .matcha-form-field__control,
847
- .matcha-field-control:focus-within,
848
- .matcha-form-field__control:focus-within {
849
- outline: 2px solid CanvasText !important;
850
- outline-offset: 2px;
851
- }
852
-
853
- // Estado erro/invalid
854
- &.is-invalid .matcha-field-control,
855
- &.has-error .matcha-form-field__control {
856
- border-color: CanvasText !important;
857
- border-width: 2px !important;
858
- }
859
-
860
- &[disabled],
861
- &.is-disabled {
862
- opacity: 0.5;
863
-
864
- .matcha-field-control,
865
- .matcha-form-field__control {
866
- border-color: GrayText !important;
867
- color: GrayText !important;
868
- }
869
- }
870
- }
871
- }
872
-
873
- // ── Print ──
874
- // Form fields preservam estado (form values). Sem fundo decorativo.
875
- @media print {
876
- matcha-form-field {
877
- .matcha-field-control,
878
- .matcha-form-field__control {
879
- border: 1px solid currentColor !important;
880
- background: transparent !important;
881
- }
882
- }
883
- }
1
+ @use "../abstracts/media-breakpoint" as *;
2
+ @use "../tokens/breakpoints-tokens" as *;
3
+ // =============================================================================
4
+ // matcha-form-field.scss — FONTE ÚNICA de estilo do matcha-form-field
5
+ // (estrutura, states, sizes, tipografia). O component SCSS é comment-only.
6
+ // =============================================================================
7
+
8
+ // ═══ Estrutura e states (migrado do matcha-form-field.component.scss) ═══════
9
+ // Histórico: estas regras viviam no SCSS do componente (ViewEncapsulation.
10
+ // Emulated). Seletor de host escrito como `matcha-form-field[...]` ganhava
11
+ // sufixo [_ngcontent-…] e virava CSS morto — caso do disabled, cuja opacity
12
+ // nunca aplicou. Global no tema, o que está escrito é o que vale; os antigos
13
+ // ::ng-deep viram seletores diretos.
14
+
15
+ // Wrapper stacked: Label → gap → Box → gap → Hint (Figma matcha/field/_base)
16
+ .matcha-form-field-wrapper {
17
+ display: flex;
18
+ flex-direction: column;
19
+ align-items: flex-start;
20
+ gap: var(--matcha-space-075, 6px); // Figma matcha/field/text: gap 6px entre label/box/hint
21
+ width: 100%;
22
+ }
23
+
24
+ // Box do input — onde fica o input/select/textarea
25
+ // Figma: align-self: stretch → ocupa toda a largura do wrapper
26
+ .matcha-form-field {
27
+ display: flex;
28
+ flex-direction: row;
29
+ align-items: center;
30
+ gap: var(--matcha-space-100, 8px);
31
+ width: 100%; // stretch pro wrapper
32
+ min-height: 44px; // MD default
33
+ padding: 0 var(--matcha-space-150, 12px); // pad horizontal 12
34
+ border: var(--matcha-border-thin, 2px) solid var(--matcha-color-surface-border, #E1E7E2);
35
+ border-radius: var(--matcha-radius-lg, 8px);
36
+ background: transparent;
37
+ box-sizing: border-box;
38
+ transition: border-color var(--matcha-duration-fast, 200ms);
39
+ }
40
+
41
+ // Slots prefix/suffix — elementos decorativos antes/depois do input
42
+ .matcha-form-field .matcha-form-field-prefix,
43
+ .matcha-form-field .matcha-form-field-suffix {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ flex: none;
47
+ color: var(--matcha-color-text-label);
48
+ font-family: var(--matcha-font-family);
49
+ font-size: var(--matcha-font-size-sm, 14px);
50
+ font-weight: var(--matcha-weight-regular, 400);
51
+ line-height: var(--matcha-leading-body-md, 20px);
52
+ user-select: none;
53
+ }
54
+
55
+ .matcha-form-field .matcha-form-field-prefix:not(:empty) + * {
56
+ padding-left: 0;
57
+ }
58
+
59
+ // Hover / focus-within (Figma matcha/field/_base State=Focus).
60
+ // Disabled não chega aqui (pointer-events: none nas regras de disabled abaixo);
61
+ // readonly neutraliza o hover na regra própria de readonly; error e data-color
62
+ // vencem por especificidade/ordem.
63
+ .matcha-form-field:hover {
64
+ border-color: var(--matcha-color-surface-border-strong);
65
+ }
66
+
67
+ .matcha-form-field:focus-within {
68
+ border-color: var(--matcha-color-primary, #1664C2);
69
+ }
70
+
71
+ // Color variants via data-color — override border-color
72
+ .matcha-form-field[data-color="accent"] { border-color: var(--matcha-color-accent); }
73
+ .matcha-form-field[data-color="primary"] { border-color: var(--matcha-color-primary); }
74
+ .matcha-form-field[data-color="danger"] { border-color: var(--matcha-color-danger); }
75
+ .matcha-form-field[data-color="warn"] { border-color: var(--matcha-color-warn); }
76
+ .matcha-form-field[data-color="success"] { border-color: var(--matcha-color-success); }
77
+ .matcha-form-field[data-color="info"] { border-color: var(--matcha-color-info); }
78
+
79
+ // Sizes (Figma heights): SM 36, MD 44, LG 52.
80
+ // Alturas não são responsivas — a tipografia responsiva fica no mixin abaixo.
81
+ matcha-form-field[size="sm"] .matcha-form-field,
82
+ matcha-form-field[size="tiny"] .matcha-form-field,
83
+ matcha-form-field[size="small"] .matcha-form-field {
84
+ min-height: 36px;
85
+ padding: 0 var(--matcha-space-150, 12px);
86
+ }
87
+ matcha-form-field[size="md"] .matcha-form-field,
88
+ matcha-form-field[size="medium"] .matcha-form-field {
89
+ min-height: 44px;
90
+ padding: 0 var(--matcha-space-150, 12px);
91
+ }
92
+ matcha-form-field[size="lg"] .matcha-form-field,
93
+ matcha-form-field[size="large"] .matcha-form-field,
94
+ matcha-form-field[size="huge"] .matcha-form-field {
95
+ min-height: 52px;
96
+ padding: 0 var(--matcha-space-200, 16px);
97
+ }
98
+
99
+ // Disabled — Figma aplica opacity 0.38 no WRAPPER inteiro
100
+ // (label, field-box e hint ficam apagados de uma vez).
101
+ // EXCEÇÃO: tooltip do label preserva opacity=1 e pointer-events pra manter a
102
+ // pergunta "por que desabilitado?" respondível (UX crítico pra forms complexos).
103
+ // Sem border-color especial: o token interaction-disabled-border tem MAIS
104
+ // contraste que a borda normal e anulava o esmaecimento — a borda fica a normal
105
+ // e a opacity apaga o conjunto uniformemente.
106
+ matcha-form-field[disabled]:not([disabled="false"]) {
107
+ opacity: var(--matcha-opacity-disabled, 0.38);
108
+
109
+ .matcha-form-field {
110
+ pointer-events: none;
111
+ cursor: default;
112
+ }
113
+
114
+ // Tooltip icon continua interativo e legível mesmo com wrapper disabled
115
+ .matcha-label-tooltip {
116
+ opacity: calc(1 / var(--matcha-opacity-disabled, 0.38));
117
+ pointer-events: auto;
118
+ }
119
+ }
120
+
121
+ // Fallback legado (classe aplicada via JS quando o input interno tem disabled);
122
+ // cursor/cores de texto dessa classe vivem no theme mixin mais abaixo.
123
+ .matcha-form-field-disabled {
124
+ pointer-events: none;
125
+ }
126
+
127
+ // Error state (Figma: border danger) — via @Input() error ou data-color="danger"
128
+ matcha-form-field[error]:not([error="false"]) .matcha-form-field {
129
+ border-color: var(--matcha-color-danger);
130
+ }
131
+
132
+ // ─── Simulação de states em docs/playgrounds ────────────────────────────
133
+ matcha-form-field.is-state-focus .matcha-form-field {
134
+ border-color: var(--matcha-color-primary) !important;
135
+ }
136
+
137
+ matcha-form-field.is-state-disabled {
138
+ opacity: var(--matcha-opacity-disabled, 0.38);
139
+ pointer-events: none;
140
+ }
141
+
142
+ // ─── Field-range override (Figma matcha/field/range) ────────────────────
143
+ // Field-range renderiza DUAS caixas bordadas próprias (start/end com separador).
144
+ // O form-field externo NÃO deve desenhar sua box — apenas empilha label + range + hint.
145
+ .matcha-form-field:has(matcha-field-range) {
146
+ border: none;
147
+ background: transparent;
148
+ padding: 0;
149
+ min-height: 0;
150
+ gap: 0;
151
+ }
152
+
153
+ matcha-form-field[size="sm"] .matcha-form-field:has(matcha-field-range),
154
+ matcha-form-field[size="tiny"] .matcha-form-field:has(matcha-field-range),
155
+ matcha-form-field[size="small"] .matcha-form-field:has(matcha-field-range),
156
+ matcha-form-field[size="md"] .matcha-form-field:has(matcha-field-range),
157
+ matcha-form-field[size="medium"] .matcha-form-field:has(matcha-field-range),
158
+ matcha-form-field[size="lg"] .matcha-form-field:has(matcha-field-range),
159
+ matcha-form-field[size="large"] .matcha-form-field:has(matcha-field-range),
160
+ matcha-form-field[size="huge"] .matcha-form-field:has(matcha-field-range) {
161
+ min-height: 0;
162
+ padding: 0;
163
+ border: none;
164
+ }
165
+
166
+ // Erro: não mexe na borda do wrapper (já é none), a cor do erro vai para as halves
167
+ matcha-form-field[error]:not([error="false"]) .matcha-form-field:has(matcha-field-range) {
168
+ border: none;
169
+ }
170
+
171
+ // ─── Textarea override (Figma matcha/field/textarea) ────────────────────
172
+ // Quando o field contém textarea, o wrapper vira column + padding all-sides
173
+ // Heights por size: SM 80px · MD 100px · LG 120px
174
+ .matcha-form-field:has(textarea) {
175
+ flex-direction: column;
176
+ align-items: flex-start;
177
+ padding: var(--matcha-space-150, 12px);
178
+ min-height: 80px; // fallback SM
179
+ }
180
+
181
+ matcha-form-field[size="sm"] .matcha-form-field:has(textarea),
182
+ matcha-form-field[size="tiny"] .matcha-form-field:has(textarea),
183
+ matcha-form-field[size="small"] .matcha-form-field:has(textarea) {
184
+ min-height: 80px;
185
+ padding: var(--matcha-space-150, 12px);
186
+ }
187
+ matcha-form-field[size="md"] .matcha-form-field:has(textarea),
188
+ matcha-form-field[size="medium"] .matcha-form-field:has(textarea) {
189
+ min-height: 100px;
190
+ padding: var(--matcha-space-150, 12px);
191
+ }
192
+ matcha-form-field[size="lg"] .matcha-form-field:has(textarea),
193
+ matcha-form-field[size="large"] .matcha-form-field:has(textarea),
194
+ matcha-form-field[size="huge"] .matcha-form-field:has(textarea) {
195
+ min-height: 120px;
196
+ padding: var(--matcha-space-150, 12px) var(--matcha-space-200, 16px);
197
+ }
198
+
199
+ // ─── Search: ícone lupa automático após o input[type=search] (Figma suffix icon) ──
200
+ // Mesmo padding horizontal dos outros fields. Por size: SM 16 · MD 20 · LG 24.
201
+ .matcha-form-field:has(input[type="search"])::after {
202
+ content: '\e90c'; // codepoint "search" em MatchaIcons
203
+ font-family: 'MatchaIcons' !important; // fonte registrada no projects/matcha-icons/matcha-icons.css
204
+ font-weight: normal;
205
+ font-style: normal;
206
+ font-variant: normal;
207
+ speak: none;
208
+ text-transform: none;
209
+ -webkit-font-smoothing: antialiased;
210
+ color: var(--matcha-color-text-placeholder);
211
+ font-size: 16px;
212
+ line-height: 16px;
213
+ flex: none;
214
+ pointer-events: none;
215
+ display: inline-flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ }
219
+ matcha-form-field[size="md"] .matcha-form-field:has(input[type="search"])::after,
220
+ matcha-form-field[size="medium"] .matcha-form-field:has(input[type="search"])::after {
221
+ font-size: 20px;
222
+ line-height: 20px;
223
+ }
224
+ matcha-form-field[size="lg"] .matcha-form-field:has(input[type="search"])::after,
225
+ matcha-form-field[size="large"] .matcha-form-field:has(input[type="search"])::after,
226
+ matcha-form-field[size="huge"] .matcha-form-field:has(input[type="search"])::after {
227
+ font-size: 24px;
228
+ line-height: 24px;
229
+ }
230
+
231
+ // Hierarquia de trailing icons: loading > clear × > suffix default.
232
+ // Cada nível oculta os abaixo pra não empilhar ícones redundantes.
233
+
234
+ // Loading: spinner assume o suffix; oculta lupa + × juntos.
235
+ .matcha-form-field:has(input[type="search"]):has(.matcha-form-field-spinner)::after,
236
+ .matcha-form-field:has(.matcha-form-field-spinner) .matcha-clear-button {
237
+ display: none !important;
238
+ }
239
+
240
+ // Clear visível (input com valor): × toma o lugar da lupa default.
241
+ .matcha-form-field:has(input[type="search"]):has(.matcha-clear-button--visible)::after {
242
+ display: none;
243
+ }
244
+
245
+ // ── Structural (inputs) — sem border, sem bg (box wraps) ────────────────────
246
+
247
+ // ─── Filhos projetáveis do form-field ────────────────────────────────────
248
+ // Todos os tipos de input que podem ser projetados DEVEM preencher o espaço
249
+ // disponível e herdar padding/border/bg do wrapper externo (.matcha-form-field).
250
+ // O componente filho NÃO deve ter padding/border próprio quando dentro do field.
251
+ .matcha-form-field {
252
+ input,
253
+ input[type="text"],
254
+ input[type="email"],
255
+ input[type="tel"],
256
+ input[type="url"],
257
+ input[type="password"],
258
+ input[type="number"],
259
+ input[type="search"],
260
+ input[type="date"],
261
+ input[type="time"],
262
+ textarea {
263
+ flex: 1 1 auto;
264
+ width: 100%;
265
+ min-width: 0;
266
+ min-height: 0; // evita que altura natural do input expanda o container
267
+ background-color: transparent;
268
+ outline: none;
269
+ box-shadow: none;
270
+ border: none;
271
+ padding: 0; // wrapper fornece padding horizontal
272
+ margin: 0;
273
+ font-family: var(--matcha-font-family);
274
+ font-size: var(--matcha-font-size-sm, 14px); // Figma MD placeholder/value 14/400/20
275
+ font-weight: var(--matcha-weight-regular, 400);
276
+ line-height: var(--matcha-leading-body-md, 20px);
277
+ color: var(--matcha-color-text-primary); // Figma filled value: #E8EAED dark / #0E1116 light
278
+ resize: none;
279
+
280
+ // Figma: text-placeholder (#6E747A dark / #9AA0A6 light); opacity 1
281
+ // porque o Firefox aplica opacity < 1 no ::placeholder por default
282
+ &::placeholder {
283
+ color: var(--matcha-color-text-placeholder);
284
+ opacity: 1;
285
+ }
286
+
287
+ &:disabled {
288
+ color: var(--matcha-color-interaction-disabled-fg);
289
+ -webkit-text-fill-color: var(--matcha-color-interaction-disabled-fg); // Safari override
290
+ }
291
+ }
292
+
293
+ // Textarea ocupa TODA a altura disponível do wrapper (que é column mode).
294
+ // Quando `matchaAutoGrow` está ligado, a directive aplica `.matcha-autogrow`
295
+ // e controla altura via JS — o override abaixo solta o 100% forçado.
296
+ textarea {
297
+ height: 100%;
298
+ min-height: 100%;
299
+ align-self: stretch;
300
+
301
+ &.matcha-autogrow {
302
+ height: auto; // directive calcula e seta inline
303
+ min-height: 0; // flex: 1 já garante piso via wrapper min-height
304
+ }
305
+ }
306
+
307
+ // ── Scrollbar estilizada no textarea (Webkit + Firefox) ──────────────────
308
+ // Usa tokens do DS em vez do scrollbar nativo cinza feio do Chrome/Windows.
309
+ // Aplica tanto no textarea "rows fixo" (se user passar rows pequeno + muito texto)
310
+ // quanto no `.matcha-autogrow` depois de atingir `maxRows`.
311
+ textarea {
312
+ scrollbar-width: thin; // Firefox
313
+ scrollbar-color: var(--matcha-color-text-placeholder)
314
+ transparent; // thumb + track
315
+
316
+ &::-webkit-scrollbar { width: 6px; height: 6px; }
317
+ &::-webkit-scrollbar-track { background: transparent; }
318
+ &::-webkit-scrollbar-thumb {
319
+ background: var(--matcha-color-text-placeholder);
320
+ border-radius: var(--matcha-radius-pill, 999px);
321
+ }
322
+ &::-webkit-scrollbar-thumb:hover { background: var(--matcha-color-text-secondary); }
323
+ }
324
+
325
+ input:focus,
326
+ textarea:focus {
327
+ outline: none;
328
+ }
329
+
330
+ // Truncate: quando o campo é menor que placeholder/value, corta com ellipsis
331
+ // Evita overflow feio em sidebars, colunas estreitas, responsive mobile.
332
+ input {
333
+ text-overflow: ellipsis;
334
+ overflow: hidden;
335
+ white-space: nowrap;
336
+
337
+ // Placeholder também trunca (Chrome/Safari precisam do ::placeholder explícito)
338
+ &::placeholder {
339
+ text-overflow: ellipsis;
340
+ overflow: hidden;
341
+ white-space: nowrap;
342
+ }
343
+ }
344
+
345
+ // ── Autofill (Chrome/Safari) — mata o azul/amarelo nativo e usa os tokens ──
346
+ // Browsers pintam autofill com um background-color INTERNO que ignora
347
+ // `background-color` normal via CSS (prioridade de UA style). O workaround
348
+ // padrão cobre esse fundo com um box-shadow inset opaco do tamanho da caixa —
349
+ // e por isso a cor do box-shadow tem que ser OPACA e igual ao fundo real por
350
+ // trás do input, não `transparent` (bug anterior: transparent não cobre nada,
351
+ // o azul nativo do Chrome continuava visível por baixo -- ilegível no tema
352
+ // escuro, onde o contraste com o texto claro inverte). O input em si é
353
+ // `background-color: transparent` (linha ~33): o que aparece por trás é o
354
+ // matcha-card que envolve o form-field na esmagadora maioria dos usos deste
355
+ // painel -- por isso surface-card, não surface-bg (fundo da PÁGINA, um tom
356
+ // mais escuro/mais claro que o card e que deixaria um contorno visível).
357
+ // `transition: ... 5000s` é o "truque dos 5000 segundos" — impede o browser de
358
+ // reaplicar o bg nativo antes do box-shadow cobrir.
359
+ input:-webkit-autofill,
360
+ input:-webkit-autofill:hover,
361
+ input:-webkit-autofill:focus,
362
+ input:-webkit-autofill:active,
363
+ textarea:-webkit-autofill {
364
+ -webkit-box-shadow: 0 0 0 1000px var(--matcha-color-surface-card) inset !important;
365
+ -webkit-text-fill-color: var(--matcha-color-text-primary) !important;
366
+ caret-color: var(--matcha-color-primary) !important;
367
+ transition: background-color 5000s ease-in-out 0s, color 5000s ease-in-out 0s;
368
+ }
369
+
370
+ // Caret color (cursor piscando) = primary pra match com focus border
371
+ input,
372
+ textarea {
373
+ caret-color: var(--matcha-color-primary);
374
+ }
375
+
376
+ // Selection highlight (quando user seleciona texto) — mesmo par accent do
377
+ // ::selection global (base/_reset.scss). Era primary-alpha aqui e azul do SO no
378
+ // resto da app: duas cores de seleção diferentes na mesma tela.
379
+ input::selection,
380
+ textarea::selection {
381
+ background-color: var(--matcha-color-accent);
382
+ color: var(--matcha-color-accent-text-on);
383
+ }
384
+
385
+ // Componentes projetados — todos preenchem a largura e não adicionam borda/padding próprio
386
+ matcha-select,
387
+ matcha-autocomplete,
388
+ matcha-date,
389
+ matcha-field-range,
390
+ matcha-time,
391
+ matcha-phone {
392
+ flex: 1 1 auto;
393
+ width: 100%;
394
+ min-width: 0;
395
+ display: flex;
396
+ align-items: center;
397
+ background: transparent;
398
+ border: none;
399
+ padding: 0;
400
+ }
401
+
402
+ // matcha-phone: padding/bg próprios do componente quebram o layout dentro do field
403
+ matcha-phone .matcha-phone {
404
+ padding: 0;
405
+ background: transparent;
406
+ font-size: inherit;
407
+ width: 100%;
408
+ }
409
+
410
+ // matcha-date/time: container do componente preenche o espaço sem padding/border próprios
411
+ matcha-date .matcha-date-container,
412
+ matcha-time .matcha-time-container {
413
+ width: 100%;
414
+ background: transparent;
415
+ border: none;
416
+ padding: 0;
417
+ min-width: 0;
418
+ gap: var(--matcha-space-100, 8px);
419
+ }
420
+
421
+ // ── matchaPasswordToggle: botão do olho injetado ao lado de input[type="password"] ──
422
+ // Figma matcha/field/password: ícone 16/20/24 por size, cor text-placeholder (#9AA0A6)
423
+ .matcha-password-toggle-button {
424
+ flex: none;
425
+ display: inline-flex;
426
+ align-items: center;
427
+ justify-content: center;
428
+ width: 16px; // SM default
429
+ height: 16px;
430
+ padding: 0;
431
+ margin: 0;
432
+ background: transparent;
433
+ border: none;
434
+ cursor: pointer;
435
+ color: var(--matcha-color-text-placeholder); // #9AA0A6
436
+ transition: color var(--matcha-duration-fast, 200ms) ease;
437
+
438
+ &:hover:not(:disabled) {
439
+ color: var(--matcha-color-text-primary);
440
+ }
441
+
442
+ &:disabled {
443
+ cursor: not-allowed;
444
+ color: var(--matcha-color-interaction-disabled-fg);
445
+ }
446
+
447
+ &:focus { outline: none; }
448
+ &:focus-visible {
449
+ outline: 2px solid var(--matcha-color-primary);
450
+ outline-offset: 2px;
451
+ border-radius: var(--matcha-radius-md, 4px);
452
+ }
453
+
454
+ > [class^="i-matcha-"],
455
+ > [class*=" i-matcha-"] {
456
+ font-size: inherit !important;
457
+ width: 1em !important;
458
+ height: 1em !important;
459
+ line-height: 1 !important;
460
+ color: inherit !important;
461
+ }
462
+ }
463
+
464
+ // ── matchaQuantityStepper: wrapper flex-row + dois botões (−/+) ──────
465
+ // Figma matcha/field/quantity: gap 4, ícones 16px constante, cor #9AA0A6
466
+ .matcha-quantity-stepper {
467
+ flex: none;
468
+ display: inline-flex;
469
+ align-items: center;
470
+ gap: var(--matcha-space-050, 4px);
471
+ }
472
+
473
+ .matcha-quantity-stepper-button {
474
+ flex: none;
475
+ display: inline-flex;
476
+ align-items: center;
477
+ justify-content: center;
478
+ width: 16px; // Figma: constante 16px em todos os sizes
479
+ height: 16px;
480
+ padding: 0;
481
+ margin: 0;
482
+ background: transparent;
483
+ border: none;
484
+ cursor: pointer;
485
+ color: var(--matcha-color-text-placeholder); // #9AA0A6
486
+ transition: color var(--matcha-duration-fast, 200ms) ease;
487
+
488
+ &:hover:not(:disabled) {
489
+ color: var(--matcha-color-text-primary);
490
+ }
491
+
492
+ &:disabled {
493
+ cursor: not-allowed;
494
+ color: var(--matcha-color-interaction-disabled-fg);
495
+ }
496
+
497
+ &:focus { outline: none; }
498
+ &:focus-visible {
499
+ outline: 2px solid var(--matcha-color-primary);
500
+ outline-offset: 2px;
501
+ border-radius: var(--matcha-radius-md, 4px);
502
+ }
503
+
504
+ > [class^="i-matcha-"],
505
+ > [class*=" i-matcha-"] {
506
+ font-size: 16px !important;
507
+ width: 16px !important;
508
+ height: 16px !important;
509
+ line-height: 1 !important;
510
+ color: inherit !important;
511
+ }
512
+ }
513
+
514
+ // Esconde spinner nativo (Figma usa botões customizados)
515
+ input[type="number"][matchaQuantityStepper] {
516
+ -moz-appearance: textfield;
517
+ appearance: textfield;
518
+
519
+ &::-webkit-outer-spin-button,
520
+ &::-webkit-inner-spin-button {
521
+ -webkit-appearance: none;
522
+ appearance: none;
523
+ margin: 0;
524
+ }
525
+ }
526
+
527
+ // Esconde botão × nativo do Chrome/Safari em input[type="search"]
528
+ // (o matchaClearButton directive injeta nosso próprio — não duplicar)
529
+ input[type="search"] {
530
+ &::-webkit-search-cancel-button,
531
+ &::-webkit-search-decoration,
532
+ &::-webkit-search-results-button,
533
+ &::-webkit-search-results-decoration {
534
+ -webkit-appearance: none;
535
+ appearance: none;
536
+ display: none;
537
+ }
538
+ }
539
+
540
+ // ── matchaClearButton: botão × ao lado de search/text ────────────────
541
+ // Figma-consistent: 16/20/24 por size, cor text-placeholder
542
+ .matcha-clear-button {
543
+ flex: none;
544
+ display: inline-flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ width: 16px; height: 16px;
548
+ padding: 0;
549
+ // margin-left: auto empurra o × pro final do flex row, imune a irmãos
550
+ // invisíveis (ng-content placeholders, matcha-autocomplete host que
551
+ // acaba como sibling quando projetado via matcha-field, etc).
552
+ margin: 0 0 0 auto;
553
+ background: transparent;
554
+ border: none;
555
+ cursor: pointer;
556
+ color: var(--matcha-color-text-placeholder);
557
+ transition: color var(--matcha-duration-fast, 200ms) ease;
558
+ position: relative;
559
+
560
+ &:hover:not(:disabled) { color: var(--matcha-color-text-primary); }
561
+
562
+ &:focus { outline: none; }
563
+ &:focus-visible {
564
+ outline: 2px solid var(--matcha-color-primary);
565
+ outline-offset: 2px;
566
+ border-radius: var(--matcha-radius-md, 4px);
567
+ }
568
+
569
+ > [class^="i-matcha-"],
570
+ > [class*=" i-matcha-"] {
571
+ font-size: 16px !important;
572
+ width: 16px !important; height: 16px !important;
573
+ line-height: 1 !important;
574
+ color: inherit !important;
575
+ }
576
+ }
577
+
578
+ // matchaCharCounter removido daqui — agora é irmão do .matcha-form-field
579
+ // (vive no wrapper), estilizado fora deste bloco pra funcionar em qualquer size.
580
+ // Nota: estilo precisa ser GLOBAL (este arquivo) porque o span é injetado via
581
+ // Renderer2 sem _ngcontent — CSS scoped do Emulated não matcha.
582
+
583
+ // ── Loading spinner no suffix do field ────────────────────────────────
584
+ .matcha-form-field-spinner {
585
+ flex: none;
586
+ display: inline-block;
587
+ width: 16px; height: 16px;
588
+ border: 2px solid var(--matcha-color-surface-border);
589
+ border-top-color: var(--matcha-color-primary);
590
+ border-radius: 50%;
591
+ animation: matcha-spin 800ms linear infinite;
592
+
593
+ @media (prefers-reduced-motion: reduce) {
594
+ animation-duration: 2000ms;
595
+ }
596
+ }
597
+
598
+ // Select trigger ocupa altura E largura totais do form-field (clique abre
599
+ // dropdown em qualquer ponto da caixa)
600
+ matcha-select,
601
+ .matcha-select-container,
602
+ .matcha-select-trigger {
603
+ height: 100%;
604
+ }
605
+
606
+ .matcha-select-container,
607
+ .matcha-select-trigger {
608
+ width: 100%;
609
+ }
610
+
611
+ // Suffix costuma agrupar mais de uma info (contador de caracteres + ícone de
612
+ // status/erro) — 8px de respiro entre elas. Regra global (sem ::ng-deep): o
613
+ // host do <matcha-suffix> tem class .matcha-form-field-suffix e vive dentro do
614
+ // matcha-form-field. O display:inline-flex vem do component scss.
615
+ .matcha-form-field-suffix {
616
+ gap: var(--matcha-space-100, 8px);
617
+ }
618
+
619
+ // @deprecated `.matcha-suffix` (div avulsa) — use o componente <matcha-suffix>
620
+ // (host `.matcha-form-field-suffix`), que já traz o gap de 8px entre múltiplas
621
+ // infos. Mantido só por retrocompat; migrar consumidores e remover numa próxima major.
622
+ .matcha-suffix {
623
+ display: flex;
624
+ flex-wrap: nowrap;
625
+ align-items: center;
626
+ gap: var(--matcha-space-100, 8px);
627
+ }
628
+ }
629
+
630
+ // ─── Password toggle: tamanho do ícone por size do form-field ──────────────
631
+ // (Figma matcha/field/password: SM 16 · MD 20 · LG 24)
632
+ matcha-form-field[size="md"] .matcha-password-toggle-button,
633
+ matcha-form-field[size="medium"] .matcha-password-toggle-button {
634
+ width: 20px;
635
+ height: 20px;
636
+ font-size: 20px;
637
+ }
638
+
639
+ matcha-form-field[size="lg"] .matcha-password-toggle-button,
640
+ matcha-form-field[size="large"] .matcha-password-toggle-button,
641
+ matcha-form-field[size="huge"] .matcha-password-toggle-button {
642
+ width: 24px;
643
+ height: 24px;
644
+ font-size: 24px;
645
+ }
646
+
647
+ // ── Spinner keyframes ────────────────────────────────────────────────────
648
+ @keyframes matcha-spin {
649
+ to { transform: rotate(360deg); }
650
+ }
651
+
652
+ // ── Readonly state ───────────────────────────────────────────────────────
653
+ // Visual mais sutil que disabled: texto legível, selecionável, copiável.
654
+ // Diferença vs disabled: cor normal do texto (não muted) + cursor `text` no input
655
+ // (sinaliza "você pode selecionar e copiar"), enquanto disabled = not-allowed.
656
+ matcha-form-field[readonly="true"] .matcha-form-field {
657
+ background: var(--matcha-color-surface-bg);
658
+
659
+ input, textarea {
660
+ cursor: text; // nativo do input — reforça que texto é selecionável
661
+ user-select: text; // explícito pra evitar reset acidental
662
+ }
663
+
664
+ // Remove hover border change (não é editável, hover não deve sugerir edição)
665
+ &:hover { border-color: var(--matcha-color-surface-border); }
666
+ }
667
+
668
+ // ── Touch targets (WCAG 2.5.5: 44x44 mínimo em coarse pointers) ─────────
669
+ // Expande hit area sem mudar visual (pseudo-element overlay).
670
+ @media (pointer: coarse) {
671
+ .matcha-password-toggle-button,
672
+ .matcha-quantity-stepper-button,
673
+ .matcha-clear-button,
674
+ matcha-date .matcha-date-picker-button,
675
+ matcha-time .matcha-time-picker-button,
676
+ matcha-field-range .matcha-field-range-button,
677
+ matcha-phone .matcha-phone-country {
678
+ position: relative;
679
+
680
+ &::before {
681
+ content: '';
682
+ position: absolute;
683
+ inset: -14px; // expande 14px em cada lado = 44x44 total (16 + 14*2)
684
+ // invisível, mas capturavel — só expande o hit area
685
+ }
686
+ }
687
+ }
688
+
689
+ // ── Char counter (injetado via Renderer2 pelo matchaCharCounter directive) ──
690
+ // Figma matcha/field/textarea: body-sm 12/16, text-secondary, align right.
691
+ // Precisa ser global (sem ngcontent) porque o span vem de fora da view do form-field.
692
+ // IN-40430 (painel): a tipografia vale pra QUALQUER host do counter — quando o span
693
+ // caía fora do .matcha-form-field-wrapper (textarea/usos legados), ficava sem estilo
694
+ // e herdava fonte maior que o próprio texto do campo.
695
+ .matcha-char-counter {
696
+ font-family: var(--matcha-font-family);
697
+ font-size: var(--matcha-text-body-sm, 12px);
698
+ font-weight: var(--matcha-weight-regular, 400);
699
+ line-height: var(--matcha-leading-body-sm, 16px);
700
+ color: var(--matcha-color-text-secondary);
701
+ text-align: right;
702
+ margin: 0;
703
+ padding: 0;
704
+ user-select: none;
705
+ }
706
+
707
+ .matcha-form-field-wrapper > .matcha-char-counter {
708
+ align-self: flex-end;
709
+ transition: color var(--matcha-duration-fast, 200ms) ease;
710
+
711
+ // 3 níveis visuais (0-89% = neutro, 90-99% = warn, 100%+ = danger).
712
+ // `is-near-limit` é aplicado a partir de 90%; `is-at-limit` sobrepõe a partir de 100%.
713
+ &.is-near-limit:not(.is-at-limit) {
714
+ color: var(--matcha-color-warn);
715
+ }
716
+
717
+ &.is-at-limit {
718
+ color: var(--matcha-color-danger);
719
+ font-weight: var(--matcha-weight-semibold, 600);
720
+ }
721
+ }
722
+
723
+ // ── Caps Lock hint (injetado pelo matchaCapsLockHint em input[type="password"]) ──
724
+ // Renderiza abaixo do field, mesmo nível do char counter. Usa cor warn (amarelo/laranja)
725
+ // pra sinalizar risco sem ser erro (user pode estar digitando intencionalmente).
726
+ .matcha-form-field-wrapper > .matcha-caps-lock-hint {
727
+ display: inline-flex;
728
+ align-items: center;
729
+ gap: var(--matcha-space-050, 4px);
730
+ align-self: flex-start;
731
+ font-family: var(--matcha-font-family);
732
+ font-size: var(--matcha-text-body-sm, 12px);
733
+ font-weight: var(--matcha-weight-regular, 400);
734
+ line-height: var(--matcha-leading-body-sm, 16px);
735
+ color: var(--matcha-color-warn);
736
+ margin-top: var(--matcha-space-050, 4px);
737
+ user-select: none;
738
+
739
+ > [class^="i-matcha-"] {
740
+ font-size: 14px;
741
+ line-height: 14px;
742
+ }
743
+ }
744
+
745
+ // ── Responsive size classes (aligned with Figma SM/MD/LG) ───────────────────
746
+
747
+ @mixin generate-matcha-form-field-size-classes($helper-breakpoints) {
748
+ @each $breakpoint, $materialBreakpoint in $helper-breakpoints {
749
+ @include media-breakpoint($materialBreakpoint) {
750
+ $infix: if($materialBreakpoint == null, "", "-#{$breakpoint}");
751
+
752
+ &[size#{$infix}="sm"],
753
+ &[size#{$infix}="tiny"],
754
+ &[size#{$infix}="small"] {
755
+ .matcha-form-field input,
756
+ .matcha-form-field textarea {
757
+ font-size: var(--matcha-text-body-sm, 12px);
758
+ line-height: var(--matcha-leading-body-sm, 16px);
759
+ }
760
+ }
761
+
762
+ &[size#{$infix}="md"],
763
+ &[size#{$infix}="medium"] {
764
+ .matcha-form-field input,
765
+ .matcha-form-field textarea {
766
+ font-size: var(--matcha-font-size-sm, 14px);
767
+ line-height: var(--matcha-leading-body-md, 20px);
768
+ }
769
+ }
770
+
771
+ &[size#{$infix}="lg"],
772
+ &[size#{$infix}="large"],
773
+ &[size#{$infix}="huge"] {
774
+ .matcha-form-field input,
775
+ .matcha-form-field textarea {
776
+ font-size: var(--matcha-font-size-md, 16px);
777
+ line-height: var(--matcha-leading-body-lg, 24px);
778
+ }
779
+ }
780
+ }
781
+ }
782
+ }
783
+
784
+ // ── Theme mixin ─────────────────────────────────────────────────────────────
785
+
786
+ @mixin matcha-form-field($theme) {
787
+ .matcha-form-field {
788
+ input[type="text"],
789
+ input[type="email"],
790
+ input[type="tel"],
791
+ input[type="url"],
792
+ input[type="password"],
793
+ input[type="number"],
794
+ input[type="search"],
795
+ input[type="date"],
796
+ input[type="time"],
797
+ textarea {
798
+ color: var(--matcha-color-text-primary);
799
+
800
+ &::placeholder {
801
+ color: var(--matcha-color-text-placeholder);
802
+ }
803
+ }
804
+
805
+ input:disabled,
806
+ textarea:disabled {
807
+ color: var(--matcha-color-interaction-disabled-fg);
808
+ }
809
+
810
+ // Disabled: muted text, cursor not-allowed (sinalização inequívoca de "não pode interagir").
811
+ // `cursor: not-allowed` vai no wrapper; `pointer-events: none` fica só no input/textarea
812
+ // pra herdar o cursor do parent e não bloquear eventos no wrapper (senão cursor não aparece).
813
+ &.matcha-form-field-disabled {
814
+ cursor: not-allowed;
815
+
816
+ input, textarea {
817
+ pointer-events: none;
818
+ color: var(--matcha-color-interaction-disabled-fg);
819
+
820
+ &::placeholder {
821
+ color: var(--matcha-color-interaction-disabled-fg);
822
+ opacity: 0.7;
823
+ }
824
+ }
825
+ }
826
+ }
827
+
828
+ matcha-form-field {
829
+ @include generate-matcha-form-field-size-classes($helper-breakpoints);
830
+ }
831
+ }
832
+
833
+ // ── Forced colors ──
834
+ // Field precisa border + bg explícitos pra ser visível como input.
835
+ @media (forced-colors: active) {
836
+ matcha-form-field {
837
+ .matcha-field-control,
838
+ .matcha-form-field__control {
839
+ border: 1px solid CanvasText !important;
840
+ background: Canvas !important;
841
+ color: CanvasText !important;
842
+ }
843
+
844
+ // Estado focused
845
+ &.is-focused .matcha-field-control,
846
+ &.is-focused .matcha-form-field__control,
847
+ .matcha-field-control:focus-within,
848
+ .matcha-form-field__control:focus-within {
849
+ outline: 2px solid CanvasText !important;
850
+ outline-offset: 2px;
851
+ }
852
+
853
+ // Estado erro/invalid
854
+ &.is-invalid .matcha-field-control,
855
+ &.has-error .matcha-form-field__control {
856
+ border-color: CanvasText !important;
857
+ border-width: 2px !important;
858
+ }
859
+
860
+ &[disabled],
861
+ &.is-disabled {
862
+ opacity: 0.5;
863
+
864
+ .matcha-field-control,
865
+ .matcha-form-field__control {
866
+ border-color: GrayText !important;
867
+ color: GrayText !important;
868
+ }
869
+ }
870
+ }
871
+ }
872
+
873
+ // ── Print ──
874
+ // Form fields preservam estado (form values). Sem fundo decorativo.
875
+ @media print {
876
+ matcha-form-field {
877
+ .matcha-field-control,
878
+ .matcha-form-field__control {
879
+ border: 1px solid currentColor !important;
880
+ background: transparent !important;
881
+ }
882
+ }
883
+ }