@webority/theme 0.7.0 → 0.7.1

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.0",
3
+ "version": "0.7.1",
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": {
@@ -11,6 +11,7 @@
11
11
  // pattern nearly every panel header uses, so it belongs here rather than being
12
12
  // re-flexed with utilities on each one.
13
13
  .card-header {
14
+ --bs-card-cap-bg: var(--color-surface);
14
15
  display: flex;
15
16
  align-items: center;
16
17
  justify-content: space-between;
@@ -889,9 +889,19 @@ wui-select[compact] {
889
889
  .field > .input-with-unit,
890
890
  .field > .input,
891
891
  .field > .datepicker,
892
+ .field > .file-input,
893
+ .field > .file-preview,
892
894
  .field > .field-error {
893
895
  align-self: stretch;
894
896
  }
897
+ // A file picker fills its column by default (align-self:stretch fills the field on the cross
898
+ // axis, but the field itself collapses to its content when it is a flex item — the showcase
899
+ // lays fields out in a flex row — so stretch the field to its container first). Override per
900
+ // use with a .file-w-* class or the --wui-file-width variable.
901
+ .field:has(> .file-input),
902
+ .field:has(> .file-preview) {
903
+ width: var(--wui-file-width, 100%);
904
+ }
895
905
  // Doubled selectors — see the lg-tier block below: a media query carries no
896
906
  // extra specificity, so a consumer's later single-class rule would win.
897
907
  @media (max-width: 575.98px) {
@@ -183,9 +183,78 @@
183
183
  stroke: #fff;
184
184
  stroke-width: 2px;
185
185
  }
186
+ // Compact attached items — used for every file in `multiple` mode and for a single
187
+ // non-image attachment (a big letterboxed preview only makes sense for one image).
188
+ .attach-list {
189
+ display: flex;
190
+ flex-direction: column;
191
+ gap: 8px;
192
+ }
193
+ .attach-list:not(:first-child) {
194
+ margin-top: 8px;
195
+ }
196
+ .attach-item {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 12px;
200
+ padding: 8px 12px;
201
+ border: 1px solid var(--color-border);
202
+ border-radius: var(--radius-lg);
203
+ background: var(--color-surface);
204
+ min-width: 0;
205
+ }
206
+ .attach-item-media {
207
+ flex: 0 0 auto;
208
+ width: 40px;
209
+ height: 40px;
210
+ display: inline-flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ border-radius: var(--radius);
214
+ overflow: hidden;
215
+ background: var(--color-bg-muted);
216
+ color: var(--color-primary);
217
+ }
218
+ .attach-item-media img {
219
+ width: 100%;
220
+ height: 100%;
221
+ object-fit: cover;
222
+ display: block;
223
+ }
224
+ .attach-item-info {
225
+ display: flex;
226
+ flex-direction: column;
227
+ gap: 2px;
228
+ flex: 1;
229
+ min-width: 0;
230
+ }
231
+ .attach-item-remove {
232
+ flex: 0 0 auto;
233
+ display: inline-flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ width: 30px;
237
+ height: 30px;
238
+ border: 1px solid var(--color-border);
239
+ border-radius: 999px;
240
+ background: var(--color-surface);
241
+ color: var(--color-text-muted);
242
+ cursor: pointer;
243
+ transition:
244
+ border-color 0.2s,
245
+ color 0.2s,
246
+ background 0.2s;
247
+ }
248
+ .attach-item-remove:hover {
249
+ border-color: var(--color-danger);
250
+ color: var(--color-danger);
251
+ background: var(--color-danger-soft);
252
+ }
253
+
186
254
  @media (prefers-reduced-motion: reduce) {
187
255
  .attach-drop,
188
- .attach-remove {
256
+ .attach-remove,
257
+ .attach-item-remove {
189
258
  transition: none;
190
259
  }
191
260
  }
@@ -246,7 +315,11 @@
246
315
  align-items: center;
247
316
  justify-content: center;
248
317
  gap: 10px;
249
- height: 350px;
318
+ height: 200px;
319
+ // Fills its column — a full-width drop target. The .field:has() rule below makes the field
320
+ // itself stretch first (the showcase lays fields out in a flex row, where a field otherwise
321
+ // collapses to its content), then align-self:stretch fills the field.
322
+ width: 100%;
250
323
  padding: 20px;
251
324
  text-align: center;
252
325
  background: var(--color-bg-subtle);
@@ -255,10 +328,79 @@
255
328
  border-color 0.15s ease,
256
329
  background-color 0.15s ease;
257
330
  }
331
+ // Hover, focus and drag-over all take the same soft primary-30% border AppInput shows on
332
+ // focus — a clear hue shift from the resting grey dashed border, without the loud full
333
+ // --color-primary the dropzone used before.
258
334
  .file-input.file-input-tall:hover,
335
+ .file-input.file-input-tall:focus-within,
336
+ .file-input.file-input-tall:active,
259
337
  .file-input.file-input-tall.dragging {
260
- border-color: var(--color-primary);
338
+ border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
261
339
  background: var(--color-bg-muted);
340
+ box-shadow: none;
341
+ }
342
+ // A rejected file (wrong type / too large) turns the dropzone border danger to match the
343
+ // inline reason shown below it.
344
+ .file-input.file-input-tall.error {
345
+ border-color: color-mix(in srgb, var(--color-danger) 55%, var(--color-surface));
346
+ }
347
+ // Multiple mode with files: the box grows to hold the picked-file list INSIDE it, below a
348
+ // compact upload prompt — not a separate list beneath the box. The prompt drops from the
349
+ // absolute-fill centred stack to a static compact row at the top; the list scrolls within.
350
+ .file-input.file-input-tall.has-files {
351
+ height: auto;
352
+ justify-content: flex-start;
353
+ align-items: stretch;
354
+ gap: 14px;
355
+ cursor: default;
356
+ }
357
+ .file-input.file-input-tall.has-files > .file-input-zone-label {
358
+ position: static;
359
+ inset: auto;
360
+ flex-direction: row;
361
+ gap: 10px;
362
+ padding: 4px;
363
+ cursor: pointer;
364
+ }
365
+ .file-input.file-input-tall.has-files .file-input-icon {
366
+ width: 40px;
367
+ height: 40px;
368
+ }
369
+ .file-input.file-input-tall.has-files .file-input-text {
370
+ display: none;
371
+ }
372
+ .file-input.file-input-tall.has-files .attach-list {
373
+ width: 100%;
374
+ max-height: 240px;
375
+ overflow-y: auto;
376
+ // The dropzone sets text-align:center for the centred prompt; the file rows must not
377
+ // inherit it — name and size read left-aligned next to the thumbnail.
378
+ text-align: left;
379
+ }
380
+ // While managing files, keep the border calm on hover/focus (it is no longer just a
381
+ // "click to upload" affordance); still highlight when a file is actually dragged over.
382
+ .file-input.file-input-tall.has-files:hover,
383
+ .file-input.file-input-tall.has-files:focus-within {
384
+ border-color: var(--color-border-strong);
385
+ background: var(--color-bg-subtle);
386
+ }
387
+ .file-input.file-input-tall.has-files.dragging {
388
+ border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
389
+ background: var(--color-bg-muted);
390
+ }
391
+ // Width presets — put a .file-w-* class on the field (React className / Razor class) to cap the
392
+ // dropzone to a fixed width instead of the full column. Mirror of the datepicker .date-w-* set.
393
+ .file-w-sm {
394
+ --wui-file-width: 18rem;
395
+ }
396
+ .file-w-md {
397
+ --wui-file-width: 24rem;
398
+ }
399
+ .file-w-lg {
400
+ --wui-file-width: 30rem;
401
+ }
402
+ .file-w-full {
403
+ --wui-file-width: 100%;
262
404
  }
263
405
  // Fill-label associates click with the hidden file input; children don't
264
406
  // steal drag hits.
@@ -310,7 +452,7 @@
310
452
  box-sizing: border-box;
311
453
  display: block;
312
454
  width: 100%;
313
- height: 350px;
455
+ height: 200px;
314
456
  border-radius: var(--radius-lg);
315
457
  corner-shape: squircle;
316
458
  border: 1px solid var(--color-border);
@@ -417,6 +559,10 @@
417
559
  // Round dropzone uploader (workspace/brand logo) and the settings-page photo
418
560
  // ring share this same interactive-uploader pattern.
419
561
  .set-uploader {
562
+ // One knob for the whole control — the ring diameter. Presets and the numeric `size`
563
+ // prop set it (inline); the ring, initials font and icons all derive from it, so any
564
+ // px value scales the picker proportionally. md (120px) is the default.
565
+ --wui-logo-size: 120px;
420
566
  display: inline-flex;
421
567
  padding: 0;
422
568
  border: none;
@@ -436,10 +582,10 @@
436
582
  }
437
583
  .set-uploader-ring {
438
584
  position: relative;
439
- width: 120px;
440
- height: 120px;
585
+ width: var(--wui-logo-size);
586
+ height: var(--wui-logo-size);
441
587
  border-radius: 50%;
442
- padding: 8px;
588
+ padding: 5px;
443
589
  border: 1px dashed color-mix(in srgb, var(--color-border-strong) 75%, transparent);
444
590
  cursor: pointer;
445
591
  transition: border-color 0.18s;
@@ -455,15 +601,21 @@
455
601
  border-radius: 50%;
456
602
  display: grid;
457
603
  place-items: center;
458
- font-size: 38px;
604
+ // Initials scale with the ring (~32% of the diameter reads well at every size).
605
+ font-size: calc(var(--wui-logo-size) * 0.32);
459
606
  font-weight: 700;
460
607
  letter-spacing: -0.02em;
461
608
  color: var(--color-primary);
462
609
  background: var(--color-primary-soft);
463
610
  }
611
+ // Avatar variant — the standard avatar disc (muted fill) instead of the soft-primary
612
+ // initials plate; used with a person icon when no initials are given.
613
+ .set-uploader--avatar .set-uploader-logo {
614
+ background: color-mix(in srgb, var(--color-primary) 15%, var(--color-surface));
615
+ }
464
616
  .set-uploader-overlay {
465
617
  position: absolute;
466
- inset: 8px;
618
+ inset: 5px;
467
619
  border-radius: 50%;
468
620
  display: flex;
469
621
  flex-direction: column;
@@ -491,12 +643,78 @@
491
643
  cursor: progress;
492
644
  }
493
645
  .set-uploader-hint {
494
- margin: 18px auto 0;
646
+ margin: 12px 0 0;
495
647
  max-width: 240px;
496
648
  font-size: 12px;
497
649
  line-height: 1.5;
498
650
  color: var(--color-text-subtle);
499
651
  }
652
+ // The picker field sizes to its content (the ring) rather than stretching to the
653
+ // form width the way a text field does.
654
+ .set-uploader-field {
655
+ gap: 8px;
656
+ width: max-content;
657
+ max-width: 100%;
658
+ }
659
+ // Positioning context for the remove control, which is a sibling of the button.
660
+ .set-uploader-shell {
661
+ position: relative;
662
+ display: inline-block;
663
+ }
664
+ // Ring highlights while a file is dragged over it.
665
+ .set-uploader.is-dragging .set-uploader-ring {
666
+ border-color: var(--color-primary);
667
+ background: var(--color-primary-soft);
668
+ }
669
+ // A plain disabled (not busy) picker: dim it and drop the pointer affordance.
670
+ .set-uploader:disabled:not([aria-busy="true"]) {
671
+ cursor: not-allowed;
672
+ opacity: 0.55;
673
+ }
674
+ .set-uploader:disabled:not([aria-busy="true"]) .set-uploader-ring {
675
+ border-style: solid;
676
+ }
677
+ .set-uploader-remove {
678
+ position: absolute;
679
+ top: 2px;
680
+ right: 2px;
681
+ display: inline-flex;
682
+ align-items: center;
683
+ justify-content: center;
684
+ width: 28px;
685
+ height: 28px;
686
+ padding: 0;
687
+ border: 2px solid var(--color-surface);
688
+ border-radius: 50%;
689
+ background: var(--color-danger, #dc3545);
690
+ color: #fff;
691
+ cursor: pointer;
692
+ transition: transform 0.15s;
693
+ }
694
+ .set-uploader-remove:hover {
695
+ transform: scale(1.08);
696
+ }
697
+ // The control's own display would otherwise beat the UA [hidden] rule and keep it visible.
698
+ .set-uploader-remove[hidden] {
699
+ display: none;
700
+ }
701
+
702
+ // Size is driven entirely by the --wui-logo-size custom property (set inline from the
703
+ // `size` prop / preset), so there are no per-size class rules to keep in sync.
704
+
705
+ // Shape variants — every round face (ring, image, logo, overlay) shares one radius.
706
+ .set-uploader--rounded .set-uploader-ring,
707
+ .set-uploader--rounded .set-uploader-img,
708
+ .set-uploader--rounded .set-uploader-logo,
709
+ .set-uploader--rounded .set-uploader-overlay {
710
+ border-radius: 18px;
711
+ }
712
+ .set-uploader--square .set-uploader-ring,
713
+ .set-uploader--square .set-uploader-img,
714
+ .set-uploader--square .set-uploader-logo,
715
+ .set-uploader--square .set-uploader-overlay {
716
+ border-radius: 8px;
717
+ }
500
718
 
501
719
  // Minimals-style rounded logo dropzone (workspace branding form).
502
720
  .logo-upload {
@@ -16,7 +16,7 @@
16
16
  --bs-offcanvas-border-width: 1px;
17
17
  --bs-offcanvas-padding-x: 0;
18
18
  --bs-offcanvas-padding-y: 0;
19
- max-width: 100vw;
19
+ max-width: var(--asm-max-width, 92vw);
20
20
  display: flex;
21
21
  flex-direction: column;
22
22
  }
@@ -202,6 +202,24 @@
202
202
  background: var(--color-primary-soft);
203
203
  color: var(--color-primary);
204
204
  }
205
+ // Semantic tone tints — React `tone` / Razor `Tone`, matching the vocabulary
206
+ // `.status-label` uses (success | warning | danger | info).
207
+ .app-tab-count.success {
208
+ background: color-mix(in srgb, var(--color-success) 16%, transparent);
209
+ color: var(--color-success-text);
210
+ }
211
+ .app-tab-count.warning {
212
+ background: color-mix(in srgb, var(--color-warning) 16%, transparent);
213
+ color: var(--color-warning-text);
214
+ }
215
+ .app-tab-count.danger {
216
+ background: color-mix(in srgb, var(--color-danger) 16%, transparent);
217
+ color: var(--color-danger-text);
218
+ }
219
+ .app-tab-count.info {
220
+ background: color-mix(in srgb, var(--color-info) 16%, transparent);
221
+ color: var(--color-info-text);
222
+ }
205
223
 
206
224
  // Wrapper for a self-contained tabs widget (strip + panels), as <app-tabs>
207
225
  // renders server-side. React composes the strip and its panels itself.