mtrl 0.5.2 → 0.5.4

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 (80) hide show
  1. package/dist/components/checkbox/api.d.ts +1 -1
  2. package/dist/components/checkbox/types.d.ts +17 -6
  3. package/dist/components/chips/api.d.ts +5 -2
  4. package/dist/components/chips/chip/api.d.ts +1 -1
  5. package/dist/components/chips/config.d.ts +5 -1
  6. package/dist/components/chips/types.d.ts +14 -3
  7. package/dist/components/index.d.ts +1 -0
  8. package/dist/components/select/types.d.ts +21 -3
  9. package/dist/components/switch/types.d.ts +8 -4
  10. package/dist/index.cjs +14 -14
  11. package/dist/index.cjs.map +13 -13
  12. package/dist/index.js +14 -14
  13. package/dist/index.js.map +13 -13
  14. package/dist/package.json +1 -1
  15. package/dist/styles.css +2 -2
  16. package/package.json +4 -2
  17. package/src/styles/abstract/_base.scss +2 -0
  18. package/src/styles/abstract/_config.scss +28 -0
  19. package/src/styles/abstract/_functions.scss +124 -0
  20. package/src/styles/abstract/_mixins.scss +401 -0
  21. package/src/styles/abstract/_theme.scss +269 -0
  22. package/src/styles/abstract/_variables.scss +338 -0
  23. package/src/styles/base/_reset.scss +86 -0
  24. package/src/styles/base/_typography.scss +155 -0
  25. package/src/styles/components/_badge.scss +183 -0
  26. package/src/styles/components/_bottom-app-bar.scss +103 -0
  27. package/src/styles/components/_button.scss +756 -0
  28. package/src/styles/components/_card.scss +401 -0
  29. package/src/styles/components/_carousel.scss +645 -0
  30. package/src/styles/components/_checkbox.scss +231 -0
  31. package/src/styles/components/_chips.scss +643 -0
  32. package/src/styles/components/_datepicker.scss +358 -0
  33. package/src/styles/components/_dialog.scss +259 -0
  34. package/src/styles/components/_divider.scss +57 -0
  35. package/src/styles/components/_extended-fab.scss +309 -0
  36. package/src/styles/components/_fab.scss +244 -0
  37. package/src/styles/components/_list.scss +774 -0
  38. package/src/styles/components/_menu.scss +245 -0
  39. package/src/styles/components/_navigation-mobile.scss +244 -0
  40. package/src/styles/components/_navigation-system.scss +151 -0
  41. package/src/styles/components/_navigation.scss +407 -0
  42. package/src/styles/components/_progress.scss +101 -0
  43. package/src/styles/components/_radios.scss +187 -0
  44. package/src/styles/components/_search.scss +306 -0
  45. package/src/styles/components/_segmented-button.scss +227 -0
  46. package/src/styles/components/_select.scss +274 -0
  47. package/src/styles/components/_sheet.scss +236 -0
  48. package/src/styles/components/_slider.scss +264 -0
  49. package/src/styles/components/_snackbar.scss +211 -0
  50. package/src/styles/components/_switch.scss +305 -0
  51. package/src/styles/components/_tabs.scss +421 -0
  52. package/src/styles/components/_textfield.scss +1035 -0
  53. package/src/styles/components/_timepicker.scss +451 -0
  54. package/src/styles/components/_tooltip.scss +241 -0
  55. package/src/styles/components/_top-app-bar.scss +225 -0
  56. package/src/styles/main.scss +129 -0
  57. package/src/styles/themes/_autumn.scss +105 -0
  58. package/src/styles/themes/_base-theme.scss +85 -0
  59. package/src/styles/themes/_baseline.scss +173 -0
  60. package/src/styles/themes/_bluekhaki.scss +125 -0
  61. package/src/styles/themes/_brownbeige.scss +125 -0
  62. package/src/styles/themes/_browngreen.scss +125 -0
  63. package/src/styles/themes/_forest.scss +77 -0
  64. package/src/styles/themes/_greenbeige.scss +125 -0
  65. package/src/styles/themes/_index.scss +19 -0
  66. package/src/styles/themes/_material.scss +125 -0
  67. package/src/styles/themes/_ocean.scss +77 -0
  68. package/src/styles/themes/_sageivory.scss +125 -0
  69. package/src/styles/themes/_spring.scss +77 -0
  70. package/src/styles/themes/_summer.scss +87 -0
  71. package/src/styles/themes/_sunset.scss +60 -0
  72. package/src/styles/themes/_tealcaramel.scss +125 -0
  73. package/src/styles/themes/_winter.scss +77 -0
  74. package/src/styles/utilities/_colors.scss +154 -0
  75. package/src/styles/utilities/_flexbox.scss +194 -0
  76. package/src/styles/utilities/_layout.scss +665 -0
  77. package/src/styles/utilities/_ripple.scss +79 -0
  78. package/src/styles/utilities/_spacing.scss +139 -0
  79. package/src/styles/utilities/_typography.scss +178 -0
  80. package/src/styles/utilities/_visibility.scss +142 -0
@@ -0,0 +1,1035 @@
1
+ // src/styles/components/_textfield.scss
2
+ @use "../../styles/abstract/base" as base;
3
+ @use "../../styles/abstract/variables" as v;
4
+ @use "../../styles/abstract/functions" as f;
5
+ @use "../../styles/abstract/mixins" as m;
6
+ @use "../../styles/abstract/theme" as t;
7
+
8
+ // Define the component once
9
+ $component: "#{base.$prefix}-textfield";
10
+
11
+ // Autofill detection animations
12
+ @keyframes onAutoFillStart {
13
+ from {
14
+ opacity: 1;
15
+ }
16
+ to {
17
+ opacity: 1;
18
+ }
19
+ }
20
+
21
+ @keyframes onAutoFillCancel {
22
+ from {
23
+ opacity: 1;
24
+ }
25
+ to {
26
+ opacity: 1;
27
+ }
28
+ }
29
+
30
+ // ===== BASE STYLES =====
31
+ .#{$component} {
32
+ position: relative;
33
+ display: inline-flex;
34
+ flex-direction: column;
35
+ // min-width: 210px;
36
+ // align-self: flex-start; // Prevents growing to fill the container's height
37
+ flex: 0 0 auto; // Don't grow or shrink; keep natural size
38
+ // Label
39
+ &-label {
40
+ @include m.typography("body-large");
41
+ user-select: none;
42
+ position: absolute;
43
+ left: 16px;
44
+ top: 50%;
45
+ transform: translateY(-50%);
46
+ transform-origin: left top;
47
+ pointer-events: none;
48
+ border-radius: 2px;
49
+ color: t.color("on-surface-variant");
50
+ transition:
51
+ transform v.motion("duration-short4") v.motion("easing-emphasized"),
52
+ color v.motion("duration-short2") v.motion("easing-standard"),
53
+ left v.motion("duration-short4") v.motion("easing-emphasized");
54
+ }
55
+
56
+ // Input element
57
+ &-input {
58
+ @include m.typography("body-large");
59
+ @include m.shape("extra-small");
60
+ padding: 13px 16px;
61
+ width: 100%;
62
+ height: 56px;
63
+ color: t.color("on-surface");
64
+ border: 0;
65
+ appearance: none;
66
+ outline: none;
67
+
68
+ // Animation to detect autofill
69
+ animation-name: onAutoFillCancel;
70
+ animation-duration: 10ms;
71
+
72
+ // Transition to help detect autofill changes
73
+ transition: background-color 0s ease-out 50000000s;
74
+
75
+ &::placeholder {
76
+ color: transparent;
77
+ }
78
+
79
+ // Autofill styles
80
+ &:-webkit-autofill {
81
+ animation-name: onAutoFillStart;
82
+ animation-duration: 10ms;
83
+ -webkit-text-fill-color: t.color("on-surface");
84
+ transition: background-color 5000s ease-in-out 0s; // Long transition to keep the background
85
+
86
+ & ~ .#{$component}-label {
87
+ transform: translateY(-95%) scale(0.75);
88
+ background-color: t.color("surface");
89
+ }
90
+ }
91
+
92
+ // Firefox autofill
93
+ &:autofill {
94
+ color: t.color("on-surface");
95
+
96
+ & ~ .#{$component}-label {
97
+ transform: translateY(-95%) scale(0.75);
98
+ background-color: t.color("surface");
99
+ }
100
+ }
101
+
102
+ // Non-empty input should also float the label
103
+ &:not(:placeholder-shown) {
104
+ & ~ .#{$component}-label {
105
+ transform: translateY(-95%) scale(0.75);
106
+ }
107
+ }
108
+ }
109
+
110
+ // Prefix text
111
+ &-prefix {
112
+ @include m.typography("body-large");
113
+ position: absolute;
114
+ left: 12px;
115
+ top: 50%;
116
+ transform: translateY(-50%);
117
+ color: t.color("on-surface");
118
+ pointer-events: none;
119
+ z-index: 2; // Increased to ensure it stays above input
120
+ white-space: nowrap; // Prevent text wrapping
121
+ max-width: 40%; // Prevent extreme overflow
122
+ overflow: hidden;
123
+ text-overflow: ellipsis;
124
+ transition: top v.motion("duration-short4")
125
+ v.motion("easing-emphasized"); // Add transition for top position
126
+ }
127
+
128
+ // Suffix text
129
+ &-suffix {
130
+ @include m.typography("body-large");
131
+ position: absolute;
132
+ right: 16px;
133
+ top: 50%;
134
+ transform: translateY(-50%);
135
+ color: t.color("on-surface");
136
+ pointer-events: none;
137
+ z-index: 2; // Increased to ensure it stays above input
138
+ white-space: nowrap; // Prevent text wrapping
139
+ max-width: 40%; // Prevent extreme overflow
140
+ overflow: hidden;
141
+ text-overflow: ellipsis;
142
+ transition: top v.motion("duration-short4")
143
+ v.motion("easing-emphasized"); // Add transition for top position
144
+ }
145
+
146
+ // Leading icon
147
+ &-leading-icon {
148
+ opacity: 0.85;
149
+ position: absolute;
150
+ left: 12px;
151
+ top: 50%;
152
+ transform: translateY(-50%);
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ width: 24px;
157
+ height: 24px;
158
+ pointer-events: none;
159
+ color: t.color("on-surface-variant");
160
+ z-index: 1;
161
+ transition: top v.motion("duration-short4")
162
+ v.motion("easing-emphasized"); // Add transition for top position
163
+
164
+ svg {
165
+ width: 20px;
166
+ height: 20px;
167
+ }
168
+ }
169
+
170
+ // Trailing icon
171
+ &-trailing-icon {
172
+ opacity: 0.85;
173
+ position: absolute;
174
+ right: 12px;
175
+ top: 50%;
176
+ transform: translateY(-50%);
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ width: 24px;
181
+ height: 24px;
182
+ color: t.color("on-surface-variant");
183
+ z-index: 1;
184
+ cursor: pointer;
185
+ transition: top v.motion("duration-short4")
186
+ v.motion("easing-emphasized"); // Add transition for top position
187
+
188
+ svg {
189
+ width: 20px;
190
+ height: 20px;
191
+ }
192
+ }
193
+
194
+ // Adjustments when prefix/suffix are present - base case styling
195
+ // JavaScript will handle dynamic sizing, these are fallbacks
196
+ &--with-prefix {
197
+ .#{$component}-label {
198
+ // Default value - will be overridden by JS for precise positioning
199
+ left: 48px;
200
+ }
201
+
202
+ .#{$component}-input {
203
+ // Default value - will be overridden by JS for precise padding
204
+ padding-left: 48px;
205
+ }
206
+ }
207
+
208
+ &--with-suffix {
209
+ .#{$component}-input {
210
+ // Default value - will be overridden by JS for precise padding
211
+ padding-right: 48px;
212
+ }
213
+ }
214
+
215
+ // Adjustments when icons are present
216
+ &--with-leading-icon {
217
+ .#{$component}-label {
218
+ left: 44px;
219
+ }
220
+
221
+ .#{$component}-input {
222
+ padding-left: 44px;
223
+ }
224
+
225
+ // If there's also a prefix, adjust positions
226
+ &.#{$component}--with-prefix {
227
+ .#{$component}-prefix {
228
+ left: 44px;
229
+ }
230
+
231
+ .#{$component}-label {
232
+ // Default value - will be overridden by JS for precise positioning
233
+ left: 76px;
234
+ }
235
+
236
+ .#{$component}-input {
237
+ // Default value - will be overridden by JS for precise padding
238
+ padding-left: 76px;
239
+ }
240
+ }
241
+ }
242
+
243
+ &--with-trailing-icon {
244
+ .#{$component}-input {
245
+ padding-right: 44px;
246
+ }
247
+
248
+ // If there's also a suffix, adjust positions
249
+ &.#{$component}--with-suffix {
250
+ .#{$component}-suffix {
251
+ right: 44px;
252
+ }
253
+
254
+ .#{$component}-input {
255
+ // Default value - will be overridden by JS for precise padding
256
+ padding-right: 76px;
257
+ }
258
+ }
259
+ }
260
+
261
+ // Error state
262
+ &--error {
263
+ border-color: t.color("error");
264
+
265
+ .#{$component}-label {
266
+ color: t.color("error");
267
+ }
268
+
269
+ .#{$component}-leading-icon,
270
+ .#{$component}-trailing-icon,
271
+ .#{$component}-prefix,
272
+ .#{$component}-suffix {
273
+ color: t.color("error");
274
+ }
275
+ }
276
+
277
+ // Disabled state
278
+ &--disabled {
279
+ .#{$component}-input {
280
+ opacity: 0.38;
281
+ border-color: t.color("on-surface");
282
+ background-color: t.alpha("on-surface", 0.04);
283
+ pointer-events: none;
284
+ }
285
+
286
+ .#{$component}-label {
287
+ color: t.color("on-surface");
288
+ opacity: 0.38;
289
+ }
290
+
291
+ .#{$component}-leading-icon,
292
+ .#{$component}-trailing-icon,
293
+ .#{$component}-prefix,
294
+ .#{$component}-suffix {
295
+ color: t.color("on-surface");
296
+ opacity: 0.38;
297
+ }
298
+ }
299
+
300
+ // Disabled state - legacy support for direct input disabled
301
+ &-input:disabled {
302
+ opacity: 0.38;
303
+ border-color: t.color("on-surface");
304
+ background-color: t.alpha("on-surface", 0.04);
305
+ pointer-events: none;
306
+
307
+ & ~ .#{$component}-label,
308
+ & ~ .#{$component}-leading-icon,
309
+ & ~ .#{$component}-trailing-icon,
310
+ & ~ .#{$component}-prefix,
311
+ & ~ .#{$component}-suffix {
312
+ color: t.color("on-surface");
313
+ opacity: 0.38;
314
+ }
315
+ }
316
+
317
+ // Helper text / Supporting text
318
+ &-helper {
319
+ @include m.typography("body-small");
320
+ position: absolute;
321
+ bottom: -18px;
322
+ left: 16px;
323
+ margin-top: 4px;
324
+ color: t.color("on-surface-variant");
325
+
326
+ &--error {
327
+ color: t.color("error");
328
+ }
329
+ }
330
+
331
+ // Required indicator
332
+ &-required {
333
+ color: t.color("error");
334
+ margin-left: 4px;
335
+ }
336
+
337
+ // Accessibility
338
+ @include m.reduced-motion {
339
+ &-label {
340
+ transition: none;
341
+ }
342
+
343
+ &-prefix,
344
+ &-suffix,
345
+ &-leading-icon,
346
+ &-trailing-icon {
347
+ transition: none;
348
+ }
349
+ }
350
+
351
+ // RTL support
352
+ @include m.rtl {
353
+ &-label {
354
+ left: auto;
355
+ right: 16px;
356
+ transform-origin: right top;
357
+ }
358
+
359
+ &-required {
360
+ margin-left: 0;
361
+ margin-right: 4px;
362
+ }
363
+
364
+ &-prefix {
365
+ left: auto;
366
+ right: 16px;
367
+ }
368
+
369
+ &-suffix {
370
+ right: auto;
371
+ left: 16px;
372
+ }
373
+
374
+ &-leading-icon {
375
+ left: auto;
376
+ right: 12px;
377
+ }
378
+
379
+ &-trailing-icon {
380
+ right: auto;
381
+ left: 12px;
382
+ }
383
+
384
+ &--with-prefix {
385
+ .#{$component}-label {
386
+ left: auto;
387
+ // Default value - will be overridden by JS for precise positioning
388
+ right: 48px;
389
+ }
390
+
391
+ .#{$component}-input {
392
+ padding-left: 16px;
393
+ // Default value - will be overridden by JS for precise padding
394
+ padding-right: 48px;
395
+ }
396
+ }
397
+
398
+ &--with-suffix {
399
+ .#{$component}-input {
400
+ padding-right: 16px;
401
+ // Default value - will be overridden by JS for precise padding
402
+ padding-left: 48px;
403
+ }
404
+ }
405
+
406
+ &--with-leading-icon {
407
+ .#{$component}-label {
408
+ left: auto;
409
+ right: 44px;
410
+ }
411
+
412
+ .#{$component}-input {
413
+ padding-left: 16px;
414
+ padding-right: 44px;
415
+ }
416
+
417
+ &.#{$component}--with-prefix {
418
+ .#{$component}-prefix {
419
+ left: auto;
420
+ right: 44px;
421
+ }
422
+
423
+ .#{$component}-label {
424
+ left: auto;
425
+ // Default value - will be overridden by JS for precise positioning
426
+ right: 76px;
427
+ }
428
+
429
+ .#{$component}-input {
430
+ padding-left: 16px;
431
+ // Default value - will be overridden by JS for precise padding
432
+ padding-right: 76px;
433
+ }
434
+ }
435
+ }
436
+
437
+ &--with-trailing-icon {
438
+ .#{$component}-input {
439
+ padding-right: 16px;
440
+ padding-left: 44px;
441
+ }
442
+
443
+ &.#{$component}--with-suffix {
444
+ .#{$component}-suffix {
445
+ right: auto;
446
+ left: 44px;
447
+ }
448
+
449
+ .#{$component}-input {
450
+ padding-right: 16px;
451
+ // Default value - will be overridden by JS for precise padding
452
+ padding-left: 76px;
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ // ===== FILLED VARIANT =====
459
+ &--filled {
460
+ .#{$component}-label {
461
+ opacity: 0.6;
462
+ }
463
+ .#{$component}-input {
464
+ background-color: t.color("surface-container-highest");
465
+ padding: 20px 16px 7px;
466
+ border-bottom: 1px solid t.color("outline");
467
+ border-radius: f.get-shape("extra-small") f.get-shape("extra-small")
468
+ 0 0;
469
+ @include m.motion-transition(background-color, border-color);
470
+
471
+ // Autofill styles for filled variant
472
+ &:-webkit-autofill {
473
+ border-radius: f.get-shape("extra-small")
474
+ f.get-shape("extra-small") 0 0;
475
+
476
+ & ~ .#{$component}-label {
477
+ transform: translateY(-95%) scale(0.75);
478
+ color: t.color("on-surface-variant");
479
+ }
480
+ }
481
+
482
+ &:autofill {
483
+ & ~ .#{$component}-label {
484
+ transform: translateY(-95%) scale(0.75);
485
+ color: t.color("on-surface-variant");
486
+ }
487
+ }
488
+ }
489
+
490
+ &::before {
491
+ content: "";
492
+ position: absolute;
493
+ opacity: 0;
494
+ bottom: 0;
495
+ width: 100%;
496
+ height: 2px;
497
+ background-color: t.color("primary");
498
+ border-radius: 0;
499
+ pointer-events: none;
500
+ transition: 0.2s opacity ease;
501
+ }
502
+
503
+ // Populated field (not empty) or focused field label position
504
+ &:not(.#{$component}--empty) .#{$component}-label,
505
+ &.#{$component}--focused .#{$component}-label {
506
+ transform: translateY(-95%) scale(0.75);
507
+ // Don't specify left position here - let JS handle it
508
+ }
509
+
510
+ // CSS-only fallback for when input has value (works without JS)
511
+ .#{$component}-input:not(:placeholder-shown) ~ .#{$component}-label {
512
+ transform: translateY(-95%) scale(0.75);
513
+ }
514
+
515
+ // Position adjustments for prefix/suffix when in filled focused/populated state
516
+ &.mtrl-textfield--with-prefix,
517
+ &.mtrl-textfield--with-suffix {
518
+ &:not(.#{$component}--empty),
519
+ &.#{$component}--focused {
520
+ .#{$component}-prefix,
521
+ .#{$component}-suffix {
522
+ top: 34px; // Align with input text when focused or populated
523
+ }
524
+ }
525
+ }
526
+ // Focus state
527
+ &.#{$component}--focused {
528
+ .#{$component}-label {
529
+ color: t.color("primary");
530
+ }
531
+
532
+ &::before {
533
+ opacity: 1;
534
+ }
535
+ }
536
+
537
+ // Error state
538
+ &.#{$component}--error {
539
+ &::before {
540
+ opacity: 1;
541
+ background-color: t.color("error");
542
+ }
543
+
544
+ .#{$component}-label {
545
+ color: t.color("error");
546
+ }
547
+ }
548
+
549
+ // Disabled state
550
+ &.#{$component}--disabled {
551
+ border-bottom-color: t.alpha("on-surface", 0.38);
552
+ pointer-events: none;
553
+
554
+ .#{$component}-input {
555
+ background-color: t.alpha("on-surface", 0.04);
556
+ }
557
+ }
558
+
559
+ // Prefix/suffix adjustments for filled variant
560
+ &.#{$component}--with-prefix {
561
+ .#{$component}-prefix {
562
+ top: 28px; // Align with input text in filled variant - default state
563
+ }
564
+ }
565
+
566
+ &.#{$component}--with-suffix {
567
+ .#{$component}-suffix {
568
+ top: 28px; // Align with input text in filled variant - default state
569
+ }
570
+ }
571
+
572
+ // Icon adjustments for filled variant
573
+ &.#{$component}--with-leading-icon {
574
+ .#{$component}-input {
575
+ padding: 20px 16px 7px 44px;
576
+ }
577
+
578
+ .#{$component}-label {
579
+ left: 44px;
580
+ }
581
+
582
+ .#{$component}-leading-icon {
583
+ top: 28px;
584
+ }
585
+ }
586
+
587
+ // Populated field (not empty) or focused field label position
588
+ &:not(.#{$component}--empty) .#{$component}-label,
589
+ &.#{$component}--focused .#{$component}-label {
590
+ // Important change: For outlined variant, when focused/populated
591
+ // we want to reset the left position back to default (or to JS calculated equivalent)
592
+ // to handle the label moving up above the field
593
+ // The JS will handle special positioning depending on if it's prefixed
594
+ left: 16px;
595
+ }
596
+
597
+ &.#{$component}--with-trailing-icon {
598
+ .#{$component}-input {
599
+ padding-right: 44px;
600
+ }
601
+
602
+ .#{$component}-trailing-icon {
603
+ top: 28px;
604
+ }
605
+ }
606
+
607
+ // RTL support
608
+ @include m.rtl {
609
+ .#{$component}-label {
610
+ left: auto;
611
+ right: 16px;
612
+ }
613
+
614
+ &.#{$component}--with-leading-icon {
615
+ .#{$component}-input {
616
+ padding: 20px 44px 7px 16px;
617
+ }
618
+
619
+ .#{$component}-label {
620
+ left: auto;
621
+ right: 44px;
622
+ }
623
+ }
624
+
625
+ &.#{$component}--with-trailing-icon {
626
+ .#{$component}-input {
627
+ padding-right: 16px;
628
+ padding-left: 44px;
629
+ }
630
+ }
631
+ }
632
+ }
633
+
634
+ // ===== OUTLINED VARIANT =====
635
+ &--outlined {
636
+ background-color: inherit;
637
+ border-radius: f.get-shape("extra-small");
638
+ @include m.motion-transition(border-color);
639
+
640
+ .#{$component}-input {
641
+ background-color: transparent;
642
+ padding: 12px 15px 13px;
643
+ border: 1px solid t.color("outline");
644
+ @include m.motion-transition(padding);
645
+
646
+ // Autofill styles for outlined variant
647
+ &:-webkit-autofill {
648
+ border-radius: f.get-shape("extra-small");
649
+
650
+ & ~ .#{$component}-label {
651
+ background-color: t.color("surface");
652
+ transform: translateY(-145%) scale(0.75);
653
+ }
654
+ }
655
+
656
+ &:autofill {
657
+ & ~ .#{$component}-label {
658
+ background-color: t.color("surface");
659
+ transform: translateY(-145%) scale(0.75);
660
+ }
661
+ }
662
+ }
663
+
664
+ .#{$component}-label {
665
+ opacity: 0.5;
666
+ background-color: inherit;
667
+ padding: 0 4px;
668
+ left: 14px;
669
+ top: 50%;
670
+ }
671
+
672
+ &::before {
673
+ content: "";
674
+ position: absolute;
675
+ opacity: 0;
676
+ width: 100%;
677
+ height: 100%;
678
+ border: 1.5px solid t.color("primary");
679
+ border-radius: f.get-shape("extra-small");
680
+ pointer-events: none;
681
+ transition: 0.1s opacity ease;
682
+ }
683
+
684
+ // Populated field (not empty) or focused field label position
685
+ &:not(.#{$component}--empty) .#{$component}-label,
686
+ &.#{$component}--focused .#{$component}-label {
687
+ padding: 0 4px;
688
+ opacity: 1;
689
+ transform: translateY(-147%) scale(0.75);
690
+ // Important change: For outlined variant, when focused/populated
691
+ // we want to reset the left position back to default (or to JS calculated equivalent)
692
+ // to handle the label moving up above the field
693
+ // The JS will handle special positioning depending on if it's prefixed
694
+ left: 14px;
695
+ }
696
+
697
+ // CSS-only fallback for when input has value (works without JS)
698
+ .#{$component}-input:not(:placeholder-shown) ~ .#{$component}-label {
699
+ padding: 0 4px;
700
+ transform: translateY(-147%) scale(0.75);
701
+ left: 14px;
702
+ background-color: t.color("surface");
703
+ }
704
+
705
+ // Focus state
706
+ &.#{$component}--focused {
707
+ &::before {
708
+ opacity: 1;
709
+ border-width: 2px;
710
+ }
711
+ // &:hover {
712
+ // &::before {
713
+
714
+ // }
715
+ // }
716
+
717
+ .#{$component}-label {
718
+ color: t.color("primary");
719
+ }
720
+ }
721
+
722
+ // Error state
723
+ &.#{$component}--error {
724
+ &::before {
725
+ opacity: 1;
726
+ border: 2px solid t.color("error");
727
+ }
728
+
729
+ .#{$component}-label {
730
+ color: t.color("error");
731
+ }
732
+ }
733
+
734
+ // Disabled state
735
+ &.#{$component}--disabled {
736
+ pointer-events: none;
737
+ &::before {
738
+ opacity: 1;
739
+ border: 1px solid t.alpha("on-surface", 0.38);
740
+ }
741
+ }
742
+
743
+ // Prefix/suffix vertical alignment for outlined variant
744
+ &.#{$component}--with-prefix {
745
+ .#{$component}-prefix {
746
+ top: 50%; // Centered vertically in outlined variant
747
+ }
748
+ }
749
+
750
+ &.#{$component}--with-suffix {
751
+ .#{$component}-suffix {
752
+ top: 50%; // Centered vertically in outlined variant
753
+ }
754
+ }
755
+
756
+ // Icon adjustments for outlined variant
757
+ &.#{$component}--with-leading-icon {
758
+ .#{$component}-input {
759
+ padding-left: 44px;
760
+ }
761
+
762
+ .#{$component}-label {
763
+ left: 44px;
764
+ }
765
+
766
+ // Important change: For outlined variant, when focused/populated
767
+ // we still reset to 12px position (default)
768
+ &:not(.#{$component}--empty) .#{$component}-label,
769
+ &.#{$component}--focused .#{$component}-label {
770
+ left: 14px;
771
+ }
772
+ }
773
+
774
+ &.#{$component}--with-trailing-icon {
775
+ .#{$component}-input {
776
+ padding-right: 44px;
777
+ }
778
+ }
779
+
780
+ // RTL support
781
+ @include m.rtl {
782
+ &:not(.#{$component}--empty) .#{$component}-label,
783
+ &.#{$component}--focused .#{$component}-label {
784
+ left: auto;
785
+ right: 12px;
786
+ }
787
+
788
+ &.#{$component}--focused .#{$component}-label {
789
+ right: 12px;
790
+ }
791
+
792
+ &.#{$component}--error .#{$component}-label {
793
+ right: 12px;
794
+ }
795
+
796
+ &.#{$component}--with-leading-icon {
797
+ .#{$component}-input {
798
+ padding-left: 16px;
799
+ padding-right: 44px;
800
+ }
801
+
802
+ .#{$component}-label {
803
+ left: auto;
804
+ right: 44px;
805
+ }
806
+
807
+ &:not(.#{$component}--empty) .#{$component}-label,
808
+ &.#{$component}--focused .#{$component}-label {
809
+ // Reset to default value for focused state
810
+ right: 12px;
811
+ left: auto;
812
+ }
813
+ }
814
+
815
+ &.#{$component}--with-trailing-icon {
816
+ .#{$component}-input {
817
+ padding-right: 16px;
818
+ padding-left: 44px;
819
+ }
820
+ }
821
+ }
822
+ }
823
+
824
+ // Multiline styles
825
+ &--multiline {
826
+ .#{$component}-input {
827
+ min-height: 100px;
828
+ height: auto;
829
+ resize: vertical;
830
+ padding-top: 12px;
831
+ }
832
+
833
+ .#{$component}-prefix,
834
+ .#{$component}-suffix {
835
+ top: 28px;
836
+ }
837
+
838
+ .#{$component}-label {
839
+ top: 24px;
840
+ }
841
+ }
842
+
843
+ // Support for multiline inputs
844
+ &-input[type="multiline"] {
845
+ min-height: 100px;
846
+ height: auto; // Allow height to adjust based on content
847
+ max-height: none; // Remove max-height constraint from regular inputs
848
+ flex-shrink: 1; // Allow some shrinking for multiline
849
+
850
+ & ~ .#{$component}-leading-icon,
851
+ & ~ .#{$component}-trailing-icon,
852
+ & ~ .#{$component}-prefix,
853
+ & ~ .#{$component}-suffix {
854
+ top: 20px;
855
+ transform: none;
856
+ }
857
+ }
858
+
859
+ // ===== DENSITY VARIANTS =====
860
+ // Compact density (40px height)
861
+ &--density-compact {
862
+ .#{$component}-input {
863
+ height: 40px;
864
+ padding: 8px 16px;
865
+ @include m.typography("body-medium"); // Slightly smaller text
866
+ }
867
+
868
+ // Adjust label position for compact
869
+ .#{$component}-label {
870
+ @include m.typography("body-medium");
871
+ }
872
+
873
+ // Adjust icon positions for compact height
874
+ .#{$component}-leading-icon,
875
+ .#{$component}-trailing-icon {
876
+ width: 20px;
877
+ height: 20px;
878
+
879
+ svg {
880
+ width: 16px;
881
+ height: 16px;
882
+ }
883
+ }
884
+
885
+ // Adjust prefix/suffix position for compact
886
+ .#{$component}-prefix,
887
+ .#{$component}-suffix {
888
+ @include m.typography("body-medium");
889
+ }
890
+
891
+ // Filled variant with compact density
892
+ &.#{$component}--filled {
893
+ .#{$component}-input {
894
+ padding: 12px 16px 4px; // Adjusted for filled variant
895
+ }
896
+
897
+ // Populated/focused label position
898
+ &:not(.#{$component}--empty) .#{$component}-label,
899
+ &.#{$component}--focused .#{$component}-label {
900
+ transform: translateY(-90%) scale(0.75); // Adjusted for compact height
901
+ }
902
+
903
+ // Adjust prefix/suffix position when focused/populated
904
+ &.mtrl-textfield--with-prefix,
905
+ &.mtrl-textfield--with-suffix {
906
+ &:not(.#{$component}--empty),
907
+ &.#{$component}--focused {
908
+ .#{$component}-prefix,
909
+ .#{$component}-suffix {
910
+ top: 22px; // Adjusted for compact filled variant
911
+ }
912
+ }
913
+ }
914
+
915
+ // Adjust icons for filled compact
916
+ .#{$component}-leading-icon,
917
+ .#{$component}-trailing-icon {
918
+ top: 20px; // Adjusted for compact filled
919
+ }
920
+ }
921
+
922
+ // Outlined variant with compact density
923
+ &.#{$component}--outlined {
924
+ .#{$component}-input {
925
+ padding: 9px 15px 8px;
926
+ }
927
+
928
+ // Focused/populated position
929
+ &:not(.#{$component}--empty) .#{$component}-label,
930
+ &.#{$component}--focused .#{$component}-label {
931
+ transform: translateY(-130%) scale(0.75); // Same as default, scale is important
932
+ }
933
+ }
934
+
935
+ // Multiline with compact density
936
+ &.#{$component}--multiline {
937
+ .#{$component}-input {
938
+ min-height: 40px;
939
+ padding: 8px 16px;
940
+ }
941
+ }
942
+
943
+ // Adjust element positions for compact with icons
944
+ &.#{$component}--with-leading-icon {
945
+ .#{$component}-label {
946
+ left: 40px; // Reduced from 44px
947
+ }
948
+
949
+ .#{$component}-input {
950
+ padding-left: 40px;
951
+ }
952
+
953
+ &.#{$component}--with-prefix {
954
+ .#{$component}-prefix {
955
+ left: 40px;
956
+ }
957
+
958
+ .#{$component}-label {
959
+ left: 68px; // Adjusted for compact
960
+ }
961
+
962
+ .#{$component}-input {
963
+ padding-left: 68px;
964
+ }
965
+ }
966
+ }
967
+
968
+ &.#{$component}--with-trailing-icon {
969
+ .#{$component}-input {
970
+ padding-right: 40px;
971
+ }
972
+
973
+ &.#{$component}--with-suffix {
974
+ .#{$component}-suffix {
975
+ right: 40px;
976
+ }
977
+
978
+ .#{$component}-input {
979
+ padding-right: 68px;
980
+ }
981
+ }
982
+ }
983
+
984
+ // RTL adjustments for compact density
985
+ @include m.rtl {
986
+ &.#{$component}--with-leading-icon {
987
+ .#{$component}-label {
988
+ left: auto;
989
+ right: 40px;
990
+ }
991
+
992
+ .#{$component}-input {
993
+ padding-left: 16px;
994
+ padding-right: 40px;
995
+ }
996
+
997
+ &.#{$component}--with-prefix {
998
+ .#{$component}-prefix {
999
+ left: auto;
1000
+ right: 40px;
1001
+ }
1002
+
1003
+ .#{$component}-label {
1004
+ left: auto;
1005
+ right: 68px;
1006
+ }
1007
+
1008
+ .#{$component}-input {
1009
+ padding-left: 16px;
1010
+ padding-right: 68px;
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ &.#{$component}--with-trailing-icon {
1016
+ .#{$component}-input {
1017
+ padding-right: 16px;
1018
+ padding-left: 40px;
1019
+ }
1020
+
1021
+ &.#{$component}--with-suffix {
1022
+ .#{$component}-suffix {
1023
+ right: auto;
1024
+ left: 40px;
1025
+ }
1026
+
1027
+ .#{$component}-input {
1028
+ padding-right: 16px;
1029
+ padding-left: 68px;
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ }