@webority/theme 0.7.5 → 0.7.6
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/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +13 -8
- package/scss/_tokens.scss +10 -2
- package/scss/_wui-aliases.scss +11 -3
- package/scss/wui/_card.scss +138 -0
- package/scss/wui/_choice.scss +124 -2
- package/scss/wui/_controls.scss +228 -3
- package/scss/wui/_data.scss +26 -13
- package/scss/wui/_display.scss +160 -1
- package/scss/wui/_flow.scss +216 -7
- package/scss/wui/_media.scss +264 -14
- package/scss/wui/_navigation.scss +1 -1
- package/scss/wui/_overlays.scss +45 -1
- package/scss/wui/_typography.scss +16 -0
- package/scss/wui/_utilities.scss +141 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"publishConfig": {
|
package/scss/_components.scss
CHANGED
|
@@ -178,10 +178,10 @@
|
|
|
178
178
|
--bs-dropdown-border-color: var(--wui-border-light);
|
|
179
179
|
--bs-dropdown-border-radius: var(--wui-radius-lg);
|
|
180
180
|
--bs-dropdown-link-color: var(--wui-text-1);
|
|
181
|
-
--bs-dropdown-link-hover-color: var(--
|
|
182
|
-
--bs-dropdown-link-hover-bg: var(--
|
|
183
|
-
--bs-dropdown-link-active-color: var(--
|
|
184
|
-
--bs-dropdown-link-active-bg: var(--
|
|
181
|
+
--bs-dropdown-link-hover-color: var(--color-primary-hover);
|
|
182
|
+
--bs-dropdown-link-hover-bg: var(--color-primary-soft);
|
|
183
|
+
--bs-dropdown-link-active-color: var(--color-primary-hover);
|
|
184
|
+
--bs-dropdown-link-active-bg: var(--color-primary-soft);
|
|
185
185
|
box-shadow: var(--wui-shadow-overlay);
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
&.danger {
|
|
222
222
|
--bs-dropdown-link-color: var(--wui-danger-text);
|
|
223
223
|
--bs-dropdown-link-hover-color: var(--wui-danger-text);
|
|
224
|
-
--bs-dropdown-link-hover-bg: var(--
|
|
224
|
+
--bs-dropdown-link-hover-bg: var(--color-danger-soft);
|
|
225
225
|
.bi, i, svg { color: var(--wui-danger); }
|
|
226
226
|
&:hover .bi, &:hover i, &:hover svg,
|
|
227
227
|
&:focus .bi, &:focus i, &:focus svg { color: var(--wui-danger); }
|
|
@@ -244,10 +244,10 @@
|
|
|
244
244
|
--bs-btn-bg: transparent;
|
|
245
245
|
--bs-btn-border-color: transparent;
|
|
246
246
|
--bs-btn-hover-color: var(--color-text);
|
|
247
|
-
--bs-btn-hover-bg: var(--
|
|
247
|
+
--bs-btn-hover-bg: var(--color-primary-soft);
|
|
248
248
|
--bs-btn-hover-border-color: transparent;
|
|
249
249
|
--bs-btn-active-color: var(--color-text);
|
|
250
|
-
--bs-btn-active-bg: var(--
|
|
250
|
+
--bs-btn-active-bg: var(--color-primary-soft);
|
|
251
251
|
--bs-btn-active-border-color: transparent;
|
|
252
252
|
}
|
|
253
253
|
|
|
@@ -388,7 +388,8 @@ button.wui-input-leading:hover {
|
|
|
388
388
|
}
|
|
389
389
|
button.wui-input-trailing:focus-visible,
|
|
390
390
|
button.wui-input-leading:focus-visible {
|
|
391
|
-
outline:
|
|
391
|
+
outline: 2px solid var(--color-primary);
|
|
392
|
+
outline-offset: 2px;
|
|
392
393
|
color: var(--wui-primary);
|
|
393
394
|
border-radius: 9999px;
|
|
394
395
|
}
|
|
@@ -447,6 +448,10 @@ button.wui-input-leading:focus-visible {
|
|
|
447
448
|
transform: translateX(-100%);
|
|
448
449
|
animation: shimmer 1.4s infinite;
|
|
449
450
|
}
|
|
451
|
+
// Dark mode shimmer — lightens the gradient for visibility on darker surfaces.
|
|
452
|
+
:root[data-theme="dark"] .shimmer::after {
|
|
453
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent);
|
|
454
|
+
}
|
|
450
455
|
|
|
451
456
|
// ---- Keyframes -------------------------------------------------------
|
|
452
457
|
|
package/scss/_tokens.scss
CHANGED
|
@@ -83,8 +83,12 @@
|
|
|
83
83
|
// and section accents. Distinct from --wui-bg-* (which are the
|
|
84
84
|
// platform/portal palette).
|
|
85
85
|
--wui-surface-soft: #F2EFFE;
|
|
86
|
-
--wui-surface-
|
|
87
|
-
--wui-surface-
|
|
86
|
+
--wui-surface-violet: #ECE4FE;
|
|
87
|
+
--wui-surface-violet-soft: #FCFCFC;
|
|
88
|
+
// Deprecated aliases — the value was always lavender, never blue. Published
|
|
89
|
+
// names, so consumers migrate on their own bump; removed in a future major.
|
|
90
|
+
--wui-surface-blue: var(--wui-surface-violet);
|
|
91
|
+
--wui-surface-blue-soft: var(--wui-surface-violet-soft);
|
|
88
92
|
--wui-surface-mute: #F9FBFC;
|
|
89
93
|
--wui-ink: #000000;
|
|
90
94
|
|
|
@@ -215,6 +219,10 @@
|
|
|
215
219
|
--wui-fs-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
|
|
216
220
|
--wui-fs-3xl: clamp(1.875rem, 1.5rem + 1.75vw, 2.75rem);
|
|
217
221
|
|
|
222
|
+
// ---- Font stacks ---------------------------------------------------
|
|
223
|
+
// Sans-serif body font (Bootstrap's system stack or product override).
|
|
224
|
+
--font-sans: var(--bs-body-font-family);
|
|
225
|
+
|
|
218
226
|
// ---- Display font slot --------------------------------------------
|
|
219
227
|
// A dedicated slot for a display/heading typeface, kept separate from
|
|
220
228
|
// $font-family-base so a product can point ONLY its display headings (.h-display) at a
|
package/scss/_wui-aliases.scss
CHANGED
|
@@ -93,8 +93,11 @@
|
|
|
93
93
|
--color-accent: var(--color-warning);
|
|
94
94
|
--color-accent-soft: var(--color-warning-soft);
|
|
95
95
|
|
|
96
|
-
// Scrim —
|
|
97
|
-
|
|
96
|
+
// Scrim — one dim ink, two strengths: scrim for page-level backdrops
|
|
97
|
+
// (drawer, modal, shell), scrim-strong for media overlays carrying white
|
|
98
|
+
// ink (logo uploader, file previews).
|
|
99
|
+
--color-scrim: color-mix(in srgb, var(--wui-text-heading) 45%, transparent);
|
|
100
|
+
--color-scrim-strong: color-mix(in srgb, var(--wui-text-heading) 62%, transparent);
|
|
98
101
|
--color-on-scrim: var(--wui-bg-light);
|
|
99
102
|
|
|
100
103
|
// Elevation — in-page flat; only floating overlays lift (matches zeroed BS
|
|
@@ -119,6 +122,7 @@
|
|
|
119
122
|
--color-surface: var(--wui-dark-bg-surface);
|
|
120
123
|
|
|
121
124
|
--color-border: var(--wui-dark-border);
|
|
125
|
+
--color-border-light: color-mix(in srgb, var(--wui-dark-border) 50%, transparent);
|
|
122
126
|
--color-border-strong: var(--wui-dark-border-strong);
|
|
123
127
|
|
|
124
128
|
--color-text: var(--wui-dark-text);
|
|
@@ -154,6 +158,9 @@
|
|
|
154
158
|
--color-primary: var(--wui-dark-primary);
|
|
155
159
|
--color-primary-hover: var(--wui-dark-primary-hover);
|
|
156
160
|
--color-primary-soft: color-mix(in srgb, var(--wui-primary) 20%, transparent);
|
|
161
|
+
// Dark mode primary text: #fff on #6B83EC measures 3.5:1, below WCAG AA (4.5:1).
|
|
162
|
+
// Map to a near-black for safe contrast (7.5:1+) when text sits on primary fill.
|
|
163
|
+
--color-primary-text: #0F0F0F;
|
|
157
164
|
|
|
158
165
|
// On a dark surface the FILL colours are already the readable ones — the darkened
|
|
159
166
|
// `-text` values would sink into the background. So dark mode maps text back to the
|
|
@@ -169,7 +176,8 @@
|
|
|
169
176
|
--color-info-soft: color-mix(in srgb, var(--wui-info) 18%, transparent);
|
|
170
177
|
--color-accent-soft: var(--color-warning-soft);
|
|
171
178
|
|
|
172
|
-
--color-scrim: color-mix(in srgb, black
|
|
179
|
+
--color-scrim: color-mix(in srgb, black 45%, transparent);
|
|
180
|
+
--color-scrim-strong: color-mix(in srgb, black 62%, transparent);
|
|
173
181
|
--color-on-scrim: var(--wui-bg-light);
|
|
174
182
|
|
|
175
183
|
color-scheme: dark;
|
package/scss/wui/_card.scss
CHANGED
|
@@ -36,3 +36,141 @@
|
|
|
36
36
|
.card-compact > .card-body {
|
|
37
37
|
padding: 16px;
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
// ── Variants ──────────────────────────────────────────────────────────────────
|
|
41
|
+
// `default` is the flat surface above. The rest are opt-in visual treatments,
|
|
42
|
+
// chosen by the `variant` prop (React) / attribute (Razor).
|
|
43
|
+
.card-elevated {
|
|
44
|
+
border-color: transparent;
|
|
45
|
+
box-shadow: var(--shadow-md);
|
|
46
|
+
}
|
|
47
|
+
.card-outlined {
|
|
48
|
+
border-width: 2px;
|
|
49
|
+
border-color: var(--color-border-strong);
|
|
50
|
+
}
|
|
51
|
+
.card-muted {
|
|
52
|
+
--bs-card-bg: var(--color-bg-muted);
|
|
53
|
+
}
|
|
54
|
+
// Accent — a coloured left stripe, tinted by `tone` (default primary).
|
|
55
|
+
.card-accent {
|
|
56
|
+
--card-accent: var(--color-primary);
|
|
57
|
+
position: relative;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
}
|
|
60
|
+
.card-accent.tone-success {
|
|
61
|
+
--card-accent: var(--color-success);
|
|
62
|
+
}
|
|
63
|
+
.card-accent.tone-warning {
|
|
64
|
+
--card-accent: var(--color-warning);
|
|
65
|
+
}
|
|
66
|
+
.card-accent.tone-danger {
|
|
67
|
+
--card-accent: var(--color-danger);
|
|
68
|
+
}
|
|
69
|
+
.card-accent.tone-info {
|
|
70
|
+
--card-accent: var(--color-info);
|
|
71
|
+
}
|
|
72
|
+
.card-accent::before {
|
|
73
|
+
content: "";
|
|
74
|
+
position: absolute;
|
|
75
|
+
top: 0;
|
|
76
|
+
bottom: 0;
|
|
77
|
+
left: 0;
|
|
78
|
+
width: 4px;
|
|
79
|
+
background: var(--card-accent);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Padding scale ── (header + body + footer). `md` is the Bootstrap default.
|
|
83
|
+
.card-pad-none > .card-header,
|
|
84
|
+
.card-pad-none > .card-body,
|
|
85
|
+
.card-pad-none > .card-footer {
|
|
86
|
+
padding: 0;
|
|
87
|
+
}
|
|
88
|
+
.card-pad-sm > .card-header,
|
|
89
|
+
.card-pad-sm > .card-body,
|
|
90
|
+
.card-pad-sm > .card-footer {
|
|
91
|
+
padding: 14px 16px;
|
|
92
|
+
}
|
|
93
|
+
.card-pad-lg > .card-header,
|
|
94
|
+
.card-pad-lg > .card-body,
|
|
95
|
+
.card-pad-lg > .card-footer {
|
|
96
|
+
padding: 28px 32px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Constrained width ── (`maxWidth`): caps + centres a standalone card. Cards
|
|
100
|
+
// are fluid (fill their column) unless this is set.
|
|
101
|
+
.card-constrained {
|
|
102
|
+
max-width: var(--card-max-width, none);
|
|
103
|
+
margin-inline: auto;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Footer ── (React `footer` / Razor <app-card-footer>): mirrors the header row.
|
|
107
|
+
.card-footer {
|
|
108
|
+
--bs-card-cap-bg: var(--color-surface);
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 12px;
|
|
112
|
+
border-top: 1px solid var(--color-border);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Header icon + title stack ──
|
|
116
|
+
// A leading glyph before the title, with the title/subtitle stacked; the group
|
|
117
|
+
// stays left while header actions push right.
|
|
118
|
+
.card-header-main {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
gap: 10px;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
}
|
|
124
|
+
.card-header-icon {
|
|
125
|
+
flex: none;
|
|
126
|
+
display: inline-grid;
|
|
127
|
+
place-items: center;
|
|
128
|
+
color: var(--color-text-muted);
|
|
129
|
+
}
|
|
130
|
+
.card-header-titles {
|
|
131
|
+
min-width: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Media ── (React `media` / Razor <app-card-media>): a top image/video band.
|
|
135
|
+
.card-media {
|
|
136
|
+
display: block;
|
|
137
|
+
overflow: hidden;
|
|
138
|
+
}
|
|
139
|
+
.card-media > img,
|
|
140
|
+
.card-media > video {
|
|
141
|
+
display: block;
|
|
142
|
+
width: 100%;
|
|
143
|
+
height: auto;
|
|
144
|
+
object-fit: cover;
|
|
145
|
+
}
|
|
146
|
+
// Media flush at the top follows the card's rounded corners.
|
|
147
|
+
.card > .card-media:first-child {
|
|
148
|
+
border-top-left-radius: var(--radius-lg);
|
|
149
|
+
border-top-right-radius: var(--radius-lg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── Clickable card ── (`href` / `onClick`): the whole surface is one control.
|
|
153
|
+
.card-clickable {
|
|
154
|
+
display: block;
|
|
155
|
+
color: inherit;
|
|
156
|
+
text-align: inherit;
|
|
157
|
+
text-decoration: none;
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
transition:
|
|
160
|
+
border-color 0.15s ease,
|
|
161
|
+
transform 0.2s var(--wui-ease-out-quart),
|
|
162
|
+
box-shadow 0.2s ease;
|
|
163
|
+
}
|
|
164
|
+
button.card-clickable {
|
|
165
|
+
width: 100%;
|
|
166
|
+
font: inherit;
|
|
167
|
+
background: var(--bs-card-bg, var(--color-surface));
|
|
168
|
+
}
|
|
169
|
+
.card-clickable:hover {
|
|
170
|
+
border-color: var(--color-primary);
|
|
171
|
+
transform: translateY(-1px);
|
|
172
|
+
}
|
|
173
|
+
.card-clickable:focus-visible {
|
|
174
|
+
outline: 2px solid var(--color-primary);
|
|
175
|
+
outline-offset: 2px;
|
|
176
|
+
}
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
.struct-card:hover {
|
|
25
25
|
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
26
26
|
}
|
|
27
|
+
.struct-card:has(.struct-card-input:focus-visible) {
|
|
28
|
+
outline: 2px solid var(--color-primary);
|
|
29
|
+
outline-offset: 2px;
|
|
30
|
+
}
|
|
27
31
|
.struct-card.checked {
|
|
28
32
|
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
29
33
|
background: color-mix(in srgb, var(--color-primary) 5%, transparent);
|
|
@@ -854,8 +858,8 @@ button.datepicker-month:hover {
|
|
|
854
858
|
font-size: 12px;
|
|
855
859
|
color: var(--color-text-subtle);
|
|
856
860
|
}
|
|
857
|
-
@media (max-width:
|
|
858
|
-
// Below
|
|
861
|
+
@media (max-width: 767.98px) {
|
|
862
|
+
// Below Bootstrap md tier: stack the calendars so the popover still fits the viewport.
|
|
859
863
|
.daterange-pop {
|
|
860
864
|
width: 320px;
|
|
861
865
|
}
|
|
@@ -1133,3 +1137,121 @@ button.datepicker-month:hover {
|
|
|
1133
1137
|
color: var(--color-text-subtle);
|
|
1134
1138
|
font-size: 14px;
|
|
1135
1139
|
}
|
|
1140
|
+
|
|
1141
|
+
// timepicker — time-of-day field with listbox popover (min/max bounds, step granularity, 12/24 display).
|
|
1142
|
+
.timepicker {
|
|
1143
|
+
position: relative;
|
|
1144
|
+
width: var(--wui-timepicker-width, 12rem);
|
|
1145
|
+
max-width: 100%;
|
|
1146
|
+
}
|
|
1147
|
+
.timepicker-trigger {
|
|
1148
|
+
display: flex;
|
|
1149
|
+
align-items: center;
|
|
1150
|
+
justify-content: space-between;
|
|
1151
|
+
gap: 8px;
|
|
1152
|
+
text-align: left;
|
|
1153
|
+
cursor: pointer;
|
|
1154
|
+
}
|
|
1155
|
+
.timepicker-trigger [data-trigger-label] {
|
|
1156
|
+
overflow: hidden;
|
|
1157
|
+
white-space: nowrap;
|
|
1158
|
+
text-overflow: ellipsis;
|
|
1159
|
+
}
|
|
1160
|
+
.timepicker-trigger > .bi {
|
|
1161
|
+
flex-shrink: 0;
|
|
1162
|
+
color: var(--color-text-subtle);
|
|
1163
|
+
}
|
|
1164
|
+
// The label span carries this while nothing is selected — same dim ink the trigger's
|
|
1165
|
+
// :not(.has-value) fallback uses, kept as its own rule so the span reads as placeholder
|
|
1166
|
+
// even when the trigger classes are toggled independently.
|
|
1167
|
+
.timepicker-placeholder {
|
|
1168
|
+
color: var(--color-text-subtle);
|
|
1169
|
+
}
|
|
1170
|
+
.timepicker-trigger:not(.has-value) {
|
|
1171
|
+
color: var(--color-text-subtle);
|
|
1172
|
+
}
|
|
1173
|
+
// Width presets.
|
|
1174
|
+
.time-w-sm {
|
|
1175
|
+
--wui-timepicker-width: 10rem;
|
|
1176
|
+
}
|
|
1177
|
+
.time-w-md {
|
|
1178
|
+
--wui-timepicker-width: 12rem;
|
|
1179
|
+
}
|
|
1180
|
+
.time-w-lg {
|
|
1181
|
+
--wui-timepicker-width: 16rem;
|
|
1182
|
+
}
|
|
1183
|
+
.time-w-full {
|
|
1184
|
+
--wui-timepicker-width: 100%;
|
|
1185
|
+
}
|
|
1186
|
+
.timepicker.open .timepicker-trigger {
|
|
1187
|
+
background-color: var(--color-bg-muted);
|
|
1188
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
1189
|
+
box-shadow: none;
|
|
1190
|
+
}
|
|
1191
|
+
.timepicker-pop {
|
|
1192
|
+
position: fixed;
|
|
1193
|
+
z-index: var(--wui-z-menu);
|
|
1194
|
+
width: 180px;
|
|
1195
|
+
max-width: calc(100vw - 16px);
|
|
1196
|
+
max-height: 360px;
|
|
1197
|
+
overflow-y: auto;
|
|
1198
|
+
padding: 8px;
|
|
1199
|
+
background: var(--color-surface);
|
|
1200
|
+
border: 1px solid var(--color-border);
|
|
1201
|
+
border-radius: var(--radius-lg);
|
|
1202
|
+
corner-shape: squircle;
|
|
1203
|
+
box-shadow: var(--shadow-overlay);
|
|
1204
|
+
animation: timepicker-in 0.14s ease;
|
|
1205
|
+
}
|
|
1206
|
+
.timepicker-pop.drop-up {
|
|
1207
|
+
animation: timepicker-in-up 0.14s ease;
|
|
1208
|
+
}
|
|
1209
|
+
@keyframes timepicker-in {
|
|
1210
|
+
from {
|
|
1211
|
+
opacity: 0;
|
|
1212
|
+
transform: translateY(-4px);
|
|
1213
|
+
}
|
|
1214
|
+
to {
|
|
1215
|
+
opacity: 1;
|
|
1216
|
+
transform: translateY(0);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
@keyframes timepicker-in-up {
|
|
1220
|
+
from {
|
|
1221
|
+
opacity: 0;
|
|
1222
|
+
transform: translateY(4px);
|
|
1223
|
+
}
|
|
1224
|
+
to {
|
|
1225
|
+
opacity: 1;
|
|
1226
|
+
transform: translateY(0);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
.timepicker-option {
|
|
1230
|
+
display: block;
|
|
1231
|
+
width: 100%;
|
|
1232
|
+
padding: 8px 12px;
|
|
1233
|
+
border: none;
|
|
1234
|
+
background: transparent;
|
|
1235
|
+
text-align: left;
|
|
1236
|
+
font-size: 14px;
|
|
1237
|
+
color: var(--color-text);
|
|
1238
|
+
cursor: pointer;
|
|
1239
|
+
border-radius: var(--radius-sm);
|
|
1240
|
+
transition:
|
|
1241
|
+
background 0.1s,
|
|
1242
|
+
color 0.1s;
|
|
1243
|
+
}
|
|
1244
|
+
.timepicker-option:hover,
|
|
1245
|
+
.timepicker-option.is-active {
|
|
1246
|
+
background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 8%, transparent));
|
|
1247
|
+
color: var(--color-primary);
|
|
1248
|
+
}
|
|
1249
|
+
.timepicker-option.is-selected {
|
|
1250
|
+
background: var(--color-primary);
|
|
1251
|
+
color: var(--color-primary-text);
|
|
1252
|
+
font-weight: 600;
|
|
1253
|
+
}
|
|
1254
|
+
.timepicker-option:focus-visible {
|
|
1255
|
+
outline: none;
|
|
1256
|
+
box-shadow: inset 0 0 0 2px var(--color-primary);
|
|
1257
|
+
}
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -566,6 +566,10 @@ textarea.form-control.autosize {
|
|
|
566
566
|
color: var(--color-primary);
|
|
567
567
|
background: transparent;
|
|
568
568
|
}
|
|
569
|
+
.pw-toggle:focus-visible {
|
|
570
|
+
outline: 2px solid var(--color-primary);
|
|
571
|
+
outline-offset: 2px;
|
|
572
|
+
}
|
|
569
573
|
|
|
570
574
|
// Clearable ✕ — a trailing reset button, placed inside .input-wrap / .input-icon. It only
|
|
571
575
|
// renders while the field has a value; .has-clear reserves the room so text never sits under it.
|
|
@@ -594,7 +598,11 @@ textarea.form-control.autosize {
|
|
|
594
598
|
}
|
|
595
599
|
.input-clear:hover {
|
|
596
600
|
color: var(--color-text);
|
|
597
|
-
background: var(--
|
|
601
|
+
background: var(--color-bg-muted);
|
|
602
|
+
}
|
|
603
|
+
.input-clear:focus-visible {
|
|
604
|
+
outline: 2px solid var(--color-primary);
|
|
605
|
+
outline-offset: 2px;
|
|
598
606
|
}
|
|
599
607
|
@keyframes wui-clear-in {
|
|
600
608
|
from { opacity: 0; transform: translateY(-50%) scale(0.72); }
|
|
@@ -633,7 +641,7 @@ textarea.form-control.autosize {
|
|
|
633
641
|
display: flex;
|
|
634
642
|
align-items: center;
|
|
635
643
|
padding: 0 12px;
|
|
636
|
-
background: var(--
|
|
644
|
+
background: var(--color-bg-muted);
|
|
637
645
|
color: var(--color-text-subtle);
|
|
638
646
|
font-size: 14px;
|
|
639
647
|
white-space: nowrap;
|
|
@@ -789,6 +797,7 @@ textarea.form-control.autosize {
|
|
|
789
797
|
wui-select,
|
|
790
798
|
wui-autocomplete,
|
|
791
799
|
wui-multiselect,
|
|
800
|
+
wui-taginput,
|
|
792
801
|
wui-phone,
|
|
793
802
|
wui-datepicker,
|
|
794
803
|
wui-date-range {
|
|
@@ -805,6 +814,7 @@ wui-select[compact] {
|
|
|
805
814
|
.field > wui-select,
|
|
806
815
|
.field > wui-autocomplete,
|
|
807
816
|
.field > wui-multiselect,
|
|
817
|
+
.field > wui-taginput,
|
|
808
818
|
.field > wui-phone,
|
|
809
819
|
.field > wui-datepicker,
|
|
810
820
|
.field > wui-date-range,
|
|
@@ -886,10 +896,104 @@ wui-select[compact] {
|
|
|
886
896
|
font-size: 14px;
|
|
887
897
|
font-weight: 500;
|
|
888
898
|
color: var(--color-text-muted);
|
|
889
|
-
font-family:
|
|
899
|
+
font-family: var(--font-sans);
|
|
900
|
+
display: inline-flex;
|
|
901
|
+
align-items: center;
|
|
902
|
+
white-space: nowrap;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Number input with stepper — decrements/increments by step, clamped to min/max.
|
|
906
|
+
// One field, one border: the WRAPPER owns the border + hover/focus/error ring
|
|
907
|
+
// so the input and stepper buttons light up together.
|
|
908
|
+
.num-wrap {
|
|
909
|
+
display: flex;
|
|
910
|
+
align-items: stretch;
|
|
911
|
+
border: 1px solid var(--color-border);
|
|
912
|
+
border-radius: var(--radius-lg);
|
|
913
|
+
corner-shape: squircle;
|
|
914
|
+
background: var(--color-surface);
|
|
915
|
+
transition:
|
|
916
|
+
border-color 0.12s,
|
|
917
|
+
background-color 0.12s;
|
|
918
|
+
overflow: hidden;
|
|
919
|
+
}
|
|
920
|
+
.num-wrap:hover:not(:focus-within) {
|
|
921
|
+
background: var(--color-bg-muted);
|
|
922
|
+
border-color: var(--color-border-strong);
|
|
923
|
+
}
|
|
924
|
+
.num-wrap:focus-within {
|
|
925
|
+
background: var(--color-bg-muted);
|
|
926
|
+
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
927
|
+
}
|
|
928
|
+
.num-wrap:has(.num-input.error) {
|
|
929
|
+
border-color: var(--color-danger);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Numeric input inside the stepper — plain text field, no native spinner.
|
|
933
|
+
// Flows seamlessly between the stepper buttons; the wrapper carries the border.
|
|
934
|
+
.num-wrap .num-input {
|
|
935
|
+
flex: 1;
|
|
936
|
+
border: none;
|
|
937
|
+
border-radius: 0;
|
|
938
|
+
background: transparent;
|
|
939
|
+
min-width: 0;
|
|
940
|
+
}
|
|
941
|
+
.num-wrap .num-input:focus {
|
|
942
|
+
box-shadow: none;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Stepper buttons — decrease/increase the value by step.
|
|
946
|
+
// Positioned left and right of the input, disabled at min/max or when the
|
|
947
|
+
// control is disabled.
|
|
948
|
+
.num-btn {
|
|
949
|
+
display: grid;
|
|
950
|
+
place-items: center;
|
|
951
|
+
width: 40px;
|
|
952
|
+
height: 100%;
|
|
953
|
+
border: none;
|
|
954
|
+
background: transparent;
|
|
955
|
+
padding: 0;
|
|
956
|
+
color: var(--color-text-muted);
|
|
957
|
+
cursor: pointer;
|
|
958
|
+
transition:
|
|
959
|
+
color 0.12s ease,
|
|
960
|
+
background 0.12s ease;
|
|
961
|
+
flex: none;
|
|
962
|
+
}
|
|
963
|
+
.num-btn:hover:not(:disabled) {
|
|
964
|
+
color: var(--color-primary);
|
|
965
|
+
background: var(--color-bg-muted);
|
|
966
|
+
}
|
|
967
|
+
.num-btn:focus-visible {
|
|
968
|
+
outline: none;
|
|
969
|
+
color: var(--color-primary);
|
|
970
|
+
}
|
|
971
|
+
.num-btn:active:not(:disabled) {
|
|
972
|
+
color: color-mix(in srgb, var(--color-primary) 80%, black);
|
|
973
|
+
}
|
|
974
|
+
.num-btn:disabled {
|
|
975
|
+
color: var(--color-text-subtle);
|
|
976
|
+
cursor: not-allowed;
|
|
977
|
+
opacity: 0.5;
|
|
978
|
+
}
|
|
979
|
+
.num-btn:not(:first-child) {
|
|
980
|
+
border-left: 1px solid var(--color-border);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Affix (unit) in the num-wrap — a trailing pill after the input.
|
|
984
|
+
.num-wrap .affix {
|
|
985
|
+
background: var(--color-bg-muted);
|
|
986
|
+
border: none;
|
|
987
|
+
border-left: 1px solid var(--color-border);
|
|
988
|
+
padding: 9px 14px;
|
|
989
|
+
font-size: 14px;
|
|
990
|
+
font-weight: 500;
|
|
991
|
+
color: var(--color-text-muted);
|
|
992
|
+
font-family: var(--font-sans);
|
|
890
993
|
display: inline-flex;
|
|
891
994
|
align-items: center;
|
|
892
995
|
white-space: nowrap;
|
|
996
|
+
flex: none;
|
|
893
997
|
}
|
|
894
998
|
|
|
895
999
|
// ── Status badge ──
|
|
@@ -1024,6 +1128,10 @@ wui-select[compact] {
|
|
|
1024
1128
|
.choice-chip:hover {
|
|
1025
1129
|
border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
|
|
1026
1130
|
}
|
|
1131
|
+
.choice-chip:has(input:focus-visible) {
|
|
1132
|
+
outline: 2px solid var(--color-primary);
|
|
1133
|
+
outline-offset: 2px;
|
|
1134
|
+
}
|
|
1027
1135
|
.choice-chip input {
|
|
1028
1136
|
position: absolute;
|
|
1029
1137
|
opacity: 0;
|
|
@@ -1159,3 +1267,120 @@ wui-otp {
|
|
|
1159
1267
|
width: 38px;
|
|
1160
1268
|
}
|
|
1161
1269
|
}
|
|
1270
|
+
|
|
1271
|
+
// ── Slider (AppSlider / <wui-slider>) ──
|
|
1272
|
+
// Single-thumb and dual-thumb (range) slider. Horizontal track with filled segment,
|
|
1273
|
+
// thumb button(s) (role="slider"), and optional value label. Pointer dragging and
|
|
1274
|
+
// keyboard navigation (arrow keys, Page Up/Down, Home/End). Form-associated for
|
|
1275
|
+
// single/range value submission.
|
|
1276
|
+
.slider {
|
|
1277
|
+
display: flex;
|
|
1278
|
+
align-items: center;
|
|
1279
|
+
gap: 12px;
|
|
1280
|
+
position: relative;
|
|
1281
|
+
user-select: none;
|
|
1282
|
+
height: 32px;
|
|
1283
|
+
min-width: 200px;
|
|
1284
|
+
}
|
|
1285
|
+
.slider-track {
|
|
1286
|
+
flex: 1;
|
|
1287
|
+
position: relative;
|
|
1288
|
+
height: 6px;
|
|
1289
|
+
background-color: var(--color-border);
|
|
1290
|
+
border-radius: 3px;
|
|
1291
|
+
cursor: pointer;
|
|
1292
|
+
}
|
|
1293
|
+
.slider-fill {
|
|
1294
|
+
position: absolute;
|
|
1295
|
+
top: 0;
|
|
1296
|
+
height: 6px;
|
|
1297
|
+
background-color: var(--color-primary);
|
|
1298
|
+
border-radius: 3px;
|
|
1299
|
+
pointer-events: none;
|
|
1300
|
+
}
|
|
1301
|
+
.slider-thumb {
|
|
1302
|
+
position: absolute;
|
|
1303
|
+
top: 50%;
|
|
1304
|
+
width: 20px;
|
|
1305
|
+
height: 20px;
|
|
1306
|
+
margin-top: -10px;
|
|
1307
|
+
margin-left: -10px;
|
|
1308
|
+
padding: 0;
|
|
1309
|
+
appearance: none;
|
|
1310
|
+
-webkit-appearance: none;
|
|
1311
|
+
background-color: var(--color-primary);
|
|
1312
|
+
border: 2px solid var(--color-surface);
|
|
1313
|
+
border-radius: 50%;
|
|
1314
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
|
1315
|
+
cursor: pointer;
|
|
1316
|
+
transition:
|
|
1317
|
+
background-color 0.15s ease,
|
|
1318
|
+
box-shadow 0.15s ease,
|
|
1319
|
+
border-color 0.15s ease;
|
|
1320
|
+
}
|
|
1321
|
+
.slider-thumb:hover:not(:disabled) {
|
|
1322
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
|
|
1323
|
+
}
|
|
1324
|
+
.slider-thumb:focus-visible {
|
|
1325
|
+
outline: 2px solid var(--color-primary);
|
|
1326
|
+
outline-offset: 4px;
|
|
1327
|
+
}
|
|
1328
|
+
.slider-thumb:disabled,
|
|
1329
|
+
.slider:has([disabled]) .slider-thumb {
|
|
1330
|
+
background-color: var(--color-text-subtle);
|
|
1331
|
+
border-color: var(--color-border);
|
|
1332
|
+
cursor: not-allowed;
|
|
1333
|
+
opacity: 0.6;
|
|
1334
|
+
}
|
|
1335
|
+
.slider-value {
|
|
1336
|
+
display: inline-block;
|
|
1337
|
+
min-width: 60px;
|
|
1338
|
+
text-align: center;
|
|
1339
|
+
font-size: 14px;
|
|
1340
|
+
font-weight: 500;
|
|
1341
|
+
color: var(--color-text);
|
|
1342
|
+
white-space: nowrap;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
// Size variants
|
|
1346
|
+
.slider-sm {
|
|
1347
|
+
height: 28px;
|
|
1348
|
+
gap: 8px;
|
|
1349
|
+
}
|
|
1350
|
+
.slider-sm .slider-track {
|
|
1351
|
+
height: 4px;
|
|
1352
|
+
}
|
|
1353
|
+
.slider-sm .slider-fill {
|
|
1354
|
+
height: 4px;
|
|
1355
|
+
}
|
|
1356
|
+
.slider-sm .slider-thumb {
|
|
1357
|
+
width: 16px;
|
|
1358
|
+
height: 16px;
|
|
1359
|
+
margin-top: -8px;
|
|
1360
|
+
margin-left: -8px;
|
|
1361
|
+
}
|
|
1362
|
+
.slider-sm .slider-value {
|
|
1363
|
+
font-size: 12px;
|
|
1364
|
+
min-width: 50px;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.slider-lg {
|
|
1368
|
+
height: 36px;
|
|
1369
|
+
gap: 16px;
|
|
1370
|
+
}
|
|
1371
|
+
.slider-lg .slider-track {
|
|
1372
|
+
height: 8px;
|
|
1373
|
+
}
|
|
1374
|
+
.slider-lg .slider-fill {
|
|
1375
|
+
height: 8px;
|
|
1376
|
+
}
|
|
1377
|
+
.slider-lg .slider-thumb {
|
|
1378
|
+
width: 24px;
|
|
1379
|
+
height: 24px;
|
|
1380
|
+
margin-top: -12px;
|
|
1381
|
+
margin-left: -12px;
|
|
1382
|
+
}
|
|
1383
|
+
.slider-lg .slider-value {
|
|
1384
|
+
font-size: 15px;
|
|
1385
|
+
min-width: 70px;
|
|
1386
|
+
}
|