@wix/web5-core 1.56.0 → 1.56.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,7 +1,7 @@
1
1
  {
2
2
  "name": "@wix/web5-core",
3
3
  "license": "MIT",
4
- "version": "1.56.0",
4
+ "version": "1.56.1",
5
5
  "author": {
6
6
  "name": "tsachis",
7
7
  "email": "tsachis@wix.com"
@@ -14,7 +14,8 @@
14
14
  "dist/cjs",
15
15
  "dist/esm",
16
16
  "dist/types",
17
- "src/styles"
17
+ "src/styles",
18
+ "src/components/**/*.css"
18
19
  ],
19
20
  "scripts": {
20
21
  "build": "yoshi-library build && node scripts/fix-esm-extensions.js",
@@ -99,5 +100,5 @@
99
100
  "wallaby": {
100
101
  "autoDetect": true
101
102
  },
102
- "falconPackageHash": "85aa415503e1949c89b77d043271b71ca3bd64f50d7027e8d8b1e0c8"
103
+ "falconPackageHash": "ddcfd2461f54a7f6bce7c9799d570b3aa2f85da1923cca6e4afb6bb4"
103
104
  }
@@ -0,0 +1,269 @@
1
+ /* PlacementResponseRenderer — placement card chrome (DL #097 —
2
+ Figma 128:5251 framing / 128:5258 conversation).
3
+ Renders the streamed answer as a warm card with a FormAI eyebrow and a
4
+ gradient highlight ring (accent → transparent, top-left → bottom-right)
5
+ that holds while the answer streams, spins twice once it settles, and then
6
+ stays put. The conversation variant additionally shows the user's question
7
+ as a chip.
8
+ Re-skin via the `--w5-placement-*` tokens below.
9
+ The loading skeleton's styles live in `../ui/PlacementLoader.css`. */
10
+
11
+ /* Outer host-page spacing — matches the loader's `--w5-placement-spacing` so
12
+ the gap to surrounding content doesn't change at swap time. */
13
+ [data-w5-placement-renderer] {
14
+ margin-block: var(--w5-placement-spacing, 24px);
15
+ }
16
+
17
+ /* The loader→content swap is height-animated by the PlacementSmoothHeight
18
+ wrapper (`.w5-placement__body` below); this fade-in covers the paint swap
19
+ itself so the card doesn't pop in while the height glides. Plays once per
20
+ renderer mount (so again after each re-submit). */
21
+ [data-w5-placement-renderer] {
22
+ animation: w5-placement-content-in 240ms ease-out both;
23
+ }
24
+
25
+ @keyframes w5-placement-content-in {
26
+ from {
27
+ opacity: 0;
28
+ }
29
+ to {
30
+ opacity: 1;
31
+ }
32
+ }
33
+
34
+ [data-w5-placement-renderer].w5-placement {
35
+ box-sizing: border-box;
36
+ padding: var(--w5-placement-card-padding, 30px);
37
+ border-radius: var(--w5-placement-card-radius, 4px);
38
+ background: var(--w5-placement-card-bg, #f1f0ee);
39
+ /* Section components inside the answer use `bg-background`
40
+ (= var(--color-background), white), which would paint a white block over
41
+ the card. Re-point that token within the placement so those sections
42
+ inherit the card colour instead — one shared background across the whole
43
+ placement. Change --w5-placement-card-bg to re-theme card + sections. */
44
+ --color-background: var(--w5-placement-card-bg, #f1f0ee);
45
+ /* Reserve the border box so the conversation highlight ring (the
46
+ `::before` gradient below) doesn't shift layout when it fades in/out. */
47
+ border: 2px solid transparent;
48
+ }
49
+
50
+ /* Height smoothing for the whole placement slot: PlacementSmoothHeight
51
+ (mounted around the loader/content swap in embed-placement) measures the
52
+ current child (ResizeObserver) and sets an explicit `height` here, so the
53
+ loader→content swap and each streamed section appending animate instead
54
+ of snapping. Until the first measurement lands there's no inline height
55
+ and the box sizes naturally. */
56
+ .w5-placement__body {
57
+ overflow: clip;
58
+ transition: height var(--w5-placement-grow-duration, 350ms) ease-out;
59
+ }
60
+
61
+ /* flow-root so children's top/bottom margins can't collapse out of the
62
+ measured inner box (they'd be clipped by the wrapper otherwise). */
63
+ .w5-placement__body-inner {
64
+ display: flow-root;
65
+ }
66
+
67
+ /* Height clamp + "Show more" (DL #113) — Google-AI-Overview style. Opt-in via
68
+ the slot's `data-clamp` attribute; the renderer adds `--clamped` to the card
69
+ ROOT and caps it directly (no inner wrapper), so the answer sections stay
70
+ direct children of the placement root — clients lay them out with
71
+ `:has(> …)` direct-child grids (feature-com's search-overview two-column
72
+ layout) that a wrapper would break. The cap is also what
73
+ `PlacementSmoothHeight` then measures, so the slot animates to the capped
74
+ height rather than the full content. */
75
+ .w5-placement--clamped {
76
+ position: relative;
77
+ max-height: var(--w5-placement-clamp-height, 220px);
78
+ overflow: hidden;
79
+ }
80
+
81
+ /* The variant highlight ring is a `::before` at `inset: -2px` (outside the
82
+ padding box) — `overflow: hidden` above would clip it to a stub. Drop it on
83
+ clamped cards; the clamp + fade is its own treatment. */
84
+ .w5-placement--clamped::before {
85
+ content: none;
86
+ }
87
+
88
+ /* Fade the cut-off bottom edge to the card background (a wash, like Google —
89
+ not a mask that makes text vanish). Rendered only when the content overflows
90
+ (the renderer omits it otherwise), pinned to the bottom of the capped root. */
91
+ .w5-placement__fade {
92
+ position: absolute;
93
+ inset-inline: 0;
94
+ bottom: 0;
95
+ height: var(--w5-placement-fade-height, 96px);
96
+ background: linear-gradient(
97
+ to bottom,
98
+ transparent,
99
+ var(--w5-placement-card-bg, #f1f0ee)
100
+ );
101
+ pointer-events: none;
102
+ }
103
+
104
+ /* "Show more" pill, pinned over the fade. Re-skins via the shared
105
+ `--w5-placement-*` tokens. */
106
+ .w5-placement__show-more {
107
+ position: absolute;
108
+ bottom: 14px;
109
+ left: 50%;
110
+ transform: translateX(-50%);
111
+ display: inline-flex;
112
+ align-items: center;
113
+ gap: 6px;
114
+ padding: 8px 18px;
115
+ border-radius: 999px;
116
+ border: 1px solid var(--w5-placement-show-more-border, #d8d5cf);
117
+ background: var(--w5-placement-show-more-bg, #fff);
118
+ color: var(--w5-placement-show-more-color, #000);
119
+ font-size: 14px;
120
+ line-height: 15px;
121
+ font-weight: 500;
122
+ text-decoration: none;
123
+ cursor: pointer;
124
+ white-space: nowrap;
125
+ }
126
+
127
+ .w5-placement__show-more:hover {
128
+ background: var(--w5-placement-show-more-bg-hover, #f6f5f3);
129
+ }
130
+
131
+ .w5-placement__show-more:focus-visible {
132
+ outline: 2px solid var(--w5-placement-accent, #ef1b28);
133
+ outline-offset: 2px;
134
+ }
135
+
136
+ .w5-placement__show-more-icon {
137
+ flex: none;
138
+ }
139
+
140
+ .w5-placement__eyebrow {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 10px;
144
+ margin-bottom: 20px;
145
+ }
146
+
147
+ .w5-placement__eyebrow-icon {
148
+ flex: none;
149
+ color: var(--w5-placement-accent, #ef1b28);
150
+ }
151
+
152
+ .w5-placement__eyebrow-label {
153
+ font-size: 14px;
154
+ line-height: 15px;
155
+ font-weight: 500;
156
+ color: var(--w5-placement-eyebrow-color, #000);
157
+ }
158
+
159
+ /* The user's question (conversation variant). */
160
+ .w5-placement__query {
161
+ display: inline-block;
162
+ margin: 0 0 20px;
163
+ padding: 7px 10px;
164
+ border-radius: 3px;
165
+ background: var(--w5-placement-query-bg, #e2dfd9);
166
+ font-size: 14px;
167
+ line-height: 15px;
168
+ color: var(--w5-placement-query-color, #000);
169
+ }
170
+
171
+ /* Placement highlight (conversation + informational): a gradient ring — fully
172
+ opaque accent at the top-left corner fading to transparent toward the
173
+ bottom-right. A solid `border-color` can't render a gradient, so the ring
174
+ is a masked pseudo-element: the gradient paints the whole box, and the
175
+ mask punches out the padding box so only the 2px border area remains.
176
+ The ring shows as soon as the placement starts to render, holds for the
177
+ whole streamed render (the `--streaming` modifier below); once the answer
178
+ settles the modifier drops and the animation plays — the gradient spins
179
+ two full counter-clockwise turns, then the ring stays in place. Replays
180
+ after each re-submit since the renderer remounts via the loader. */
181
+ .w5-placement--conversation,
182
+ .w5-placement--informational {
183
+ position: relative;
184
+ }
185
+
186
+ /* Registered so the gradient angle interpolates in the keyframes below.
187
+ Browsers without `@property` (Firefox < 128) apply custom-property
188
+ keyframes discretely — harmless here, since -585deg ≡ 135deg (mod 360°)
189
+ the jump is invisible and only the hold + fade-out remains. */
190
+ @property --w5-placement-highlight-angle {
191
+ syntax: '<angle>';
192
+ inherits: false;
193
+ initial-value: 135deg;
194
+ }
195
+
196
+ .w5-placement--conversation::before,
197
+ .w5-placement--informational::before {
198
+ content: '';
199
+ position: absolute;
200
+ /* Cover the reserved 2px transparent border (absolute positioning is
201
+ relative to the padding box, which excludes it). */
202
+ inset: -2px;
203
+ padding: 2px;
204
+ /* Outer radius of the card's border box: card radius + border width. */
205
+ border-radius: calc(var(--w5-placement-card-radius, 4px) + 2px);
206
+ background: linear-gradient(
207
+ var(--w5-placement-highlight-angle, 135deg),
208
+ var(--w5-placement-accent, #ef1b28),
209
+ transparent
210
+ );
211
+ /* Keep only the ring: padding-box ⊖ full box. */
212
+ -webkit-mask:
213
+ linear-gradient(#fff 0 0) content-box,
214
+ linear-gradient(#fff 0 0);
215
+ -webkit-mask-composite: xor;
216
+ mask:
217
+ linear-gradient(#fff 0 0) content-box,
218
+ linear-gradient(#fff 0 0);
219
+ mask-composite: exclude;
220
+ pointer-events: none;
221
+ /* ~9.4s default for the 2.5 counter-clockwise turns (same angular speed as
222
+ the original 2 turns in 7.5s); the ring then holds its final state
223
+ (`both` fill mode) and stays visible. */
224
+ animation: w5-placement-highlight
225
+ var(--w5-placement-highlight-duration, 9375ms) linear both;
226
+ }
227
+
228
+ /* Still streaming: hold the ring fully visible; the spin only starts
229
+ when this class is removed (answer settled) and the animation kicks in. */
230
+ .w5-placement--conversation.w5-placement--streaming::before,
231
+ .w5-placement--informational.w5-placement--streaming::before {
232
+ animation: none;
233
+ opacity: 1;
234
+ }
235
+
236
+ @keyframes w5-placement-highlight {
237
+ /* Two and a half counter-clockwise turns (135° → -765° ≡ 315°) at constant
238
+ speed, so the accent settles on the opposite side (bottom-right); the
239
+ ring stays fully visible throughout and after. */
240
+ 0% {
241
+ --w5-placement-highlight-angle: 135deg;
242
+ opacity: 1;
243
+ }
244
+ 100% {
245
+ --w5-placement-highlight-angle: -765deg;
246
+ opacity: 1;
247
+ }
248
+ }
249
+
250
+ @media (prefers-reduced-motion: reduce) {
251
+ /* No spin — show the ring statically in its settled position
252
+ (accent at the bottom-right). */
253
+ .w5-placement--conversation::before,
254
+ .w5-placement--informational::before,
255
+ .w5-placement--conversation.w5-placement--streaming::before,
256
+ .w5-placement--informational.w5-placement--streaming::before {
257
+ animation: none;
258
+ opacity: 1;
259
+ --w5-placement-highlight-angle: 315deg;
260
+ }
261
+
262
+ .w5-placement__body {
263
+ transition: none;
264
+ }
265
+
266
+ [data-w5-placement-renderer] {
267
+ animation: none;
268
+ }
269
+ }
@@ -0,0 +1,35 @@
1
+ /* "AI can make mistakes. Learn more" footer. Mounted by the host app
2
+ alongside the FeedbackBar; clients can override the whole component via
3
+ the DisclaimerComponent UI slot.
4
+
5
+ Typography defaults mirror `.web5-feedback-bar` so a single client-side
6
+ override (`--web5-feedback-bar-color`) re-skins both together. The
7
+ disclaimer-specific vars stay as escape hatches when divergence is
8
+ needed. */
9
+
10
+ .web5-disclaimer {
11
+ margin: 0;
12
+ font-size: var(
13
+ --web5-disclaimer-font-size,
14
+ var(--web5-feedback-bar-font-size, 13px)
15
+ );
16
+ font-weight: 400;
17
+ line-height: normal;
18
+ color: var(
19
+ --web5-disclaimer-color,
20
+ var(--web5-feedback-bar-color, var(--web5-feedback-label-color, #6b7280))
21
+ );
22
+ text-align: var(--web5-disclaimer-text-align, left);
23
+ }
24
+
25
+ .web5-disclaimer__link {
26
+ color: inherit;
27
+ text-decoration: underline;
28
+ text-decoration-skip-ink: none;
29
+ cursor: pointer;
30
+ }
31
+
32
+ .web5-disclaimer__link:hover,
33
+ .web5-disclaimer__link:focus-visible {
34
+ color: var(--web5-disclaimer-link-hover-color, inherit);
35
+ }
@@ -0,0 +1,267 @@
1
+ /* "Was this helpful?" bar + popover. Mounted by the host app inside the
2
+ sections container; clients can override the whole component via the
3
+ FeedbackBarComponent UI slot. */
4
+
5
+ .web5-feedback-bar {
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: flex-start;
9
+ gap: 12px;
10
+ width: fit-content;
11
+ /* Negative top margin pulls the bar up into the previous section's bottom
12
+ padding so it visually sits inside that section's color band (matches
13
+ Figma — bar at the bottom of the NextSteps purple gradient) without
14
+ changing its DOM position. Tune via --web5-feedback-margin-top. */
15
+ margin: var(--web5-feedback-margin-top, -4rem) 0
16
+ var(--web5-feedback-margin-bottom, 1.5rem) 0;
17
+ font-size: var(--web5-feedback-bar-font-size, 13px);
18
+ font-weight: 400;
19
+ line-height: normal;
20
+ /* Bar-scoped label color falls back to the shared muted color so popover
21
+ elements (title-hint, close button) stay readable on white when Circana
22
+ re-skins the bar text to light. */
23
+ color: var(
24
+ --web5-feedback-bar-color,
25
+ var(--web5-feedback-label-color, #6b7280)
26
+ );
27
+ }
28
+
29
+ .web5-feedback-label {
30
+ margin: 0;
31
+ }
32
+
33
+ .web5-feedback-thumb {
34
+ position: relative;
35
+ width: 32px;
36
+ height: 32px;
37
+ border-radius: 50%;
38
+ border: 0;
39
+ background: transparent;
40
+ color: var(--web5-feedback-thumb-color, #2b2b2b);
41
+ display: inline-flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ cursor: pointer;
45
+ transition:
46
+ background 0.12s,
47
+ color 0.12s;
48
+ }
49
+
50
+ /* Hover tooltip — pill above the thumb. Scoped to bar thumbs only; the
51
+ popover header thumbs are sentiment switchers and don't need tooltips. */
52
+ .web5-feedback-bar .web5-feedback-thumb[data-tooltip]::after {
53
+ content: attr(data-tooltip);
54
+ position: absolute;
55
+ bottom: calc(100% + 8px);
56
+ left: 50%;
57
+ transform: translateX(-50%);
58
+ padding: 8px 16px;
59
+ background: var(--web5-feedback-tooltip-bg, #1f1f1f);
60
+ color: var(--web5-feedback-tooltip-color, #fff);
61
+ border-radius: 999px;
62
+ font-size: 14px;
63
+ font-weight: 500;
64
+ line-height: 1;
65
+ white-space: nowrap;
66
+ opacity: 0;
67
+ pointer-events: none;
68
+ transition: opacity 0.12s;
69
+ z-index: 50;
70
+ }
71
+
72
+ .web5-feedback-bar .web5-feedback-thumb[data-tooltip]:hover::after,
73
+ .web5-feedback-bar .web5-feedback-thumb[data-tooltip]:focus-visible::after {
74
+ opacity: 1;
75
+ }
76
+
77
+ .web5-feedback-thumb:hover {
78
+ background: var(--web5-feedback-thumb-hover-bg, rgba(0, 0, 0, 0.06));
79
+ }
80
+
81
+ .web5-feedback-thumb--active {
82
+ background: var(--web5-feedback-accent, #1a1422);
83
+ color: #fff;
84
+ }
85
+
86
+ .web5-feedback-thumb--active:hover {
87
+ background: var(--web5-feedback-accent-hover, #2a2236);
88
+ }
89
+
90
+ /* Bar-only overrides — the bar may sit on a colored band (e.g. NextSteps'
91
+ purple gradient) and need light icons/hover, while the popover thumbs
92
+ always sit on white and stay dark. Selector path stops at the bar's own
93
+ thumbs-row to avoid leaking into the popover header thumbs. */
94
+ .web5-feedback-bar > .web5-feedback-thumbs-row > .web5-feedback-thumb {
95
+ color: var(
96
+ --web5-feedback-bar-thumb-color,
97
+ var(--web5-feedback-thumb-color, #2b2b2b)
98
+ );
99
+ }
100
+
101
+ .web5-feedback-bar > .web5-feedback-thumbs-row > .web5-feedback-thumb:hover {
102
+ background: var(
103
+ --web5-feedback-bar-thumb-hover-bg,
104
+ var(--web5-feedback-thumb-hover-bg, rgba(0, 0, 0, 0.06))
105
+ );
106
+ }
107
+
108
+ /* Dim the rest of the page while the popover is open. */
109
+ .web5-feedback-backdrop {
110
+ position: fixed;
111
+ inset: 0;
112
+ background: rgba(0, 0, 0, 0.4);
113
+ z-index: 65;
114
+ animation: web5-feedback-backdrop-in 0.18s ease both;
115
+ }
116
+
117
+ @keyframes web5-feedback-backdrop-in {
118
+ from {
119
+ opacity: 0;
120
+ }
121
+ }
122
+
123
+ /* Centered on the viewport rather than anchored to the bar — so the form
124
+ reads as a focused modal regardless of where the bar sits on the page. */
125
+ .web5-feedback-popover {
126
+ position: fixed;
127
+ top: 50%;
128
+ left: 50%;
129
+ transform: translate(-50%, -50%);
130
+ width: min(574px, 92vw);
131
+ z-index: 70;
132
+ background: #fff;
133
+ border: 1px solid rgba(0, 0, 0, 0.06);
134
+ border-radius: 14px;
135
+ padding: 30px;
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 25px;
139
+ box-shadow: 0 24px 48px -12px rgba(20, 14, 32, 0.25),
140
+ 0 8px 16px -8px rgba(20, 14, 32, 0.12);
141
+ animation: web5-feedback-pop-in 0.18s ease both;
142
+ }
143
+
144
+ @keyframes web5-feedback-pop-in {
145
+ from {
146
+ opacity: 0;
147
+ transform: translate(-50%, calc(-50% + 8px));
148
+ }
149
+ }
150
+
151
+ .web5-feedback-head {
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 15px;
155
+ }
156
+
157
+ .web5-feedback-thumbs-row {
158
+ display: flex;
159
+ gap: 0;
160
+ }
161
+
162
+ .web5-feedback-title {
163
+ margin: 0;
164
+ font-size: 15px;
165
+ font-weight: 600;
166
+ color: var(--web5-feedback-title-color, #2b2b2b);
167
+ }
168
+
169
+ .web5-feedback-title-hint {
170
+ font-weight: 400;
171
+ color: var(--web5-feedback-label-color, #6b7280);
172
+ }
173
+
174
+ .web5-feedback-close {
175
+ position: absolute;
176
+ top: 24px;
177
+ right: 24px;
178
+ width: 24px;
179
+ height: 24px;
180
+ border-radius: 50%;
181
+ border: 0;
182
+ background: transparent;
183
+ color: var(--web5-feedback-label-color, #6b7280);
184
+ cursor: pointer;
185
+ display: inline-flex;
186
+ align-items: center;
187
+ justify-content: center;
188
+ }
189
+
190
+ .web5-feedback-close:hover {
191
+ background: rgba(0, 0, 0, 0.06);
192
+ color: var(--web5-feedback-title-color, #2b2b2b);
193
+ }
194
+
195
+ .web5-feedback-chips {
196
+ display: flex;
197
+ gap: 13px;
198
+ flex-wrap: wrap;
199
+ }
200
+
201
+ .web5-feedback-chip {
202
+ display: inline-flex;
203
+ align-items: center;
204
+ padding: 8px 15px;
205
+ height: 30px;
206
+ border-radius: 999px;
207
+ border: 1px solid rgba(0, 0, 0, 0.12);
208
+ background: #fff;
209
+ color: var(--web5-feedback-chip-color, #2b2b2b);
210
+ font-size: 13px;
211
+ cursor: pointer;
212
+ transition:
213
+ background 0.12s,
214
+ border-color 0.12s,
215
+ color 0.12s;
216
+ }
217
+
218
+ /* Hover: neutral fill, default border — no accent tint. */
219
+ .web5-feedback-chip:hover {
220
+ background: var(--web5-feedback-chip-hover-bg, rgba(0, 0, 0, 0.04));
221
+ }
222
+
223
+ /* Selected: accent-colored border, subtle fill, default text color
224
+ (matches Figma — the chip label stays dark, only the outline changes). */
225
+ .web5-feedback-chip.web5-feedback-chip--selected,
226
+ .web5-feedback-chip.web5-feedback-chip--selected:hover {
227
+ background: var(--web5-feedback-chip-selected-bg, rgba(0, 0, 0, 0.04));
228
+ border-color: var(--web5-feedback-accent, #1a1422);
229
+ }
230
+
231
+ .web5-feedback-textarea {
232
+ width: 100%;
233
+ min-height: 100px;
234
+ padding: 15px;
235
+ border-radius: 10px;
236
+ border: 1px solid rgba(0, 0, 0, 0.12);
237
+ background: #fff;
238
+ font: 14px/1.4 inherit;
239
+ color: var(--web5-feedback-title-color, #2b2b2b);
240
+ resize: vertical;
241
+ }
242
+
243
+ .web5-feedback-textarea::placeholder {
244
+ color: var(--web5-feedback-placeholder-color, #7a838a);
245
+ }
246
+
247
+ .web5-feedback-submit {
248
+ width: 100%;
249
+ height: 38px;
250
+ border-radius: 999px;
251
+ border: 0;
252
+ background: var(--web5-feedback-accent, #1a1422);
253
+ color: #fff;
254
+ font-size: 14px;
255
+ font-weight: 500;
256
+ cursor: pointer;
257
+ transition: background 0.12s;
258
+ }
259
+
260
+ .web5-feedback-submit:hover {
261
+ background: var(--web5-feedback-accent-hover, #2a2236);
262
+ }
263
+
264
+ .web5-feedback-submit:disabled {
265
+ background: var(--web5-feedback-accent-disabled, rgba(26, 20, 34, 0.45));
266
+ cursor: not-allowed;
267
+ }
@@ -0,0 +1,101 @@
1
+ /* PlacementLoader — placement loading skeleton.
2
+ Rendered before the streamed response arrives. Client packages can re-skin
3
+ via the `--w5-placement-loader-*` tokens below without touching the markup.
4
+ Defaults mirror the canonical wireframe: a soft-bordered rounded container
5
+ with two stub text lines and a larger content block, all using a very
6
+ light accent-tinted skeleton wash.
7
+ The rendered placement's card chrome lives in
8
+ `../placement/PlacementResponseRenderer.css`. */
9
+
10
+ /* Wrapper around the loader. Drives the entrance animation: the slot starts
11
+ empty, the wrapper expands from a 0-height grid row to its natural row,
12
+ smoothly pushing surrounding host content down instead of popping in. The
13
+ margin-block on either side gives breathing room from neighbouring blocks
14
+ on the host page and animates in alongside the height for a coherent
15
+ reveal. Clients can re-skin the spacing via `--w5-placement-spacing`.
16
+ The loader→content swap itself is height-animated by the
17
+ PlacementSmoothHeight wrapper (see `../placement/PlacementResponseRenderer.css`). */
18
+ .w5-placement--loading {
19
+ display: grid;
20
+ grid-template-rows: 1fr;
21
+ margin-block: var(--w5-placement-spacing, 24px);
22
+ animation: w5-placement-loader-reveal 420ms ease-out both;
23
+ }
24
+
25
+ .w5-placement--loading > * {
26
+ min-height: 0;
27
+ overflow: hidden;
28
+ }
29
+
30
+ @keyframes w5-placement-loader-reveal {
31
+ from {
32
+ grid-template-rows: 0fr;
33
+ margin-block: 0;
34
+ opacity: 0;
35
+ }
36
+ to {
37
+ grid-template-rows: 1fr;
38
+ margin-block: var(--w5-placement-spacing, 24px);
39
+ opacity: 1;
40
+ }
41
+ }
42
+
43
+ .w5-placement-loader {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: var(--w5-placement-loader-gap, 24px);
47
+ padding: var(--w5-placement-loader-padding, 20px 24px);
48
+ border: var(--w5-placement-loader-border, 1px solid rgb(226 232 240));
49
+ border-radius: var(--w5-placement-loader-radius, 12px);
50
+ background: var(--w5-placement-loader-bg, #fff);
51
+ width: 100%;
52
+ box-sizing: border-box;
53
+ }
54
+
55
+ .w5-placement-loader__lines {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: var(--w5-placement-loader-line-gap, 10px);
59
+ }
60
+
61
+ .w5-placement-loader__line,
62
+ .w5-placement-loader__block {
63
+ display: block;
64
+ /* Very light tint of the placement accent (oklch(0.6 0.24 26.24)) —
65
+ same hue, lightness raised and chroma dropped for a skeleton wash. */
66
+ background: var(--w5-placement-loader-skeleton, oklch(0.95 0.04 26.24));
67
+ border-radius: var(--w5-placement-loader-skeleton-radius, 6px);
68
+ animation: w5-placement-loader-pulse 1.4s ease-in-out infinite;
69
+ }
70
+
71
+ .w5-placement-loader__line {
72
+ height: var(--w5-placement-loader-line-height, 14px);
73
+ width: 100%;
74
+ }
75
+
76
+ .w5-placement-loader__line--short {
77
+ width: var(--w5-placement-loader-line-short-width, 70%);
78
+ }
79
+
80
+ .w5-placement-loader__block {
81
+ height: var(--w5-placement-loader-block-height, 120px);
82
+ width: 100%;
83
+ }
84
+
85
+ @keyframes w5-placement-loader-pulse {
86
+ 0%,
87
+ 100% {
88
+ opacity: 1;
89
+ }
90
+ 50% {
91
+ opacity: 0.55;
92
+ }
93
+ }
94
+
95
+ @media (prefers-reduced-motion: reduce) {
96
+ .w5-placement--loading,
97
+ .w5-placement-loader__line,
98
+ .w5-placement-loader__block {
99
+ animation: none;
100
+ }
101
+ }