@fluixi-ui/skin-material 0.3.0-alpha.6 → 0.3.0-alpha.7

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 (2) hide show
  1. package/material.css +397 -31
  2. package/package.json +1 -1
package/material.css CHANGED
@@ -40,7 +40,7 @@
40
40
  }
41
41
 
42
42
  /* ---- Button: pill shape, state layer, filled/tonal/outlined/text ---------- */
43
- [data-ui='material'] :where([data-variant]) {
43
+ [data-ui='material'] :where([data-variant]:not([data-badge])) {
44
44
  position: relative;
45
45
  overflow: hidden;
46
46
  border-radius: 9999px;
@@ -56,7 +56,7 @@
56
56
  /* The Material "state layer": a currentColor overlay that fades in on interaction.
57
57
  On a filled button currentColor is the on-primary ink (white) → a subtle
58
58
  lightening; on outlined/text/tonal it's the primary colour. */
59
- [data-ui='material'] :where([data-variant])::before {
59
+ [data-ui='material'] :where([data-variant]:not([data-badge]))::before {
60
60
  content: '';
61
61
  position: absolute;
62
62
  inset: 0;
@@ -68,35 +68,28 @@
68
68
  pointer-events: none;
69
69
  transition: opacity var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
70
70
  }
71
- [data-ui='material'] :where([data-variant]:hover)::before { opacity: 0.08; }
72
- [data-ui='material'] :where([data-variant]:focus-visible)::before { opacity: 0.12; }
73
- [data-ui='material'] :where([data-variant]:active)::before { opacity: 0.16; }
74
- [data-ui='material'] :where([data-variant]:focus-visible) { box-shadow: none; outline: none; }
75
- [data-ui='material'] :where([data-variant][disabled], [data-variant]:disabled) {
71
+ [data-ui='material'] :where([data-variant]:not([data-badge]):hover)::before { opacity: 0.08; }
72
+ [data-ui='material'] :where([data-variant]:not([data-badge]):focus-visible)::before { opacity: 0.12; }
73
+ [data-ui='material'] :where([data-variant]:not([data-badge]):active)::before { opacity: 0.16; }
74
+ [data-ui='material'] :where([data-variant]:not([data-badge]):focus-visible) { box-shadow: none; outline: none; }
75
+ [data-ui='material'] :where([data-variant]:not([data-badge])[disabled], [data-variant]:not([data-badge]):disabled) {
76
76
  box-shadow: none;
77
77
  opacity: 0.38;
78
78
  }
79
79
 
80
80
  /* Filled (solid) = the classic contained button: rests on elevation, lifts
81
81
  further on hover. Elevation is per-variant — outlined/text/tonal stay flat. */
82
- [data-ui='material'] :where([data-variant='solid']) { box-shadow: var(--flx-ui-shadow-sm); }
83
- [data-ui='material'] :where([data-variant='solid']:hover) { box-shadow: var(--flx-ui-shadow-md); }
84
- [data-ui='material'] :where([data-variant='solid']:active) { box-shadow: var(--flx-ui-shadow-sm); }
82
+ [data-ui='material'] :where([data-variant='solid']:not([data-badge])) { box-shadow: var(--flx-ui-shadow-sm); }
83
+ [data-ui='material'] :where([data-variant='solid']:not([data-badge]):hover) { box-shadow: var(--flx-ui-shadow-md); }
84
+ [data-ui='material'] :where([data-variant='solid']:not([data-badge]):active) { box-shadow: var(--flx-ui-shadow-sm); }
85
85
  /* Tonal (soft): secondary-container feel. */
86
- [data-ui='material'] :where([data-variant='soft']) { background: var(--flx-ui-color-primary-subtle); color: var(--flx-ui-color-primary-active); }
86
+ [data-ui='material'] :where([data-variant='soft']:not([data-badge])) { background: var(--flx-ui-color-primary-subtle); color: var(--flx-ui-color-primary-active); }
87
87
  /* Outlined: 1px hairline, no fill. */
88
- [data-ui='material'] :where([data-variant='outline']) { border: 1px solid var(--flx-ui-color-border-strong); }
88
+ [data-ui='material'] :where([data-variant='outline']:not([data-badge])) { border: 1px solid var(--flx-ui-color-border-strong); }
89
89
  /* Text: tighter padding, no border. */
90
- [data-ui='material'] :where([data-variant='ghost']) { padding: 0 12px; }
91
-
92
- /* Badges also carry [data-variant] undo the button treatment (they're chips,
93
- not buttons): restore size, keep the pill, drop the state layer. */
94
- [data-ui='material'] :where(.flx-ui-badge[data-variant]) {
95
- height: auto;
96
- padding: 2px 10px;
97
- box-shadow: none;
98
- }
99
- [data-ui='material'] :where(.flx-ui-badge[data-variant])::before { content: none; }
90
+ [data-ui='material'] :where([data-variant='ghost']:not([data-badge])) { padding: 0 12px; }
91
+ /* Badges carry [data-variant] too, but every button rule above excludes
92
+ [data-badge], so a badge is never treated as a button no guard needed. */
100
93
 
101
94
  /* ---- Switch: M3 geometry (52×32 track, growing thumb) --------------------- */
102
95
  [data-ui='material'] :where([role='switch']) {
@@ -132,9 +125,26 @@
132
125
  border-width: 2px;
133
126
  border-radius: 2px;
134
127
  }
128
+ /* The check (rotated border) and indeterminate bar are flex items; the tighter
129
+ 2px-border box lets flex shrink them to a sliver on the main axis. Pin them. */
130
+ [data-ui='material'] :where([role='checkbox'][data-state='checked'])::after,
131
+ [data-ui='material'] :where([role='checkbox'][data-state='indeterminate'])::after {
132
+ flex: none;
133
+ }
134
+ [data-ui='material'] :where([role='checkbox'][data-state='checked'])::after {
135
+ width: 4px;
136
+ height: 8px;
137
+ margin-top: -3px;
138
+ }
139
+ /* Multi-select listbox/select indicator box → match the material checkbox. */
140
+ [data-ui='material'] :where([data-select-item-indicator]) {
141
+ box-sizing: border-box;
142
+ border-width: 2px;
143
+ border-radius: 2px;
144
+ }
135
145
 
136
146
  /* ---- Slider: M3 filled thumb -------------------------------------------- */
137
- [data-ui='material'] :where([role='slider']) {
147
+ [data-ui='material'] :where([data-slider] [role='slider']) {
138
148
  width: 20px;
139
149
  height: 20px;
140
150
  background: var(--flx-ui-color-primary);
@@ -149,18 +159,48 @@
149
159
  border-top-right-radius: 3px;
150
160
  }
151
161
 
152
- /* ---- Filled text field + combobox control -------------------------------- */
153
- [data-ui='material'] :where([data-input], [data-combobox-control]) {
162
+ /* ---- Text field / select / combobox appearance --------------------------
163
+ Baseline (no data-field-variant) and the explicit `filled` variant → M3
164
+ filled underline; `outlined` → M3 outlined box. 4px M3 radius (the tokens'
165
+ radius-md is 12px here). */
166
+ [data-ui='material'] :where(
167
+ [data-input]:not([data-field-variant]),
168
+ [data-combobox-control]:not([data-field-variant]),
169
+ button[role='combobox']:not([data-field-variant]),
170
+ [data-field-variant='filled']) {
154
171
  background: var(--flx-ui-color-bg-muted);
155
- border: none;
172
+ border: 1px solid transparent;
156
173
  border-bottom: 1px solid var(--flx-ui-color-border-strong);
157
174
  border-radius: 4px 4px 0 0;
158
175
  }
159
- [data-ui='material'] :where([data-input]:focus, [data-input]:focus-visible, [data-combobox-control]:focus-within) {
176
+ [data-ui='material'] :where(
177
+ [data-input]:not([data-field-variant]):focus,
178
+ [data-combobox-control]:not([data-field-variant]):focus-within,
179
+ button[role='combobox']:not([data-field-variant]):focus-visible,
180
+ button[role='combobox']:not([data-field-variant])[data-state='open'],
181
+ [data-field-variant='filled']:focus,
182
+ [data-field-variant='filled']:focus-visible,
183
+ [data-field-variant='filled']:focus-within) {
160
184
  border-bottom: 2px solid var(--flx-ui-color-primary);
161
185
  box-shadow: none;
162
186
  outline: none;
163
187
  }
188
+ /* Outlined → M3 outlined box (4px), 2px primary border on focus. */
189
+ [data-ui='material'] :where([data-field-variant='outlined']) {
190
+ background: transparent;
191
+ border: 1px solid var(--flx-ui-color-border-strong);
192
+ border-radius: 4px;
193
+ }
194
+ [data-ui='material'] :where([data-field-variant='outlined']:focus, [data-field-variant='outlined']:focus-visible, [data-field-variant='outlined']:focus-within) {
195
+ border: 2px solid var(--flx-ui-color-primary);
196
+ box-shadow: none;
197
+ outline: none;
198
+ }
199
+ /* The combobox's inner input must stay bare — the wrapper carries the field. */
200
+ [data-ui='material'] :where([data-combobox-control] [role='combobox']) {
201
+ background: transparent;
202
+ border: none;
203
+ }
164
204
 
165
205
  /* ---- Menus & lists: full-bleed M3 rows, roomier padding ------------------- */
166
206
  [data-ui='material'] :where([role='menuitem'], [role='option']) {
@@ -348,7 +388,7 @@
348
388
  }
349
389
 
350
390
  /* ---- Linear progress: 4dp fully-rounded track, tonal remainder ---------- */
351
- [data-ui='material'] :where([role='progressbar']) {
391
+ [data-ui='material'] :where([role='progressbar']:not([data-circular])) {
352
392
  height: 4px;
353
393
  border-radius: 9999px;
354
394
  background: var(--flx-ui-color-primary-subtle);
@@ -406,9 +446,31 @@
406
446
  }
407
447
 
408
448
  /* ---- Breadcrumb: state-layer link affordance ---------------------------- */
449
+ /* Padded pill targets so the state-layer has room to breathe (M3). */
450
+ [data-ui='material'] :where([data-breadcrumb-link]) {
451
+ padding: 2px 8px;
452
+ margin: 0 -2px;
453
+ border-radius: 999px;
454
+ }
409
455
  [data-ui='material'] :where([data-breadcrumb-link]:hover) {
410
456
  background: color-mix(in oklab, var(--flx-ui-color-fg) 8%, transparent);
411
- border-radius: 4px;
457
+ color: var(--flx-ui-color-fg);
458
+ }
459
+ [data-ui='material'] :where([data-breadcrumb-page]) {
460
+ padding: 2px 8px;
461
+ }
462
+
463
+ /* ---- Toolbar: M3 surface + pill state-layer buttons ---------------------- */
464
+ [data-ui='material'] :where([role='toolbar']) {
465
+ border-radius: 8px;
466
+ gap: 2px;
467
+ }
468
+ [data-ui='material'] :where([role='toolbar'] > button:not([aria-pressed])) {
469
+ border-radius: 999px;
470
+ }
471
+ [data-ui='material'] :where([role='toolbar'] > button:not([aria-pressed]):hover) {
472
+ background: color-mix(in oklab, var(--flx-ui-color-fg) 8%, transparent);
473
+ color: var(--flx-ui-color-fg);
412
474
  }
413
475
 
414
476
  /* ---- Navigation menu & menubar: state-layer triggers, rounded ----------- */
@@ -472,13 +534,13 @@
472
534
  }
473
535
 
474
536
  /* ---- Click-origin ripple (installed by the ripple primitive) ------------- */
475
- [data-ui='material'] :where([data-variant], [role='menuitem'], [role='option'], [role='tab']) {
537
+ [data-ui='material'] :where([data-variant]:not([data-badge]), [role='menuitem'], [role='option'], [role='tab']) {
476
538
  position: relative;
477
539
  overflow: hidden;
478
540
  }
479
541
  /* A glow button draws its ring on ::after at the border edge; the ripple's
480
542
  overflow:hidden would clip it, so let a glow button overflow. */
481
- [data-ui='material'] :where([data-variant][data-glow]) {
543
+ [data-ui='material'] :where([data-variant]:not([data-badge])[data-glow]) {
482
544
  overflow: visible;
483
545
  }
484
546
  [data-ui='material'] .flx-mat-ripple {
@@ -507,4 +569,308 @@
507
569
  opacity: 0;
508
570
  }
509
571
  }
572
+
573
+ /* ---- Progress: M3 linear (4px, tonal track) ------------------------------- */
574
+ [data-ui='material'] :where([role='progressbar']:not([data-circular])) {
575
+ height: 4px;
576
+ background: var(--flx-ui-color-primary-subtle);
577
+ }
578
+ [data-ui='material'] :where([role='progressbar']:not([data-circular]))::after {
579
+ background: var(--flx-ui-color-primary);
580
+ }
581
+
582
+ /* ---- Slider: M3 (thin tonal track, filled active range) ------------------- */
583
+ [data-ui='material'] :where([data-slider-track]) {
584
+ height: 4px;
585
+ background: var(--flx-ui-color-primary-subtle);
586
+ }
587
+ [data-ui='material'] :where([data-slider-range]) {
588
+ background: var(--flx-ui-color-primary);
589
+ }
590
+
591
+ /* ---- Badge: M3 label (medium weight, subtle tracking) --------------------- */
592
+ [data-ui='material'] :where([data-badge]) {
593
+ font-weight: var(--flx-ui-font-weight-medium);
594
+ letter-spacing: 0.02em;
595
+ }
596
+
597
+ /* ---- Avatar: M3 rounded-square option stays circular; add tonal ring ------ */
598
+ [data-ui='material'] :where([data-avatar]) {
599
+ font-weight: var(--flx-ui-font-weight-medium);
600
+ }
601
+
602
+ /* ---- Statistic: M3 display number (tighter tracking) ---------------------- */
603
+ [data-ui='material'] :where([data-statistic-value]) {
604
+ font-weight: var(--flx-ui-font-weight-semibold);
605
+ letter-spacing: -0.01em;
606
+ }
607
+ [data-ui='material'] :where([data-statistic-label]) {
608
+ letter-spacing: 0.06em;
609
+ }
610
+
611
+ /* ---- Empty: M3 icon tile (rounded-large, tonal) --------------------------- */
612
+ [data-ui='material'] :where([data-empty-icon]) {
613
+ width: 48px;
614
+ height: 48px;
615
+ border-radius: 16px;
616
+ background: var(--flx-ui-color-primary-subtle);
617
+ color: var(--flx-ui-color-primary);
618
+ }
619
+
620
+ /* ---- Timeline: M3 dot state-layer ---------------------------------------- */
621
+ [data-ui='material'] :where([data-timeline-indicator]:empty)::before {
622
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--flx-timeline-dot) 14%, transparent);
623
+ }
624
+
625
+ /* ---- Descriptions: M3 term (medium, tracked) ------------------------------ */
626
+ [data-ui='material'] :where([data-descriptions-term]) {
627
+ font-weight: var(--flx-ui-font-weight-medium);
628
+ letter-spacing: 0.04em;
629
+ text-transform: uppercase;
630
+ font-size: var(--flx-ui-font-size-2xs, 11px);
631
+ }
632
+
633
+ /* ---- Switch: Material (thin track, prominent thumb, ripple state-layer) ---- */
634
+ [data-ui='material'] :where([role='switch']) {
635
+ width: 34px;
636
+ height: 14px;
637
+ padding: 0;
638
+ border: none;
639
+ overflow: visible;
640
+ position: relative;
641
+ border-radius: 999px;
642
+ background: color-mix(in srgb, var(--flx-ui-color-fg) 26%, transparent);
643
+ }
644
+ [data-ui='material'] :where([role='switch'][data-state='checked']) {
645
+ background: color-mix(in srgb, var(--flx-ui-color-primary) 50%, transparent);
646
+ filter: none;
647
+ }
648
+ [data-ui='material'] :where([role='switch']:hover:not([data-disabled])) {
649
+ filter: none;
650
+ }
651
+ /* Thumb — 20px circle, larger than the 14px track so it overlaps it. */
652
+ [data-ui='material'] :where([role='switch'])::after {
653
+ content: '';
654
+ position: absolute;
655
+ top: 50%;
656
+ left: 0;
657
+ width: 20px;
658
+ height: 20px;
659
+ margin: 0;
660
+ border-radius: 50%;
661
+ background: #fff;
662
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
663
+ transform: translate(-2px, -50%);
664
+ transition:
665
+ transform var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized),
666
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
667
+ }
668
+ [data-ui='material'] :where([role='switch'][data-state='checked'])::after {
669
+ background: var(--flx-ui-color-primary);
670
+ transform: translate(16px, -50%);
671
+ }
672
+ /* Ripple / state-layer — a translucent disc behind the thumb, following it. */
673
+ [data-ui='material'] :where([role='switch'])::before {
674
+ content: '';
675
+ position: absolute;
676
+ top: 50%;
677
+ left: 0;
678
+ width: 40px;
679
+ height: 40px;
680
+ border-radius: 50%;
681
+ background: var(--flx-ui-color-fg);
682
+ opacity: 0;
683
+ pointer-events: none;
684
+ transform: translate(-12px, -50%) scale(0.7);
685
+ transition:
686
+ opacity var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
687
+ transform var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
688
+ }
689
+ [data-ui='material'] :where([role='switch'][data-state='checked'])::before {
690
+ background: var(--flx-ui-color-primary);
691
+ transform: translate(6px, -50%) scale(0.7);
692
+ }
693
+ [data-ui='material'] :where([role='switch']:hover:not([data-disabled]))::before {
694
+ opacity: 0.09;
695
+ transform: translate(-12px, -50%) scale(1);
696
+ }
697
+ [data-ui='material'] :where([role='switch'][data-state='checked']:hover:not([data-disabled]))::before {
698
+ transform: translate(6px, -50%) scale(1);
699
+ }
700
+ [data-ui='material'] :where([role='switch']:active:not([data-disabled]))::before {
701
+ opacity: 0.18;
702
+ }
703
+
704
+ /* ---- Slider thumb: Material filled handle + ripple state-layer ------------- */
705
+ [data-ui='material'] :where([data-slider] [role='slider']) {
706
+ background: var(--flx-ui-color-primary);
707
+ border-color: var(--flx-ui-color-primary);
708
+ }
709
+ [data-ui='material'] :where([data-slider] [role='slider'])::before {
710
+ content: '';
711
+ position: absolute;
712
+ top: 50%;
713
+ left: 50%;
714
+ width: 40px;
715
+ height: 40px;
716
+ border-radius: 50%;
717
+ background: var(--flx-ui-color-primary);
718
+ opacity: 0;
719
+ pointer-events: none;
720
+ transform: translate(-50%, -50%) scale(0.4);
721
+ transition:
722
+ opacity var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
723
+ transform var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
724
+ }
725
+ [data-ui='material'] :where([data-slider] [role='slider']:hover:not([data-disabled]))::before {
726
+ opacity: 0.12;
727
+ transform: translate(-50%, -50%) scale(1);
728
+ }
729
+ [data-ui='material'] :where([data-slider] [role='slider']:active)::before,
730
+ [data-ui='material'] :where([data-slider] [role='slider']:focus-visible)::before {
731
+ opacity: 0.2;
732
+ transform: translate(-50%, -50%) scale(1);
733
+ }
734
+
735
+ /* ---- Popup surfaces: one consistent M3 shape (small radius, tight elevation)
736
+ * Unifies every floating surface — menus, listboxes, datepicker, command,
737
+ * hovercard, nav-menu — which previously ranged from radius-md (12px) to
738
+ * radius-lg (16px) with a heavy shadow-lg. Tooltip already matches at 4px. */
739
+ [data-ui='material'] :where(
740
+ [role='menu'],
741
+ [role='listbox'],
742
+ [data-date-picker-content],
743
+ [data-navigation-menu-content],
744
+ [data-command],
745
+ [data-hovercard-content]
746
+ ) {
747
+ border-radius: 4px;
748
+ box-shadow:
749
+ 0 2px 6px rgba(0, 0, 0, 0.16),
750
+ 0 1px 3px rgba(0, 0, 0, 0.1);
751
+ }
752
+
753
+ /* ---- Checkbox & radio: M3 state-layer ripple -------------------------------
754
+ * The ripple lives on ::before at z-index:-1 so it paints ABOVE the control's
755
+ * background but BELOW its content (the base check/dot on ::after). This leaves
756
+ * the base marks completely untouched — do NOT restyle ::after here, the base
757
+ * check is a flex-centred rotated border and the radio dot is position:absolute;
758
+ * inset:3px, both of which break if position/box-sizing is overridden. */
759
+ [data-ui='material'] :where([role='checkbox']),
760
+ [data-ui='material'] :where([role='radio']) {
761
+ position: relative;
762
+ overflow: visible;
763
+ }
764
+ [data-ui='material'] :where([role='checkbox'])::before,
765
+ [data-ui='material'] :where([role='radio'])::before {
766
+ content: '';
767
+ position: absolute;
768
+ z-index: -1;
769
+ top: 50%;
770
+ left: 50%;
771
+ width: 40px;
772
+ height: 40px;
773
+ border-radius: 50%;
774
+ background: var(--flx-ui-color-primary);
775
+ opacity: 0;
776
+ pointer-events: none;
777
+ transform: translate(-50%, -50%) scale(0.5);
778
+ transition:
779
+ opacity var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
780
+ transform var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
781
+ }
782
+ [data-ui='material'] :where([role='checkbox']:hover:not([data-disabled]))::before,
783
+ [data-ui='material'] :where([role='radio']:hover:not([data-disabled]))::before {
784
+ opacity: 0.1;
785
+ transform: translate(-50%, -50%) scale(1);
786
+ }
787
+ [data-ui='material'] :where([role='checkbox']:active:not([data-disabled]))::before,
788
+ [data-ui='material'] :where([role='radio']:active:not([data-disabled]))::before,
789
+ [data-ui='material'] :where([role='checkbox']:focus-visible)::before,
790
+ [data-ui='material'] :where([role='radio']:focus-visible)::before {
791
+ opacity: 0.18;
792
+ transform: translate(-50%, -50%) scale(1);
793
+ }
794
+
795
+ /* ---- Circular progress: 4dp tonal track, round cap (matches linear) ------ */
796
+ [data-ui='material'] :where([data-progress-track]) {
797
+ stroke: var(--flx-ui-color-primary-subtle);
798
+ }
799
+ [data-ui='material'] :where([data-progress-indicator]) {
800
+ stroke-linecap: round;
801
+ }
802
+
803
+ /* ---- Avatar group: tonal secondary-container overflow chip --------------- */
804
+ [data-ui='material'] :where([data-avatar-group-overflow]) {
805
+ background: var(--flx-ui-color-primary-subtle);
806
+ color: var(--flx-ui-color-primary-active);
807
+ }
808
+
809
+ /* ---- Rating: filled primary stars, roomier M3 spacing ------------------- */
810
+ [data-ui='material'] :where([data-rating]) {
811
+ --flx-ui-rating-gap: 4px;
812
+ --flx-ui-rating-color: var(--flx-ui-color-primary);
813
+ }
814
+
815
+ /* ---- PIN input: M3 filled cells with the shared underline treatment ------ */
816
+ [data-ui='material'] :where([data-pin-input-field]) {
817
+ background: var(--flx-ui-color-bg-muted);
818
+ border: none;
819
+ border-bottom: 1px solid var(--flx-ui-color-border-strong);
820
+ border-radius: 4px 4px 0 0;
821
+ }
822
+ [data-ui='material'] :where([data-pin-input-field]:focus, [data-pin-input-field]:focus-visible) {
823
+ border-bottom: 2px solid var(--flx-ui-color-primary);
824
+ box-shadow: none;
825
+ outline: none;
826
+ }
827
+
828
+ /* ---- Icon button: M3 round, size-aware (overrides the fixed 40px pill) ---- */
829
+ [data-ui='material'] :where([data-icon-button]) {
830
+ padding: 0;
831
+ width: var(--flx-ui-size-md);
832
+ height: var(--flx-ui-size-md);
833
+ border-radius: 9999px;
834
+ }
835
+ [data-ui='material'] :where([data-icon-button][data-size='sm']) {
836
+ width: var(--flx-ui-size-sm);
837
+ height: var(--flx-ui-size-sm);
838
+ }
839
+ [data-ui='material'] :where([data-icon-button][data-size='lg']) {
840
+ width: var(--flx-ui-size-lg);
841
+ height: var(--flx-ui-size-lg);
842
+ }
843
+
844
+ /* ---- FAB: M3 primary-container, resting elevation, larger radius. Re-assert
845
+ the FAB geometry the fixed-40px button rule above would otherwise impose. */
846
+ [data-ui='material'] :where([data-fab]) {
847
+ width: 56px;
848
+ height: 56px;
849
+ padding: 0;
850
+ border-radius: 16px;
851
+ background: var(--flx-ui-color-primary-subtle);
852
+ color: var(--flx-ui-color-primary-active);
853
+ box-shadow: var(--flx-ui-shadow-md);
854
+ }
855
+ [data-ui='material'] :where([data-fab]:hover:not([data-disabled])) {
856
+ box-shadow: var(--flx-ui-shadow-lg);
857
+ }
858
+ [data-ui='material'] :where([data-fab][data-fab-size='sm']) { width: 40px; height: 40px; border-radius: 12px; }
859
+ [data-ui='material'] :where([data-fab][data-fab-size='lg']) { width: 64px; height: 64px; border-radius: 28px; }
860
+ [data-ui='material'] :where([data-fab][data-fab-extended]) {
861
+ width: auto;
862
+ height: 56px;
863
+ padding: 0 20px;
864
+ border-radius: 16px;
865
+ }
866
+ /* A solid-toned FAB keeps the primary fill instead of the tonal container. */
867
+ [data-ui='material'] :where([data-fab][data-variant='solid']) {
868
+ background: var(--flx-ui-color-primary);
869
+ color: var(--flx-ui-color-primary-fg);
870
+ }
871
+
872
+ /* ---- Speed dial: elevated action buttons -------------------------------- */
873
+ [data-ui='material'] :where([data-speed-dial-action]) {
874
+ box-shadow: var(--flx-ui-shadow-sm);
875
+ }
510
876
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluixi-ui/skin-material",
3
- "version": "0.3.0-alpha.6",
3
+ "version": "0.3.0-alpha.7",
4
4
  "description": "Material Design skin for Fluixi UI — activate with data-ui=\"material\"",
5
5
  "license": "MIT",
6
6
  "private": false,