@webority/theme 0.8.0 → 0.8.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/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/wui/_display.scss +173 -15
- package/scss/wui/_workspace.scss +61 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.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": {
|
package/scss/wui/_display.scss
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
display: flex;
|
|
13
13
|
flex-direction: column;
|
|
14
14
|
padding: 18px 20px;
|
|
15
|
-
border: 1px solid var(--color-border);
|
|
16
15
|
border-radius: var(--radius-lg);
|
|
17
16
|
corner-shape: squircle;
|
|
18
17
|
background: var(--color-surface);
|
|
@@ -119,21 +118,18 @@
|
|
|
119
118
|
margin-top: 2px;
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
// Surface variants — plain (above) is the
|
|
123
|
-
//
|
|
124
|
-
// drawing its own outline.
|
|
121
|
+
// Surface variants — plain (above) is borderless on the surface fill; these just swap the
|
|
122
|
+
// fill so a dashboard can mix a KPI strip with the surrounding card.
|
|
125
123
|
.kpi.kpi-filled {
|
|
126
|
-
border-color: transparent;
|
|
127
124
|
background: var(--color-bg-subtle);
|
|
128
125
|
}
|
|
129
126
|
.kpi.kpi-accent {
|
|
130
|
-
border-color: transparent;
|
|
131
127
|
background: var(--color-primary-soft);
|
|
132
128
|
}
|
|
133
129
|
// Needs-action state — orthogonal to variant, so it composes with plain/filled/accent
|
|
134
|
-
// rather than replacing the surface.
|
|
130
|
+
// rather than replacing the surface. Draws its own outline since the base tile no longer does.
|
|
135
131
|
.kpi.kpi-attention {
|
|
136
|
-
border
|
|
132
|
+
border: 1px solid var(--color-warning);
|
|
137
133
|
background: var(--color-warning-soft);
|
|
138
134
|
}
|
|
139
135
|
|
|
@@ -153,18 +149,64 @@
|
|
|
153
149
|
height: 44px;
|
|
154
150
|
}
|
|
155
151
|
|
|
152
|
+
// Spotlight — an editorial feature tile: the label sits top-left with the icon chip pinned
|
|
153
|
+
// top-right, the value is set in the display face (a product's serif brand face when it
|
|
154
|
+
// overrides --wui-font-display), and an optional caption reads under the number. For the
|
|
155
|
+
// single showcase stat on a marketing/portal surface where the number is the hero.
|
|
156
|
+
.kpi.kpi-spotlight {
|
|
157
|
+
display: grid;
|
|
158
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
159
|
+
column-gap: 16px;
|
|
160
|
+
padding: 22px 24px;
|
|
161
|
+
}
|
|
162
|
+
.kpi.kpi-spotlight .kpi-icon {
|
|
163
|
+
grid-column: 2;
|
|
164
|
+
grid-row: 1;
|
|
165
|
+
align-self: start;
|
|
166
|
+
justify-self: end;
|
|
167
|
+
margin-bottom: 0;
|
|
168
|
+
}
|
|
169
|
+
.kpi.kpi-spotlight .kpi-label,
|
|
170
|
+
.kpi.kpi-spotlight .kpi-value,
|
|
171
|
+
.kpi.kpi-spotlight .kpi-caption,
|
|
172
|
+
.kpi.kpi-spotlight .kpi-delta {
|
|
173
|
+
grid-column: 1;
|
|
174
|
+
}
|
|
175
|
+
.kpi.kpi-spotlight .kpi-label {
|
|
176
|
+
align-self: center;
|
|
177
|
+
text-transform: uppercase;
|
|
178
|
+
letter-spacing: 0.08em;
|
|
179
|
+
font-weight: 600;
|
|
180
|
+
}
|
|
181
|
+
.kpi.kpi-spotlight .kpi-value {
|
|
182
|
+
margin-top: 14px;
|
|
183
|
+
// The editorial serif is this variant's signature (see the reference tile), so the value gets
|
|
184
|
+
// a serif stack directly — not --wui-font-display, which defaults to the body sans and so would
|
|
185
|
+
// never read as serif. A product that wants its own face overrides this one selector.
|
|
186
|
+
font-family: Georgia, 'Times New Roman', 'Noto Serif', serif;
|
|
187
|
+
font-size: 44px;
|
|
188
|
+
line-height: 1;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
}
|
|
191
|
+
// Caption — the sub-line under the value ("Total sent"). Spotlight-only today, but the class
|
|
192
|
+
// is variant-agnostic so a plain tile can carry one too.
|
|
193
|
+
.kpi-caption {
|
|
194
|
+
margin-top: 8px;
|
|
195
|
+
font-size: 14px;
|
|
196
|
+
font-weight: 400;
|
|
197
|
+
color: var(--color-text-muted);
|
|
198
|
+
}
|
|
199
|
+
|
|
156
200
|
// The whole tile as a link (href). Colour/underline reset so the anchor reads as the
|
|
157
201
|
// card, not as text; the hover lift matches .res-card's tint-and-lift treatment.
|
|
158
202
|
a.kpi {
|
|
159
203
|
text-decoration: none;
|
|
160
204
|
color: inherit;
|
|
161
205
|
transition:
|
|
162
|
-
border-color 0.15s ease,
|
|
163
206
|
box-shadow 0.15s ease,
|
|
164
207
|
transform 0.15s ease;
|
|
165
208
|
}
|
|
166
209
|
a.kpi:hover {
|
|
167
|
-
border-color: var(--color-primary);
|
|
168
210
|
box-shadow: var(--shadow-md);
|
|
169
211
|
transform: translateY(-2px);
|
|
170
212
|
}
|
|
@@ -566,6 +608,9 @@ a.info-row:hover .info-value {
|
|
|
566
608
|
// cannot break into segments when an entry wraps to two lines.
|
|
567
609
|
// ============================================================================
|
|
568
610
|
.timeline {
|
|
611
|
+
// One knob for the whole component: the dot diameter. The rail, halo and
|
|
612
|
+
// glyph all derive from it, so `size` (px) scales everything in proportion.
|
|
613
|
+
--wui-timeline-dot: 24px;
|
|
569
614
|
position: relative;
|
|
570
615
|
margin: 0;
|
|
571
616
|
padding: 0 0 0 6px;
|
|
@@ -575,9 +620,9 @@ a.info-row:hover .info-value {
|
|
|
575
620
|
content: "";
|
|
576
621
|
position: absolute;
|
|
577
622
|
// Inset top and bottom so the rail starts and ends inside the first/last dot.
|
|
578
|
-
top:
|
|
579
|
-
bottom:
|
|
580
|
-
left:
|
|
623
|
+
top: calc(var(--wui-timeline-dot) / 2 - 2px);
|
|
624
|
+
bottom: calc(var(--wui-timeline-dot) / 2 - 2px);
|
|
625
|
+
left: calc(6px + var(--wui-timeline-dot) / 2 - 1px);
|
|
581
626
|
width: 2px;
|
|
582
627
|
background: var(--color-border);
|
|
583
628
|
}
|
|
@@ -594,8 +639,8 @@ a.info-row:hover .info-value {
|
|
|
594
639
|
position: relative;
|
|
595
640
|
z-index: 1;
|
|
596
641
|
flex: none;
|
|
597
|
-
width:
|
|
598
|
-
height:
|
|
642
|
+
width: var(--wui-timeline-dot);
|
|
643
|
+
height: var(--wui-timeline-dot);
|
|
599
644
|
display: inline-grid;
|
|
600
645
|
place-items: center;
|
|
601
646
|
border-radius: 999px;
|
|
@@ -659,6 +704,119 @@ a.info-row:hover .info-value {
|
|
|
659
704
|
white-space: pre-wrap;
|
|
660
705
|
overflow-wrap: anywhere;
|
|
661
706
|
}
|
|
707
|
+
// Plain inline subtext under the title — a muted line (distinct from the boxed
|
|
708
|
+
// .timeline-note). Works in both variants; used heavily by the progress variant.
|
|
709
|
+
.timeline-desc {
|
|
710
|
+
margin-top: 2px;
|
|
711
|
+
font-size: 13px;
|
|
712
|
+
line-height: 1.45;
|
|
713
|
+
color: var(--color-text-muted);
|
|
714
|
+
overflow-wrap: anywhere;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// ----------------------------------------------------------------------------
|
|
718
|
+
// Progress variant — a status tracker (Initiated → Submitted → … → Disbursed).
|
|
719
|
+
// Solid status dots (done/current/rejected) or a hollow ring (upcoming); the
|
|
720
|
+
// rail is drawn PER SEGMENT so a completed step's outgoing line reads filled.
|
|
721
|
+
// ----------------------------------------------------------------------------
|
|
722
|
+
.timeline--progress {
|
|
723
|
+
// Halo widths scale with the dot: the coloured ring and the thin blank ring.
|
|
724
|
+
--wui-timeline-halo: calc(var(--wui-timeline-dot) / 4);
|
|
725
|
+
--wui-timeline-halo-thin: calc(var(--wui-timeline-dot) / 12);
|
|
726
|
+
// Room on the left so a status dot's soft halo ring isn't clipped.
|
|
727
|
+
padding-left: calc(var(--wui-timeline-halo) + 1px);
|
|
728
|
+
}
|
|
729
|
+
// No single list rail — each item owns the segment down to the next dot so it
|
|
730
|
+
// can be tinted independently.
|
|
731
|
+
.timeline--progress::before {
|
|
732
|
+
display: none;
|
|
733
|
+
}
|
|
734
|
+
.timeline--progress .timeline-item {
|
|
735
|
+
padding-bottom: 22px;
|
|
736
|
+
}
|
|
737
|
+
// The connector from this dot down into the next one — anchored to the dot's
|
|
738
|
+
// bottom edge and centre, so it stays aligned at any dot size.
|
|
739
|
+
.timeline--progress .timeline-item:not(:last-child)::before {
|
|
740
|
+
content: "";
|
|
741
|
+
position: absolute;
|
|
742
|
+
top: var(--wui-timeline-dot);
|
|
743
|
+
bottom: -2px;
|
|
744
|
+
left: calc(var(--wui-timeline-dot) / 2 - 1px);
|
|
745
|
+
width: 2px;
|
|
746
|
+
background: var(--color-border);
|
|
747
|
+
}
|
|
748
|
+
// The rail fills green only BETWEEN two completed steps, so a lone/last done
|
|
749
|
+
// step doesn't leave a green stub dangling into an incomplete step below it.
|
|
750
|
+
.timeline--progress .timeline-item.is-done:has(+ .timeline-item.is-done)::before {
|
|
751
|
+
background: var(--color-success);
|
|
752
|
+
}
|
|
753
|
+
// Base / upcoming dot: a hollow ring for a not-yet-reached step. Its 2px grey
|
|
754
|
+
// halo is OPAQUE (mixed toward the surface, not transparent) so it masks the
|
|
755
|
+
// rail behind the dot — the connector only shows in the clear gap, never
|
|
756
|
+
// bleeding through the ring's halo.
|
|
757
|
+
.timeline--progress .timeline-dot,
|
|
758
|
+
.timeline--progress .timeline-item.is-upcoming .timeline-dot {
|
|
759
|
+
color: var(--color-text-subtle);
|
|
760
|
+
background: var(--color-surface);
|
|
761
|
+
box-shadow:
|
|
762
|
+
inset 0 0 0 2px var(--color-border),
|
|
763
|
+
0 0 0 var(--wui-timeline-halo-thin) color-mix(in srgb, var(--color-text-subtle) 12%, var(--color-surface));
|
|
764
|
+
}
|
|
765
|
+
// Solid status discs with a white glyph and a soft concentric halo ring in the
|
|
766
|
+
// dot's own hue. The fill is softened a touch (mixed toward the surface) so the
|
|
767
|
+
// states read gentler than the raw brand tones.
|
|
768
|
+
.timeline--progress .timeline-item.is-done .timeline-dot {
|
|
769
|
+
color: var(--color-on-scrim);
|
|
770
|
+
background: color-mix(in srgb, var(--color-success) 82%, var(--color-surface));
|
|
771
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-success-soft);
|
|
772
|
+
}
|
|
773
|
+
.timeline--progress .timeline-item.is-current .timeline-dot {
|
|
774
|
+
color: var(--color-on-scrim);
|
|
775
|
+
background: color-mix(in srgb, var(--color-warning) 82%, var(--color-surface));
|
|
776
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-warning-soft);
|
|
777
|
+
}
|
|
778
|
+
.timeline--progress .timeline-item.is-rejected .timeline-dot {
|
|
779
|
+
color: var(--color-on-scrim);
|
|
780
|
+
background: color-mix(in srgb, var(--color-danger) 82%, var(--color-surface));
|
|
781
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-danger-soft);
|
|
782
|
+
}
|
|
783
|
+
// Blocked / waiting on an external action — informational blue.
|
|
784
|
+
.timeline--progress .timeline-item.is-on-hold .timeline-dot {
|
|
785
|
+
color: var(--color-on-scrim);
|
|
786
|
+
background: color-mix(in srgb, var(--color-info) 82%, var(--color-surface));
|
|
787
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) var(--color-info-soft);
|
|
788
|
+
}
|
|
789
|
+
// Cancelled / skipped — resolved-but-inactive outcomes read as muted slate,
|
|
790
|
+
// with a soft neutral halo, so they sit visually below the live states.
|
|
791
|
+
.timeline--progress .timeline-item.is-cancelled .timeline-dot,
|
|
792
|
+
.timeline--progress .timeline-item.is-skipped .timeline-dot {
|
|
793
|
+
color: var(--color-on-scrim);
|
|
794
|
+
background: color-mix(in srgb, var(--color-text-subtle) 82%, var(--color-surface));
|
|
795
|
+
box-shadow: 0 0 0 var(--wui-timeline-halo) color-mix(in srgb, var(--color-text-subtle) 16%, transparent);
|
|
796
|
+
}
|
|
797
|
+
// A cancelled step's title is struck through — the plan changed.
|
|
798
|
+
.timeline--progress .timeline-item.is-cancelled .timeline-title {
|
|
799
|
+
text-decoration: line-through;
|
|
800
|
+
color: var(--color-text-muted);
|
|
801
|
+
}
|
|
802
|
+
// Title row: title on the left, the timestamp pinned to the right.
|
|
803
|
+
.timeline--progress .timeline-head {
|
|
804
|
+
display: flex;
|
|
805
|
+
align-items: baseline;
|
|
806
|
+
justify-content: space-between;
|
|
807
|
+
gap: 12px;
|
|
808
|
+
}
|
|
809
|
+
.timeline--progress .timeline-head .timeline-at {
|
|
810
|
+
flex: none;
|
|
811
|
+
font-size: 12.5px;
|
|
812
|
+
color: var(--color-text-subtle);
|
|
813
|
+
white-space: nowrap;
|
|
814
|
+
}
|
|
815
|
+
// An upcoming step greys down — it hasn't happened yet.
|
|
816
|
+
.timeline--progress .timeline-item.is-upcoming .timeline-title {
|
|
817
|
+
color: var(--color-text-muted);
|
|
818
|
+
font-weight: 500;
|
|
819
|
+
}
|
|
662
820
|
|
|
663
821
|
// ============================================================================
|
|
664
822
|
// Feature-comparison table — us vs them. The highlighted column is tinted so
|
package/scss/wui/_workspace.scss
CHANGED
|
@@ -25,21 +25,31 @@
|
|
|
25
25
|
width: 100%;
|
|
26
26
|
min-height: 48px;
|
|
27
27
|
padding: 6px 10px;
|
|
28
|
-
|
|
29
|
-
border
|
|
28
|
+
// The trigger IS a closed picker, so it wears AppSelect's control skin (.input): a muted
|
|
29
|
+
// resting fill, a grey border, and the same border/box-shadow transition — nothing bespoke.
|
|
30
|
+
background: var(--color-bg-muted);
|
|
31
|
+
border: 1px solid var(--color-border);
|
|
30
32
|
border-radius: var(--radius-lg);
|
|
31
33
|
corner-shape: squircle;
|
|
32
34
|
color: var(--color-text);
|
|
33
35
|
text-align: left;
|
|
34
36
|
cursor: pointer;
|
|
35
|
-
transition:
|
|
37
|
+
transition:
|
|
38
|
+
border-color 0.12s,
|
|
39
|
+
box-shadow 0.12s;
|
|
36
40
|
}
|
|
37
|
-
.
|
|
38
|
-
|
|
41
|
+
// Hover while closed darkens the border, exactly like a resting AppSelect on hover.
|
|
42
|
+
.workspace-switcher-trigger:hover:not(:focus):not([aria-expanded="true"]) {
|
|
43
|
+
border-color: var(--color-border-strong);
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
// Focus / open lifts onto a white surface with the soft brand border — the same active cue
|
|
46
|
+
// AppSelect gives a focused or open control (border is the cue; no ring, no shadow).
|
|
47
|
+
.workspace-switcher-trigger:focus-visible,
|
|
48
|
+
.workspace-switcher-trigger[aria-expanded="true"] {
|
|
49
|
+
outline: none;
|
|
50
|
+
background: var(--color-surface);
|
|
51
|
+
border-color: var(--wui-primary-30);
|
|
52
|
+
box-shadow: none;
|
|
43
53
|
}
|
|
44
54
|
.workspace-switcher-trigger.is-compact {
|
|
45
55
|
width: 36px;
|
|
@@ -86,6 +96,49 @@
|
|
|
86
96
|
overflow: hidden;
|
|
87
97
|
}
|
|
88
98
|
|
|
99
|
+
// ── Size tiers ──
|
|
100
|
+
// md is the base above. sm/lg scale the TRIGGER only — the card (and the compact square),
|
|
101
|
+
// its mark and its name; the dropdown menu is a fixed 280px panel at every size, the same
|
|
102
|
+
// way `compact` is a shape switch rather than a resize.
|
|
103
|
+
.workspace-switcher-sm .workspace-switcher-trigger {
|
|
104
|
+
min-height: 40px;
|
|
105
|
+
padding: 4px 8px;
|
|
106
|
+
gap: 8px;
|
|
107
|
+
}
|
|
108
|
+
.workspace-switcher-sm .workspace-switcher-trigger.is-compact {
|
|
109
|
+
width: 32px;
|
|
110
|
+
min-height: 32px;
|
|
111
|
+
height: 32px;
|
|
112
|
+
padding: 0;
|
|
113
|
+
}
|
|
114
|
+
.workspace-switcher-sm .workspace-switcher-mark {
|
|
115
|
+
width: 24px;
|
|
116
|
+
height: 24px;
|
|
117
|
+
font-size: 10px;
|
|
118
|
+
}
|
|
119
|
+
.workspace-switcher-sm .workspace-switcher-trigger-name {
|
|
120
|
+
font-size: 13px;
|
|
121
|
+
}
|
|
122
|
+
.workspace-switcher-lg .workspace-switcher-trigger {
|
|
123
|
+
min-height: 56px;
|
|
124
|
+
padding: 8px 12px;
|
|
125
|
+
gap: 12px;
|
|
126
|
+
}
|
|
127
|
+
.workspace-switcher-lg .workspace-switcher-trigger.is-compact {
|
|
128
|
+
width: 44px;
|
|
129
|
+
min-height: 44px;
|
|
130
|
+
height: 44px;
|
|
131
|
+
padding: 0;
|
|
132
|
+
}
|
|
133
|
+
.workspace-switcher-lg .workspace-switcher-mark {
|
|
134
|
+
width: 32px;
|
|
135
|
+
height: 32px;
|
|
136
|
+
font-size: 12px;
|
|
137
|
+
}
|
|
138
|
+
.workspace-switcher-lg .workspace-switcher-trigger-name {
|
|
139
|
+
font-size: 15px;
|
|
140
|
+
}
|
|
141
|
+
|
|
89
142
|
// ── Menu ──
|
|
90
143
|
.workspace-switcher .workspace-switcher-menu {
|
|
91
144
|
width: 280px;
|