@webority/theme 0.7.5 → 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
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": {
@@ -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(--wui-primary-pressed);
182
- --bs-dropdown-link-hover-bg: var(--wui-primary-5);
183
- --bs-dropdown-link-active-color: var(--wui-primary-pressed);
184
- --bs-dropdown-link-active-bg: var(--wui-primary-10);
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(--wui-danger-10);
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(--wui-primary-5);
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(--wui-primary-10);
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: none;
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-blue: #ECE4FE;
87
- --wui-surface-blue-soft: #FCFCFC;
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
@@ -222,6 +222,23 @@ $link-hover-decoration: underline;
222
222
  $focus-ring-width: 3px; // BS 0.25rem (4px)
223
223
 
224
224
 
225
+ // ---- Z-index ----------------------------------------------------------
226
+ // Mirrors the --wui-z-* ladder in _tokens.scss (full rationale there). Our modal is
227
+ // elevated to 1505 via a CSS custom property set directly in wui/_overlays.scss —
228
+ // Bootstrap's own $zindex-modal SCSS var is unused by it — but Bootstrap's stock
229
+ // popover/tooltip/toast (1070/1080/1090) are still miles below that, so any of
230
+ // those fired from inside an <AppModal> painted UNDER the dialog. Bring them onto
231
+ // the ladder's -menu/-tooltip/-toast rungs. Dropdown/sticky/fixed/offcanvas already
232
+ // sit at or near their matching -dropdown/-sticky/-fixed/-shell-scrim/-shell-drawer
233
+ // rungs, so those are left at Bootstrap's defaults.
234
+ //
235
+ // SCSS variables are compile-time and can't reference the runtime custom
236
+ // properties above — keep these numbers in sync with _tokens.scss by hand.
237
+ $zindex-popover: 1600;
238
+ $zindex-tooltip: 1650;
239
+ $zindex-toast: 1700;
240
+
241
+
225
242
  // ---- Table ----------------------------------------------------------
226
243
  // Set HERE, not in the component layer, and the reason is specificity.
227
244
  // Bootstrap emits `.table > :not(caption) > * > * { padding: … }` at (0,1,1).
@@ -93,8 +93,11 @@
93
93
  --color-accent: var(--color-warning);
94
94
  --color-accent-soft: var(--color-warning-soft);
95
95
 
96
- // Scrim — media hover overlays (logo uploader)
97
- --color-scrim: color-mix(in srgb, var(--wui-text-heading) 62%, transparent);
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 62%, transparent);
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;
@@ -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
+ }
@@ -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: 660px) {
858
- // Below the two-month width, stack the calendars so the popover still fits the viewport.
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
+ }