@fluixi-ui/tokens 0.4.0-alpha.1 → 0.4.0-alpha.11
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/dist/css/accordion.css +81 -3
- package/dist/css/alert.css +52 -23
- package/dist/css/avatar.css +19 -0
- package/dist/css/badge.css +129 -5
- package/dist/css/base.css +3 -1
- package/dist/css/bottom-sheet.css +4 -4
- package/dist/css/breadcrumb.css +1 -1
- package/dist/css/bulk-editor.css +2 -17
- package/dist/css/button.css +76 -16
- package/dist/css/calendar.css +9 -7
- package/dist/css/card.css +5 -1
- package/dist/css/carousel.css +80 -0
- package/dist/css/checkbox-radio.css +55 -1
- package/dist/css/chip.css +175 -0
- package/dist/css/date-field.css +1 -1
- package/dist/css/date-picker.css +2 -2
- package/dist/css/descriptions.css +29 -0
- package/dist/css/drawer.css +4 -4
- package/dist/css/empty.css +40 -0
- package/dist/css/fab.css +48 -0
- package/dist/css/field-variants.css +139 -0
- package/dist/css/file-upload.css +40 -0
- package/dist/css/glow.css +127 -0
- package/dist/css/icon-button.css +42 -0
- package/dist/css/image.css +67 -0
- package/dist/css/input.css +15 -0
- package/dist/css/layout.css +10 -0
- package/dist/css/navigation-menu.css +8 -7
- package/dist/css/number-field.css +3 -3
- package/dist/css/page.css +138 -0
- package/dist/css/pagination.css +3 -3
- package/dist/css/parts/listbox.css +9 -4
- package/dist/css/pin-input.css +40 -0
- package/dist/css/progress.css +56 -0
- package/dist/css/rating.css +59 -0
- package/dist/css/scroll-area.css +44 -0
- package/dist/css/select-combobox.css +19 -4
- package/dist/css/sidenav.css +180 -10
- package/dist/css/slider.css +4 -4
- package/dist/css/speed-dial.css +85 -0
- package/dist/css/splitter.css +81 -0
- package/dist/css/statistic.css +34 -0
- package/dist/css/stepper.css +3 -3
- package/dist/css/surface.css +71 -0
- package/dist/css/switch.css +12 -0
- package/dist/css/table.css +11 -0
- package/dist/css/tabs.css +10 -1
- package/dist/css/tags-input.css +91 -0
- package/dist/css/text.css +124 -0
- package/dist/css/timeline.css +59 -0
- package/dist/css/toggle.css +6 -0
- package/dist/css/tree-view.css +70 -0
- package/dist/lib/css/index.d.ts +10 -12
- package/dist/lib/css/index.d.ts.map +1 -1
- package/dist/lib/css/index.js +21 -7
- package/dist/lib/reference/colors.d.ts.map +1 -1
- package/dist/lib/reference/colors.js +15 -1
- package/dist/preset.css +23 -1
- package/dist/tokens.css +52 -4
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* ----------------------------------------------------------------------------
|
|
3
|
+
* Carousel
|
|
4
|
+
* ------------------------------------------------------------------------- */
|
|
5
|
+
:where([data-carousel]) {
|
|
6
|
+
position: relative;
|
|
7
|
+
}
|
|
8
|
+
:where([data-carousel]:focus-visible) {
|
|
9
|
+
outline: none;
|
|
10
|
+
border-radius: var(--flx-ui-radius-md);
|
|
11
|
+
box-shadow: var(--flx-ui-ctl-ring);
|
|
12
|
+
}
|
|
13
|
+
:where([data-carousel-viewport]) {
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
/* Let the page scroll along the cross axis; the carousel owns the main axis. */
|
|
16
|
+
touch-action: pan-y;
|
|
17
|
+
}
|
|
18
|
+
:where([data-carousel-viewport][data-orientation='vertical']) {
|
|
19
|
+
touch-action: pan-x;
|
|
20
|
+
}
|
|
21
|
+
:where([data-carousel-track]) {
|
|
22
|
+
display: flex;
|
|
23
|
+
transition: transform var(--flx-ui-duration-normal, 250ms) var(--flx-ui-ease-standard, ease);
|
|
24
|
+
}
|
|
25
|
+
/* Follow the pointer 1:1 while dragging — no animated lag. */
|
|
26
|
+
:where([data-carousel-track][data-dragging]) {
|
|
27
|
+
transition: none;
|
|
28
|
+
user-select: none;
|
|
29
|
+
}
|
|
30
|
+
:where([data-carousel-track][data-orientation='vertical']) {
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
height: 100%;
|
|
33
|
+
}
|
|
34
|
+
/* One slide per view; the track translates by whole viewports. */
|
|
35
|
+
:where([data-carousel-slide]) {
|
|
36
|
+
flex: 0 0 100%;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
:where([data-carousel-dots]) {
|
|
41
|
+
display: flex;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
gap: var(--flx-ui-space-2);
|
|
44
|
+
margin-top: var(--flx-ui-space-3);
|
|
45
|
+
}
|
|
46
|
+
:where([data-carousel-dot]) {
|
|
47
|
+
width: 8px;
|
|
48
|
+
height: 8px;
|
|
49
|
+
padding: 0;
|
|
50
|
+
border: 0;
|
|
51
|
+
border-radius: var(--flx-ui-radius-full, 999px);
|
|
52
|
+
background: var(--flx-ui-color-border-strong);
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
transition: background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
55
|
+
}
|
|
56
|
+
:where([data-carousel-dot]:hover) {
|
|
57
|
+
background: var(--flx-ui-color-fg-subtle);
|
|
58
|
+
}
|
|
59
|
+
:where([data-carousel-dot][data-active]) {
|
|
60
|
+
background: var(--flx-ui-color-primary);
|
|
61
|
+
}
|
|
62
|
+
:where([data-carousel-dot]:focus-visible) {
|
|
63
|
+
outline: none;
|
|
64
|
+
box-shadow: var(--flx-ui-ctl-ring);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:where([data-carousel-prev], [data-carousel-next]) {
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
}
|
|
70
|
+
:where([data-carousel-prev][disabled], [data-carousel-next][disabled]) {
|
|
71
|
+
opacity: var(--flx-ui-opacity-disabled);
|
|
72
|
+
cursor: not-allowed;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@media (prefers-reduced-motion: reduce) {
|
|
77
|
+
:where([data-carousel-track]) {
|
|
78
|
+
transition: none;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
background: var(--flx-ui-color-primary);
|
|
24
24
|
border-color: var(--flx-ui-color-primary);
|
|
25
25
|
}
|
|
26
|
+
:where([role='checkbox']:active:not([data-disabled])) {
|
|
27
|
+
transform: scale(0.92);
|
|
28
|
+
}
|
|
26
29
|
:where([role='checkbox'][data-state='checked'])::after {
|
|
27
30
|
content: '';
|
|
28
31
|
width: 5px;
|
|
@@ -31,6 +34,8 @@
|
|
|
31
34
|
border: solid currentColor;
|
|
32
35
|
border-width: 0 2px 2px 0;
|
|
33
36
|
transform: rotate(45deg);
|
|
37
|
+
transform-origin: center;
|
|
38
|
+
animation: flx-ui-check-pop var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
|
|
34
39
|
}
|
|
35
40
|
:where([role='checkbox'][data-state='indeterminate'])::after {
|
|
36
41
|
content: '';
|
|
@@ -38,6 +43,7 @@
|
|
|
38
43
|
height: 2px;
|
|
39
44
|
background: currentColor;
|
|
40
45
|
border-radius: 1px;
|
|
46
|
+
animation: flx-ui-scale-pop var(--flx-ui-duration-fast) var(--flx-ui-ease-emphasized);
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
:where([role='radio']) {
|
|
@@ -50,7 +56,12 @@
|
|
|
50
56
|
border: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border-strong);
|
|
51
57
|
border-radius: var(--flx-ui-radius-full);
|
|
52
58
|
background: var(--flx-ui-color-bg);
|
|
53
|
-
transition:
|
|
59
|
+
transition:
|
|
60
|
+
border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
61
|
+
border-width var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
62
|
+
}
|
|
63
|
+
:where([role='radio']:active:not([data-disabled])) {
|
|
64
|
+
transform: scale(0.92);
|
|
54
65
|
}
|
|
55
66
|
:where([role='radio'][data-state='checked']) {
|
|
56
67
|
border-color: var(--flx-ui-color-primary);
|
|
@@ -62,5 +73,48 @@
|
|
|
62
73
|
inset: 3px;
|
|
63
74
|
border-radius: var(--flx-ui-radius-full);
|
|
64
75
|
background: var(--flx-ui-color-primary);
|
|
76
|
+
animation: flx-ui-scale-pop var(--flx-ui-duration-normal) var(--flx-ui-ease-emphasized);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
:where([role='checkbox']:hover:not([data-disabled])),
|
|
80
|
+
:where([role='radio']:hover:not([data-disabled])) {
|
|
81
|
+
border-color: var(--flx-ui-color-primary);
|
|
82
|
+
}
|
|
83
|
+
:where([role='checkbox']:focus-visible),
|
|
84
|
+
:where([role='radio']:focus-visible) {
|
|
85
|
+
outline: none;
|
|
86
|
+
box-shadow: var(--flx-ui-ctl-ring);
|
|
87
|
+
}
|
|
88
|
+
:where([role='checkbox'][data-disabled]),
|
|
89
|
+
:where([role='radio'][data-disabled]) {
|
|
90
|
+
opacity: var(--flx-ui-opacity-disabled);
|
|
91
|
+
cursor: not-allowed;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@keyframes flx-ui-check-pop {
|
|
95
|
+
from {
|
|
96
|
+
transform: rotate(45deg) scale(0.3);
|
|
97
|
+
opacity: 0;
|
|
98
|
+
}
|
|
99
|
+
to {
|
|
100
|
+
transform: rotate(45deg) scale(1);
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
@keyframes flx-ui-scale-pop {
|
|
105
|
+
from {
|
|
106
|
+
transform: scale(0);
|
|
107
|
+
opacity: 0;
|
|
108
|
+
}
|
|
109
|
+
to {
|
|
110
|
+
transform: scale(1);
|
|
111
|
+
opacity: 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
@media (prefers-reduced-motion: reduce) {
|
|
115
|
+
:where([role='checkbox'][data-state]::after),
|
|
116
|
+
:where([role='radio'][data-state='checked'])::after {
|
|
117
|
+
animation: none;
|
|
118
|
+
}
|
|
65
119
|
}
|
|
66
120
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* Chip (from @fluixi-ui/chip). An interactive pill: a `data-variant` fill style
|
|
3
|
+
crossed with a semantic `data-tone`, like Badge — but taller, with room for a
|
|
4
|
+
checkbox, media slot and remove button. Tone sets four custom props and the
|
|
5
|
+
variants consume them, so tone × variant needs no per-combo rules. */
|
|
6
|
+
:where([data-chip]) {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: var(--flx-ui-space-1);
|
|
10
|
+
max-width: 100%;
|
|
11
|
+
min-width: 0;
|
|
12
|
+
padding-inline: var(--flx-ui-space-2);
|
|
13
|
+
border-radius: var(--flx-ui-radius-full);
|
|
14
|
+
font-size: var(--flx-ui-font-size-sm);
|
|
15
|
+
font-weight: var(--flx-ui-font-weight-medium);
|
|
16
|
+
line-height: 1.35;
|
|
17
|
+
border: var(--flx-ui-border-width-thin) solid transparent;
|
|
18
|
+
vertical-align: middle;
|
|
19
|
+
|
|
20
|
+
/* neutral defaults; each tone overrides these four. */
|
|
21
|
+
--flx-ui-chip-color: var(--flx-ui-color-fg-muted);
|
|
22
|
+
--flx-ui-chip-text: var(--flx-ui-color-fg-muted);
|
|
23
|
+
--flx-ui-chip-bg: var(--flx-ui-color-bg-muted);
|
|
24
|
+
--flx-ui-chip-on: var(--flx-ui-color-bg);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:where([data-chip][data-size='md']) { block-size: var(--flx-ui-space-8); }
|
|
28
|
+
:where([data-chip][data-size='sm']) {
|
|
29
|
+
block-size: var(--flx-ui-space-6);
|
|
30
|
+
font-size: var(--flx-ui-font-size-xs);
|
|
31
|
+
padding-inline: var(--flx-ui-space-1-5, var(--flx-ui-space-1));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:where([data-chip][data-tone='primary']),
|
|
35
|
+
:where([data-chip][data-tone='info']) {
|
|
36
|
+
--flx-ui-chip-color: var(--flx-ui-color-primary);
|
|
37
|
+
--flx-ui-chip-text: var(--flx-ui-color-primary-text);
|
|
38
|
+
--flx-ui-chip-bg: var(--flx-ui-color-primary-subtle);
|
|
39
|
+
--flx-ui-chip-on: var(--flx-ui-color-primary-fg);
|
|
40
|
+
}
|
|
41
|
+
:where([data-chip][data-tone='success']) {
|
|
42
|
+
--flx-ui-chip-color: var(--flx-ui-color-success);
|
|
43
|
+
--flx-ui-chip-text: var(--flx-ui-color-success-text);
|
|
44
|
+
--flx-ui-chip-bg: var(--flx-ui-color-success-subtle);
|
|
45
|
+
--flx-ui-chip-on: var(--flx-ui-color-success-fg);
|
|
46
|
+
}
|
|
47
|
+
:where([data-chip][data-tone='warning']) {
|
|
48
|
+
--flx-ui-chip-color: var(--flx-ui-color-warning);
|
|
49
|
+
--flx-ui-chip-text: var(--flx-ui-color-warning-text);
|
|
50
|
+
--flx-ui-chip-bg: var(--flx-ui-color-warning-subtle);
|
|
51
|
+
--flx-ui-chip-on: var(--flx-ui-color-warning-fg);
|
|
52
|
+
}
|
|
53
|
+
:where([data-chip][data-tone='danger']) {
|
|
54
|
+
--flx-ui-chip-color: var(--flx-ui-color-danger);
|
|
55
|
+
--flx-ui-chip-text: var(--flx-ui-color-danger-text);
|
|
56
|
+
--flx-ui-chip-bg: var(--flx-ui-color-danger-subtle);
|
|
57
|
+
--flx-ui-chip-on: var(--flx-ui-color-danger-fg);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:where([data-chip][data-variant='soft']) {
|
|
61
|
+
background: var(--flx-ui-chip-bg);
|
|
62
|
+
color: var(--flx-ui-chip-text);
|
|
63
|
+
}
|
|
64
|
+
:where([data-chip][data-variant='solid']) {
|
|
65
|
+
background: var(--flx-ui-chip-color);
|
|
66
|
+
color: var(--flx-ui-chip-on);
|
|
67
|
+
}
|
|
68
|
+
:where([data-chip][data-variant='outline']) {
|
|
69
|
+
background: transparent;
|
|
70
|
+
color: var(--flx-ui-chip-text);
|
|
71
|
+
border-color: var(--flx-ui-chip-color);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* A selected filter chip reads as "on" regardless of variant. */
|
|
75
|
+
:where([data-chip][data-selected]) {
|
|
76
|
+
background: var(--flx-ui-chip-color);
|
|
77
|
+
color: var(--flx-ui-chip-on);
|
|
78
|
+
border-color: var(--flx-ui-chip-color);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:where([data-chip][data-interactive]:not([data-disabled])) { cursor: pointer; }
|
|
82
|
+
|
|
83
|
+
:where([data-chip][data-disabled]) {
|
|
84
|
+
opacity: var(--flx-ui-opacity-disabled, 0.5);
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* The label truncates rather than pushing the remove button out of the pill —
|
|
89
|
+
a chip sits in a row of chips and must not grow unbounded. */
|
|
90
|
+
:where([data-chip-label]),
|
|
91
|
+
:where([data-chip-action]) {
|
|
92
|
+
min-width: 0;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* The action is a real <button>; strip its chrome so it reads as the chip's label. */
|
|
99
|
+
:where([data-chip-action]) {
|
|
100
|
+
appearance: none;
|
|
101
|
+
background: none;
|
|
102
|
+
border: 0;
|
|
103
|
+
padding: 0;
|
|
104
|
+
margin: 0;
|
|
105
|
+
font: inherit;
|
|
106
|
+
color: inherit;
|
|
107
|
+
cursor: inherit;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
:where([data-chip-media]) {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
flex: none;
|
|
114
|
+
margin-inline-start: calc(var(--flx-ui-space-1) * -1);
|
|
115
|
+
}
|
|
116
|
+
:where([data-chip-media]) :where(img, [data-avatar]) {
|
|
117
|
+
block-size: 1.5em;
|
|
118
|
+
inline-size: 1.5em;
|
|
119
|
+
border-radius: var(--flx-ui-radius-full);
|
|
120
|
+
object-fit: cover;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
:where([data-chip-remove]) {
|
|
124
|
+
appearance: none;
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
flex: none;
|
|
129
|
+
background: none;
|
|
130
|
+
border: 0;
|
|
131
|
+
padding: 0;
|
|
132
|
+
margin-inline-end: calc(var(--flx-ui-space-1) * -1);
|
|
133
|
+
/* A comfortable target that still fits the pill: the glyph is small, the
|
|
134
|
+
hit area is not. */
|
|
135
|
+
block-size: 1.5em;
|
|
136
|
+
inline-size: 1.5em;
|
|
137
|
+
border-radius: var(--flx-ui-radius-full);
|
|
138
|
+
font: inherit;
|
|
139
|
+
line-height: 1;
|
|
140
|
+
color: inherit;
|
|
141
|
+
opacity: 0.7;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
transition: opacity var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
144
|
+
background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
145
|
+
}
|
|
146
|
+
:where([data-chip-remove]:hover) {
|
|
147
|
+
opacity: 1;
|
|
148
|
+
background: color-mix(in oklab, currentColor 15%, transparent);
|
|
149
|
+
}
|
|
150
|
+
:where([data-chip-remove]:disabled) { cursor: not-allowed; }
|
|
151
|
+
|
|
152
|
+
:where([data-chip-action]:focus-visible),
|
|
153
|
+
:where([data-chip-remove]:focus-visible),
|
|
154
|
+
:where([data-chip-checkbox]:focus-visible) {
|
|
155
|
+
outline: var(--flx-ui-border-width-thick) solid var(--flx-ui-color-focus-ring);
|
|
156
|
+
outline-offset: 2px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Shrink the checkbox to chip scale without touching the checkbox component. */
|
|
160
|
+
:where([data-chip-checkbox]) {
|
|
161
|
+
flex: none;
|
|
162
|
+
block-size: 1.15em;
|
|
163
|
+
inline-size: 1.15em;
|
|
164
|
+
margin-inline-start: calc(var(--flx-ui-space-1) * -1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
:where([data-chip-group]) {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-wrap: wrap;
|
|
170
|
+
align-items: center;
|
|
171
|
+
gap: var(--flx-ui-space-2);
|
|
172
|
+
min-width: 0;
|
|
173
|
+
}
|
|
174
|
+
:where([data-chip-group][data-size='sm']) { gap: var(--flx-ui-space-1); }
|
|
175
|
+
}
|
package/dist/css/date-field.css
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
font-size: var(--flx-ui-font-size-sm);
|
|
12
12
|
font-weight: var(--flx-ui-font-weight-medium);
|
|
13
13
|
color: var(--flx-ui-color-fg);
|
|
14
|
-
line-height: var(--flx-ui-
|
|
14
|
+
line-height: var(--flx-ui-line-height-none);
|
|
15
15
|
}
|
|
16
16
|
:where([data-date-field-input]) {
|
|
17
17
|
display: inline-flex;
|
package/dist/css/date-picker.css
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
font-size: var(--flx-ui-font-size-sm);
|
|
12
12
|
font-weight: var(--flx-ui-font-weight-medium);
|
|
13
13
|
color: var(--flx-ui-color-fg);
|
|
14
|
-
line-height: var(--flx-ui-
|
|
14
|
+
line-height: var(--flx-ui-line-height-none);
|
|
15
15
|
}
|
|
16
16
|
/* The control box owns the border; the embedded field input goes borderless. */
|
|
17
17
|
:where([data-date-picker-control]) {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
border-radius: var(--flx-ui-radius-lg);
|
|
72
72
|
background: var(--flx-ui-color-bg);
|
|
73
73
|
box-shadow: var(--flx-ui-shadow-lg, 0 10px 30px rgba(17, 24, 39, 0.12));
|
|
74
|
-
animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-
|
|
74
|
+
animation: flx-ui-pop-in var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
75
75
|
}
|
|
76
76
|
@media (prefers-reduced-motion: reduce) {
|
|
77
77
|
:where([data-date-picker-content]) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* Descriptions (from @fluixi-ui/descriptions). A key/value grid; `columns` drives
|
|
3
|
+
the track count and an item's `span` stretches it. */
|
|
4
|
+
:where([data-descriptions]) {
|
|
5
|
+
display: grid;
|
|
6
|
+
grid-template-columns: repeat(var(--flx-descriptions-columns, 1), minmax(0, 1fr));
|
|
7
|
+
gap: var(--flx-ui-space-4) var(--flx-ui-space-6);
|
|
8
|
+
margin: 0;
|
|
9
|
+
}
|
|
10
|
+
:where([data-descriptions-item]) {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: var(--flx-ui-space-1);
|
|
14
|
+
min-width: 0;
|
|
15
|
+
}
|
|
16
|
+
:where([data-descriptions-term]) {
|
|
17
|
+
font-size: var(--flx-ui-font-size-xs);
|
|
18
|
+
font-weight: var(--flx-ui-font-weight-medium);
|
|
19
|
+
color: var(--flx-ui-color-fg-subtle);
|
|
20
|
+
letter-spacing: var(--flx-ui-letter-spacing-wide);
|
|
21
|
+
}
|
|
22
|
+
:where([data-descriptions-detail]) {
|
|
23
|
+
margin: 0;
|
|
24
|
+
font-size: var(--flx-ui-font-size-sm);
|
|
25
|
+
color: var(--flx-ui-color-fg);
|
|
26
|
+
min-width: 0;
|
|
27
|
+
overflow-wrap: anywhere;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/css/drawer.css
CHANGED
|
@@ -70,14 +70,14 @@
|
|
|
70
70
|
align-items: flex-start;
|
|
71
71
|
justify-content: space-between;
|
|
72
72
|
gap: var(--flx-ui-space-3);
|
|
73
|
-
padding: var(--flx-ui-space-
|
|
73
|
+
padding: var(--flx-ui-space-4);
|
|
74
74
|
border-bottom: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
|
|
75
75
|
}
|
|
76
76
|
:where([data-drawer-title]) {
|
|
77
77
|
margin: 0;
|
|
78
78
|
font-size: var(--flx-ui-font-size-lg);
|
|
79
79
|
font-weight: var(--flx-ui-font-weight-semibold);
|
|
80
|
-
line-height: var(--flx-ui-
|
|
80
|
+
line-height: var(--flx-ui-line-height-tight);
|
|
81
81
|
color: var(--flx-ui-color-fg);
|
|
82
82
|
}
|
|
83
83
|
:where([data-drawer-description]) {
|
|
@@ -89,14 +89,14 @@
|
|
|
89
89
|
flex: 1;
|
|
90
90
|
min-height: 0;
|
|
91
91
|
overflow-y: auto;
|
|
92
|
-
padding: var(--flx-ui-space-
|
|
92
|
+
padding: var(--flx-ui-space-4);
|
|
93
93
|
}
|
|
94
94
|
:where([data-drawer-footer]) {
|
|
95
95
|
display: flex;
|
|
96
96
|
align-items: center;
|
|
97
97
|
justify-content: flex-end;
|
|
98
98
|
gap: var(--flx-ui-space-2);
|
|
99
|
-
padding: var(--flx-ui-space-
|
|
99
|
+
padding: var(--flx-ui-space-4);
|
|
100
100
|
border-top: var(--flx-ui-border-width-thin) solid var(--flx-ui-color-border);
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* Empty (from @fluixi-ui/empty). A centred empty-state: icon tile, title,
|
|
3
|
+
description, and an actions row. */
|
|
4
|
+
:where([data-empty]) {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
text-align: center;
|
|
10
|
+
gap: var(--flx-ui-space-2);
|
|
11
|
+
padding: var(--flx-ui-space-8) var(--flx-ui-space-4);
|
|
12
|
+
color: var(--flx-ui-color-fg-subtle);
|
|
13
|
+
}
|
|
14
|
+
:where([data-empty-icon]) {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
width: 44px;
|
|
19
|
+
height: 44px;
|
|
20
|
+
margin-bottom: var(--flx-ui-space-1);
|
|
21
|
+
border-radius: var(--flx-ui-radius-lg);
|
|
22
|
+
background: var(--flx-ui-color-bg-subtle);
|
|
23
|
+
color: var(--flx-ui-color-fg-subtle);
|
|
24
|
+
}
|
|
25
|
+
:where([data-empty-title]) {
|
|
26
|
+
font-size: var(--flx-ui-font-size-sm);
|
|
27
|
+
font-weight: var(--flx-ui-font-weight-semibold);
|
|
28
|
+
color: var(--flx-ui-color-fg);
|
|
29
|
+
}
|
|
30
|
+
:where([data-empty-description]) {
|
|
31
|
+
font-size: var(--flx-ui-font-size-sm);
|
|
32
|
+
color: var(--flx-ui-color-fg-subtle);
|
|
33
|
+
max-width: 42ch;
|
|
34
|
+
}
|
|
35
|
+
:where([data-empty-actions]) {
|
|
36
|
+
margin-top: var(--flx-ui-space-3);
|
|
37
|
+
display: flex;
|
|
38
|
+
gap: var(--flx-ui-space-2);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/css/fab.css
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* ----------------------------------------------------------------------------
|
|
3
|
+
* FAB — a circular, elevated [data-variant] button. Colours / states / ripple
|
|
4
|
+
* come from the button slice + skin; this owns size, shape and elevation.
|
|
5
|
+
* ------------------------------------------------------------------------- */
|
|
6
|
+
:where([data-fab]) {
|
|
7
|
+
width: 56px;
|
|
8
|
+
height: 56px;
|
|
9
|
+
padding: 0;
|
|
10
|
+
min-width: 0;
|
|
11
|
+
flex: none;
|
|
12
|
+
border-radius: var(--flx-ui-radius-full);
|
|
13
|
+
box-shadow: var(--flx-ui-shadow-lg);
|
|
14
|
+
}
|
|
15
|
+
:where([data-fab]:hover:not([data-disabled])) {
|
|
16
|
+
box-shadow: var(--flx-ui-shadow-xl);
|
|
17
|
+
}
|
|
18
|
+
:where([data-fab][data-fab-size='sm']) {
|
|
19
|
+
width: 40px;
|
|
20
|
+
height: 40px;
|
|
21
|
+
}
|
|
22
|
+
:where([data-fab][data-fab-size='lg']) {
|
|
23
|
+
width: 64px;
|
|
24
|
+
height: 64px;
|
|
25
|
+
}
|
|
26
|
+
:where([data-fab] svg) {
|
|
27
|
+
width: 24px;
|
|
28
|
+
height: 24px;
|
|
29
|
+
}
|
|
30
|
+
:where([data-fab][data-fab-size='sm'] svg) {
|
|
31
|
+
width: 20px;
|
|
32
|
+
height: 20px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Extended: a pill holding an icon plus a label. */
|
|
36
|
+
:where([data-fab][data-fab-extended]) {
|
|
37
|
+
width: auto;
|
|
38
|
+
min-width: 56px;
|
|
39
|
+
padding: 0 var(--flx-ui-space-5);
|
|
40
|
+
gap: var(--flx-ui-space-2);
|
|
41
|
+
border-radius: var(--flx-ui-radius-full);
|
|
42
|
+
font-weight: var(--flx-ui-font-weight-semibold);
|
|
43
|
+
}
|
|
44
|
+
:where([data-fab][data-fab-extended][data-fab-size='sm']) {
|
|
45
|
+
min-width: 40px;
|
|
46
|
+
padding: 0 var(--flx-ui-space-4);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
@layer flx.components {
|
|
2
|
+
/* ----------------------------------------------------------------------------
|
|
3
|
+
* Field variants — an appearance axis shared by every field control that opts in
|
|
4
|
+
* via `data-field-variant` (Input, the Select trigger `button[role=combobox]`,
|
|
5
|
+
* the Combobox control, a field-styled dropdown trigger). `default` (no attr)
|
|
6
|
+
* keeps each skin's baseline; `outlined` and `filled` are explicit Material-style
|
|
7
|
+
* modes that every skin renders.
|
|
8
|
+
*
|
|
9
|
+
* These deliberately DON'T use `:where()` — the `[data-field-variant]` attribute
|
|
10
|
+
* carries a little specificity so the variant wins over a control's own baseline
|
|
11
|
+
* slice regardless of import order (per-component self-imports dedupe to the
|
|
12
|
+
* first position). Consumer CSS is unlayered and still wins over this whole
|
|
13
|
+
* layer; the skins (a higher @layer) still override per skin.
|
|
14
|
+
* ------------------------------------------------------------------------- */
|
|
15
|
+
|
|
16
|
+
/* Give a plain trigger button (not the select's role=combobox, which already has
|
|
17
|
+
a field box) the field box model when it opts into a variant. */
|
|
18
|
+
button[data-field-variant]:not([role='combobox']) {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: var(--flx-ui-space-2);
|
|
23
|
+
min-height: var(--flx-ui-size-md);
|
|
24
|
+
padding: 0 var(--flx-ui-space-3);
|
|
25
|
+
font: inherit;
|
|
26
|
+
color: var(--flx-ui-color-fg);
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
transition:
|
|
29
|
+
border-color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
30
|
+
background var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
31
|
+
box-shadow var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Outlined — a bordered box on a transparent ground, with a more defined border
|
|
35
|
+
than the baseline so the two read as distinct. */
|
|
36
|
+
[data-field-variant='outlined'] {
|
|
37
|
+
background: transparent;
|
|
38
|
+
border: var(--flx-ui-border-width-thin) solid
|
|
39
|
+
color-mix(in oklab, var(--flx-ui-color-border-strong), var(--flx-ui-color-fg) 22%);
|
|
40
|
+
border-radius: var(--flx-ui-radius-md);
|
|
41
|
+
}
|
|
42
|
+
[data-field-variant='outlined']:hover:not(:focus):not(:focus-within):not([data-invalid]) {
|
|
43
|
+
border-color: var(--flx-ui-color-fg-muted);
|
|
44
|
+
}
|
|
45
|
+
[data-field-variant='outlined']:focus,
|
|
46
|
+
[data-field-variant='outlined']:focus-within {
|
|
47
|
+
outline: none;
|
|
48
|
+
border-color: var(--flx-ui-color-focus-ring);
|
|
49
|
+
box-shadow: var(--flx-ui-ctl-ring);
|
|
50
|
+
}
|
|
51
|
+
[data-field-variant='outlined'][data-invalid] {
|
|
52
|
+
border-color: var(--flx-ui-color-danger);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Filled — a tinted fill with a bottom rule that thickens/colours on focus. */
|
|
56
|
+
[data-field-variant='filled'] {
|
|
57
|
+
background: var(--flx-ui-color-bg-muted);
|
|
58
|
+
border: var(--flx-ui-border-width-thin) solid transparent;
|
|
59
|
+
border-bottom-color: var(--flx-ui-color-border-strong);
|
|
60
|
+
border-radius: var(--flx-ui-radius-md) var(--flx-ui-radius-md) 0 0;
|
|
61
|
+
}
|
|
62
|
+
[data-field-variant='filled']:hover:not(:focus):not(:focus-within):not([data-invalid]) {
|
|
63
|
+
background: color-mix(in oklab, var(--flx-ui-color-bg-muted), var(--flx-ui-color-fg) 4%);
|
|
64
|
+
}
|
|
65
|
+
[data-field-variant='filled']:focus,
|
|
66
|
+
[data-field-variant='filled']:focus-within {
|
|
67
|
+
outline: none;
|
|
68
|
+
border-bottom-width: 2px;
|
|
69
|
+
border-bottom-color: var(--flx-ui-color-focus-ring);
|
|
70
|
+
box-shadow: none;
|
|
71
|
+
}
|
|
72
|
+
[data-field-variant='filled'][data-invalid] {
|
|
73
|
+
border-bottom-color: var(--flx-ui-color-danger);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ----------------------------------------------------------------------------
|
|
77
|
+
* Floating label — a frameless positioning wrapper. The INPUT keeps its own
|
|
78
|
+
* frame (base + variant + skin), so there's exactly one border and it always
|
|
79
|
+
* matches the active skin. The label overlays and lifts into the top strip on
|
|
80
|
+
* focus / when filled (driven by the input's :placeholder-shown). The input is
|
|
81
|
+
* taller with bottom-weighted text so the lifted label never overlaps the value.
|
|
82
|
+
* ------------------------------------------------------------------------- */
|
|
83
|
+
[data-field-float] {
|
|
84
|
+
position: relative;
|
|
85
|
+
display: block;
|
|
86
|
+
width: 100%;
|
|
87
|
+
}
|
|
88
|
+
[data-field-float] > [data-input] {
|
|
89
|
+
width: 100%;
|
|
90
|
+
min-height: calc(var(--flx-ui-size-md) + 14px);
|
|
91
|
+
padding-top: 20px;
|
|
92
|
+
padding-bottom: 4px;
|
|
93
|
+
}
|
|
94
|
+
/* Outlined notches the top border (label rides on the edge), so the value text
|
|
95
|
+
stays vertically centred rather than pushed down for an inside label. */
|
|
96
|
+
[data-field-float] > [data-input][data-field-variant='outlined'] {
|
|
97
|
+
padding-top: var(--flx-ui-space-2);
|
|
98
|
+
padding-bottom: var(--flx-ui-space-2);
|
|
99
|
+
}
|
|
100
|
+
[data-field-float] > [data-field-float-label] {
|
|
101
|
+
position: absolute;
|
|
102
|
+
left: var(--flx-ui-space-3);
|
|
103
|
+
top: 50%;
|
|
104
|
+
transform: translateY(-50%);
|
|
105
|
+
transform-origin: left top;
|
|
106
|
+
max-width: calc(100% - 2 * var(--flx-ui-space-3));
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
text-overflow: ellipsis;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
color: var(--flx-ui-color-fg-muted);
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
transition:
|
|
113
|
+
top var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
114
|
+
transform var(--flx-ui-duration-fast) var(--flx-ui-ease-standard),
|
|
115
|
+
color var(--flx-ui-duration-fast) var(--flx-ui-ease-standard);
|
|
116
|
+
}
|
|
117
|
+
/* Lift to the top strip when focused or filled (default + filled: inside). */
|
|
118
|
+
[data-field-float] > [data-input]:focus ~ [data-field-float-label],
|
|
119
|
+
[data-field-float] > [data-input]:not(:placeholder-shown) ~ [data-field-float-label] {
|
|
120
|
+
top: 8px;
|
|
121
|
+
transform: translateY(0) scale(0.8);
|
|
122
|
+
}
|
|
123
|
+
/* Outlined lifts ONTO the top border and notches it (paints the surface behind
|
|
124
|
+
the label). Only outlined is reliably a transparent bordered box across skins. */
|
|
125
|
+
[data-field-float] > [data-input][data-field-variant='outlined']:focus ~ [data-field-float-label],
|
|
126
|
+
[data-field-float] > [data-input][data-field-variant='outlined']:not(:placeholder-shown) ~ [data-field-float-label] {
|
|
127
|
+
top: 0;
|
|
128
|
+
transform: translateY(-50%) scale(0.8);
|
|
129
|
+
padding: 0 var(--flx-ui-space-1);
|
|
130
|
+
background: var(--flx-ui-color-bg);
|
|
131
|
+
}
|
|
132
|
+
/* Accent the label only while the field is focused. */
|
|
133
|
+
[data-field-float] > [data-input]:focus ~ [data-field-float-label] {
|
|
134
|
+
color: var(--flx-ui-color-focus-ring);
|
|
135
|
+
}
|
|
136
|
+
[data-field-float] > [data-input][data-invalid] ~ [data-field-float-label] {
|
|
137
|
+
color: var(--flx-ui-color-danger);
|
|
138
|
+
}
|
|
139
|
+
}
|