@webority/theme 0.8.3 → 0.8.5

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.8.3",
3
+ "version": "0.8.5",
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": {
@@ -170,9 +170,9 @@
170
170
  // sizes step font-size, horizontal padding and radius together (vertical size
171
171
  // comes from --wui-control-height, so a button lines up with an input). Weight
172
172
  // is 600 on every rung — buttons read as a deliberate action, not body text.
173
- // Medium (default): 15px / 600, 24px side padding, 22px squircle radius, 45px tall.
173
+ // Medium (default): 15px / 600, 24px side padding, 18px squircle radius, 45px tall.
174
174
  .btn {
175
- --bs-btn-border-radius: 22px;
175
+ --bs-btn-border-radius: 18px;
176
176
  --bs-btn-font-size: 0.9375rem;
177
177
  --bs-btn-font-weight: 600;
178
178
  --bs-btn-padding-x: 1.5rem;
@@ -180,10 +180,10 @@
180
180
  // keep a plain border-radius arc — not a cross-browser design contract.
181
181
  corner-shape: squircle;
182
182
  }
183
- // Small: 14px, 18px side padding, 17px squircle radius (proportional to md), 35px tall.
183
+ // Small: 14px, 18px side padding, 14px squircle radius (proportional to md), 35px tall.
184
184
  .btn-sm:not(.btn-icon),
185
185
  .btn-group-sm > .btn:not(.btn-icon) {
186
- --bs-btn-border-radius: 17px;
186
+ --bs-btn-border-radius: 14px;
187
187
  --bs-btn-font-size: 0.875rem;
188
188
  --bs-btn-padding-x: 1.125rem;
189
189
  display: inline-flex;
@@ -191,10 +191,10 @@
191
191
  justify-content: center;
192
192
  min-height: 35px;
193
193
  }
194
- // Large: 17px, 32px side padding, 27px squircle radius (proportional to md), 55px tall — a spacious CTA.
194
+ // Large: 17px, 32px side padding, 22px squircle radius (proportional to md), 55px tall — a spacious CTA.
195
195
  .btn-lg:not(.btn-icon),
196
196
  .btn-group-lg > .btn:not(.btn-icon) {
197
- --bs-btn-border-radius: 27px;
197
+ --bs-btn-border-radius: 22px;
198
198
  --bs-btn-font-size: 1.0625rem;
199
199
  --bs-btn-padding-x: 2rem;
200
200
  display: inline-flex;
@@ -355,16 +355,16 @@
355
355
  font-weight: 500;
356
356
  transition: background-color 0.12s ease, color 0.12s ease;
357
357
 
358
- // Leading glyph: quiet by default, brand-tinted on hover — a font-size so both
359
- // the React <Icon> (a sized <i>) and the Razor <i class="bi"> land the same.
360
- .bi, i, svg {
358
+ // Leading glyph: quiet by default, brand-tinted on hover. The library's own icons
359
+ // are .wui-icon SVGs; bare i/svg keeps product-supplied glyph nodes in step.
360
+ .wui-icon, i, svg {
361
361
  flex-shrink: 0;
362
362
  font-size: 1rem;
363
363
  color: var(--wui-text-3);
364
364
  transition: color 0.12s ease;
365
365
  }
366
- &:hover .bi, &:hover i, &:hover svg,
367
- &:focus .bi, &:focus i, &:focus svg {
366
+ &:hover .wui-icon, &:hover i, &:hover svg,
367
+ &:focus .wui-icon, &:focus i, &:focus svg {
368
368
  color: var(--wui-primary);
369
369
  }
370
370
 
@@ -383,9 +383,9 @@
383
383
  --bs-dropdown-link-color: var(--wui-danger-text);
384
384
  --bs-dropdown-link-hover-color: var(--wui-danger-text);
385
385
  --bs-dropdown-link-hover-bg: var(--color-danger-soft);
386
- .bi, i, svg { color: var(--wui-danger); }
387
- &:hover .bi, &:hover i, &:hover svg,
388
- &:focus .bi, &:focus i, &:focus svg { color: var(--wui-danger); }
386
+ .wui-icon, i, svg { color: var(--wui-danger); }
387
+ &:hover .wui-icon, &:hover i, &:hover svg,
388
+ &:focus .wui-icon, &:focus i, &:focus svg { color: var(--wui-danger); }
389
389
  }
390
390
  }
391
391
 
package/scss/_tokens.scss CHANGED
@@ -195,6 +195,14 @@
195
195
  --wui-radius-lg: 14px;
196
196
  --wui-radius-xl: 16px;
197
197
 
198
+ // Field (input/select/datepicker/…) radius — a rounder squircle than the
199
+ // generic radius ladder, matched to the button radius language. md is 18px;
200
+ // sm/lg step with the control height (36/44/52px → ~0.41 ratio) so every
201
+ // field size keeps a proportional corner.
202
+ --wui-field-radius: 18px;
203
+ --wui-field-radius-sm: 15px;
204
+ --wui-field-radius-lg: 21px;
205
+
198
206
  // ---- Portal dark palette ----------------------------------------
199
207
  // Marketing stays light. Portal flips [data-theme="dark"] and maps
200
208
  // --color-* aliases onto these so dark hexes live in theme/, not the app.
@@ -203,11 +203,13 @@
203
203
  height: var(--wui-control-height-sm);
204
204
  min-height: var(--wui-control-height-sm);
205
205
  font-size: 13px;
206
+ border-radius: var(--wui-field-radius-sm);
206
207
  }
207
208
  .search-select-lg .search-select-control {
208
209
  height: var(--wui-control-height-lg);
209
210
  min-height: var(--wui-control-height-lg);
210
211
  font-size: 16px;
212
+ border-radius: var(--wui-field-radius-lg);
211
213
  }
212
214
  // Multi-select grows with its chips, so a size sets the FLOOR (min-height from the rule
213
215
  // above) but never a fixed height — reset height:auto so a second row of chips can appear.
@@ -277,9 +279,10 @@
277
279
  min-width: 0;
278
280
  vertical-align: middle;
279
281
  }
280
- .wui-opt-label > .bi {
282
+ .wui-opt-label > .wui-icon {
281
283
  flex-shrink: 0;
282
- font-size: 15px;
284
+ width: 15px;
285
+ height: 15px;
283
286
  }
284
287
 
285
288
  // AppSelectMini — the compact single-purpose picker (page size, a range switch). It shares
@@ -460,7 +463,7 @@
460
463
  white-space: nowrap;
461
464
  text-overflow: ellipsis;
462
465
  }
463
- .datepicker-trigger > .bi {
466
+ .datepicker-trigger > .wui-icon {
464
467
  flex-shrink: 0;
465
468
  color: var(--color-text-subtle);
466
469
  }
@@ -795,7 +798,7 @@ button.datepicker-month:hover {
795
798
  white-space: nowrap;
796
799
  text-overflow: ellipsis;
797
800
  }
798
- .daterange-trigger > .bi {
801
+ .daterange-trigger > .wui-icon {
799
802
  flex-shrink: 0;
800
803
  color: var(--color-text-subtle);
801
804
  }
@@ -1179,7 +1182,7 @@ button.datepicker-month:hover {
1179
1182
  white-space: nowrap;
1180
1183
  text-overflow: ellipsis;
1181
1184
  }
1182
- .timepicker-trigger > .bi {
1185
+ .timepicker-trigger > .wui-icon {
1183
1186
  flex-shrink: 0;
1184
1187
  color: var(--color-text-subtle);
1185
1188
  }
@@ -255,7 +255,8 @@
255
255
  width: 100%;
256
256
  height: var(--wui-control-height);
257
257
  padding: 12px;
258
- border-radius: var(--radius);
258
+ border-radius: var(--wui-field-radius);
259
+ corner-shape: squircle;
259
260
  border: 1px solid var(--color-border);
260
261
  background: var(--color-bg-muted);
261
262
  font-size: 14px;
@@ -274,12 +275,14 @@
274
275
  height: var(--wui-control-height-sm);
275
276
  padding: 6px 10px;
276
277
  font-size: 13px;
278
+ border-radius: var(--wui-field-radius-sm);
277
279
  }
278
280
  .form-control-lg,
279
281
  .input-lg {
280
282
  height: var(--wui-control-height-lg);
281
283
  padding: 14px 16px;
282
284
  font-size: 16px;
285
+ border-radius: var(--wui-field-radius-lg);
283
286
  }
284
287
  .textarea,
285
288
  textarea.form-control {
@@ -420,7 +423,12 @@ textarea.form-control.autosize {
420
423
  .field-error {
421
424
  font-size: 12px;
422
425
  line-height: normal;
426
+ // Must clear AA against the surface: --color-danger-active (#E82646) is 4.38:1 at this
427
+ // 12px size, under the 4.5:1 floor the a11y gate enforces.
423
428
  color: var(--color-danger-text);
429
+ // Tighten the space above the message: .field's 4px gap → effective 2px, without
430
+ // touching the label→control gap.
431
+ margin-top: -2px;
424
432
  }
425
433
 
426
434
  // Native select with a rounded squircle, custom chevron, off-surface fill.
@@ -675,7 +683,8 @@ textarea.form-control.autosize {
675
683
  align-items: stretch;
676
684
  width: 100%;
677
685
  border: 1px solid var(--color-border);
678
- border-radius: var(--radius);
686
+ border-radius: var(--wui-field-radius);
687
+ corner-shape: squircle;
679
688
  // Soft-filled at rest, white on focus — one shared fill for the whole control so the
680
689
  // prefix/suffix and the input body are always the SAME colour combination as the field.
681
690
  background: var(--color-bg-muted);
@@ -714,11 +723,11 @@ textarea.form-control.autosize {
714
723
  }
715
724
  .input-affixed .affix-pre {
716
725
  border-right: 1px solid var(--color-border);
717
- border-radius: var(--radius) 0 0 var(--radius);
726
+ border-radius: var(--wui-field-radius) 0 0 var(--wui-field-radius);
718
727
  }
719
728
  .input-affixed .affix-suf {
720
729
  border-left: 1px solid var(--color-border);
721
- border-radius: 0 var(--radius) var(--radius) 0;
730
+ border-radius: 0 var(--wui-field-radius) var(--wui-field-radius) 0;
722
731
  }
723
732
 
724
733
  // Floating label — the label starts centred as the placeholder, then slides up and shrinks
@@ -831,12 +840,14 @@ textarea.form-control.autosize {
831
840
  color: var(--color-text-subtle);
832
841
  }
833
842
  .required {
843
+ // AA against the surface — see .field-error; --color-danger-active is 4.38:1.
834
844
  color: var(--color-danger-text);
835
845
  // The gap is CSS, not a significant space in the markup. RequiredMark used to render
836
846
  // `{' '}*` while AppDatePicker and every Razor helper rendered a bare `*`, so the same
837
847
  // asterisk sat 4px further out in some fields than others — and no check could see it,
838
- // because HTML comparison collapses whitespace by design.
839
- margin-inline-start: 0.25em;
848
+ // because HTML comparison collapses whitespace by design. Gap removed — the star sits
849
+ // flush against the label.
850
+ margin-inline-start: 0;
840
851
  }
841
852
  .field {
842
853
  display: flex;
@@ -942,7 +953,7 @@ wui-select[compact] {
942
953
  display: flex;
943
954
  align-items: stretch;
944
955
  border: 1px solid var(--color-border);
945
- border-radius: var(--radius-lg);
956
+ border-radius: var(--wui-field-radius);
946
957
  corner-shape: squircle;
947
958
  // Soft-filled at rest, white on focus — the numeric input and its unit pill share the
948
959
  // one wrapper fill, same colour combination as every other field.
@@ -234,10 +234,10 @@
234
234
  outline: 2px solid var(--color-primary);
235
235
  outline-offset: 2px;
236
236
  }
237
- .wui-expand-toggle .bi {
237
+ .wui-expand-toggle .wui-icon {
238
238
  transition: transform 0.18s var(--wui-ease-out-quart, ease);
239
239
  }
240
- .wui-expand-toggle.is-open .bi {
240
+ .wui-expand-toggle.is-open .wui-icon {
241
241
  transform: rotate(180deg);
242
242
  }
243
243
  .table-ledger tr.wui-row-expanded > td {
@@ -521,7 +521,7 @@ a.info-row:hover .info-value {
521
521
  background: transparent;
522
522
  }
523
523
  // Icon pop — the selected segment's glyph springs up a touch as it becomes active.
524
- .segmented-option:has(.segmented-input:checked) .bi {
524
+ .segmented-option:has(.segmented-input:checked) .wui-icon {
525
525
  transform: scale(1.14);
526
526
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
527
527
  }
@@ -561,7 +561,7 @@ a.info-row:hover .info-value {
561
561
  // The thumb still jumps to the selected segment — it just does not slide, pop or press.
562
562
  .segmented-option,
563
563
  .segmented.is-animated .segmented-thumb,
564
- .segmented-option:has(.segmented-input:checked) .bi {
564
+ .segmented-option:has(.segmented-input:checked) .wui-icon {
565
565
  transition: none;
566
566
  }
567
567
  }
@@ -182,6 +182,19 @@
182
182
  color 0.12s ease,
183
183
  border-color 0.12s ease;
184
184
  }
185
+ // Leading icon / image — sit before the label with the strip's 8px gap (from .app-tab).
186
+ // The library Icon sizes itself; a custom <img> is constrained to a small rounded square
187
+ // (product logo / avatar). Both never shrink when the label truncates.
188
+ .app-tab-icon {
189
+ flex-shrink: 0;
190
+ }
191
+ .app-tab-img {
192
+ flex-shrink: 0;
193
+ width: 18px;
194
+ height: 18px;
195
+ object-fit: cover;
196
+ border-radius: var(--radius-sm);
197
+ }
185
198
  .app-tab:hover:not(:disabled):not(.active) {
186
199
  color: var(--color-text);
187
200
  }
@@ -296,10 +309,18 @@
296
309
  gap: 6px;
297
310
  font-size: 13px;
298
311
  }
312
+ .app-tabs-sm .app-tab-img {
313
+ width: 16px;
314
+ height: 16px;
315
+ }
299
316
  .app-tabs-lg .app-tab {
300
317
  padding: 14px 18px;
301
318
  font-size: 15px;
302
319
  }
320
+ .app-tabs-lg .app-tab-img {
321
+ width: 20px;
322
+ height: 20px;
323
+ }
303
324
 
304
325
  // ── Fitted: tabs share the width equally (no scroll). ──
305
326
  .app-tabs-fitted .app-tab {
@@ -395,6 +395,65 @@
395
395
  box-shadow: var(--wui-tooltip-shadow);
396
396
  }
397
397
 
398
+ // ── CSS-only tooltip ──────────────────────────────────────────────────────────────────
399
+ // Any element carrying `data-wui-tip="…"` grows a hover/focus bubble reusing the AppTooltip
400
+ // surface look — no JS, no Popper, instant, and identical on React and Razor by construction.
401
+ // It sits above the element; for a collision-aware / richer tooltip, use AppTooltip /
402
+ // <app-tooltip> instead.
403
+ //
404
+ // Two users, for different reasons:
405
+ // - `.wui-icon-tip`, the wrapper the `tooltip` prop on AppIcon / <app-icon> puts around a
406
+ // glyph (it adds the inline-flex layout below).
407
+ // - <app-tabs> tab buttons, which CANNOT use a Bootstrap tooltip: the button already owns a
408
+ // `bs.tab` instance and Bootstrap permits only one instance per element, so binding
409
+ // `bs.Tooltip` to it logs "Bootstrap doesn't allow more than one instance per element"
410
+ // and the tab instance is never registered.
411
+ [data-wui-tip] {
412
+ position: relative;
413
+ }
414
+ .wui-icon-tip {
415
+ display: inline-flex;
416
+ vertical-align: -0.125em;
417
+ }
418
+ [data-wui-tip]::after {
419
+ content: attr(data-wui-tip);
420
+ position: absolute;
421
+ bottom: calc(100% + 8px);
422
+ left: 50%;
423
+ transform: translateX(-50%);
424
+ z-index: var(--wui-z-menu, 1080);
425
+ width: max-content;
426
+ max-width: 240px;
427
+ padding: 7px 11px;
428
+ border-radius: var(--radius, 10px);
429
+ corner-shape: squircle;
430
+ background: var(--color-surface);
431
+ color: var(--color-text);
432
+ border: 1px solid var(--color-border-light);
433
+ box-shadow: var(--wui-shadow-overlay);
434
+ font-size: 0.8125rem;
435
+ font-weight: 500;
436
+ line-height: 1.4;
437
+ text-align: left;
438
+ white-space: normal;
439
+ pointer-events: none;
440
+ opacity: 0;
441
+ visibility: hidden;
442
+ transition:
443
+ opacity 0.12s ease,
444
+ visibility 0.12s;
445
+ }
446
+ [data-wui-tip]:hover::after,
447
+ [data-wui-tip]:focus-visible::after {
448
+ opacity: 1;
449
+ visibility: visible;
450
+ }
451
+ @media (prefers-reduced-motion: reduce) {
452
+ [data-wui-tip]::after {
453
+ transition: none;
454
+ }
455
+ }
456
+
398
457
  // Size presets — swap the four size vars. `md` is the :where default (no class).
399
458
  .tooltip.wui-tooltip.size-sm {
400
459
  --wui-tooltip-max-width: 200px;
@@ -76,7 +76,7 @@
76
76
  // Bootstrap Icons carry a -.125em baseline nudge meant for inline-with-text use;
77
77
  // inside the chip's flex row it just drops the glyph below the label's centre.
78
78
  // Reset it so a library-icon leading glyph lines up with the label.
79
- .chip-ic .bi::before {
79
+ .chip-ic .wui-icon {
80
80
  vertical-align: middle;
81
81
  }
82
82
  .chip-avatar {
@@ -6,6 +6,14 @@
6
6
  // partials are a split of one stylesheet, not independent modules, so a rule
7
7
  // here may be overridden by a later partial. Never reorder the imports.
8
8
 
9
+ // ── Icon base ──
10
+ // Every library glyph (React <Icon>, Razor <app-icon>, wui-element internals) is an
11
+ // inline SVG carrying this class; size comes from its width/height attributes.
12
+ .wui-icon {
13
+ display: inline-block;
14
+ flex-shrink: 0;
15
+ }
16
+
9
17
  // ── Grid / layout utils ──
10
18
  // Responsive card grids (Bootstrap has no grid-template utility).
11
19
  .wui-grid {
@@ -587,7 +595,7 @@
587
595
  border: 1px solid var(--callout-border);
588
596
  // A thick coloured left edge — the callout's signature aside marker.
589
597
  border-left: 4px solid var(--callout-accent);
590
- border-radius: var(--radius-lg);
598
+ border-radius: 20px;
591
599
  corner-shape: squircle;
592
600
  background: var(--callout-bg);
593
601
  font-size: 14px;
@@ -616,6 +624,14 @@
616
624
  --callout-bg: var(--color-danger-10);
617
625
  }
618
626
 
627
+ // Borderless variant (`bordered={false}` / `bordered="false"`): drop the soft ring AND
628
+ // the thick tone-coloured left edge, keeping only the tint fill. Colours go `transparent`
629
+ // rather than the widths to 0 so the box keeps its 1px / 4px-left footprint — nothing
630
+ // shifts when toggling the border on and off. Mirrors `.alert-borderless`.
631
+ .info-callout-borderless {
632
+ border-color: transparent;
633
+ }
634
+
619
635
  // The leading glyph is a solid circular badge — tone fill, white glyph centred, with a
620
636
  // soft 3px same-colour halo ring. Base is the single-content size (20px badge / 20px
621
637
  // glyph); a title+body stack steps it up to 24px. The badge stays VERTICALLY CENTRED
@@ -633,7 +649,7 @@
633
649
  // 4px translucent ring in the badge's own colour — the "outer shadow".
634
650
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--callout-accent) 20%, transparent);
635
651
 
636
- .bi {
652
+ .wui-icon {
637
653
  line-height: 1;
638
654
  }
639
655
  }
@@ -715,7 +731,9 @@
715
731
  // the heading to the variant. Only the heading takes the variant colour — the body
716
732
  // stays dark ink so the message reads calmly.
717
733
  .alert {
718
- --bs-alert-border-radius: var(--wui-radius-lg);
734
+ --bs-alert-border-radius: 20px;
735
+ border-radius: 20px;
736
+ corner-shape: squircle;
719
737
  padding: 16px 18px;
720
738
  border-width: 1px;
721
739
  }
@@ -786,7 +804,7 @@
786
804
  // 4px translucent ring in the badge's own colour — the "outer shadow".
787
805
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--alert-accent) 20%, transparent);
788
806
 
789
- .bi {
807
+ .wui-icon {
790
808
  line-height: 1;
791
809
  }
792
810
  }