@geosdi/ngx-ui 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/styles/ui.scss ADDED
@@ -0,0 +1,2220 @@
1
+ // ---- pulsante ------------------------------------------------------------
2
+
3
+ .ml-button {
4
+ --ml-bg: var(--ml-primary);
5
+ --ml-bg-hover: var(--ml-primary-hover);
6
+ --ml-fg: var(--ml-on-primary);
7
+ --ml-soft: var(--ml-selection-background);
8
+
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: 0.5rem;
13
+ min-height: var(--ml-control-height);
14
+ padding: var(--ml-button-padding);
15
+ border: 1px solid var(--ml-bg);
16
+ border-radius: var(--ml-radius);
17
+ background: var(--ml-bg);
18
+ color: var(--ml-fg);
19
+ font: inherit;
20
+ font-family: var(--ml-font-family);
21
+ font-weight: 600;
22
+ line-height: 1.2;
23
+ text-decoration: none;
24
+ cursor: pointer;
25
+ transition:
26
+ background-color 0.15s,
27
+ border-color 0.15s,
28
+ color 0.15s;
29
+
30
+ &:focus-visible {
31
+ outline: 2px solid var(--ml-focus-color);
32
+ outline-offset: 2px;
33
+ }
34
+
35
+ &:disabled,
36
+ &[aria-disabled="true"] {
37
+ opacity: 0.6;
38
+ cursor: default;
39
+ pointer-events: none;
40
+ }
41
+
42
+ &[data-severity="secondary"] {
43
+ --ml-bg: var(--ml-text-secondary);
44
+ --ml-bg-hover: var(--ml-secondary-hover);
45
+ --ml-soft: var(--ml-secondary-subtle);
46
+ }
47
+
48
+ &[data-severity="success"] {
49
+ --ml-bg: var(--ml-success);
50
+ --ml-bg-hover: var(--ml-success-hover);
51
+ --ml-soft: var(--ml-success-subtle);
52
+ }
53
+
54
+ &[data-severity="info"] {
55
+ --ml-bg: var(--ml-info);
56
+ --ml-bg-hover: var(--ml-info-hover);
57
+ }
58
+
59
+ &[data-severity="warn"] {
60
+ --ml-bg: var(--ml-warning);
61
+ --ml-bg-hover: var(--ml-warning-hover);
62
+ --ml-soft: var(--ml-warning-subtle);
63
+ }
64
+
65
+ &[data-severity="danger"] {
66
+ --ml-bg: var(--ml-danger);
67
+ --ml-bg-hover: var(--ml-danger-hover);
68
+ --ml-soft: var(--ml-danger-subtle);
69
+ }
70
+
71
+ // Solo il bordo, o niente : il colore passa al testo.
72
+ &.ml-button--outlined,
73
+ &.ml-button--text {
74
+ background: transparent;
75
+ color: var(--ml-bg);
76
+ }
77
+
78
+ &.ml-button--text {
79
+ border-color: transparent;
80
+ }
81
+
82
+ &.ml-button--small {
83
+ min-height: var(--ml-control-height-small);
84
+ padding: var(--ml-button-padding-small);
85
+ font-size: var(--ml-control-font-size-small);
86
+ }
87
+
88
+ &.ml-button--icon-only {
89
+ width: var(--ml-control-height);
90
+ min-width: var(--ml-control-height);
91
+ padding: 0;
92
+
93
+ &.ml-button--small {
94
+ width: var(--ml-control-height-small);
95
+ min-width: var(--ml-control-height-small);
96
+ }
97
+ }
98
+
99
+ &.ml-button--rounded {
100
+ border-radius: 999px;
101
+ }
102
+ }
103
+
104
+ @media (hover: hover) and (pointer: fine) {
105
+ .ml-button:hover {
106
+ border-color: var(--ml-bg-hover);
107
+ background: var(--ml-bg-hover);
108
+ }
109
+
110
+ .ml-button.ml-button--outlined:hover,
111
+ .ml-button.ml-button--text:hover {
112
+ background: var(--ml-soft);
113
+ color: var(--ml-bg-hover);
114
+ }
115
+
116
+ .ml-button.ml-button--text:hover {
117
+ border-color: transparent;
118
+ }
119
+ }
120
+
121
+ // ---- campo di testo -------------------------------------------------------
122
+
123
+ .ml-input {
124
+ width: auto;
125
+ min-height: var(--ml-control-height);
126
+ padding: var(--ml-input-padding);
127
+ border: 1px solid var(--ml-control-border);
128
+ border-radius: var(--ml-radius);
129
+ background: var(--ml-surface);
130
+ color: var(--ml-text);
131
+ font: inherit;
132
+ font-family: var(--ml-font-family);
133
+ transition:
134
+ border-color 0.15s,
135
+ box-shadow 0.15s;
136
+
137
+ &::placeholder {
138
+ color: var(--ml-text-muted);
139
+ }
140
+
141
+ &:focus {
142
+ border-color: var(--ml-primary);
143
+ outline: none;
144
+ box-shadow: 0 0 0 2px var(--ml-focus-shadow-color);
145
+ }
146
+
147
+ &:disabled {
148
+ background: var(--ml-surface-muted);
149
+ color: var(--ml-text-muted);
150
+ cursor: default;
151
+ }
152
+
153
+ &.ng-invalid.ng-touched,
154
+ &.ng-invalid.ng-dirty {
155
+ border-color: var(--ml-danger);
156
+ }
157
+ }
158
+
159
+ // ---- casella --------------------------------------------------------------
160
+
161
+ .ml-checkbox {
162
+ position: relative;
163
+ display: inline-flex;
164
+ flex: none;
165
+ width: 1.25rem;
166
+ height: 1.25rem;
167
+ vertical-align: middle;
168
+ }
169
+
170
+ // La casella vera, sopra il disegno e trasparente : prende il clic, la
171
+ // tastiera e il lettore di schermo.
172
+ .ml-checkbox__input {
173
+ position: absolute;
174
+ inset: 0;
175
+ z-index: 1;
176
+ margin: 0;
177
+ opacity: 0;
178
+ cursor: pointer;
179
+
180
+ &:disabled {
181
+ cursor: default;
182
+ }
183
+ }
184
+
185
+ .ml-checkbox__box {
186
+ display: inline-flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ width: 100%;
190
+ height: 100%;
191
+ border: 1px solid var(--ml-control-border);
192
+ border-radius: var(--ml-radius);
193
+ background: var(--ml-surface);
194
+ transition:
195
+ background-color 0.15s,
196
+ border-color 0.15s;
197
+
198
+ svg {
199
+ width: 0.875rem;
200
+ height: 0.875rem;
201
+ fill: none;
202
+ stroke: var(--ml-on-primary);
203
+ stroke-width: 2;
204
+ stroke-linecap: round;
205
+ stroke-linejoin: round;
206
+ visibility: hidden;
207
+ }
208
+ }
209
+
210
+ .ml-checkbox--checked .ml-checkbox__box {
211
+ border-color: var(--ml-primary);
212
+ background: var(--ml-primary);
213
+
214
+ svg {
215
+ visibility: visible;
216
+ }
217
+ }
218
+
219
+ .ml-checkbox__input:focus-visible + .ml-checkbox__box {
220
+ outline: 2px solid var(--ml-focus-color);
221
+ outline-offset: 2px;
222
+ }
223
+
224
+ .ml-checkbox--disabled .ml-checkbox__box {
225
+ opacity: 0.6;
226
+ }
227
+
228
+ @media (hover: hover) and (pointer: fine) {
229
+ .ml-checkbox:not(.ml-checkbox--disabled, .ml-checkbox--checked):hover
230
+ .ml-checkbox__box {
231
+ border-color: var(--ml-primary);
232
+ }
233
+ }
234
+
235
+ // ---- suggerimento ---------------------------------------------------------
236
+
237
+ .cdk-overlay-container {
238
+ z-index: var(--ml-overlay-z-index);
239
+ }
240
+
241
+ .ml-tooltip {
242
+ display: block;
243
+ max-width: 20rem;
244
+ padding: 0.5rem 0.75rem;
245
+ border-radius: var(--ml-radius);
246
+ background: var(--ml-tooltip-background);
247
+ color: var(--ml-on-primary);
248
+ font-family: var(--ml-font-family);
249
+ font-size: 0.875rem;
250
+ line-height: 1.35;
251
+ overflow-wrap: anywhere;
252
+ box-shadow: var(--ml-shadow);
253
+ pointer-events: none;
254
+ }
255
+
256
+ // ---- finestra -------------------------------------------------------------
257
+
258
+ // La maschera della finestra scritta nel template (`ml-dialog`) : sta sul
259
+ // posto, fissa sulla pagina. Quella delle finestre aperte da codice è lo
260
+ // sfondo dell'overlay del CDK, qui sotto.
261
+ .ml-dialog-mask {
262
+ position: fixed;
263
+ inset: 0;
264
+ z-index: 1100;
265
+ display: flex;
266
+ align-items: center;
267
+ justify-content: center;
268
+ padding: 1rem;
269
+ pointer-events: none;
270
+
271
+ > .ml-dialog {
272
+ pointer-events: auto;
273
+ }
274
+ }
275
+
276
+ .ml-dialog-mask--modal,
277
+ .ml-dialog-backdrop {
278
+ background: rgb(0 0 0 / 50%);
279
+ pointer-events: auto;
280
+ }
281
+
282
+ .ml-dialog {
283
+ display: flex;
284
+ flex-direction: column;
285
+ min-width: 300px;
286
+ max-width: 100%;
287
+ max-height: 90%;
288
+ border: 1px solid var(--ml-divider);
289
+ border-radius: 12px;
290
+ background: var(--ml-surface);
291
+ color: var(--ml-text);
292
+ box-shadow:
293
+ 0 20px 25px -5px rgb(0 0 0 / 10%),
294
+ 0 8px 10px -6px rgb(0 0 0 / 10%);
295
+ outline: none;
296
+ }
297
+
298
+ // Nell'overlay il riquadro è il pannello del CDK, e la finestra lo riempie.
299
+ .ml-dialog--overlay {
300
+ width: 100%;
301
+ height: 100%;
302
+ max-height: inherit;
303
+ }
304
+
305
+ .ml-dialog-pane {
306
+ max-height: 90vh;
307
+ }
308
+
309
+ .ml-dialog__header {
310
+ display: flex;
311
+ flex: none;
312
+ align-items: center;
313
+ justify-content: space-between;
314
+ gap: 0.5rem;
315
+ padding: 1.25rem;
316
+ }
317
+
318
+ .ml-dialog__title {
319
+ margin: 0;
320
+ color: var(--ml-text-heading);
321
+ font-size: 1.25rem;
322
+ font-weight: 600;
323
+ }
324
+
325
+ .ml-dialog__close {
326
+ display: inline-flex;
327
+ flex: none;
328
+ align-items: center;
329
+ justify-content: center;
330
+ width: 2.5rem;
331
+ height: 2.5rem;
332
+ margin-left: auto;
333
+ border: 0;
334
+ border-radius: 50%;
335
+ background: transparent;
336
+ color: var(--ml-text-secondary);
337
+ cursor: pointer;
338
+
339
+ &:focus-visible {
340
+ outline: 2px solid var(--ml-focus-color);
341
+ outline-offset: 2px;
342
+ }
343
+ }
344
+
345
+ .ml-dialog__content {
346
+ flex: 1 1 auto;
347
+ min-height: 0;
348
+ padding: 0 1.25rem 1.25rem;
349
+ overflow-y: auto;
350
+ }
351
+
352
+ .ml-dialog__footer {
353
+ display: flex;
354
+ flex: none;
355
+ justify-content: flex-end;
356
+ gap: 0.5rem;
357
+ padding: 0 1.25rem 1.25rem;
358
+ }
359
+
360
+ @media (hover: hover) and (pointer: fine) {
361
+ .ml-dialog__close:hover {
362
+ background: var(--ml-surface-muted);
363
+ color: var(--ml-text-heading);
364
+ }
365
+ }
366
+
367
+ // ---- conferma -------------------------------------------------------------
368
+
369
+ .ml-confirm__message {
370
+ margin: 0 0 1.25rem;
371
+ line-height: 1.5;
372
+ white-space: pre-line;
373
+ }
374
+
375
+ .ml-confirm__actions {
376
+ display: flex;
377
+ justify-content: flex-end;
378
+ gap: 0.5rem;
379
+ }
380
+
381
+ // ---- avvisi ---------------------------------------------------------------
382
+
383
+ // In alto a destra, con il livello configurabile dal tema.
384
+ .ml-toast-stack {
385
+ position: fixed;
386
+ top: calc(1rem + env(safe-area-inset-top, 0px));
387
+ right: max(1rem, env(safe-area-inset-right, 0px));
388
+ z-index: var(--ml-toast-z-index);
389
+ display: flex;
390
+ flex-direction: column;
391
+ gap: 0.75rem;
392
+ width: min(25rem, calc(100vw - 2rem));
393
+ pointer-events: none;
394
+ }
395
+
396
+ @keyframes ml-toast-enter {
397
+ from {
398
+ opacity: 0;
399
+ transform: translateY(-8px);
400
+ }
401
+
402
+ to {
403
+ opacity: 1;
404
+ transform: translateY(0);
405
+ }
406
+ }
407
+
408
+ .ml-toast {
409
+ --ml-toast: var(--ml-primary);
410
+
411
+ display: flex;
412
+ align-items: flex-start;
413
+ gap: 0.75rem;
414
+ padding: 1rem;
415
+ border: 1px solid color-mix(in srgb, var(--ml-toast) 35%, transparent);
416
+ border-radius: 8px;
417
+ background: color-mix(
418
+ in srgb,
419
+ color-mix(in srgb, var(--ml-toast) 10%, var(--ml-surface)) 90%,
420
+ transparent
421
+ );
422
+ color: color-mix(in srgb, var(--ml-toast) 75%, var(--ml-text));
423
+ box-shadow: 0 4px 12px rgb(0 0 0 / 8%);
424
+ backdrop-filter: blur(8px);
425
+ pointer-events: auto;
426
+ animation: ml-toast-enter var(--ml-toast-enter-duration) ease-out;
427
+
428
+ @media (prefers-reduced-motion: reduce) {
429
+ animation: none;
430
+ }
431
+
432
+ > .ml-icon {
433
+ margin-top: 0.15rem;
434
+ color: var(--ml-toast);
435
+ font-size: 1.4rem;
436
+ }
437
+
438
+ &[data-severity="success"] {
439
+ --ml-toast: var(--ml-success);
440
+ }
441
+
442
+ &[data-severity="warn"] {
443
+ --ml-toast: var(--ml-warning);
444
+ }
445
+
446
+ &[data-severity="error"] {
447
+ --ml-toast: var(--ml-danger);
448
+ }
449
+ }
450
+
451
+ .ml-toast__text {
452
+ flex: 1 1 auto;
453
+ min-width: 0;
454
+ line-height: 1.4;
455
+ font-weight: 500;
456
+ overflow-wrap: anywhere;
457
+ }
458
+
459
+ .ml-toast__close {
460
+ display: inline-flex;
461
+ flex: none;
462
+ align-items: center;
463
+ justify-content: center;
464
+ width: 1.75rem;
465
+ height: 1.75rem;
466
+ border: 0;
467
+ border-radius: 50%;
468
+ background: transparent;
469
+ color: inherit;
470
+ cursor: pointer;
471
+
472
+ &:hover {
473
+ background: color-mix(in srgb, var(--ml-toast) 12%, transparent);
474
+ }
475
+
476
+ &:focus-visible {
477
+ outline: 2px solid var(--ml-focus-color);
478
+ }
479
+ }
480
+
481
+ // ---- tendina --------------------------------------------------------------
482
+
483
+ .ml-select {
484
+ position: relative;
485
+ display: inline-flex;
486
+ min-width: 0;
487
+ vertical-align: middle;
488
+ }
489
+
490
+ .ml-select--fluid {
491
+ width: 100%;
492
+ }
493
+
494
+ .ml-select__trigger {
495
+ display: flex;
496
+ align-items: center;
497
+ width: 100%;
498
+ min-height: 2.5rem;
499
+ border: 1px solid var(--ml-control-border);
500
+ border-radius: var(--ml-radius);
501
+ background: var(--ml-surface);
502
+ color: var(--ml-text);
503
+ cursor: pointer;
504
+ transition:
505
+ border-color 0.15s,
506
+ box-shadow 0.15s;
507
+ }
508
+
509
+ .ml-select__label {
510
+ flex: 1 1 auto;
511
+ min-width: 0;
512
+ padding: 0.5rem 0.75rem;
513
+ overflow: hidden;
514
+ text-overflow: ellipsis;
515
+ white-space: nowrap;
516
+ outline: none;
517
+ }
518
+
519
+ // Con il testo libero il campo è un `input` vero, senza bordo suo : il bordo
520
+ // è quello del riquadro.
521
+ .ml-select__input {
522
+ border: 0;
523
+ background: transparent;
524
+ color: inherit;
525
+ font: inherit;
526
+ cursor: text;
527
+
528
+ &::placeholder {
529
+ color: var(--ml-text-muted);
530
+ }
531
+ }
532
+
533
+ .ml-select__placeholder {
534
+ color: var(--ml-text-muted);
535
+ }
536
+
537
+ .ml-select__clear,
538
+ .ml-select__arrow {
539
+ display: inline-flex;
540
+ flex: none;
541
+ align-items: center;
542
+ justify-content: center;
543
+ width: 2rem;
544
+ color: var(--ml-text-muted);
545
+ }
546
+
547
+ .ml-select__clear {
548
+ height: 2rem;
549
+ padding: 0;
550
+ border: 0;
551
+ border-radius: 50%;
552
+ background: transparent;
553
+ cursor: pointer;
554
+ }
555
+
556
+ .ml-select__arrow {
557
+ width: 2.5rem;
558
+ font-size: 0.875rem;
559
+ }
560
+
561
+ .ml-select--focus .ml-select__trigger {
562
+ border-color: var(--ml-primary);
563
+ box-shadow: 0 0 0 2px var(--ml-focus-shadow-color);
564
+ }
565
+
566
+ .ml-select--disabled .ml-select__trigger {
567
+ background: var(--ml-surface-muted);
568
+ color: var(--ml-text-muted);
569
+ cursor: default;
570
+ }
571
+
572
+ .ml-select--small .ml-select__trigger {
573
+ min-height: 2rem;
574
+ font-size: 0.875rem;
575
+ }
576
+
577
+ .ml-select--small .ml-select__label {
578
+ padding: 0.375rem 0.625rem;
579
+ }
580
+
581
+ .ml-select.ng-invalid.ng-touched .ml-select__trigger {
582
+ border-color: var(--ml-danger);
583
+ }
584
+
585
+ // Il pannello riempie il riquadro dell'overlay, che il CDK fa largo almeno
586
+ // quanto il campo : il riquadro è un flex in riga, e da solo il pannello
587
+ // resterebbe largo quanto le sue voci.
588
+ .ml-select__panel {
589
+ display: flex;
590
+ flex: 1 1 auto;
591
+ flex-direction: column;
592
+ max-width: calc(100vw - 1rem);
593
+ border: 1px solid var(--ml-divider);
594
+ border-radius: var(--ml-radius);
595
+ background: var(--ml-surface);
596
+ color: var(--ml-text);
597
+ box-shadow:
598
+ 0 4px 6px -1px rgb(0 0 0 / 10%),
599
+ 0 2px 4px -2px rgb(0 0 0 / 10%);
600
+ font-family: var(--ml-font-family);
601
+ font-size: 1rem;
602
+ }
603
+
604
+ .ml-select__search {
605
+ position: relative;
606
+ padding: 0.5rem 0.5rem 0.25rem;
607
+
608
+ .ml-input {
609
+ width: 100%;
610
+ padding-right: 2rem;
611
+ }
612
+
613
+ > .ml-icon {
614
+ position: absolute;
615
+ top: 50%;
616
+ right: 1.25rem;
617
+ margin-top: 0.125rem;
618
+ color: var(--ml-text-muted);
619
+ transform: translateY(-50%);
620
+ }
621
+ }
622
+
623
+ .ml-select__list {
624
+ max-height: 15rem;
625
+ margin: 0;
626
+ padding: 0.25rem;
627
+ overflow-y: auto;
628
+ list-style: none;
629
+ }
630
+
631
+ .ml-select__option {
632
+ display: flex;
633
+ align-items: center;
634
+ gap: 0.5rem;
635
+ padding: 0.5rem 0.75rem;
636
+ border-radius: calc(var(--ml-radius) - 1px);
637
+ cursor: pointer;
638
+ }
639
+
640
+ // La voce attiva (sotto il mouse o scelta con le frecce) e quella scelta
641
+ // hanno la barra blu a sinistra e il fondo azzurro, come nel menu a cascata
642
+ // e nella ricerca indirizzi (richiesta di Vito).
643
+ .ml-select__option--active,
644
+ .ml-select__option--selected {
645
+ background: var(--ml-selection-background);
646
+ color: var(--ml-primary-hover);
647
+ box-shadow: inset 3px 0 0 var(--ml-primary);
648
+ }
649
+
650
+ .ml-select__option--selected {
651
+ font-weight: 600;
652
+ }
653
+
654
+ .ml-select__check {
655
+ display: inline-flex;
656
+ flex: none;
657
+ align-items: center;
658
+ justify-content: center;
659
+ width: 1.125rem;
660
+ height: 1.125rem;
661
+ border: 1px solid var(--ml-control-border);
662
+ border-radius: 4px;
663
+ background: var(--ml-surface);
664
+ color: var(--ml-on-primary);
665
+ font-size: 0.7rem;
666
+
667
+ .ml-icon {
668
+ visibility: hidden;
669
+ }
670
+ }
671
+
672
+ .ml-select__option--selected .ml-select__check {
673
+ border-color: var(--ml-primary);
674
+ background: var(--ml-primary);
675
+
676
+ .ml-icon {
677
+ visibility: visible;
678
+ }
679
+ }
680
+
681
+ .ml-select__group {
682
+ padding: 0.6rem 0.75rem 0.3rem;
683
+ color: var(--ml-text-secondary);
684
+ font-size: 0.8rem;
685
+ font-weight: 600;
686
+ }
687
+
688
+ .ml-select__empty {
689
+ padding: 0.5rem 0.75rem;
690
+ color: var(--ml-text-muted);
691
+ }
692
+
693
+ // ---- campo con le proposte -----------------------------------------------
694
+
695
+ // Ha il disegno della tendina : qui solo quello che cambia. Si scrive, quindi
696
+ // il cursore è quello del testo, e la freccia è un pulsante.
697
+ .ml-autocomplete .ml-select__trigger {
698
+ cursor: text;
699
+ }
700
+
701
+ .ml-autocomplete__dropdown {
702
+ align-self: stretch;
703
+ padding: 0;
704
+ border: 0;
705
+ border-left: 1px solid var(--ml-divider);
706
+ background: transparent;
707
+ cursor: pointer;
708
+
709
+ &:disabled {
710
+ cursor: default;
711
+ }
712
+ }
713
+
714
+ // ---- etichetta sul campo --------------------------------------------------
715
+
716
+ // I campi che la fanno salire : quelli con un valore (`ml-input--filled`,
717
+ // `ml-select--filled`), col fuoco, o con un segnaposto da non coprire.
718
+
719
+ .ml-floatlabel {
720
+ position: relative;
721
+ display: block;
722
+
723
+ > label {
724
+ position: absolute;
725
+ top: 50%;
726
+ left: 0.75rem;
727
+ margin: 0;
728
+ color: var(--ml-text-muted);
729
+ font-size: 1rem;
730
+ font-weight: 500;
731
+ line-height: 1;
732
+ pointer-events: none;
733
+ transform: translateY(-50%);
734
+ transition: all 0.2s ease;
735
+ }
736
+
737
+ &:has(textarea) > label {
738
+ top: 0.5rem;
739
+ transform: none;
740
+ }
741
+
742
+ &:has(
743
+ input:focus,
744
+ textarea:focus,
745
+ input:-webkit-autofill,
746
+ .ml-input--filled,
747
+ .ml-select--filled,
748
+ .ml-select--focus,
749
+ input[placeholder]:not([placeholder=""]),
750
+ textarea[placeholder]:not([placeholder=""])
751
+ )
752
+ > label {
753
+ top: -1.25rem;
754
+ transform: none;
755
+ color: var(--ml-text-secondary);
756
+ font-size: 0.75rem;
757
+ font-weight: 400;
758
+ }
759
+
760
+ &:has(input:focus, textarea:focus, .ml-select--focus) > label {
761
+ color: var(--ml-primary);
762
+ }
763
+ }
764
+
765
+ // Sul bordo del campo : il fondo del campo sotto l'etichetta copre la riga.
766
+ .ml-floatlabel--on:has(
767
+ input:focus,
768
+ textarea:focus,
769
+ input:-webkit-autofill,
770
+ .ml-input--filled,
771
+ .ml-select--filled,
772
+ .ml-select--focus,
773
+ input[placeholder]:not([placeholder=""]),
774
+ textarea[placeholder]:not([placeholder=""])
775
+ )
776
+ > label {
777
+ top: 0;
778
+ padding: 0 0.125rem;
779
+ border-radius: 2px;
780
+ background: var(--ml-surface);
781
+ transform: translateY(-50%);
782
+ }
783
+
784
+ .ml-floatlabel:has(.ml-select--fluid, .w-full) {
785
+ width: 100%;
786
+ }
787
+
788
+ // ---- campo numerico -------------------------------------------------------
789
+
790
+ .ml-inputnumber {
791
+ position: relative;
792
+ display: inline-flex;
793
+ min-width: 0;
794
+ vertical-align: middle;
795
+
796
+ > .ml-input {
797
+ flex: 1 1 auto;
798
+ width: 100%;
799
+ min-width: 0;
800
+ }
801
+ }
802
+
803
+ .ml-inputnumber--buttons > .ml-input {
804
+ padding-right: 2rem;
805
+ }
806
+
807
+ .ml-inputnumber__buttons {
808
+ position: absolute;
809
+ top: 1px;
810
+ right: 1px;
811
+ bottom: 1px;
812
+ display: flex;
813
+ flex-direction: column;
814
+ width: 1.75rem;
815
+
816
+ button {
817
+ display: flex;
818
+ flex: 1 1 0;
819
+ align-items: center;
820
+ justify-content: center;
821
+ padding: 0;
822
+ border: 0;
823
+ background: transparent;
824
+ color: var(--ml-text-muted);
825
+ font-size: 0.75rem;
826
+ cursor: pointer;
827
+
828
+ &:disabled {
829
+ cursor: default;
830
+ opacity: 0.6;
831
+ }
832
+ }
833
+ }
834
+
835
+ // ---- interruttore ---------------------------------------------------------
836
+
837
+ .ml-switch {
838
+ position: relative;
839
+ display: inline-flex;
840
+ flex: none;
841
+ width: 2.5rem;
842
+ height: 1.5rem;
843
+ vertical-align: middle;
844
+ }
845
+
846
+ .ml-switch__input {
847
+ position: absolute;
848
+ inset: 0;
849
+ z-index: 1;
850
+ margin: 0;
851
+ opacity: 0;
852
+ cursor: pointer;
853
+
854
+ &:disabled {
855
+ cursor: default;
856
+ }
857
+ }
858
+
859
+ .ml-switch__track {
860
+ position: relative;
861
+ width: 100%;
862
+ height: 100%;
863
+ border-radius: 999px;
864
+ background: var(--ml-control-border);
865
+ transition: background-color 0.15s;
866
+ }
867
+
868
+ .ml-switch__handle {
869
+ position: absolute;
870
+ top: 0.25rem;
871
+ left: 0.25rem;
872
+ width: 1rem;
873
+ height: 1rem;
874
+ border-radius: 50%;
875
+ background: var(--ml-surface);
876
+ box-shadow: 0 1px 2px rgb(0 0 0 / 20%);
877
+ transition: transform 0.15s;
878
+ }
879
+
880
+ .ml-switch--on .ml-switch__track {
881
+ background: var(--ml-primary);
882
+ }
883
+
884
+ .ml-switch--on .ml-switch__handle {
885
+ transform: translateX(1rem);
886
+ }
887
+
888
+ .ml-switch--disabled {
889
+ opacity: 0.6;
890
+ }
891
+
892
+ .ml-switch__input:focus-visible + .ml-switch__track {
893
+ outline: 2px solid var(--ml-focus-color);
894
+ outline-offset: 2px;
895
+ }
896
+
897
+ // ---- cursore --------------------------------------------------------------
898
+
899
+ .ml-slider {
900
+ display: block;
901
+ min-width: 6rem;
902
+ }
903
+
904
+ // La pista è piena fino al pollice : la parte piena la scrive il componente
905
+ // in `--ml-slider-fill`.
906
+ .ml-slider__input {
907
+ width: 100%;
908
+ height: 1.25rem;
909
+ margin: 0;
910
+ background: transparent;
911
+ cursor: pointer;
912
+ appearance: none;
913
+
914
+ &::-webkit-slider-runnable-track {
915
+ height: 0.25rem;
916
+ border-radius: 999px;
917
+ background: linear-gradient(
918
+ to right,
919
+ var(--ml-primary) var(--ml-slider-fill, 0%),
920
+ var(--ml-control-border) var(--ml-slider-fill, 0%)
921
+ );
922
+ }
923
+
924
+ &::-moz-range-track {
925
+ height: 0.25rem;
926
+ border-radius: 999px;
927
+ background: var(--ml-control-border);
928
+ }
929
+
930
+ &::-moz-range-progress {
931
+ height: 0.25rem;
932
+ border-radius: 999px;
933
+ background: var(--ml-primary);
934
+ }
935
+
936
+ &::-webkit-slider-thumb {
937
+ width: 1.1rem;
938
+ height: 1.1rem;
939
+ margin-top: -0.425rem;
940
+ border: 2px solid var(--ml-primary);
941
+ border-radius: 50%;
942
+ background: var(--ml-surface);
943
+ appearance: none;
944
+ }
945
+
946
+ &::-moz-range-thumb {
947
+ width: 1rem;
948
+ height: 1rem;
949
+ border: 2px solid var(--ml-primary);
950
+ border-radius: 50%;
951
+ background: var(--ml-surface);
952
+ }
953
+
954
+ &:focus-visible {
955
+ outline: 2px solid var(--ml-focus-color);
956
+ outline-offset: 2px;
957
+ }
958
+
959
+ &:disabled {
960
+ cursor: default;
961
+ opacity: 0.6;
962
+ }
963
+ }
964
+
965
+ // ---- scelta a bottoni -----------------------------------------------------
966
+
967
+ .ml-selectbutton {
968
+ display: inline-flex;
969
+ flex-wrap: wrap;
970
+ }
971
+
972
+ .ml-selectbutton__option {
973
+ min-height: 2.25rem;
974
+ padding: 0.4rem 0.85rem;
975
+ border: 1px solid var(--ml-control-border);
976
+ background: var(--ml-surface);
977
+ color: var(--ml-text);
978
+ font: inherit;
979
+ cursor: pointer;
980
+
981
+ & + & {
982
+ margin-left: -1px;
983
+ }
984
+
985
+ &:first-child {
986
+ border-radius: var(--ml-radius) 0 0 var(--ml-radius);
987
+ }
988
+
989
+ &:last-child {
990
+ border-radius: 0 var(--ml-radius) var(--ml-radius) 0;
991
+ }
992
+
993
+ &:focus-visible {
994
+ position: relative;
995
+ z-index: 1;
996
+ outline: 2px solid var(--ml-focus-color);
997
+ outline-offset: 1px;
998
+ }
999
+ }
1000
+
1001
+ .ml-selectbutton__option--on {
1002
+ position: relative;
1003
+ border-color: var(--ml-primary);
1004
+ background: var(--ml-primary);
1005
+ color: var(--ml-on-primary);
1006
+ }
1007
+
1008
+ // ---- radio ----------------------------------------------------------------
1009
+
1010
+ .ml-radio {
1011
+ position: relative;
1012
+ display: inline-flex;
1013
+ flex: none;
1014
+ width: 1.25rem;
1015
+ height: 1.25rem;
1016
+ vertical-align: middle;
1017
+ }
1018
+
1019
+ .ml-radio__input {
1020
+ position: absolute;
1021
+ inset: 0;
1022
+ z-index: 1;
1023
+ margin: 0;
1024
+ opacity: 0;
1025
+ cursor: pointer;
1026
+ }
1027
+
1028
+ .ml-radio__dot {
1029
+ width: 100%;
1030
+ height: 100%;
1031
+ border: 1px solid var(--ml-control-border);
1032
+ border-radius: 50%;
1033
+ background: var(--ml-surface);
1034
+ }
1035
+
1036
+ .ml-radio--checked .ml-radio__dot {
1037
+ border: 6px solid var(--ml-primary);
1038
+ }
1039
+
1040
+ .ml-radio__input:focus-visible + .ml-radio__dot {
1041
+ outline: 2px solid var(--ml-focus-color);
1042
+ outline-offset: 2px;
1043
+ }
1044
+
1045
+ .ml-radio--disabled {
1046
+ opacity: 0.6;
1047
+ }
1048
+
1049
+ // ---- popover --------------------------------------------------------------
1050
+
1051
+ .ml-popover {
1052
+ padding: 0.75rem;
1053
+ border: 1px solid var(--ml-divider);
1054
+ border-radius: var(--ml-radius);
1055
+ background: var(--ml-surface);
1056
+ color: var(--ml-text);
1057
+ box-shadow:
1058
+ 0 4px 6px -1px rgb(0 0 0 / 10%),
1059
+ 0 2px 4px -2px rgb(0 0 0 / 10%);
1060
+ font-family: var(--ml-font-family);
1061
+ }
1062
+
1063
+ // ---- cassetto -------------------------------------------------------------
1064
+
1065
+ .ml-drawer-mask {
1066
+ position: fixed;
1067
+ inset: 0;
1068
+ z-index: 1100;
1069
+ background: rgb(0 0 0 / 50%);
1070
+ }
1071
+
1072
+ .ml-drawer {
1073
+ position: fixed;
1074
+ top: 0;
1075
+ bottom: 0;
1076
+ z-index: 1101;
1077
+ display: flex;
1078
+ flex-direction: column;
1079
+ width: min(20rem, 100vw);
1080
+ background: var(--ml-surface);
1081
+ color: var(--ml-text);
1082
+ box-shadow: var(--ml-shadow);
1083
+ outline: none;
1084
+
1085
+ &[data-position="left"] {
1086
+ left: 0;
1087
+ }
1088
+
1089
+ &[data-position="right"] {
1090
+ right: 0;
1091
+ }
1092
+ }
1093
+
1094
+ .ml-drawer__close {
1095
+ align-self: flex-end;
1096
+ margin: 0.5rem;
1097
+ }
1098
+
1099
+ .ml-drawer__content {
1100
+ flex: 1 1 auto;
1101
+ min-height: 0;
1102
+ padding: 0 1.25rem 1.25rem;
1103
+ overflow-y: auto;
1104
+ }
1105
+
1106
+ // ---- rotella --------------------------------------------------------------
1107
+
1108
+ .ml-spinner {
1109
+ display: inline-block;
1110
+ width: 2rem;
1111
+ height: 2rem;
1112
+
1113
+ svg {
1114
+ width: 100%;
1115
+ height: 100%;
1116
+ animation: ml-spin 1.4s linear infinite;
1117
+ }
1118
+
1119
+ circle {
1120
+ stroke: var(--ml-primary);
1121
+ stroke-dasharray: 90 150;
1122
+ stroke-linecap: round;
1123
+ }
1124
+ }
1125
+
1126
+ @keyframes ml-spin {
1127
+ to {
1128
+ transform: rotate(360deg);
1129
+ }
1130
+ }
1131
+
1132
+ @media (prefers-reduced-motion: reduce) {
1133
+ .ml-spinner svg {
1134
+ animation-duration: 4s;
1135
+ }
1136
+ }
1137
+
1138
+ // ---- solo css : separatore, segnaposto, etichette, campo con icona --------
1139
+
1140
+ .ml-divider {
1141
+ height: 0;
1142
+ margin: 1rem 0;
1143
+ border: 0;
1144
+ border-top: 1px solid var(--ml-divider);
1145
+ }
1146
+
1147
+ // Il segnaposto di qualcosa che sta arrivando : quadrato, della misura che
1148
+ // gli si dà.
1149
+ .ml-skeleton {
1150
+ display: inline-block;
1151
+ width: 2rem;
1152
+ height: 2rem;
1153
+ border-radius: var(--ml-radius);
1154
+ background: linear-gradient(
1155
+ 90deg,
1156
+ var(--ml-surface-muted) 25%,
1157
+ var(--ml-divider) 50%,
1158
+ var(--ml-surface-muted) 75%
1159
+ );
1160
+ background-size: 200% 100%;
1161
+ animation: ml-skeleton 1.2s ease-in-out infinite;
1162
+ }
1163
+
1164
+ @keyframes ml-skeleton {
1165
+ to {
1166
+ background-position: -200% 0;
1167
+ }
1168
+ }
1169
+
1170
+ // Un chip che si clicca porta `role="button"` : il cursore lo dice.
1171
+ .ml-chip[role="button"] {
1172
+ cursor: pointer;
1173
+ }
1174
+
1175
+ .ml-chip {
1176
+ display: inline-flex;
1177
+ align-items: center;
1178
+ gap: 0.35rem;
1179
+ padding: 0.3rem 0.75rem;
1180
+ border-radius: 999px;
1181
+ background: var(--ml-surface-muted);
1182
+ color: var(--ml-text);
1183
+ font-size: 0.875rem;
1184
+ }
1185
+
1186
+ .ml-badge {
1187
+ display: inline-flex;
1188
+ align-items: center;
1189
+ justify-content: center;
1190
+ min-width: 1.5rem;
1191
+ height: 1.5rem;
1192
+ padding: 0 0.4rem;
1193
+ border-radius: 999px;
1194
+ background: var(--ml-primary);
1195
+ color: var(--ml-on-primary);
1196
+ font-size: 0.75rem;
1197
+ font-weight: 700;
1198
+
1199
+ &[data-severity="secondary"] {
1200
+ background: var(--ml-surface-muted);
1201
+ color: var(--ml-text);
1202
+ }
1203
+ }
1204
+
1205
+ // Un campo con l'icona dentro, a sinistra.
1206
+ .ml-iconfield {
1207
+ position: relative;
1208
+ display: inline-flex;
1209
+
1210
+ > .ml-input {
1211
+ width: 100%;
1212
+ padding-left: 2.5rem;
1213
+ }
1214
+ }
1215
+
1216
+ .ml-inputicon {
1217
+ position: absolute;
1218
+ top: 50%;
1219
+ left: 0.85rem;
1220
+ color: var(--ml-text-muted);
1221
+ pointer-events: none;
1222
+ transform: translateY(-50%);
1223
+ }
1224
+
1225
+ // ---- schede ---------------------------------------------------------------
1226
+
1227
+ .ml-tabs {
1228
+ display: block;
1229
+ }
1230
+
1231
+ .ml-tablist {
1232
+ display: flex;
1233
+ min-width: 0;
1234
+ border-bottom: 1px solid var(--ml-divider);
1235
+ }
1236
+
1237
+ .ml-tablist__viewport {
1238
+ display: flex;
1239
+ flex: 1 1 auto;
1240
+ min-width: 0;
1241
+ overflow-x: auto;
1242
+ overflow-y: hidden;
1243
+ scrollbar-width: thin;
1244
+ }
1245
+
1246
+ .ml-tablist--scrollable .ml-tablist__viewport {
1247
+ scrollbar-width: none;
1248
+
1249
+ &::-webkit-scrollbar {
1250
+ display: none;
1251
+ }
1252
+ }
1253
+
1254
+ .ml-tablist__scroll-button {
1255
+ display: inline-flex;
1256
+ flex: 0 0 2.5rem;
1257
+ align-items: center;
1258
+ justify-content: center;
1259
+ padding: 0;
1260
+ border: 0;
1261
+ background: var(--ml-surface);
1262
+ color: var(--ml-primary);
1263
+ cursor: pointer;
1264
+
1265
+ svg {
1266
+ width: 1rem;
1267
+ height: 1rem;
1268
+ fill: none;
1269
+ stroke: currentColor;
1270
+ stroke-width: 2;
1271
+ }
1272
+
1273
+ &:disabled {
1274
+ opacity: 0.35;
1275
+ cursor: default;
1276
+ }
1277
+ &:focus-visible {
1278
+ outline: 2px solid var(--ml-focus-color);
1279
+ outline-offset: -2px;
1280
+ }
1281
+ }
1282
+
1283
+ .ml-tab {
1284
+ flex: none;
1285
+ margin-bottom: -1px;
1286
+ padding: 0.75rem 1rem;
1287
+ border-bottom: 2px solid transparent;
1288
+ color: var(--ml-text-secondary);
1289
+ font-weight: 600;
1290
+ white-space: nowrap;
1291
+ cursor: pointer;
1292
+ user-select: none;
1293
+ transition:
1294
+ color 0.15s,
1295
+ border-color 0.15s;
1296
+
1297
+ &:focus-visible {
1298
+ outline: 2px solid var(--ml-focus-color);
1299
+ outline-offset: -2px;
1300
+ }
1301
+ }
1302
+
1303
+ .ml-tab--active {
1304
+ border-bottom-color: var(--ml-primary);
1305
+ color: var(--ml-primary);
1306
+ }
1307
+
1308
+ .ml-tabpanels {
1309
+ display: block;
1310
+ padding-top: 0.75rem;
1311
+ }
1312
+
1313
+ .ml-tabpanel {
1314
+ display: block;
1315
+
1316
+ &[hidden] {
1317
+ display: none;
1318
+ }
1319
+ }
1320
+
1321
+ @media (hover: hover) and (pointer: fine) {
1322
+ .ml-tab:not(.ml-tab--active):hover {
1323
+ color: var(--ml-text-heading);
1324
+ }
1325
+ }
1326
+
1327
+ // ---- fisarmonica ----------------------------------------------------------
1328
+
1329
+ .ml-accordion {
1330
+ display: block;
1331
+ }
1332
+
1333
+ .ml-accordion-panel {
1334
+ display: block;
1335
+ border-bottom: 1px solid var(--ml-divider);
1336
+ }
1337
+
1338
+ .ml-accordion-header {
1339
+ display: flex;
1340
+ align-items: center;
1341
+ justify-content: space-between;
1342
+ gap: 0.5rem;
1343
+ padding: 1rem 1.125rem;
1344
+ color: var(--ml-text-secondary);
1345
+ font-weight: 600;
1346
+ cursor: pointer;
1347
+ user-select: none;
1348
+
1349
+ &:focus-visible {
1350
+ outline: 2px solid var(--ml-focus-color);
1351
+ outline-offset: -2px;
1352
+ }
1353
+ }
1354
+
1355
+ .ml-accordion-header__title {
1356
+ flex: 1 1 auto;
1357
+ min-width: 0;
1358
+ }
1359
+
1360
+ .ml-accordion-header__icon {
1361
+ flex: none;
1362
+ font-size: 0.75rem;
1363
+ transition: transform 0.15s;
1364
+ }
1365
+
1366
+ .ml-accordion-panel--active > .ml-accordion-header {
1367
+ color: var(--ml-text-heading);
1368
+ }
1369
+
1370
+ .ml-accordion-panel--active > .ml-accordion-header .ml-accordion-header__icon {
1371
+ transform: rotate(180deg);
1372
+ }
1373
+
1374
+ // L'apertura si anima : la riga della griglia passa da `0fr` a `1fr`, cioè
1375
+ // dall'altezza zero a quella del contenuto, che con `height: auto` non si
1376
+ // potrebbe animare. `visibility` cambia subito all'apertura e alla fine della
1377
+ // chiusura, così il contenuto si vede mentre scende e sparisce dopo.
1378
+ .ml-accordion-content {
1379
+ display: grid;
1380
+ grid-template-rows: 0fr;
1381
+ visibility: hidden;
1382
+ transition:
1383
+ grid-template-rows 0.2s ease,
1384
+ visibility 0s linear 0.2s;
1385
+ }
1386
+
1387
+ .ml-accordion-content--open {
1388
+ grid-template-rows: 1fr;
1389
+ visibility: visible;
1390
+ transition:
1391
+ grid-template-rows 0.2s ease,
1392
+ visibility 0s linear 0s;
1393
+ }
1394
+
1395
+ .ml-accordion-content__clip {
1396
+ min-height: 0;
1397
+ overflow: hidden;
1398
+ }
1399
+
1400
+ @media (prefers-reduced-motion: reduce) {
1401
+ .ml-accordion-content,
1402
+ .ml-accordion-content--open {
1403
+ transition: none;
1404
+ }
1405
+ }
1406
+
1407
+ .ml-accordion-content__inner {
1408
+ padding: 0 1.125rem 1.125rem;
1409
+ }
1410
+
1411
+ // ---- paginatore -----------------------------------------------------------
1412
+
1413
+ .ml-paginator {
1414
+ display: flex;
1415
+ flex-wrap: wrap;
1416
+ align-items: center;
1417
+ justify-content: center;
1418
+ gap: 0.25rem;
1419
+ padding: 0.5rem 0;
1420
+ }
1421
+
1422
+ .ml-paginator__pages {
1423
+ display: inline-flex;
1424
+ gap: 0.25rem;
1425
+ }
1426
+
1427
+ .ml-paginator__button {
1428
+ display: inline-flex;
1429
+ align-items: center;
1430
+ justify-content: center;
1431
+ min-width: 2.25rem;
1432
+ height: 2.25rem;
1433
+ padding: 0 0.5rem;
1434
+ border: 0;
1435
+ border-radius: var(--ml-radius);
1436
+ background: transparent;
1437
+ color: var(--ml-text-secondary);
1438
+ font: inherit;
1439
+ cursor: pointer;
1440
+
1441
+ &:disabled {
1442
+ color: var(--ml-text-muted);
1443
+ cursor: default;
1444
+ opacity: 0.5;
1445
+ }
1446
+
1447
+ &:focus-visible {
1448
+ outline: 2px solid var(--ml-primary);
1449
+ outline-offset: 1px;
1450
+ }
1451
+ }
1452
+
1453
+ .ml-paginator__button--current {
1454
+ background: var(--ml-selection-background);
1455
+ color: var(--ml-primary-hover);
1456
+ font-weight: 600;
1457
+ }
1458
+
1459
+ @media (hover: hover) and (pointer: fine) {
1460
+ .ml-paginator__button:not(:disabled, .ml-paginator__button--current):hover {
1461
+ background: var(--ml-surface-muted);
1462
+ color: var(--ml-text-heading);
1463
+ }
1464
+ }
1465
+
1466
+ // Il conto «3 / 12» al posto dei numeri, dove i numeri non ci stanno.
1467
+ .ml-paginator__count {
1468
+ display: none;
1469
+ padding: 0 0.5rem;
1470
+ color: var(--ml-text-secondary);
1471
+ font-variant-numeric: tabular-nums;
1472
+ }
1473
+
1474
+ @media (max-width: 480px) {
1475
+ .ml-paginator__pages {
1476
+ display: none;
1477
+ }
1478
+
1479
+ .ml-paginator__count {
1480
+ display: inline;
1481
+ }
1482
+ }
1483
+
1484
+ // ---- tendina a più livelli -------------------------------------------------
1485
+
1486
+ // Le colonne stanno una di fianco all'altra, ognuna una card allineata alla
1487
+ // voce del gruppo da cui parte : il riquadro dell'overlay non ha fondo suo.
1488
+ .ml-cascadeselect__columns {
1489
+ display: flex;
1490
+ align-items: flex-start;
1491
+ gap: 0.375rem;
1492
+ }
1493
+
1494
+ // Verso sinistra la prima colonna sta a destra, sotto il campo.
1495
+ .ml-cascadeselect__columns--to-left {
1496
+ flex-direction: row-reverse;
1497
+ }
1498
+
1499
+ .ml-cascadeselect__column {
1500
+ flex: none;
1501
+ min-width: 12rem;
1502
+ max-width: min(20rem, calc(100vw - 1rem));
1503
+ overflow: hidden;
1504
+ border: 0;
1505
+ border-radius: 10px;
1506
+ box-shadow:
1507
+ 0 10px 28px rgb(15 23 42 / 16%),
1508
+ 0 0 0 1px rgb(15 23 42 / 6%);
1509
+ animation: ml-cascade-in 0.14s ease-out;
1510
+
1511
+ .ml-select__list {
1512
+ max-height: min(22rem, 70dvh);
1513
+ padding: 0.375rem;
1514
+ }
1515
+
1516
+ .ml-select__option {
1517
+ min-height: 2.5rem;
1518
+ padding: 0.5rem 0.625rem 0.5rem 0.75rem;
1519
+ border-radius: 6px;
1520
+ }
1521
+ }
1522
+
1523
+ // Una colonna nuova entra spostandosi di poco dal lato da cui si apre.
1524
+ @keyframes ml-cascade-in {
1525
+ from {
1526
+ opacity: 0;
1527
+ transform: translateX(-0.25rem);
1528
+ }
1529
+ }
1530
+
1531
+ .ml-cascadeselect__columns--to-left .ml-cascadeselect__column {
1532
+ animation-name: ml-cascade-in-left;
1533
+ }
1534
+
1535
+ @keyframes ml-cascade-in-left {
1536
+ from {
1537
+ opacity: 0;
1538
+ transform: translateX(0.25rem);
1539
+ }
1540
+ }
1541
+
1542
+ @media (prefers-reduced-motion: reduce) {
1543
+ .ml-cascadeselect__column {
1544
+ animation: none;
1545
+ }
1546
+ }
1547
+
1548
+ // In cima a una colonna, il nome del gruppo da cui viene.
1549
+ .ml-cascadeselect__title {
1550
+ padding: 0.375rem 0.75rem 0.5rem;
1551
+ margin-bottom: 0.25rem;
1552
+ border-bottom: 1px solid var(--ml-divider);
1553
+ color: var(--ml-text-muted);
1554
+ font-size: 0.75rem;
1555
+ font-weight: 600;
1556
+ letter-spacing: 0.04em;
1557
+ text-transform: uppercase;
1558
+ }
1559
+
1560
+ .ml-cascadeselect__text {
1561
+ flex: 1 1 auto;
1562
+ min-width: 0;
1563
+ overflow: hidden;
1564
+ text-overflow: ellipsis;
1565
+ white-space: nowrap;
1566
+ }
1567
+
1568
+ .ml-cascadeselect__column .ml-icon-chevron-right {
1569
+ flex: none;
1570
+ color: var(--ml-text-muted);
1571
+ font-size: 0.75rem;
1572
+ transition:
1573
+ color 0.15s,
1574
+ transform 0.15s;
1575
+ }
1576
+
1577
+ // La voce sotto il mouse, o scelta da tastiera, ha la barra blu e il fondo
1578
+ // azzurro del gruppo aperto, in qualunque colonna (richiesta di Vito).
1579
+ .ml-cascadeselect__column
1580
+ .ml-select__option--active:not(.ml-cascadeselect__option--disabled) {
1581
+ background: var(--ml-selection-background);
1582
+ color: var(--ml-primary-hover);
1583
+ box-shadow: inset 3px 0 0 var(--ml-primary);
1584
+ }
1585
+
1586
+ .ml-cascadeselect__columns--to-left
1587
+ .ml-select__option--active:not(.ml-cascadeselect__option--disabled) {
1588
+ box-shadow: inset -3px 0 0 var(--ml-primary);
1589
+ }
1590
+
1591
+ // Il gruppo aperto : la barra blu a sinistra, come le schede del builder, e
1592
+ // la freccia nel colore del tema spostata verso la sua colonna.
1593
+ .ml-cascadeselect__option--open {
1594
+ background: var(--ml-selection-background);
1595
+ color: var(--ml-primary-hover);
1596
+ font-weight: 600;
1597
+ box-shadow: inset 3px 0 0 var(--ml-primary);
1598
+
1599
+ .ml-icon-chevron-right {
1600
+ color: var(--ml-primary);
1601
+ transform: translateX(2px);
1602
+ }
1603
+ }
1604
+
1605
+ .ml-cascadeselect__columns--to-left .ml-cascadeselect__option--open {
1606
+ box-shadow: inset -3px 0 0 var(--ml-primary);
1607
+ }
1608
+
1609
+ // Una voce spenta non prende la barra né il fondo, nemmeno sotto il mouse.
1610
+ .ml-cascadeselect__option--disabled.ml-select__option--active {
1611
+ background: transparent;
1612
+ color: var(--ml-text-muted);
1613
+ box-shadow: none;
1614
+ }
1615
+
1616
+ .ml-cascadeselect__option--disabled {
1617
+ color: var(--ml-text-muted);
1618
+ cursor: not-allowed;
1619
+ opacity: 0.55;
1620
+ }
1621
+
1622
+ .ml-cascadeselect__back {
1623
+ margin-bottom: 0.25rem;
1624
+ border-bottom: 1px solid var(--ml-divider);
1625
+ border-radius: 0;
1626
+ color: var(--ml-text-secondary);
1627
+ font-weight: 600;
1628
+
1629
+ .ml-icon {
1630
+ color: var(--ml-primary);
1631
+ font-size: 0.75rem;
1632
+ }
1633
+ }
1634
+
1635
+ // ---- calendario -----------------------------------------------------------
1636
+
1637
+ .ml-datepicker {
1638
+ position: relative;
1639
+ display: inline-flex;
1640
+ min-width: 0;
1641
+ vertical-align: middle;
1642
+ }
1643
+
1644
+ .ml-datepicker__field {
1645
+ position: relative;
1646
+ display: flex;
1647
+ align-items: center;
1648
+ width: 100%;
1649
+ }
1650
+
1651
+ // Il campo non si scrive : il cursore è quello di un pulsante, e lo spazio a
1652
+ // destra è per la X e l'icona.
1653
+ .ml-datepicker__input {
1654
+ width: 100%;
1655
+ min-width: 10rem;
1656
+ padding-right: 4.25rem;
1657
+ cursor: pointer;
1658
+
1659
+ &:disabled {
1660
+ cursor: default;
1661
+ }
1662
+ }
1663
+
1664
+ .ml-datepicker__clear,
1665
+ .ml-datepicker__icon {
1666
+ position: absolute;
1667
+ top: 50%;
1668
+ display: inline-flex;
1669
+ align-items: center;
1670
+ justify-content: center;
1671
+ width: 2rem;
1672
+ height: 2rem;
1673
+ padding: 0;
1674
+ border: 0;
1675
+ border-radius: 50%;
1676
+ background: transparent;
1677
+ color: var(--ml-text-muted);
1678
+ cursor: pointer;
1679
+ transform: translateY(-50%);
1680
+
1681
+ &:disabled {
1682
+ cursor: default;
1683
+ }
1684
+ }
1685
+
1686
+ .ml-datepicker__icon {
1687
+ right: 0.25rem;
1688
+ color: var(--ml-primary);
1689
+ }
1690
+
1691
+ .ml-datepicker__clear {
1692
+ right: 2.25rem;
1693
+ }
1694
+
1695
+ .ml-datepicker:not(:has(.ml-datepicker__icon)) .ml-datepicker__clear {
1696
+ right: 0.25rem;
1697
+ }
1698
+
1699
+ .ml-datepicker__panel {
1700
+ width: 20rem;
1701
+ max-width: calc(100vw - 1rem);
1702
+ padding: 0.75rem;
1703
+ border-radius: 10px;
1704
+ background: var(--ml-surface);
1705
+ color: var(--ml-text);
1706
+ box-shadow:
1707
+ 0 10px 28px rgb(15 23 42 / 16%),
1708
+ 0 0 0 1px rgb(15 23 42 / 6%);
1709
+ font-family: var(--ml-font-family);
1710
+ }
1711
+
1712
+ // Al centro dello schermo, sul telefono : un po' più largo, e i giorni più
1713
+ // grandi da toccare (le misure stanno in fondo, con le altre per il tocco).
1714
+ .ml-datepicker__panel--sheet {
1715
+ width: min(24rem, calc(100vw - 2rem));
1716
+ }
1717
+
1718
+ .ml-datepicker__head {
1719
+ display: flex;
1720
+ align-items: center;
1721
+ gap: 0.125rem;
1722
+ margin-bottom: 0.5rem;
1723
+ }
1724
+
1725
+ .ml-datepicker__title {
1726
+ flex: 1 1 auto;
1727
+ color: var(--ml-text-heading);
1728
+ font-weight: 600;
1729
+ text-align: center;
1730
+ text-transform: capitalize;
1731
+ }
1732
+
1733
+ .ml-datepicker__nav {
1734
+ display: inline-flex;
1735
+ flex: none;
1736
+ align-items: center;
1737
+ justify-content: center;
1738
+ width: 2rem;
1739
+ height: 2rem;
1740
+ padding: 0;
1741
+ border: 0;
1742
+ border-radius: 6px;
1743
+ background: transparent;
1744
+ color: var(--ml-text-secondary);
1745
+ cursor: pointer;
1746
+ }
1747
+
1748
+ .ml-datepicker__grid {
1749
+ display: grid;
1750
+ grid-template-columns: repeat(7, 1fr);
1751
+ gap: 0.125rem;
1752
+ }
1753
+
1754
+ .ml-datepicker__weekday {
1755
+ padding: 0.25rem 0;
1756
+ color: var(--ml-text-muted);
1757
+ font-size: 0.75rem;
1758
+ font-weight: 600;
1759
+ text-align: center;
1760
+ text-transform: capitalize;
1761
+ }
1762
+
1763
+ .ml-datepicker__day {
1764
+ aspect-ratio: 1;
1765
+ display: inline-flex;
1766
+ align-items: center;
1767
+ justify-content: center;
1768
+ min-width: 0;
1769
+ padding: 0;
1770
+ border: 0;
1771
+ border-radius: 8px;
1772
+ background: transparent;
1773
+ color: var(--ml-text);
1774
+ font: inherit;
1775
+ font-size: 0.9rem;
1776
+ cursor: pointer;
1777
+ }
1778
+
1779
+ .ml-datepicker__day--other {
1780
+ color: var(--ml-text-muted);
1781
+ opacity: 0.6;
1782
+ }
1783
+
1784
+ .ml-datepicker__day--today {
1785
+ box-shadow: inset 0 0 0 1px var(--ml-primary);
1786
+ color: var(--ml-primary-hover);
1787
+ font-weight: 600;
1788
+ }
1789
+
1790
+ .ml-datepicker__day--in-range {
1791
+ border-radius: 0;
1792
+ background: var(--ml-selection-background);
1793
+ color: var(--ml-primary-hover);
1794
+ }
1795
+
1796
+ .ml-datepicker__day--selected {
1797
+ background: var(--ml-primary);
1798
+ color: var(--ml-on-primary);
1799
+ font-weight: 600;
1800
+ opacity: 1;
1801
+ }
1802
+
1803
+ .ml-datepicker__day:focus-visible,
1804
+ .ml-datepicker__nav:focus-visible,
1805
+ .ml-datepicker__link:focus-visible {
1806
+ outline: 2px solid var(--ml-primary);
1807
+ outline-offset: 1px;
1808
+ }
1809
+
1810
+ @media (hover: hover) and (pointer: fine) {
1811
+ .ml-datepicker__day:not(.ml-datepicker__day--selected):hover,
1812
+ .ml-datepicker__nav:hover {
1813
+ background: var(--ml-surface-muted);
1814
+ }
1815
+
1816
+ .ml-datepicker__link:hover {
1817
+ background: var(--ml-selection-background);
1818
+ }
1819
+ }
1820
+
1821
+ .ml-datepicker__time {
1822
+ display: flex;
1823
+ align-items: center;
1824
+ justify-content: space-between;
1825
+ gap: 0.75rem;
1826
+ margin: 0.75rem 0 0;
1827
+ padding-top: 0.75rem;
1828
+ border-top: 1px solid var(--ml-divider);
1829
+ color: var(--ml-text-secondary);
1830
+ font-weight: 600;
1831
+ }
1832
+
1833
+ .ml-datepicker__foot {
1834
+ display: flex;
1835
+ justify-content: space-between;
1836
+ gap: 0.5rem;
1837
+ margin-top: 0.75rem;
1838
+ padding-top: 0.5rem;
1839
+ border-top: 1px solid var(--ml-divider);
1840
+ }
1841
+
1842
+ .ml-datepicker__link {
1843
+ min-height: 2rem;
1844
+ padding: 0.25rem 0.625rem;
1845
+ border: 0;
1846
+ border-radius: 6px;
1847
+ background: transparent;
1848
+ color: var(--ml-primary);
1849
+ font: inherit;
1850
+ font-weight: 600;
1851
+ cursor: pointer;
1852
+ }
1853
+
1854
+ .ml-datepicker__link--done {
1855
+ margin-left: auto;
1856
+ }
1857
+
1858
+ // ---- tree ----------------------------------------------------------------
1859
+
1860
+ .ml-tree {
1861
+ display: block;
1862
+ }
1863
+
1864
+ .ml-tree__list,
1865
+ .ml-tree__children {
1866
+ margin: 0;
1867
+ padding: 0;
1868
+ list-style: none;
1869
+ }
1870
+
1871
+ // Ogni livello rientra della larghezza della freccia, così le righe figlie
1872
+ // stanno sotto l'etichetta del padre.
1873
+ .ml-tree__children {
1874
+ padding-left: 1.5rem;
1875
+ }
1876
+
1877
+ .ml-tree__row {
1878
+ display: flex;
1879
+ align-items: center;
1880
+ gap: 0.25rem;
1881
+ min-height: 2.25rem;
1882
+ padding: 0.125rem 0.25rem;
1883
+ border-radius: 6px;
1884
+ }
1885
+
1886
+ .ml-tree__toggle,
1887
+ .ml-tree__spacer {
1888
+ display: inline-flex;
1889
+ flex: none;
1890
+ align-items: center;
1891
+ justify-content: center;
1892
+ width: 1.75rem;
1893
+ height: 1.75rem;
1894
+ }
1895
+
1896
+ .ml-tree__toggle {
1897
+ padding: 0;
1898
+ border: 0;
1899
+ border-radius: 50%;
1900
+ background: transparent;
1901
+ color: var(--ml-text-muted);
1902
+ cursor: pointer;
1903
+
1904
+ .ml-icon {
1905
+ font-size: 0.75rem;
1906
+ transition: transform 0.15s;
1907
+ }
1908
+
1909
+ &:focus-visible {
1910
+ outline: 2px solid var(--ml-primary);
1911
+ outline-offset: 1px;
1912
+ }
1913
+ }
1914
+
1915
+ .ml-tree__toggle--open .ml-icon {
1916
+ transform: rotate(90deg);
1917
+ }
1918
+
1919
+ @media (hover: hover) and (pointer: fine) {
1920
+ .ml-tree__toggle:hover {
1921
+ background: var(--ml-surface-muted);
1922
+ color: var(--ml-text-heading);
1923
+ }
1924
+ }
1925
+
1926
+ @media (prefers-reduced-motion: reduce) {
1927
+ .ml-tree__toggle .ml-icon {
1928
+ transition: none;
1929
+ }
1930
+ }
1931
+
1932
+ .ml-tree__content {
1933
+ flex: 0 1 auto;
1934
+ min-width: 0;
1935
+ }
1936
+
1937
+ // ---- tabella --------------------------------------------------------------
1938
+
1939
+ // L'aspetto deciso per la tabella delle feature e per quella WMS (10 novembre
1940
+ // 2026, richieste di Vito) : intestazioni piccole, maiuscole e in grassetto su
1941
+ // fondo grigio, righe alterne bianco e azzurro, il passaggio del mouse più
1942
+ // azzurro, la riga scelta ancora di più con la barra blu a sinistra, come le
1943
+ // voci scelte delle tendine. Le righe le scrivono i modelli di chi usa la
1944
+ // tabella : la riga scelta si riconosce dalla casella spuntata.
1945
+ .ml-table {
1946
+ display: flex;
1947
+ flex-direction: column;
1948
+ min-width: 0;
1949
+ color: var(--ml-text);
1950
+ }
1951
+
1952
+ // `flex` : la tabella riempie l'altezza del suo contenitore e scorre dentro.
1953
+ .ml-table--flex {
1954
+ flex: 1 1 0;
1955
+ min-height: 0;
1956
+ }
1957
+
1958
+ .ml-table__caption {
1959
+ padding: 0.5rem 0.75rem;
1960
+ border-bottom: 1px solid var(--ml-focus-shadow-color);
1961
+ }
1962
+
1963
+ .ml-table__container {
1964
+ position: relative;
1965
+ overflow: auto;
1966
+ min-height: 0;
1967
+ }
1968
+
1969
+ .ml-table--flex .ml-table__container {
1970
+ flex: 1 1 0;
1971
+ }
1972
+
1973
+ .ml-table__table {
1974
+ width: 100%;
1975
+ border-collapse: separate;
1976
+ border-spacing: 0;
1977
+ font-size: 0.875rem;
1978
+ }
1979
+
1980
+ .ml-table__table th,
1981
+ .ml-table__table td {
1982
+ padding: 0.6rem 0.75rem;
1983
+ border-bottom: 1px solid var(--ml-focus-shadow-color);
1984
+ text-align: start;
1985
+ }
1986
+
1987
+ .ml-table--small .ml-table__table th,
1988
+ .ml-table--small .ml-table__table td {
1989
+ padding: 0.375rem 0.5rem;
1990
+ }
1991
+
1992
+ // Le intestazioni restano ferme mentre la tabella scorre. Ferma è tutta
1993
+ // l'intestazione e non le singole celle : con due righe (i titoli e i filtri
1994
+ // per colonna) le celle si fermavano tutte in cima, e i filtri coprivano i
1995
+ // titoli.
1996
+ .ml-table__thead {
1997
+ position: sticky;
1998
+ z-index: 1;
1999
+ top: 0;
2000
+ }
2001
+
2002
+ .ml-table__thead th {
2003
+ background: var(--ml-surface-muted);
2004
+ color: var(--ml-text-heading);
2005
+ font-size: 0.72rem;
2006
+ font-weight: 700;
2007
+ letter-spacing: 0.04em;
2008
+ text-transform: uppercase;
2009
+ white-space: nowrap;
2010
+ }
2011
+
2012
+ .ml-table__thead tr:last-child th {
2013
+ border-bottom-width: 2px;
2014
+ }
2015
+
2016
+ .ml-table__tbody > tr > td {
2017
+ background: var(--ml-surface);
2018
+ }
2019
+
2020
+ .ml-table--striped .ml-table__tbody > tr:nth-child(even) > td {
2021
+ background: color-mix(in srgb, var(--ml-primary) 7%, var(--ml-surface));
2022
+ }
2023
+
2024
+ @media (hover: hover) and (pointer: fine) {
2025
+ .ml-table--hover .ml-table__tbody > tr:not(.table-empty):hover > td {
2026
+ background: color-mix(
2027
+ in srgb,
2028
+ var(--ml-primary) 12%,
2029
+ var(--ml-surface)
2030
+ );
2031
+ }
2032
+ }
2033
+
2034
+ .ml-table__tbody > tr:has(.ml-checkbox--checked) > td {
2035
+ background: color-mix(in srgb, var(--ml-primary) 18%, var(--ml-surface));
2036
+ color: var(--ml-text-heading);
2037
+ }
2038
+
2039
+ .ml-table__tbody > tr:has(.ml-checkbox--checked) > td:first-child {
2040
+ box-shadow: inset 3px 0 0 var(--ml-primary);
2041
+ }
2042
+
2043
+ // Il messaggio della tabella vuota : una riga `table-empty` scritta da chi la
2044
+ // usa, su tutta la larghezza, centrata e senza il fondo delle righe di dati.
2045
+ .ml-table__tbody > tr.table-empty > td {
2046
+ padding: 2.5rem 1rem;
2047
+ border-bottom: 0;
2048
+ background: var(--ml-surface);
2049
+ color: var(--ml-text-muted);
2050
+ font-size: 0.9rem;
2051
+ text-align: center;
2052
+ }
2053
+
2054
+ .ml-table__tbody > tr.table-empty .ml-icon {
2055
+ display: block;
2056
+ margin-bottom: 0.5rem;
2057
+ color: var(--ml-text-secondary);
2058
+ font-size: 1.75rem;
2059
+ opacity: 0.5;
2060
+ }
2061
+
2062
+ .ml-table__tfoot td {
2063
+ border-bottom: 0;
2064
+ }
2065
+
2066
+ .ml-table__loading {
2067
+ position: absolute;
2068
+ inset: 0;
2069
+ display: flex;
2070
+ align-items: center;
2071
+ justify-content: center;
2072
+ background: rgb(255 255 255 / 60%);
2073
+ }
2074
+
2075
+ .ml-table .ml-paginator {
2076
+ border-top: 1px solid var(--ml-focus-shadow-color);
2077
+ }
2078
+
2079
+ // La maniglia per allargare una colonna, sul bordo destro dell'intestazione.
2080
+ .ml-table__resizable {
2081
+ position: relative;
2082
+ }
2083
+
2084
+ .ml-table__resizer {
2085
+ position: absolute;
2086
+ top: 0;
2087
+ right: 0;
2088
+ bottom: 0;
2089
+ width: 0.5rem;
2090
+ cursor: col-resize;
2091
+ touch-action: none;
2092
+ }
2093
+
2094
+ @media (hover: hover) and (pointer: fine) {
2095
+ .ml-table__resizer:hover {
2096
+ box-shadow: inset -2px 0 0 var(--ml-primary);
2097
+ }
2098
+ }
2099
+
2100
+ // Il filtro di una colonna, nella riga sotto le intestazioni : largo quanto
2101
+ // la colonna, e la riga resta bianca e non maiuscola.
2102
+ .ml-table__thead tr.filter-row th {
2103
+ background: var(--ml-surface);
2104
+ font-weight: 400;
2105
+ text-transform: none;
2106
+ letter-spacing: normal;
2107
+ }
2108
+
2109
+ .ml-column-filter {
2110
+ display: block;
2111
+ }
2112
+
2113
+ .ml-column-filter__control.ml-input {
2114
+ width: 100%;
2115
+ font-weight: 400;
2116
+ min-width: 4rem;
2117
+ height: 1.875rem;
2118
+ padding-block: 0;
2119
+ font-size: 0.8rem;
2120
+ }
2121
+
2122
+ // ---- telefono e tablet ----------------------------------------------------
2123
+
2124
+ // Con un puntatore grosso (il dito) tutti i componenti hanno un'area da
2125
+ // toccare di almeno 44 px e i campi scrivono a 16 px : sotto, Safari su
2126
+ // iPhone ingrandisce la pagina appena il campo prende il fuoco. Col mouse
2127
+ // restano le misure di sopra.
2128
+
2129
+ // Le misure dei pulsanti restano configurabili tramite i token del tema.
2130
+ @media (pointer: coarse) {
2131
+ // La maniglia delle colonne e il filtro per colonna, da toccare col dito.
2132
+ .ml-table__resizer {
2133
+ width: 1.25rem;
2134
+ }
2135
+
2136
+ .ml-column-filter__control.ml-input {
2137
+ height: 2.75rem;
2138
+ }
2139
+
2140
+ .ml-input,
2141
+ .ml-select__input,
2142
+ .ml-select--small .ml-select__input {
2143
+ font-size: 16px;
2144
+ }
2145
+
2146
+ // Casella e radio : l'input trasparente sopra il disegno si allarga oltre
2147
+ // il disegno, che resta della sua misura.
2148
+ .ml-checkbox__input,
2149
+ .ml-radio__input {
2150
+ inset: -0.75rem;
2151
+ }
2152
+
2153
+ .ml-switch__input {
2154
+ inset: -0.625rem -0.25rem;
2155
+ }
2156
+
2157
+ .ml-slider__input {
2158
+ height: 2.75rem;
2159
+ }
2160
+
2161
+ .ml-select__trigger {
2162
+ min-height: 2.75rem;
2163
+ }
2164
+
2165
+ .ml-select__clear,
2166
+ .ml-select__arrow,
2167
+ .ml-inputnumber__buttons {
2168
+ width: 2.75rem;
2169
+ }
2170
+
2171
+ .ml-select__clear {
2172
+ height: 2.75rem;
2173
+ }
2174
+
2175
+ .ml-inputnumber--buttons > .ml-input {
2176
+ padding-right: 3rem;
2177
+ }
2178
+
2179
+ .ml-select__option {
2180
+ min-height: 2.75rem;
2181
+ }
2182
+
2183
+ .ml-selectbutton__option,
2184
+ .ml-tab {
2185
+ min-height: 2.75rem;
2186
+ }
2187
+
2188
+ .ml-paginator__button {
2189
+ min-width: 2.75rem;
2190
+ height: 2.75rem;
2191
+ }
2192
+
2193
+ .ml-datepicker__nav,
2194
+ .ml-datepicker__clear,
2195
+ .ml-datepicker__icon,
2196
+ .ml-datepicker__link {
2197
+ min-width: 2.75rem;
2198
+ min-height: 2.75rem;
2199
+ }
2200
+
2201
+ .ml-datepicker__input {
2202
+ padding-right: 5.75rem;
2203
+ }
2204
+
2205
+ .ml-datepicker__clear {
2206
+ right: 3rem;
2207
+ }
2208
+
2209
+ .ml-tree__toggle,
2210
+ .ml-tree__spacer {
2211
+ width: 2.75rem;
2212
+ height: 2.75rem;
2213
+ }
2214
+
2215
+ .ml-dialog__close,
2216
+ .ml-toast__close {
2217
+ width: 2.75rem;
2218
+ height: 2.75rem;
2219
+ }
2220
+ }