@fluixi-ui/tokens 0.4.0-alpha.17 → 0.4.0-alpha.19

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.
@@ -10,7 +10,6 @@
10
10
  width: 40px;
11
11
  height: 40px;
12
12
  flex: none;
13
- overflow: hidden;
14
13
  border-radius: var(--flx-ui-radius-full);
15
14
  background: var(--flx-ui-color-bg-muted);
16
15
  color: var(--flx-ui-color-fg-muted);
@@ -32,6 +31,100 @@
32
31
  justify-content: center;
33
32
  width: 100%;
34
33
  height: 100%;
34
+ overflow: hidden;
35
+ border-radius: inherit;
36
+ }
37
+
38
+ /* ----------------------------------------------------------------------------
39
+ * Avatar indicator: a dot, count or icon centred on a point of the edge. The
40
+ * avatar no longer clips, so it can sit half outside; image and fallback round
41
+ * their own corners. On a round avatar a corner point lies on the circle at 45
42
+ * degrees, r(1 - 1/sqrt 2) = 14.645% in from each side.
43
+ * ------------------------------------------------------------------------- */
44
+ :where([data-avatar-indicator]) {
45
+ --flx-ui-avatar-indicator-corner: 14.645%;
46
+ --flx-ui-avatar-indicator-x: calc(100% - var(--flx-ui-avatar-indicator-corner));
47
+ --flx-ui-avatar-indicator-y: calc(100% - var(--flx-ui-avatar-indicator-corner));
48
+ --flx-ui-avatar-indicator-shift: -50%;
49
+ --flx-ui-avatar-indicator-bg: var(--flx-ui-color-success);
50
+ --flx-ui-avatar-indicator-fg: var(--flx-ui-color-success-fg);
51
+ position: absolute;
52
+ z-index: 1;
53
+ inset-inline-start: var(--flx-ui-avatar-indicator-x);
54
+ inset-block-start: var(--flx-ui-avatar-indicator-y);
55
+ transform: translate(var(--flx-ui-avatar-indicator-shift), -50%);
56
+ display: inline-flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ box-sizing: border-box;
60
+ min-inline-size: var(--flx-ui-avatar-indicator-size, max(16px, 40%));
61
+ block-size: var(--flx-ui-avatar-indicator-size, max(16px, 40%));
62
+ padding-inline: 4px;
63
+ border-radius: var(--flx-ui-radius-full);
64
+ background: var(--flx-ui-avatar-indicator-bg);
65
+ color: var(--flx-ui-avatar-indicator-fg);
66
+ /* A ring in the page colour cuts the indicator out of the avatar. */
67
+ box-shadow: 0 0 0 2px var(--flx-ui-avatar-indicator-ring, var(--flx-ui-color-bg));
68
+ font-size: var(--flx-ui-font-size-xs);
69
+ font-weight: var(--flx-ui-font-weight-semibold);
70
+ line-height: 1;
71
+ letter-spacing: normal;
72
+ white-space: nowrap;
73
+ pointer-events: none;
74
+ }
75
+ :where([data-avatar-indicator][data-dot]) {
76
+ min-inline-size: 0;
77
+ inline-size: var(--flx-ui-avatar-indicator-dot, max(8px, 25%));
78
+ block-size: var(--flx-ui-avatar-indicator-dot, max(8px, 25%));
79
+ padding: 0;
80
+ }
81
+ :where([data-avatar-indicator]) :where(svg) { inline-size: 70%; block-size: 70%; }
82
+ /* In right-to-left text the start side is the right, so centring pulls the other way. */
83
+ :where([data-avatar-indicator]:dir(rtl)) { --flx-ui-avatar-indicator-shift: 50%; }
84
+
85
+ :where([data-avatar-indicator][data-overlap='rectangular']) { --flx-ui-avatar-indicator-corner: 0%; }
86
+ :where([data-avatar-indicator][data-placement='top-start']) {
87
+ --flx-ui-avatar-indicator-x: var(--flx-ui-avatar-indicator-corner);
88
+ --flx-ui-avatar-indicator-y: var(--flx-ui-avatar-indicator-corner);
89
+ }
90
+ :where([data-avatar-indicator][data-placement='top']) {
91
+ --flx-ui-avatar-indicator-x: 50%;
92
+ --flx-ui-avatar-indicator-y: 0%;
93
+ }
94
+ :where([data-avatar-indicator][data-placement='top-end']) {
95
+ --flx-ui-avatar-indicator-y: var(--flx-ui-avatar-indicator-corner);
96
+ }
97
+ :where([data-avatar-indicator][data-placement='end']) {
98
+ --flx-ui-avatar-indicator-x: 100%;
99
+ --flx-ui-avatar-indicator-y: 50%;
100
+ }
101
+ :where([data-avatar-indicator][data-placement='bottom']) {
102
+ --flx-ui-avatar-indicator-x: 50%;
103
+ --flx-ui-avatar-indicator-y: 100%;
104
+ }
105
+ :where([data-avatar-indicator][data-placement='bottom-start']) {
106
+ --flx-ui-avatar-indicator-x: var(--flx-ui-avatar-indicator-corner);
107
+ }
108
+ :where([data-avatar-indicator][data-placement='start']) {
109
+ --flx-ui-avatar-indicator-x: 0%;
110
+ --flx-ui-avatar-indicator-y: 50%;
111
+ }
112
+
113
+ :where([data-avatar-indicator][data-tone='neutral']) {
114
+ --flx-ui-avatar-indicator-bg: var(--flx-ui-color-fg-muted);
115
+ --flx-ui-avatar-indicator-fg: var(--flx-ui-color-bg);
116
+ }
117
+ :where([data-avatar-indicator]:is([data-tone='primary'], [data-tone='info'])) {
118
+ --flx-ui-avatar-indicator-bg: var(--flx-ui-color-primary);
119
+ --flx-ui-avatar-indicator-fg: var(--flx-ui-color-primary-fg);
120
+ }
121
+ :where([data-avatar-indicator][data-tone='warning']) {
122
+ --flx-ui-avatar-indicator-bg: var(--flx-ui-color-warning);
123
+ --flx-ui-avatar-indicator-fg: var(--flx-ui-color-warning-fg);
124
+ }
125
+ :where([data-avatar-indicator][data-tone='danger']) {
126
+ --flx-ui-avatar-indicator-bg: var(--flx-ui-color-danger);
127
+ --flx-ui-avatar-indicator-fg: var(--flx-ui-color-danger-fg);
35
128
  }
36
129
 
37
130
  /* ----------------------------------------------------------------------------
@@ -309,6 +309,9 @@
309
309
  padding: 0 var(--flx-ui-space-2);
310
310
  outline: none;
311
311
  }
312
+ :where(.flx-ui-be-filter)::placeholder {
313
+ color: var(--flx-ui-color-fg-muted);
314
+ }
312
315
  :where(.flx-ui-be-filter:focus) {
313
316
  border-color: var(--flx-ui-color-primary);
314
317
  }
@@ -414,7 +417,7 @@
414
417
  z-index: 4;
415
418
  user-select: none;
416
419
  }
417
- :where(.flx-ui-be-col-resize::after) {
420
+ :where(.flx-ui-be-col-resize)::after {
418
421
  content: '';
419
422
  position: absolute;
420
423
  top: 20%;
@@ -425,8 +428,8 @@
425
428
  background: transparent;
426
429
  transition: background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
427
430
  }
428
- :where(.flx-ui-be-colh:hover .flx-ui-be-col-resize::after),
429
- :where(.flx-ui-be-col-resize:hover::after) {
431
+ :where(.flx-ui-be-colh:hover .flx-ui-be-col-resize)::after,
432
+ :where(.flx-ui-be-col-resize:hover)::after {
430
433
  background: var(--flx-ui-color-primary);
431
434
  }
432
435
 
package/dist/css/card.css CHANGED
@@ -5,16 +5,16 @@
5
5
  :where([data-card]) {
6
6
  display: flex;
7
7
  flex-direction: column;
8
- background: var(--flx-ui-color-bg);
8
+ background: var(--flx-ui-card-bg, var(--flx-ui-color-bg));
9
9
  color: var(--flx-ui-color-fg);
10
- border: 1px solid var(--flx-ui-color-border);
11
- border-radius: var(--flx-ui-radius-lg);
12
- box-shadow: var(--flx-ui-shadow-sm);
10
+ border: var(--flx-ui-card-border, 1px solid var(--flx-ui-color-border));
11
+ border-radius: var(--flx-ui-card-radius, var(--flx-ui-radius-lg));
12
+ box-shadow: var(--flx-ui-card-shadow, var(--flx-ui-shadow-sm));
13
13
  overflow: hidden;
14
14
  }
15
15
  /* Tonal elevation in dark — a card lifts by lightening, not by an unseen shadow. */
16
16
  [data-theme='dark'] :where([data-card]) {
17
- background: var(--flx-ui-gray-2);
17
+ background: var(--flx-ui-card-bg, var(--flx-ui-gray-2));
18
18
  }
19
19
  :where([data-card-header]) {
20
20
  display: flex;
@@ -125,7 +125,7 @@
125
125
  }
126
126
  }
127
127
  @media (prefers-reduced-motion: reduce) {
128
- :where([role='checkbox'][data-state]::after),
128
+ :where([role='checkbox'][data-state])::after,
129
129
  :where([role='radio'][data-state='checked'])::after {
130
130
  animation: none;
131
131
  }
@@ -24,7 +24,7 @@
24
24
  border-bottom: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
25
25
  outline: none;
26
26
  }
27
- :where([data-command-input]::placeholder) {
27
+ :where([data-command-input])::placeholder {
28
28
  color: var(--flx-ui-color-fg-muted);
29
29
  }
30
30
  :where([data-command-list]) {
package/dist/css/glow.css CHANGED
@@ -1,29 +1,38 @@
1
1
  @layer flx.components {
2
2
  /* ----------------------------------------------------------------------------
3
- * Glow — an animated gradient border ring, opt-in via `data-glow` on any element
4
- * (button, surface, card…). The ::after covers the border box and is masked to
5
- * just the ring, so the element keeps its own fill and centred content. Colours
6
- * follow the accent and are overridable per-instance with --flx-glow-from / -via
7
- * / -to (and rest speed via --flx-glow-speed). `data-glow-side` restricts it to
8
- * one edge that slides to the opposite side on hover.
3
+ * Glow: an emphasis ring, opt-in via `data-glow` on any element (button, icon
4
+ * button, surface, card). The ::after is drawn over the element's border and
5
+ * masked to a ring, so the element keeps its fill, its content and its clipping.
6
+ *
7
+ * Colours: --flx-ui-glow-from / -via / -to, defaulting to the primary colour and
8
+ * a light tint of it, so a tone or an accent carries through. Shape and motion:
9
+ * --flx-ui-glow-width (2px) and --flx-ui-glow-speed (one turn, 3s). The ring
10
+ * rests still and turns only while hovered or focused; paused, it costs nothing.
11
+ * `data-glow-side` lights one edge, which slides to the opposite side on hover.
12
+ * Skins restyle the ring with their own ::after rules.
9
13
  * ------------------------------------------------------------------------- */
10
14
  :where([data-glow]) {
11
15
  position: relative;
12
16
  border-color: transparent;
13
- /* Show the edge ring even when the host would otherwise clip (e.g. a card). */
14
- overflow: visible;
17
+ }
18
+ /* A card clips its content to its corners. It keeps doing so, with room for the ring. */
19
+ :where([data-glow][data-card]) {
20
+ overflow: clip;
21
+ overflow-clip-margin: var(--flx-ui-glow-width, 2px);
15
22
  }
16
23
  :where([data-glow])::after {
17
24
  content: '';
18
25
  position: absolute;
19
- inset: 0;
26
+ inset: calc(var(--flx-ui-border-width-thin) * -1);
20
27
  border-radius: inherit;
21
- padding: 2px;
28
+ padding: var(--flx-ui-glow-width, 2px);
22
29
  background: conic-gradient(
23
- from var(--flx-angle, 0deg),
24
- var(--flx-glow-from, var(--flx-ui-color-primary)),
25
- var(--flx-glow-via, var(--flx-ui-accent-5)),
26
- var(--flx-glow-to, var(--flx-glow-from, var(--flx-ui-color-primary)))
30
+ from var(--flx-ui-glow-angle),
31
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary)),
32
+ var(--flx-ui-glow-via, color-mix(in oklab, var(--flx-ui-color-primary), #fff 45%)),
33
+ var(--flx-ui-glow-to, var(--flx-ui-glow-from, var(--flx-ui-color-primary))),
34
+ var(--flx-ui-glow-via, color-mix(in oklab, var(--flx-ui-color-primary), #fff 45%)),
35
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary))
27
36
  );
28
37
  -webkit-mask:
29
38
  linear-gradient(#000 0 0) content-box,
@@ -33,79 +42,62 @@
33
42
  linear-gradient(#000 0 0) content-box,
34
43
  linear-gradient(#000 0 0);
35
44
  mask-composite: exclude;
36
- animation: flx-border-spin var(--flx-glow-speed, 4s) linear infinite;
37
- transition: filter var(--flx-ui-duration-normal) var(--flx-ui-ease-standard);
45
+ animation: flx-ui-glow-turn var(--flx-ui-glow-speed, 3s) linear infinite paused;
46
+ transition:
47
+ opacity var(--flx-ui-duration-normal) var(--flx-ui-ease-standard),
48
+ filter var(--flx-ui-duration-normal) var(--flx-ui-ease-standard);
38
49
  pointer-events: none;
39
50
  }
40
- /* Comes alive on hover: the ring spins faster and blooms in its accent colour. */
41
- :where([data-glow]:hover:not([data-disabled]))::after {
42
- --flx-glow-speed: 1.2s;
43
- filter: drop-shadow(0 0 5px var(--flx-glow-via, var(--flx-ui-color-primary)));
51
+ :where([data-glow]:is(:hover, :focus-visible):not([data-disabled]))::after {
52
+ animation-play-state: running;
53
+ filter: drop-shadow(0 0 4px var(--flx-ui-glow-via, color-mix(in oklab, var(--flx-ui-color-primary), #fff 45%)));
54
+ }
55
+ :where([data-glow][data-disabled])::after {
56
+ opacity: 0.35;
57
+ animation: none;
44
58
  }
45
59
 
46
- /* Single-edge glow: reuse the fixed full ring mask, but paint the colour as a
47
- band positioned on one side, so only that edge (and its corners) lights up.
48
- Because it's the *background* that's positioned, hover slides it to the opposite
49
- side smoothly. Positional, so no spin. */
60
+ /* Single edge: the full ring mask stays, and the colour is a band positioned on
61
+ one side, so only that edge and its corners light up. Moving the band is what
62
+ slides it to the opposite side on hover. */
50
63
  :where([data-glow][data-glow-side])::after {
51
64
  animation: none;
52
65
  background-repeat: no-repeat;
53
- /* A soft resting glow so the lit edge reads as intentional even before hover. */
54
- filter: drop-shadow(0 0 2px var(--flx-glow-via, var(--flx-ui-color-primary)));
55
66
  transition:
56
- background-position 450ms var(--flx-ui-ease-standard),
67
+ background-position var(--flx-ui-duration-slow) var(--flx-ui-ease-emphasized),
68
+ opacity var(--flx-ui-duration-normal) var(--flx-ui-ease-standard),
57
69
  filter var(--flx-ui-duration-normal) var(--flx-ui-ease-standard);
58
70
  }
59
- /* Wider colour plateau (12%→88%) so the whole edge lights evenly and only the
60
- far edge fades, rather than a thin central sliver. */
61
- :where([data-glow][data-glow-side='top'])::after,
62
- :where([data-glow][data-glow-side='bottom'])::after {
71
+ :where([data-glow][data-glow-side='top'], [data-glow][data-glow-side='bottom'])::after {
63
72
  background-image: linear-gradient(
64
73
  to bottom,
65
74
  transparent 12%,
66
- var(--flx-glow-from, var(--flx-ui-color-primary)) 38%,
67
- var(--flx-glow-via, var(--flx-ui-accent-5)) 50%,
68
- var(--flx-glow-from, var(--flx-ui-color-primary)) 62%,
75
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary)) 38%,
76
+ var(--flx-ui-glow-via, color-mix(in oklab, var(--flx-ui-color-primary), #fff 45%)) 50%,
77
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary)) 62%,
69
78
  transparent 88%
70
79
  );
71
80
  background-size: 100% 200%;
72
81
  }
73
- :where([data-glow][data-glow-side='left'])::after,
74
- :where([data-glow][data-glow-side='right'])::after {
82
+ :where([data-glow][data-glow-side='left'], [data-glow][data-glow-side='right'])::after {
75
83
  background-image: linear-gradient(
76
84
  to right,
77
85
  transparent 12%,
78
- var(--flx-glow-from, var(--flx-ui-color-primary)) 38%,
79
- var(--flx-glow-via, var(--flx-ui-accent-5)) 50%,
80
- var(--flx-glow-from, var(--flx-ui-color-primary)) 62%,
86
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary)) 38%,
87
+ var(--flx-ui-glow-via, color-mix(in oklab, var(--flx-ui-color-primary), #fff 45%)) 50%,
88
+ var(--flx-ui-glow-from, var(--flx-ui-color-primary)) 62%,
81
89
  transparent 88%
82
90
  );
83
91
  background-size: 200% 100%;
84
92
  }
85
- :where([data-glow][data-glow-side='bottom'])::after {
86
- background-position: 50% 0%;
87
- }
88
- :where([data-glow][data-glow-side='bottom']:hover:not([data-disabled]))::after {
89
- background-position: 50% 100%;
90
- }
91
- :where([data-glow][data-glow-side='top'])::after {
92
- background-position: 50% 100%;
93
- }
94
- :where([data-glow][data-glow-side='top']:hover:not([data-disabled]))::after {
95
- background-position: 50% 0%;
96
- }
97
- :where([data-glow][data-glow-side='left'])::after {
98
- background-position: 100% 50%;
99
- }
100
- :where([data-glow][data-glow-side='left']:hover:not([data-disabled]))::after {
101
- background-position: 0% 50%;
102
- }
103
- :where([data-glow][data-glow-side='right'])::after {
104
- background-position: 0% 50%;
105
- }
106
- :where([data-glow][data-glow-side='right']:hover:not([data-disabled]))::after {
107
- background-position: 100% 50%;
108
- }
93
+ :where([data-glow][data-glow-side='bottom'])::after { background-position: 50% 0%; }
94
+ :where([data-glow][data-glow-side='bottom']:is(:hover, :focus-visible):not([data-disabled]))::after { background-position: 50% 100%; }
95
+ :where([data-glow][data-glow-side='top'])::after { background-position: 50% 100%; }
96
+ :where([data-glow][data-glow-side='top']:is(:hover, :focus-visible):not([data-disabled]))::after { background-position: 50% 0%; }
97
+ :where([data-glow][data-glow-side='left'])::after { background-position: 100% 50%; }
98
+ :where([data-glow][data-glow-side='left']:is(:hover, :focus-visible):not([data-disabled]))::after { background-position: 0% 50%; }
99
+ :where([data-glow][data-glow-side='right'])::after { background-position: 0% 50%; }
100
+ :where([data-glow][data-glow-side='right']:is(:hover, :focus-visible):not([data-disabled]))::after { background-position: 100% 50%; }
109
101
 
110
102
  @media (prefers-reduced-motion: reduce) {
111
103
  :where([data-glow])::after {
@@ -114,14 +106,14 @@
114
106
  }
115
107
  }
116
108
 
117
- /* Registered so the angle can actually animate; unlayered like other keyframes. */
118
- @property --flx-angle {
109
+ /* Registered so the angle can animate; unlayered like other keyframes. */
110
+ @property --flx-ui-glow-angle {
119
111
  syntax: '<angle>';
120
112
  inherits: false;
121
113
  initial-value: 0deg;
122
114
  }
123
- @keyframes flx-border-spin {
115
+ @keyframes flx-ui-glow-turn {
124
116
  to {
125
- --flx-angle: 360deg;
117
+ --flx-ui-glow-angle: 360deg;
126
118
  }
127
119
  }
@@ -3,14 +3,15 @@
3
3
  :where([data-input]) {
4
4
  box-sizing: border-box;
5
5
  width: 100%;
6
- min-height: var(--flx-ui-size-md);
6
+ min-height: var(--flx-ui-field-height, var(--flx-ui-size-md));
7
7
  padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
8
8
  font: inherit;
9
9
  color: var(--flx-ui-color-fg);
10
- background: var(--flx-ui-color-bg);
11
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
12
- border-radius: var(--flx-ui-radius-md);
10
+ background: var(--flx-ui-field-bg, var(--flx-ui-color-bg));
11
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-field-border, var(--flx-ui-color-border-strong));
12
+ border-radius: var(--flx-ui-field-radius, var(--flx-ui-radius-md));
13
13
  transition:
14
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
14
15
  border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
15
16
  box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
16
17
  }
@@ -18,13 +19,14 @@
18
19
  min-height: calc(var(--flx-ui-size-md) * 2);
19
20
  resize: vertical;
20
21
  }
21
- :where([data-input]::placeholder) {
22
+ :where([data-input], [data-input-group] > input)::placeholder {
22
23
  color: var(--flx-ui-color-fg-muted);
23
24
  }
24
25
  :where([data-input]:focus-visible) {
25
26
  outline: none;
27
+ background: var(--flx-ui-field-focus-bg, var(--flx-ui-field-bg, var(--flx-ui-color-bg)));
26
28
  border-color: var(--flx-ui-color-focus-ring);
27
- box-shadow: var(--flx-ui-ctl-ring);
29
+ box-shadow: var(--flx-ui-field-focus-ring, var(--flx-ui-ctl-ring));
28
30
  }
29
31
  :where([data-input][data-invalid]) {
30
32
  border-color: var(--flx-ui-color-danger);
@@ -37,10 +39,10 @@
37
39
  -moz-appearance: textfield;
38
40
  appearance: textfield;
39
41
  }
40
- :where([data-input][type='number']::-webkit-outer-spin-button),
41
- :where([data-input][type='number']::-webkit-inner-spin-button),
42
- :where([data-input-group] input[type='number']::-webkit-outer-spin-button),
43
- :where([data-input-group] input[type='number']::-webkit-inner-spin-button) {
42
+ :where([data-input][type='number'])::-webkit-outer-spin-button,
43
+ :where([data-input][type='number'])::-webkit-inner-spin-button,
44
+ :where([data-input-group] input[type='number'])::-webkit-outer-spin-button,
45
+ :where([data-input-group] input[type='number'])::-webkit-inner-spin-button {
44
46
  -webkit-appearance: none;
45
47
  margin: 0;
46
48
  }
@@ -52,19 +54,21 @@
52
54
  align-items: center;
53
55
  gap: var(--flx-ui-space-2);
54
56
  width: 100%;
55
- min-height: var(--flx-ui-size-md);
57
+ min-height: var(--flx-ui-field-height, var(--flx-ui-size-md));
56
58
  padding: 0 var(--flx-ui-space-3);
57
59
  color: var(--flx-ui-color-fg);
58
- background: var(--flx-ui-color-bg);
59
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
60
- border-radius: var(--flx-ui-radius-md);
60
+ background: var(--flx-ui-field-bg, var(--flx-ui-color-bg));
61
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-field-border, var(--flx-ui-color-border-strong));
62
+ border-radius: var(--flx-ui-field-radius, var(--flx-ui-radius-md));
61
63
  transition:
64
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
62
65
  border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
63
66
  box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
64
67
  }
65
68
  :where([data-input-group]:focus-within) {
69
+ background: var(--flx-ui-field-focus-bg, var(--flx-ui-field-bg, var(--flx-ui-color-bg)));
66
70
  border-color: var(--flx-ui-color-focus-ring);
67
- box-shadow: var(--flx-ui-ctl-ring);
71
+ box-shadow: var(--flx-ui-field-focus-ring, var(--flx-ui-ctl-ring));
68
72
  }
69
73
  :where([data-input-group][data-invalid]) {
70
74
  border-color: var(--flx-ui-color-danger);
@@ -36,8 +36,8 @@
36
36
  text-align: center;
37
37
  -moz-appearance: textfield;
38
38
  }
39
- :where([data-number-field-input]::-webkit-inner-spin-button),
40
- :where([data-number-field-input]::-webkit-outer-spin-button) {
39
+ :where([data-number-field-input])::-webkit-inner-spin-button,
40
+ :where([data-number-field-input])::-webkit-outer-spin-button {
41
41
  -webkit-appearance: none;
42
42
  }
43
43
  :where([data-number-field-input]:focus-visible) {
@@ -29,6 +29,9 @@
29
29
  border-radius: var(--flx-ui-radius-sm);
30
30
  outline: none;
31
31
  }
32
+ :where([data-listbox-search], [data-select-search])::placeholder {
33
+ color: var(--flx-ui-color-fg-muted);
34
+ }
32
35
  :where([data-listbox-search]:focus-visible, [data-select-search]:focus-visible) {
33
36
  border-color: var(--flx-ui-color-focus-ring);
34
37
  box-shadow: var(--flx-ui-ctl-ring);
@@ -40,16 +43,16 @@
40
43
  align-items: center;
41
44
  justify-content: space-between;
42
45
  gap: var(--flx-ui-space-2);
43
- padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
44
- border-radius: var(--flx-ui-radius-sm);
46
+ padding: var(--flx-ui-item-padding, var(--flx-ui-space-2) var(--flx-ui-space-3));
47
+ border-radius: var(--flx-ui-item-radius, var(--flx-ui-radius-sm));
45
48
  color: var(--flx-ui-color-fg);
46
49
  cursor: pointer;
47
50
  user-select: none;
48
51
  outline: none;
49
52
  }
50
53
  :where([role='option'][data-active]) {
51
- background: var(--flx-ui-color-primary-subtle);
52
- color: var(--flx-ui-color-primary-active);
54
+ background: var(--flx-ui-item-active-bg, var(--flx-ui-color-primary-subtle));
55
+ color: var(--flx-ui-item-active-fg, var(--flx-ui-color-primary-active));
53
56
  }
54
57
  :where([role='option'][data-selected]) {
55
58
  font-weight: var(--flx-ui-font-weight-medium);
@@ -4,20 +4,20 @@
4
4
  min-width: 200px;
5
5
  max-height: 320px;
6
6
  overflow-y: auto;
7
- padding: var(--flx-ui-space-1);
8
- background: var(--flx-ui-color-bg);
7
+ padding: var(--flx-ui-floating-padding, var(--flx-ui-space-1));
8
+ background: var(--flx-ui-floating-bg, var(--flx-ui-color-bg));
9
9
  color: var(--flx-ui-color-fg);
10
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
11
- border-radius: var(--flx-ui-radius-lg);
12
- box-shadow: var(--flx-ui-shadow-lg);
10
+ border: var(--flx-ui-floating-border, var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border));
11
+ border-radius: var(--flx-ui-floating-radius, var(--flx-ui-radius-lg));
12
+ box-shadow: var(--flx-ui-floating-shadow, var(--flx-ui-shadow-lg));
13
13
  animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
14
14
  }
15
- :where([role='menuitem']) {
15
+ :where([role='menuitem'], [role='menuitemradio']) {
16
16
  display: flex;
17
17
  align-items: center;
18
18
  gap: var(--flx-ui-space-2);
19
- padding: var(--flx-ui-space-2) var(--flx-ui-space-3);
20
- border-radius: var(--flx-ui-radius-sm);
19
+ padding: var(--flx-ui-item-padding, var(--flx-ui-space-2) var(--flx-ui-space-3));
20
+ border-radius: var(--flx-ui-item-radius, var(--flx-ui-radius-sm));
21
21
  cursor: pointer;
22
22
  outline: none;
23
23
  user-select: none;
@@ -29,15 +29,27 @@
29
29
  font: inherit;
30
30
  color: inherit;
31
31
  }
32
- :where([role='menuitem']:hover, [role='menuitem']:focus, [role='menuitem'][data-active]) {
33
- background: var(--flx-ui-color-primary-subtle);
34
- color: var(--flx-ui-color-primary-active);
32
+ :where([role='menuitem']:hover, [role='menuitem']:focus, [role='menuitem'][data-active], [role='menuitemradio']:hover, [role='menuitemradio']:focus, [role='menuitemradio'][data-active]) {
33
+ background: var(--flx-ui-item-active-bg, var(--flx-ui-color-primary-subtle));
34
+ color: var(--flx-ui-item-active-fg, var(--flx-ui-color-primary-active));
35
35
  box-shadow: none;
36
36
  }
37
- :where([role='menuitem'][data-disabled]) {
37
+ :where([role='menuitem'][data-disabled], [role='menuitemradio'][data-disabled]) {
38
38
  opacity: var(--flx-ui-opacity-disabled);
39
39
  pointer-events: none;
40
40
  }
41
+ /* A setting's choices: the current one carries a check at the end. */
42
+ :where([role='menuitemradio'])::after {
43
+ content: '';
44
+ width: 1em;
45
+ margin-left: auto;
46
+ flex-shrink: 0;
47
+ text-align: center;
48
+ color: var(--flx-ui-color-primary);
49
+ }
50
+ :where([role='menuitemradio'][aria-checked='true'])::after {
51
+ content: '✓';
52
+ }
41
53
  :where([role='menu'] [role='separator']) {
42
54
  height: 1px;
43
55
  margin: var(--flx-ui-space-1) 0;
@@ -63,7 +75,7 @@
63
75
  flex-shrink: 0;
64
76
  }
65
77
  :where([role='menuitem'][aria-haspopup='menu'][data-state='open']) {
66
- background: var(--flx-ui-color-primary-subtle);
67
- color: var(--flx-ui-color-primary-active);
78
+ background: var(--flx-ui-item-active-bg, var(--flx-ui-color-primary-subtle));
79
+ color: var(--flx-ui-item-active-fg, var(--flx-ui-color-primary-active));
68
80
  }
69
81
  }
@@ -15,22 +15,22 @@
15
15
  transform: translate(-50%, -50%);
16
16
  width: min(460px, calc(100vw - var(--flx-ui-space-8)));
17
17
  padding: var(--flx-ui-space-6);
18
- background: var(--flx-ui-color-bg);
18
+ background: var(--flx-ui-dialog-bg, var(--flx-ui-color-bg));
19
19
  color: var(--flx-ui-color-fg);
20
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
21
- border-radius: var(--flx-ui-radius-xl);
22
- box-shadow: var(--flx-ui-shadow-xl);
20
+ border: var(--flx-ui-dialog-border, var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border));
21
+ border-radius: var(--flx-ui-dialog-radius, var(--flx-ui-radius-xl));
22
+ box-shadow: var(--flx-ui-dialog-shadow, var(--flx-ui-shadow-xl));
23
23
  animation: flx-ui-dialog-in var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
24
24
  }
25
25
 
26
26
  :where([role='dialog'][data-placement]) {
27
27
  min-width: 220px;
28
28
  padding: var(--flx-ui-space-4);
29
- background: var(--flx-ui-color-bg);
29
+ background: var(--flx-ui-floating-bg, var(--flx-ui-color-bg));
30
30
  color: var(--flx-ui-color-fg);
31
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
32
- border-radius: var(--flx-ui-radius-lg);
33
- box-shadow: var(--flx-ui-shadow-lg);
31
+ border: var(--flx-ui-floating-border, var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border));
32
+ border-radius: var(--flx-ui-floating-radius, var(--flx-ui-radius-lg));
33
+ box-shadow: var(--flx-ui-floating-shadow, var(--flx-ui-shadow-lg));
34
34
  animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
35
35
  }
36
36
  }
@@ -25,7 +25,7 @@
25
25
  border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
26
26
  box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
27
27
  }
28
- :where([data-pin-input-field]::placeholder) {
28
+ :where([data-pin-input-field])::placeholder {
29
29
  color: var(--flx-ui-color-fg-muted);
30
30
  }
31
31
  :where([data-pin-input-field]:focus-visible) {
@@ -7,16 +7,17 @@
7
7
  display: inline-flex;
8
8
  align-items: center;
9
9
  gap: var(--flx-ui-space-2);
10
- height: var(--flx-ui-size-md);
10
+ height: var(--flx-ui-field-height, var(--flx-ui-size-md));
11
11
  min-width: 200px;
12
12
  padding: 0 var(--flx-ui-space-3);
13
13
  font: inherit;
14
14
  color: var(--flx-ui-color-fg);
15
- background: var(--flx-ui-color-bg);
16
- border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
17
- border-radius: var(--flx-ui-radius-md);
15
+ background: var(--flx-ui-field-bg, var(--flx-ui-color-bg));
16
+ border: var(--flx-ui-border-width-thin) solid var(--flx-ui-field-border, var(--flx-ui-color-border-strong));
17
+ border-radius: var(--flx-ui-field-radius, var(--flx-ui-radius-md));
18
18
  cursor: pointer;
19
19
  transition:
20
+ background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
20
21
  border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
21
22
  box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
22
23
  }
@@ -29,8 +30,9 @@
29
30
  }
30
31
  :where(button[role='combobox']:focus-visible) {
31
32
  outline: none;
33
+ background: var(--flx-ui-field-focus-bg, var(--flx-ui-field-bg, var(--flx-ui-color-bg)));
32
34
  border-color: var(--flx-ui-color-focus-ring);
33
- box-shadow: var(--flx-ui-ctl-ring);
35
+ box-shadow: var(--flx-ui-field-focus-ring, var(--flx-ui-ctl-ring));
34
36
  }
35
37
  :where(button[role='combobox'])::after {
36
38
  content: '';
@@ -51,8 +53,9 @@
51
53
  padding-right: var(--flx-ui-space-1);
52
54
  }
53
55
  :where([data-combobox-control]:focus-within) {
56
+ background: var(--flx-ui-field-focus-bg, var(--flx-ui-field-bg, var(--flx-ui-color-bg)));
54
57
  border-color: var(--flx-ui-color-focus-ring);
55
- box-shadow: var(--flx-ui-ctl-ring);
58
+ box-shadow: var(--flx-ui-field-focus-ring, var(--flx-ui-ctl-ring));
56
59
  }
57
60
  :where([data-combobox-control] input[role='combobox']) {
58
61
  flex: 1;
@@ -72,7 +75,7 @@
72
75
  box-shadow: none;
73
76
  outline: none;
74
77
  }
75
- :where(input[role='combobox']::placeholder) {
78
+ :where(input[role='combobox'])::placeholder {
76
79
  color: var(--flx-ui-color-fg-muted);
77
80
  }
78
81
  :where([data-combobox-control] > button) {