@revenuecat/purchases-ui-js 4.0.0 → 4.2.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.
Files changed (42) hide show
  1. package/dist/components/carousel/Carousel.stories.svelte +29 -0
  2. package/dist/components/carousel/Carousel.svelte +74 -8
  3. package/dist/components/paywall/BackgroundVideoSurface.svelte +162 -0
  4. package/dist/components/paywall/BackgroundVideoSurface.svelte.d.ts +12 -0
  5. package/dist/components/paywall/Paywall.stories.svelte +134 -10
  6. package/dist/components/paywall/Paywall.svelte +36 -6
  7. package/dist/components/paywall/Sheet.svelte +66 -3
  8. package/dist/components/paywall/ViewportBackdrop.svelte +92 -0
  9. package/dist/components/{layout/Main → paywall}/ViewportBackdrop.svelte.d.ts +1 -1
  10. package/dist/components/paywall/fixtures/sheet-video-stacking-paywall.d.ts +6 -0
  11. package/dist/components/paywall/fixtures/sheet-video-stacking-paywall.js +42 -0
  12. package/dist/components/stack/Stack.stories.svelte +74 -0
  13. package/dist/components/stack/Stack.svelte +58 -3
  14. package/dist/components/tabs/Tabs.stories.svelte +27 -0
  15. package/dist/components/tabs/Tabs.svelte +69 -4
  16. package/dist/components/tabs/tabs-video-stacking-story-args.d.ts +3 -0
  17. package/dist/components/tabs/tabs-video-stacking-story-args.js +131 -0
  18. package/dist/components/video/Video.svelte +185 -20
  19. package/dist/components/workflows/Screen.stories.svelte +3 -6
  20. package/dist/index.d.ts +0 -1
  21. package/dist/index.js +0 -1
  22. package/dist/stories/video-background-story-fixture.d.ts +3 -0
  23. package/dist/stories/video-background-story-fixture.js +36 -0
  24. package/dist/types/background.d.ts +9 -1
  25. package/dist/ui/molecules/button.svelte +2 -2
  26. package/dist/utils/background-utils.d.ts +30 -1
  27. package/dist/utils/background-utils.js +53 -5
  28. package/dist/utils/document-background.d.ts +3 -0
  29. package/dist/utils/document-background.js +59 -0
  30. package/dist/utils/match-media-compat.d.ts +4 -0
  31. package/dist/utils/match-media-compat.js +11 -0
  32. package/dist/utils/video-background-host.d.ts +31 -0
  33. package/dist/utils/video-background-host.js +25 -0
  34. package/dist/utils/video-inline-playback.d.ts +25 -0
  35. package/dist/utils/video-inline-playback.js +78 -0
  36. package/dist/web-components/index.css +1 -1
  37. package/package.json +2 -2
  38. package/dist/components/layout/Main/Main.stories.svelte +0 -263
  39. package/dist/components/layout/Main/Main.stories.svelte.d.ts +0 -19
  40. package/dist/components/layout/Main/Main.svelte +0 -117
  41. package/dist/components/layout/Main/Main.svelte.d.ts +0 -11
  42. package/dist/components/layout/Main/ViewportBackdrop.svelte +0 -65
@@ -2,8 +2,16 @@
2
2
  import { getColorModeContext } from "../../stores/color-mode";
3
3
  import { getPaywallContext } from "../../stores/paywall";
4
4
  import { getSelectedStateContext } from "../../stores/selected";
5
+ import BackgroundVideoSurface from "./BackgroundVideoSurface.svelte";
5
6
  import type { SheetProps } from "../../types/components/sheet";
6
- import { mapBackground } from "../../utils/background-utils";
7
+ import {
8
+ mapBackground,
9
+ resolveBackgroundVideoForSurface,
10
+ } from "../../utils/background-utils";
11
+ import {
12
+ rcVideoBackgroundHostClass,
13
+ videoBackgroundHostStyles,
14
+ } from "../../utils/video-background-host";
7
15
  import { css, mapSize } from "../../utils/base-utils";
8
16
  import { getActiveStateProps } from "../../utils/style-utils";
9
17
  import { onMount } from "svelte";
@@ -23,6 +31,14 @@
23
31
  const getColorMode = getColorModeContext();
24
32
  const colorMode = $derived(getColorMode());
25
33
 
34
+ const hasVideoBg = $derived(background?.type === "video");
35
+ const videoSurface = $derived.by(() => {
36
+ if (!hasVideoBg) return null;
37
+ const bg = background;
38
+ if (!bg || bg.type !== "video") return null;
39
+ return resolveBackgroundVideoForSurface(colorMode, bg);
40
+ });
41
+
26
42
  const backdropStyle = $derived(
27
43
  css({
28
44
  background: background_blur ? "#0000003f" : "",
@@ -32,6 +48,10 @@
32
48
  const sheetStyle = $derived(
33
49
  css({
34
50
  height: mapSize(size.height),
51
+ ...videoBackgroundHostStyles({
52
+ hasVideoBg,
53
+ clipOverflow: true,
54
+ }),
35
55
  ...mapBackground(colorMode, null, background),
36
56
  }),
37
57
  );
@@ -75,7 +95,14 @@
75
95
  }
76
96
  };
77
97
 
78
- let sheetClass = $derived(`sheet ${visible ? "visible" : ""}`);
98
+ let sheetClass = $derived(
99
+ [
100
+ `sheet ${visible ? "visible" : ""}`,
101
+ hasVideoBg ? rcVideoBackgroundHostClass("sheet") : "",
102
+ ]
103
+ .filter(Boolean)
104
+ .join(" "),
105
+ );
79
106
  </script>
80
107
 
81
108
  <div
@@ -95,7 +122,21 @@
95
122
  aria-modal="true"
96
123
  {ontransitionend}
97
124
  >
98
- <Stack {...stack} />
125
+ {#if videoSurface}
126
+ <BackgroundVideoSurface
127
+ url={videoSurface.url}
128
+ urlLowRes={videoSurface.url_low_res}
129
+ objectFit={videoSurface.fit}
130
+ objectPosition={videoSurface.position}
131
+ poster={videoSurface.posterWebp}
132
+ mute={videoSurface.mute}
133
+ loop={videoSurface.loop}
134
+ />
135
+ {/if}
136
+ <Stack
137
+ {...stack}
138
+ class={hasVideoBg ? "rc-sheet-stack-above-video" : undefined}
139
+ />
99
140
  </div>
100
141
  </div>
101
142
 
@@ -119,8 +160,30 @@
119
160
  transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
120
161
  transform: translateY(100%);
121
162
 
163
+ &::before {
164
+ content: "";
165
+ position: absolute;
166
+ inset: 0;
167
+ background: var(--overlay);
168
+ pointer-events: none;
169
+ }
170
+
122
171
  &.visible {
123
172
  transform: translateY(0);
124
173
  }
125
174
  }
175
+
176
+ .sheet.rc-sheet-video-bg::before {
177
+ z-index: 2;
178
+ }
179
+
180
+ /* Match stack video stacking: tint above video (::before z-index). */
181
+ .sheet.rc-sheet-video-bg > :global(.rc-bg-video) {
182
+ z-index: 1;
183
+ }
184
+
185
+ .sheet.rc-sheet-video-bg > :global(.rc-sheet-stack-above-video) {
186
+ position: relative;
187
+ z-index: 3;
188
+ }
126
189
  </style>
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { PaywallRootBackgroundModel } from "../../utils/background-utils";
3
+ import BackgroundVideoSurface from "./BackgroundVideoSurface.svelte";
4
+
5
+ // iOS WebKit only reliably propagates background-color (not background-image)
6
+ // through to the safe-area canvas. A position:fixed element that explicitly
7
+ // fills the viewport — including safe areas — is the only consistent paint
8
+ // surface for gradients and images on iOS Safari.
9
+ const { model }: { model: PaywallRootBackgroundModel } = $props();
10
+
11
+ const backdropStyle = $derived.by((): string => {
12
+ if (
13
+ model.kind === "style" &&
14
+ model.style.background?.includes("gradient")
15
+ ) {
16
+ return Object.entries(model.style)
17
+ .map(([k, v]) => `${k}:${v}`)
18
+ .join(";");
19
+ }
20
+ if (model.kind === "image") {
21
+ return [
22
+ `background-image:url("${model.src}")`,
23
+ `background-size:${model.fit}`,
24
+ `background-position:${model.position}`,
25
+ `background-repeat:no-repeat`,
26
+ ].join(";");
27
+ }
28
+ return "";
29
+ });
30
+
31
+ const overlayStyle = $derived.by((): string | null => {
32
+ if (model.kind !== "image" && model.kind !== "video") {
33
+ return null;
34
+ }
35
+ if (!model.overlay || model.overlay === "none") {
36
+ return null;
37
+ }
38
+ return `background:${model.overlay}`;
39
+ });
40
+
41
+ const shouldRenderBackdrop = $derived(
42
+ backdropStyle !== "" || model.kind === "video",
43
+ );
44
+ </script>
45
+
46
+ {#if shouldRenderBackdrop}
47
+ <div
48
+ class="viewport-backdrop"
49
+ style={backdropStyle !== "" ? backdropStyle : undefined}
50
+ >
51
+ {#if model.kind === "video"}
52
+ <BackgroundVideoSurface
53
+ url={model.url}
54
+ urlLowRes={model.url_low_res}
55
+ objectFit={model.fit}
56
+ objectPosition={model.position}
57
+ poster={model.posterWebp}
58
+ mute={model.mute}
59
+ loop={model.loop}
60
+ />
61
+ {/if}
62
+ {#if overlayStyle}
63
+ <div
64
+ class="viewport-backdrop-overlay"
65
+ class:z-over-video-bg={model.kind === "video"}
66
+ style={overlayStyle}
67
+ ></div>
68
+ {/if}
69
+ </div>
70
+ {/if}
71
+
72
+ <style>
73
+ .viewport-backdrop {
74
+ position: fixed;
75
+ inset: 0;
76
+ z-index: 0;
77
+ pointer-events: none;
78
+ overflow: hidden;
79
+ isolation: isolate;
80
+ }
81
+
82
+ .viewport-backdrop-overlay {
83
+ position: absolute;
84
+ inset: 0;
85
+ pointer-events: none;
86
+ z-index: 1;
87
+ }
88
+
89
+ .viewport-backdrop-overlay.z-over-video-bg {
90
+ z-index: 2;
91
+ }
92
+ </style>
@@ -1,4 +1,4 @@
1
- import type { PaywallRootBackgroundModel } from "../../../utils/background-utils";
1
+ import type { PaywallRootBackgroundModel } from "../../utils/background-utils";
2
2
  type $$ComponentProps = {
3
3
  model: PaywallRootBackgroundModel;
4
4
  };
@@ -0,0 +1,6 @@
1
+ import type { PaywallData } from "../../../types/paywall";
2
+ /**
3
+ * Minimal variant of {@link SHEET_PAYWALL} with a sheet-level video background for
4
+ * Storybook z-order regression checks (`rc-sheet-video-bg`).
5
+ */
6
+ export declare const SHEET_PAYWALL_VIDEO_STACKING: PaywallData;
@@ -0,0 +1,42 @@
1
+ import { STORY_BACKGROUND_VIDEO_FILL } from "../../../stories/video-background-story-fixture";
2
+ import { SHEET_PAYWALL } from "./sheet-paywall";
3
+ /**
4
+ * Minimal variant of {@link SHEET_PAYWALL} with a sheet-level video background for
5
+ * Storybook z-order regression checks (`rc-sheet-video-bg`).
6
+ */
7
+ export const SHEET_PAYWALL_VIDEO_STACKING = (() => {
8
+ const data = structuredClone(SHEET_PAYWALL);
9
+ data.id = "sheet_paywall_video_stacking";
10
+ data.components_localizations ??= {};
11
+ data.components_localizations.en_US ??= {};
12
+ Object.assign(data.components_localizations.en_US, {
13
+ "5GWqi3MXpt": "Content above tinted video",
14
+ E6CK5Xd1cE: "Open sheet · video backdrop",
15
+ });
16
+ const first = data.components_config.base.stack.components[0];
17
+ if (!first || first.type !== "button") {
18
+ return data;
19
+ }
20
+ const { action } = first;
21
+ if (!action ||
22
+ action.type !== "navigate_to" ||
23
+ action.destination !== "sheet") {
24
+ return data;
25
+ }
26
+ const { sheet } = action;
27
+ if (!sheet) {
28
+ return data;
29
+ }
30
+ sheet.background = STORY_BACKGROUND_VIDEO_FILL;
31
+ sheet.stack.background = null;
32
+ const headline = sheet.stack.components[0];
33
+ if (headline?.type === "text") {
34
+ headline.horizontal_alignment = "center";
35
+ headline.color = {
36
+ light: { type: "hex", value: "#ffffff" },
37
+ };
38
+ headline.font_size = "heading_m";
39
+ headline.font_weight = "semibold";
40
+ }
41
+ return data;
42
+ })();
@@ -6,6 +6,7 @@
6
6
  import { localizationDecorator } from "../../stories/localization-decorator";
7
7
  import type { StackProps } from "../../types/components/stack";
8
8
  import type { Component } from "../../types/component";
9
+ import { STORY_BACKGROUND_VIDEO_FILL } from "../../stories/video-background-story-fixture";
9
10
  import { DEFAULT_TEXT_COLOR } from "../../utils/constants";
10
11
  import { defineMeta } from "@storybook/addon-svelte-csf";
11
12
 
@@ -153,6 +154,79 @@
153
154
  }}
154
155
  />
155
156
 
157
+ <Story
158
+ name="Surface — video background"
159
+ parameters={{
160
+ chromatic: { disableSnapshot: true },
161
+ docs: {
162
+ description: {
163
+ story:
164
+ "**Z-order:** `BackgroundVideoSurface` (`z-index: 1`), `::before` tint (`z-index: 2`), siblings (`z-index: 3`). Content and controls should remain sharp above the tint; motion is disabled in Chromatic.",
165
+ },
166
+ },
167
+ }}
168
+ args={{
169
+ size: {
170
+ width: { type: "fixed", value: 380 },
171
+ height: { type: "fixed", value: 260 },
172
+ },
173
+ shape: {
174
+ type: "rectangle",
175
+ corners: {
176
+ top_leading: 20,
177
+ top_trailing: 20,
178
+ bottom_leading: 20,
179
+ bottom_trailing: 20,
180
+ },
181
+ },
182
+ background: STORY_BACKGROUND_VIDEO_FILL,
183
+ components: [
184
+ {
185
+ type: "text",
186
+ size: {
187
+ width: { type: "fit" },
188
+ height: { type: "fit" },
189
+ },
190
+ horizontal_alignment: "center",
191
+ name: "Item 1",
192
+ id: "vbg-1",
193
+ text_lid: "id1",
194
+ color: {
195
+ light: { type: "hex", value: "#ffffff" },
196
+ },
197
+ font_name: null,
198
+ font_size: "heading_s",
199
+ font_weight: "bold",
200
+ background_color: {
201
+ dark: { type: "alias", value: "transparent" },
202
+ light: { type: "alias", value: "transparent" },
203
+ },
204
+ },
205
+ {
206
+ type: "text",
207
+ size: {
208
+ width: { type: "fill" },
209
+ height: { type: "fit" },
210
+ },
211
+ horizontal_alignment: "center",
212
+ name: "Item 2",
213
+ id: "vbg-2",
214
+ text_lid: "id2",
215
+ color: {
216
+ light: { type: "hex", value: "#e2e8f0" },
217
+ },
218
+ font_name: null,
219
+ font_size: "body_m",
220
+ font_weight: "regular",
221
+ background_color: {
222
+ dark: { type: "alias", value: "transparent" },
223
+ light: { type: "alias", value: "transparent" },
224
+ },
225
+ },
226
+ ] as unknown as TextNodeProps[],
227
+ }}
228
+ />
229
+
156
230
  <Story
157
231
  name="Z Layer"
158
232
  args={{
@@ -6,8 +6,16 @@
6
6
  import { getPaywallContext } from "../../stores/paywall";
7
7
  import { getSelectedStateContext } from "../../stores/selected";
8
8
  import { getOptionalVariablesContext } from "../../stores/variables";
9
+ import BackgroundVideoSurface from "../paywall/BackgroundVideoSurface.svelte";
9
10
  import type { StackProps } from "../../types/components/stack";
10
- import { mapBackground } from "../../utils/background-utils";
11
+ import {
12
+ mapBackground,
13
+ resolveBackgroundVideoForSurface,
14
+ } from "../../utils/background-utils";
15
+ import {
16
+ rcVideoBackgroundHostClass,
17
+ videoBackgroundHostStyles,
18
+ } from "../../utils/video-background-host";
11
19
  import {
12
20
  css,
13
21
  mapBorder,
@@ -66,6 +74,15 @@
66
74
  const getColorMode = getColorModeContext();
67
75
  const colorMode = $derived(getColorMode());
68
76
 
77
+ const hasVideoBg = $derived(background?.type === "video");
78
+ const videoSurface = $derived.by(() => {
79
+ if (!hasVideoBg) return null;
80
+ const bg = background;
81
+ if (!bg || bg.type !== "video") return null;
82
+ return resolveBackgroundVideoForSurface(colorMode, bg);
83
+ });
84
+ const borderRadiusCss = $derived(mapBorderRadius(shape));
85
+
69
86
  const stackStyle = $derived(
70
87
  css({
71
88
  display: "flex",
@@ -78,8 +95,12 @@
78
95
  padding: mapSpacing(padding),
79
96
  ...mapBackground(colorMode, background_color, background),
80
97
  ...mapBorder(colorMode, border),
81
- "border-radius": mapBorderRadius(shape),
98
+ "border-radius": borderRadiusCss,
82
99
  "box-shadow": mapShadow(colorMode, shadow),
100
+ ...videoBackgroundHostStyles({
101
+ hasVideoBg,
102
+ clipOverflow: borderRadiusCss !== "0",
103
+ }),
83
104
  // Read from props proxy so $derived re-evaluates when parent updates style
84
105
  ...props.style,
85
106
  }),
@@ -124,9 +145,28 @@
124
145
  role={onclick !== undefined ? "button" : undefined}
125
146
  {onclick}
126
147
  style={stackStyle}
127
- class={["stack", "rc-gradient-border", classProp].filter(Boolean).join(" ")}
148
+ class={[
149
+ "stack",
150
+ "rc-gradient-border",
151
+ classProp,
152
+ hasVideoBg ? rcVideoBackgroundHostClass("stack") : "",
153
+ ]
154
+ .filter(Boolean)
155
+ .join(" ")}
128
156
  data-testid={testId}
129
157
  >
158
+ {#if videoSurface}
159
+ <BackgroundVideoSurface
160
+ url={videoSurface.url}
161
+ urlLowRes={videoSurface.url_low_res}
162
+ objectFit={videoSurface.fit}
163
+ objectPosition={videoSurface.position}
164
+ poster={videoSurface.posterWebp}
165
+ mute={videoSurface.mute}
166
+ loop={videoSurface.loop}
167
+ />
168
+ {/if}
169
+
130
170
  {#if badge}
131
171
  <div style={badgeStyle}>
132
172
  <Node nodeData={badge.stack} />
@@ -173,4 +213,19 @@
173
213
  background: var(--overlay);
174
214
  }
175
215
  }
216
+
217
+ .stack.rc-stack-video-bg::before {
218
+ z-index: 2;
219
+ pointer-events: none;
220
+ }
221
+
222
+ .stack.rc-stack-video-bg > :global(.rc-bg-video) {
223
+ z-index: 1;
224
+ }
225
+
226
+ /* Keep flex children interactive and above tinted overlay (::before z-index). */
227
+ .stack.rc-stack-video-bg > :global(:not(.rc-bg-video)) {
228
+ position: relative;
229
+ z-index: 3;
230
+ }
176
231
  </style>
@@ -5,6 +5,7 @@
5
5
  import { variablesDecorator } from "../../stories/variables-decorator";
6
6
  import { defineMeta } from "@storybook/addon-svelte-csf";
7
7
  import { VARIABLES } from "../paywall/fixtures/variables";
8
+ import { TABS_VIDEO_STACKING_STORY_ARGS } from "./tabs-video-stacking-story-args";
8
9
 
9
10
  const defaultLocale = "en_US";
10
11
 
@@ -1428,3 +1429,29 @@
1428
1429
  type: "tabs",
1429
1430
  }}
1430
1431
  />
1432
+
1433
+ <Story
1434
+ name="Surface — video background (stacking / z-order)"
1435
+ parameters={{
1436
+ chromatic: { disableSnapshot: true },
1437
+ docs: {
1438
+ description: {
1439
+ story:
1440
+ "**Z-order:** video layer → tinted `::before` → tab content clip. Rounded corners should clip the backdrop; Chromatic skips motion snapshots.",
1441
+ },
1442
+ },
1443
+ }}
1444
+ decorators={[
1445
+ componentDecorator(),
1446
+ localizationDecorator({
1447
+ defaultLocale,
1448
+ localizations: {
1449
+ [defaultLocale]: {
1450
+ "t-a-body": "Tab A · content above video",
1451
+ "t-b-body": "Tab B · content above video",
1452
+ },
1453
+ },
1454
+ }),
1455
+ ]}
1456
+ args={TABS_VIDEO_STACKING_STORY_ARGS}
1457
+ />
@@ -4,8 +4,16 @@
4
4
  import { getPaywallContext } from "../../stores/paywall";
5
5
  import { getSelectedStateContext } from "../../stores/selected";
6
6
  import { getOptionalVariablesContext } from "../../stores/variables";
7
+ import BackgroundVideoSurface from "../paywall/BackgroundVideoSurface.svelte";
7
8
  import type { TabsProps } from "../../types/components/tabs";
8
- import { mapBackground } from "../../utils/background-utils";
9
+ import {
10
+ mapBackground,
11
+ resolveBackgroundVideoForSurface,
12
+ } from "../../utils/background-utils";
13
+ import {
14
+ rcVideoBackgroundHostClass,
15
+ videoBackgroundHostStyles,
16
+ } from "../../utils/video-background-host";
9
17
  import {
10
18
  css,
11
19
  mapBorder,
@@ -40,15 +48,30 @@
40
48
  const getColorMode = getColorModeContext();
41
49
  const colorMode = $derived(getColorMode());
42
50
 
51
+ const hasVideoBg = $derived(background?.type === "video");
52
+ const videoSurface = $derived.by(() => {
53
+ if (!hasVideoBg) return null;
54
+ const bg = background;
55
+ if (!bg || bg.type !== "video") return null;
56
+ return resolveBackgroundVideoForSurface(colorMode, bg);
57
+ });
58
+
59
+ const tabsBorderRadiusCss = $derived(mapBorderRadius(shape));
60
+
43
61
  const styles = $derived(
44
62
  css({
63
+ position: "relative",
45
64
  width: mapSize(size.width),
46
65
  height: mapSize(size.height),
47
66
  margin: mapSpacing(margin),
48
67
  padding: mapSpacing(padding),
68
+ ...videoBackgroundHostStyles({
69
+ hasVideoBg,
70
+ clipOverflow: tabsBorderRadiusCss !== "0",
71
+ }),
49
72
  ...mapBackground(colorMode, null, background),
50
73
  ...mapBorder(colorMode, border),
51
- "border-radius": mapBorderRadius(shape),
74
+ "border-radius": tabsBorderRadiusCss,
52
75
  "box-shadow": mapShadow(colorMode, shadow),
53
76
  }),
54
77
  );
@@ -117,9 +140,51 @@
117
140
  </script>
118
141
 
119
142
  {#if isVisible}
120
- <div class="rc-gradient-border" style={styles}>
143
+ <div
144
+ class={[
145
+ "rc-gradient-border",
146
+ hasVideoBg ? rcVideoBackgroundHostClass("tabs") : "",
147
+ ]
148
+ .filter(Boolean)
149
+ .join(" ")}
150
+ style={styles}
151
+ >
152
+ {#if videoSurface}
153
+ <BackgroundVideoSurface
154
+ url={videoSurface.url}
155
+ urlLowRes={videoSurface.url_low_res}
156
+ objectFit={videoSurface.fit}
157
+ objectPosition={videoSurface.position}
158
+ poster={videoSurface.posterWebp}
159
+ mute={videoSurface.mute}
160
+ loop={videoSurface.loop}
161
+ />
162
+ {/if}
121
163
  {#if $tab !== undefined}
122
- <Stack {...$tab.stack} />
164
+ <Stack
165
+ {...$tab.stack}
166
+ class={hasVideoBg ? "rc-tabs-stack-front" : undefined}
167
+ />
123
168
  {/if}
124
169
  </div>
125
170
  {/if}
171
+
172
+ <style>
173
+ .rc-tabs-video-bg::before {
174
+ content: "";
175
+ position: absolute;
176
+ inset: 0;
177
+ background: var(--overlay);
178
+ pointer-events: none;
179
+ z-index: 2;
180
+ }
181
+
182
+ .rc-tabs-video-bg > :global(.rc-bg-video) {
183
+ z-index: 1;
184
+ }
185
+
186
+ .rc-tabs-video-bg > :global(.rc-tabs-stack-front) {
187
+ position: relative;
188
+ z-index: 3;
189
+ }
190
+ </style>
@@ -0,0 +1,3 @@
1
+ import type { TabsProps } from "../../types/components/tabs";
2
+ /** Minimal Tabs tree for Storybook layering checks (`rc-tabs-video-bg`). */
3
+ export declare const TABS_VIDEO_STACKING_STORY_ARGS: TabsProps;