@webority/theme 0.75.1 → 0.77.0

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.75.1",
3
+ "version": "0.77.0",
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": {
@@ -27,12 +27,11 @@
27
27
  }
28
28
 
29
29
  .btn-primary {
30
- // Pin hover/active to the pressed-primary token so a rebrand tracks
31
- // automatically instead of Bootstrap auto-shading $primary off-spec.
32
- --bs-btn-hover-bg: var(--wui-primary-pressed);
33
- --bs-btn-hover-border-color: var(--wui-primary-pressed);
34
- --bs-btn-active-bg: var(--wui-primary-pressed);
35
- --bs-btn-active-border-color: var(--wui-primary-pressed);
30
+ // Hover and press read --color-primary-hover: the pressed primary in light and the lifted dark primary in dark (_wui-aliases.scss), so a rebrand and dark mode both track it.
31
+ --bs-btn-hover-bg: var(--color-primary-hover);
32
+ --bs-btn-hover-border-color: var(--color-primary-hover);
33
+ --bs-btn-active-bg: var(--color-primary-hover);
34
+ --bs-btn-active-border-color: var(--color-primary-hover);
36
35
  // Bootstrap computes this label colour at SCSS compile time from the library's
37
36
  // default $primary, so it can never react to a product overriding --wui-primary as a
38
37
  // runtime CSS var. Routing it through the token makes a light-primary brand's
@@ -858,9 +857,10 @@
858
857
  .card-header-actions + .card-close {
859
858
  margin-left: 0.25rem;
860
859
  }
860
+ // Hover tints the button danger, the same as the modal and drawer close buttons.
861
861
  .card-close:hover {
862
- background: var(--color-bg-muted);
863
- color: var(--color-text);
862
+ background: var(--color-danger-soft);
863
+ color: var(--color-danger-text);
864
864
  }
865
865
  .card-close:focus-visible {
866
866
  outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
@@ -25,9 +25,10 @@
25
25
  --color-progress-track: var(--wui-progress-track);
26
26
  // Label text inside a progress bar fill.
27
27
  --color-progress-label: var(--wui-bg-light);
28
- // Ink on a solid status fill (success, warning, danger, info): near-black in both modes, since
29
- // white fails AA on the bright fills.
30
- --color-on-status-fill: #0F0F0F;
28
+ // Icon ink on a solid status fill (the filled timeline dots): white in both modes, by design
29
+ // choice. White on the bright success and warning fills sits under the 3:1 icon floor, so the
30
+ // contrast gate does not measure this pair.
31
+ --color-on-status-fill: #FFFFFF;
31
32
  // Ink on a DEEP status fill (the -text tokens, used as a fill the way primary is): white in
32
33
  // light mode, where those shades measure 5.5 to 6.2:1; near-black in dark mode, where -text is
33
34
  // the bright fill.
@@ -0,0 +1,88 @@
1
+ // ============================================================================
2
+ // _wui-core.scss
3
+ // ----------------------------------------------------------------------------
4
+ // The core widget layer: every partial of _wui-components.scss except the
5
+ // on-demand set, in the same order. A portal imports this globally, and each
6
+ // on-demand partial (wui/<name>) from the lazy route that renders it.
7
+ //
8
+ // scripts/check-theme-split.mjs keeps this file in step with _wui-components.scss.
9
+ // The split guards two things: a widget rule left in core defeats the split,
10
+ // and a core rule that styles an on-demand class silently changes when that
11
+ // layer is absent.
12
+ // ============================================================================
13
+
14
+ // Shared tone-pill mixin — used by .badge, .status-label, and .chip.
15
+ // Must be imported before components that use it.
16
+ @import "wui/tone-pill";
17
+
18
+ // The order below is LOAD-BEARING. A later partial may deliberately override an
19
+ // earlier one, so reordering these imports silently changes the cascade — and
20
+ // nothing in the build will complain. `pnpm check:theme` measures the rules that
21
+ // have to win against Bootstrap; the rest is guarded by review.
22
+ @import "wui/controls";
23
+ @import "wui/choice";
24
+ @import "wui/media";
25
+ @import "wui/avatar-picker";
26
+ @import "wui/navigation";
27
+ @import "wui/card";
28
+ @import "wui/data";
29
+ @import "wui/illustration";
30
+ @import "wui/table-server";
31
+ @import "wui/inline-edit";
32
+ @import "wui/mention-input";
33
+ @import "wui/comment-thread";
34
+ @import "wui/overlays";
35
+ @import "wui/drawer";
36
+ @import "wui/modal-remote";
37
+ @import "wui/utilities";
38
+ @import "wui/typography";
39
+ @import "wui/disclosure";
40
+ @import "wui/display";
41
+ @import "wui/flow";
42
+ // After `flow`: the pricing section extends its `.plan-grid` rules.
43
+ @import "wui/sections";
44
+ @import "wui/chart";
45
+ @import "wui/chart-card";
46
+ @import "wui/scrollbar";
47
+ @import "wui/sortable";
48
+ // Before `split-view`, which places the legend sheet inside the list column.
49
+ @import "wui/record-pager";
50
+ @import "wui/shortcuts";
51
+ @import "wui/split-view";
52
+ @import "wui/groups";
53
+ @import "wui/carousel";
54
+ @import "wui/onboarding-checklist";
55
+ @import "wui/rating";
56
+ @import "wui/score-scale";
57
+ @import "wui/relative-time";
58
+ @import "wui/resizable";
59
+ @import "wui/rolling-number";
60
+ @import "wui/show-more";
61
+ @import "wui/truncate";
62
+ @import "wui/page-header-bar";
63
+ @import "wui/filter-popover";
64
+ @import "wui/metric-strip";
65
+ @import "wui/due-date";
66
+
67
+ // Country flags are NOT part of this layer. The 245 `.wui-flag-*` rules are about 187KB raw
68
+ // (38KB Brotli) of inlined SVG, and most sites never render a phone field or country picker.
69
+ // A site that does imports `@webority/theme/scss/flags` once, after this file; without it
70
+ // <wui-phone> and <wui-country-select> show an empty grey box where the flag should be.
71
+ @import "wui/shell";
72
+ @import "wui/shell-parts";
73
+ @import "wui/consent-banner";
74
+ @import "wui/lightbox";
75
+ @import "wui/toolbar";
76
+ @import "wui/theme-toggle";
77
+ @import "wui/floating";
78
+ @import "wui/code-block";
79
+ @import "wui/context-menu";
80
+ @import "wui/menubar";
81
+ @import "wui/navigation-menu";
82
+ @import "wui/hover-card";
83
+ @import "wui/notification-center";
84
+ @import "wui/whats-new";
85
+ // After every component partial: the shared focus ring and pressed wash settle ties with their hover rules.
86
+ @import "wui/interaction";
87
+ // Last: restates the settled state of menus other partials style, so it must win.
88
+ @import "wui/menu-motion";
@@ -254,3 +254,8 @@ wui-calendar[data-view="week"] .wui-calendar-surface > * {
254
254
  transition: none;
255
255
  }
256
256
  }
257
+
258
+ // The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
259
+ :is(.wui-calendar-nav-btn, .wui-calendar-today-btn, .wui-calendar-view-btn):active:not(:disabled, [aria-disabled="true"]) {
260
+ background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
261
+ }
@@ -21,14 +21,24 @@
21
21
  // Default card is borderless — a flat surface. The border-STYLE stays solid (Bootstrap's
22
22
  // `border: var(--bs-card-border-width) solid …`), so a variant or a consumer class can
23
23
  // bring the edge back just by giving it a width (set --bs-card-border-width).
24
- --bs-card-border-width: 0;
24
+ // With a unit: the inner radius below subtracts it, and calc() drops a bare 0.
25
+ --bs-card-border-width: 0px;
26
+ // Bootstrap compiles the inner radius from $card-border-radius, which is smaller than the
27
+ // token radius the card actually draws. The header, footer and media round their corners
28
+ // with it, so their fill poked out over a bordered card's corners and the border looked cut.
29
+ // Derive it from the live radius and border width instead.
30
+ --bs-card-inner-border-radius: calc(var(--bs-card-border-radius) - var(--bs-card-border-width));
25
31
  }
26
32
 
27
33
  // A card header that carries a title on the left and controls on the right — the
28
34
  // pattern nearly every panel header uses, so it belongs here rather than being
29
35
  // re-flexed with utilities on each one.
36
+ // The header and footer share the card's own fill (a muted card stays muted to its top edge),
37
+ // and the header carries no rule of its own: Bootstrap's divider followed the card's border
38
+ // width, so only the elevated variant drew one.
30
39
  .card-header {
31
- --bs-card-cap-bg: var(--color-surface);
40
+ --bs-card-cap-bg: transparent;
41
+ border-bottom: 0;
32
42
  display: flex;
33
43
  align-items: center;
34
44
  justify-content: space-between;
@@ -63,8 +73,9 @@
63
73
  --bs-card-border-width: 1px;
64
74
  border-color: var(--color-border-strong);
65
75
  }
76
+ // Through the variable, so the inner radius and the border agree.
66
77
  .card-outlined {
67
- border-width: 2px;
78
+ --bs-card-border-width: 2px;
68
79
  border-color: var(--color-border-strong);
69
80
  }
70
81
  .card-muted {
@@ -88,14 +99,16 @@
88
99
  .card-accent.tone-info {
89
100
  --card-accent: var(--color-info);
90
101
  }
102
+ // The stripe is a left-only border on a layer with the card's own corners. CSS corner shaping
103
+ // curves its inner edge with the radius minus each side's width, so the stripe thins smoothly
104
+ // to nothing round both left corners. A straight bar clipped by the corners showed sliced ends.
91
105
  .card-accent::before {
92
106
  content: "";
93
107
  position: absolute;
94
- top: 0;
95
- bottom: 0;
96
- left: 0;
97
- width: 4px;
98
- background: var(--card-accent);
108
+ inset: 0;
109
+ border-left: 4px solid var(--card-accent);
110
+ border-radius: inherit;
111
+ pointer-events: none;
99
112
  }
100
113
 
101
114
  // ── Padding scale ── (header + body + footer). `md` is the Bootstrap default.
@@ -114,6 +127,12 @@
114
127
  .card-pad-lg > .card-footer {
115
128
  padding: 28px 32px;
116
129
  }
130
+ // With no rule under the header, its bottom padding alone separates the title from the body;
131
+ // the body's own top padding on top of it left a double gap. After the scale, so every
132
+ // padding step keeps the single gap.
133
+ .card > .card-header + .card-body {
134
+ padding-top: 0;
135
+ }
117
136
 
118
137
  // ── Constrained width ── (`maxWidth`): caps + centres a standalone card. Cards
119
138
  // are fluid (fill their column) unless this is set.
@@ -124,7 +143,7 @@
124
143
 
125
144
  // ── Footer ── (React `footer` / Razor <app-card-footer>): mirrors the header row.
126
145
  .card-footer {
127
- --bs-card-cap-bg: var(--color-surface);
146
+ --bs-card-cap-bg: transparent;
128
147
  display: flex;
129
148
  align-items: center;
130
149
  gap: 12px;
@@ -164,8 +183,8 @@
164
183
  }
165
184
  // Media flush at the top follows the card's rounded corners.
166
185
  .card > .card-media:first-child {
167
- border-top-left-radius: var(--radius-lg);
168
- border-top-right-radius: var(--radius-lg);
186
+ border-top-left-radius: var(--bs-card-inner-border-radius);
187
+ border-top-right-radius: var(--bs-card-inner-border-radius);
169
188
  }
170
189
 
171
190
  // ── Clickable card ── (`href` / `onClick`): the whole surface is one control.
@@ -180,9 +180,13 @@ button.wui-cmd-crumb:focus-visible {
180
180
  // Keeps a little of the next row visible when the keyboard scrolls the active one in.
181
181
  scroll-margin: 6px;
182
182
  }
183
+ // Focus stays in the input, and the hover tint alone marks the keyboard's row at about 1.2:1, so
184
+ // an inset ring makes it plain; inset, because the list scrolls and would clip an outer ring.
183
185
  .wui-cmd-item[aria-selected="true"] {
184
186
  background: var(--color-primary-soft);
185
187
  color: var(--color-primary-ink);
188
+ outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
189
+ outline-offset: var(--wui-focus-ring-inset);
186
190
  }
187
191
  .wui-cmd-item[aria-disabled="true"] {
188
192
  color: var(--color-text-muted);
@@ -296,3 +300,8 @@ button.wui-cmd-crumb:focus-visible {
296
300
  color 0.12s ease;
297
301
  }
298
302
  }
303
+
304
+ // The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
305
+ :is(.wui-cmd-back, button.wui-cmd-crumb):active:not(:disabled, [aria-disabled="true"]) {
306
+ background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
307
+ }
@@ -536,7 +536,8 @@ textarea.form-control.autosize {
536
536
  // the hint's place instead of adding a line. Inside a form an empty line still keeps its height, so
537
537
  // showing an error never moves the form; elsewhere (toolbars, filter bars, inline edits) an empty
538
538
  // line takes no space. The library's compact containers never reserve it, since a Razor filter bar is a
539
- // GET form. .wui-form-compact on any container opts a form out.
539
+ // GET form. .wui-form-compact on any container opts a form out with the small control height;
540
+ // .wui-form-inline opts out and changes nothing else, for a field and button on one row.
540
541
  .wui-field-message {
541
542
  display: grid;
542
543
  margin-top: -2px;
@@ -550,7 +551,7 @@ form .wui-field-message:empty {
550
551
  display: grid;
551
552
  min-height: 16px;
552
553
  }
553
- :is(.wui-form-compact, .wui-filter-bar, .wui-filter-popover, .wui-table-toolbar, .wui-inline-edit) .wui-field-message:empty {
554
+ :is(.wui-form-compact, .wui-form-inline, .wui-filter-bar, .wui-filter-popover, .wui-table-toolbar, .wui-inline-edit) .wui-field-message:empty {
554
555
  display: none;
555
556
  }
556
557
  .wui-field-message > .field-hint,
@@ -1028,6 +1029,42 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
1028
1029
  .field-float .input[aria-invalid="true"] ~ .float-label {
1029
1030
  background: var(--color-surface);
1030
1031
  }
1032
+ // Above placement (floatingLabel="above"): the same slide, but the label comes to rest above
1033
+ // the box at the top-left, clear of the border, so it needs no mask. The shell reserves the
1034
+ // label's line above the box, so a floated label never overlaps what sits above the field.
1035
+ .field-float.wui-float-above {
1036
+ --wui-float-above-space: 22px;
1037
+ padding-top: var(--wui-float-above-space);
1038
+ }
1039
+ .field-float.wui-float-above .float-label {
1040
+ // Centred on the box, not the shell: the shell is the reserved line plus the box.
1041
+ top: calc(50% + var(--wui-float-above-space) / 2);
1042
+ background: transparent;
1043
+ transition:
1044
+ top 0.15s var(--wui-ease-ui),
1045
+ left 0.15s var(--wui-ease-ui),
1046
+ padding 0.15s var(--wui-ease-ui),
1047
+ transform 0.15s var(--wui-ease-ui),
1048
+ color 0.15s var(--wui-ease-ui);
1049
+ }
1050
+ .field-float.wui-float-above .input:focus ~ .float-label,
1051
+ .field-float.wui-float-above .input:not(:placeholder-shown) ~ .float-label {
1052
+ top: 0;
1053
+ left: 0;
1054
+ padding: 0;
1055
+ transform: scale(0.86);
1056
+ font-weight: 500;
1057
+ }
1058
+ .field-float.wui-float-above .input:not(:focus):not(:placeholder-shown):not(.error):not(.is-invalid) ~ .float-label {
1059
+ color: var(--color-text);
1060
+ }
1061
+ .field-float.wui-float-above .input ~ .float-label,
1062
+ .field-float.wui-float-above .input:focus ~ .float-label,
1063
+ .field-float.wui-float-above .input.error ~ .float-label,
1064
+ .field-float.wui-float-above .input.is-invalid ~ .float-label,
1065
+ .field-float.wui-float-above .input[aria-invalid="true"] ~ .float-label {
1066
+ background: transparent;
1067
+ }
1031
1068
  // Floating label for AppSelect — the trigger is a <button>, so there is no
1032
1069
  // :placeholder-shown to key off. :has() raises the label when the control is
1033
1070
  // focused/open, or once a value is chosen (the label span drops
@@ -1108,8 +1145,7 @@ $wui-field-error-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/
1108
1145
  gap: 4px;
1109
1146
  min-width: 0;
1110
1147
  }
1111
- // Inside the flex column these two size to their content, so they stretch to the field instead.
1112
- .field > wui-rich-text,
1148
+ // Inside the flex column it sizes to its content, so it stretches to the field instead.
1113
1149
  .field > wui-mention-input {
1114
1150
  align-self: stretch;
1115
1151
  width: 100%;
@@ -198,6 +198,7 @@
198
198
  font-size: 12px;
199
199
  font-weight: 500;
200
200
  letter-spacing: 0.04em;
201
+ text-transform: uppercase;
201
202
  white-space: nowrap;
202
203
  vertical-align: middle;
203
204
  transition:
@@ -562,6 +563,10 @@
562
563
  white-space: nowrap;
563
564
  flex: none;
564
565
  }
566
+ // 3px padding and a 1px border on each side, so the strip reaches the small control height beside the other filter controls.
567
+ .wui-filter-bar .segmented-sm {
568
+ --segmented-option-h: calc(var(--wui-control-height-sm) - 8px);
569
+ }
565
570
  // Facets folded behind the Filters button stack in one panel, each at the panel's width. Both surfaces
566
571
  // draw it as the same <details>; in the Razor bar it sits inside the form, so the controls still submit.
567
572
  .wui-filter-more-panel {
@@ -1082,7 +1087,9 @@
1082
1087
  align-items: flex-end;
1083
1088
  }
1084
1089
  // The leading cell is the card's title line: full width, no label.
1090
+ // Block, not the flex row of the other cells, so a name with a sub-line stacks instead of sitting beside it.
1085
1091
  .table-ledger.wui-table-stack > * > tr > td:not(.wui-select-cell, .wui-expand-cell):is(:first-child, .wui-select-cell + td, .wui-expand-cell + td) {
1092
+ display: block;
1086
1093
  justify-content: flex-start;
1087
1094
  padding: 0 32px 6px 0;
1088
1095
  text-align: left;
@@ -175,21 +175,27 @@
175
175
  }
176
176
 
177
177
  // Spotlight — an editorial feature tile: the label sits top-left with the icon chip pinned
178
- // top-right, the value is set in the display face (a product's serif brand face when it
179
- // overrides --wui-font-display), and an optional caption reads under the number. For the
180
- // single showcase stat on a marketing/portal surface where the number is the hero.
178
+ // top-right, the value is set large in the product's own font, and an optional caption reads
179
+ // under the number. For the single showcase stat on a marketing/portal surface where the
180
+ // number is the hero.
181
181
  .kpi.kpi-spotlight {
182
182
  display: grid;
183
183
  grid-template-columns: minmax(0, 1fr) auto;
184
+ // Rows pack at the top: a tile stretched to a taller neighbour's height puts the spare
185
+ // space below the caption, so the label and icon row stays level across tiles.
186
+ align-content: start;
184
187
  column-gap: 16px;
185
188
  padding: 22px 24px;
186
- --wui-kpi-value-size: 44px;
189
+ --wui-kpi-value-size: 36px;
187
190
  }
188
191
  .kpi.kpi-spotlight .kpi-icon {
189
192
  grid-column: 2;
190
193
  grid-row: 1;
191
194
  align-self: start;
192
195
  justify-self: end;
196
+ // A larger chip than the plain tile's 40px, to match the hero-sized value; the glyph keeps its size.
197
+ width: 48px;
198
+ height: 48px;
193
199
  margin-bottom: 0;
194
200
  }
195
201
  .kpi.kpi-spotlight .kpi-label,
@@ -206,12 +212,9 @@
206
212
  }
207
213
  .kpi.kpi-spotlight .kpi-value {
208
214
  margin-top: 14px;
209
- // The editorial serif is this variant's signature (see the reference tile), so the value gets
210
- // a serif stack directly — not --wui-font-display, which defaults to the body sans and so would
211
- // never read as serif. A product that wants its own face overrides this one selector.
212
- // Size comes from --wui-kpi-value-size (44px default, set on the spotlight root above), so a
215
+ // No font-family: the value inherits the product's own font like every other tile.
216
+ // Size comes from --wui-kpi-value-size (36px default, set on the spotlight root above), so a
213
217
  // custom `valueSize` (px) can tame a too-large headline.
214
- font-family: var(--wui-font-display);
215
218
  line-height: 1;
216
219
  font-weight: 600;
217
220
  }
@@ -357,6 +360,11 @@ a.info-row:hover .info-value {
357
360
  border-bottom: 1px solid var(--color-border);
358
361
  grid-column: span var(--descs-span, 1);
359
362
  }
363
+ @for $n from 1 through 12 {
364
+ .wui-descs-span-#{$n} {
365
+ --descs-span: #{$n};
366
+ }
367
+ }
360
368
  .descs-label {
361
369
  font-size: 12px;
362
370
  font-weight: 600;
@@ -1312,7 +1320,7 @@ a.info-row:hover .info-value {
1312
1320
  position: absolute;
1313
1321
  left: 0;
1314
1322
  right: 0;
1315
- bottom: -2px;
1323
+ bottom: 1px;
1316
1324
  height: 1px;
1317
1325
  background: currentColor;
1318
1326
  transform: scaleX(0);
@@ -100,3 +100,8 @@ wui-dock {
100
100
  transition: none;
101
101
  }
102
102
  }
103
+
104
+ // The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
105
+ .dock-item:active:not(:disabled, [aria-disabled="true"]) {
106
+ background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
107
+ }
@@ -500,3 +500,8 @@ wui-file-viewer:fullscreen {
500
500
  min-width: 48px;
501
501
  }
502
502
 
503
+ // The shared pressed wash of wui/interaction, restated here so it ships with this on-demand layer.
504
+ .wui-file-viewer-zoom:active:not(:disabled, [aria-disabled="true"]) {
505
+ background-image: linear-gradient(var(--color-pressed-overlay), var(--color-pressed-overlay)) !important;
506
+ }
507
+
@@ -14,6 +14,10 @@
14
14
  flex-direction: column-reverse;
15
15
  gap: 12px;
16
16
  margin: 1rem;
17
+ // Clear the home indicator and the notch on a phone.
18
+ margin-bottom: max(1rem, env(safe-area-inset-bottom));
19
+ margin-left: max(1rem, env(safe-area-inset-left));
20
+ margin-right: max(1rem, env(safe-area-inset-right));
17
21
  // The container spans only its items, but a gap between them must not swallow clicks on the page.
18
22
  pointer-events: none;
19
23
  }
@@ -53,9 +57,9 @@
53
57
  // Quieter than the primary circle: a light brand tint, the brand icon and a thin brand border.
54
58
  .wui-floating-btn--secondary {
55
59
  --wui-floating-size: 40px;
56
- --bs-btn-border-color: var(--wui-primary-30);
57
- --bs-btn-hover-border-color: var(--wui-primary-30);
58
- --bs-btn-active-border-color: var(--wui-primary-30);
60
+ --bs-btn-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
61
+ --bs-btn-hover-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
62
+ --bs-btn-active-border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
59
63
  }
60
64
  @media (prefers-reduced-motion: reduce) {
61
65
  .wui-floating-btn {