srcdev-nuxt-components 9.1.28 → 9.1.30
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/.claude/settings.json +7 -2
- package/.claude/settings.local.json +2 -1
- package/.claude/skills/components/banner-video.md +178 -0
- package/.claude/skills/components/grid-stack.md +133 -0
- package/.claude/skills/components/scroll-reveal-frame.md +136 -0
- package/.claude/skills/components/scroll-reveal-image.md +161 -0
- package/.claude/skills/index.md +9 -1
- package/.claude/skills/new-app-scaffold.md +301 -0
- package/.claude/skills/setup-postinstall.md +45 -0
- package/.claude/skills/theming-override-default.md +6 -0
- package/.claude/skills/theming-partial-override.md +131 -0
- package/.claude/skills/vue-video-autoplay.md +74 -0
- package/README.md +30 -5
- package/app/components/01.atoms/banner-video/BannerVideo.vue +207 -0
- package/app/components/01.atoms/banner-video/stories/BannerVideo.stories.ts +180 -0
- package/app/components/01.atoms/banner-video/tests/BannerVideo.spec.ts +350 -0
- package/app/components/01.atoms/banner-video/tests/__snapshots__/BannerVideo.spec.ts.snap +13 -0
- package/app/components/01.atoms/grid-stack/GridStack.vue +34 -0
- package/app/components/01.atoms/grid-stack/stories/GridStack.stories.ts +162 -0
- package/app/components/01.atoms/grid-stack/tests/GridStack.spec.ts +66 -0
- package/app/components/01.atoms/scroll-reveal-frame/ScrollRevealFrame.vue +125 -0
- package/app/components/01.atoms/scroll-reveal-frame/stories/ScrollRevealFrame.stories.ts +166 -0
- package/app/components/01.atoms/scroll-reveal-frame/tests/ScrollRevealFrame.spec.ts +148 -0
- package/app/components/01.atoms/scroll-reveal-frame/tests/__snapshots__/ScrollRevealFrame.spec.ts.snap +13 -0
- package/app/components/01.atoms/scroll-reveal-image/ScrollRevealImage.vue +96 -0
- package/app/components/01.atoms/scroll-reveal-image/stories/ScrollRevealImage.stories.ts +181 -0
- package/app/components/01.atoms/scroll-reveal-image/tests/ScrollRevealImage.spec.ts +235 -0
- package/app/components/01.atoms/scroll-reveal-image/tests/__snapshots__/ScrollRevealImage.spec.ts.snap +13 -0
- package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +1 -0
- package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +1 -0
- package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +1 -0
- package/app/components/03.organisms/services/services-grids/stories/ServicesSectionGrid.stories.ts +1 -0
- package/app/components/03.organisms/services/services-grids/tests/ServicesCardGrid.spec.ts +1 -0
- package/app/components/03.organisms/services/services-grids/tests/ServicesSectionGrid.spec.ts +1 -0
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesSectionGrid.spec.ts.snap +3 -3
- package/app/components/03.organisms/services/services-section/stories/ServicesSection.stories.ts +1 -0
- package/app/components/03.organisms/services/services-section/tests/ServicesSection.spec.ts +1 -0
- package/app/components/03.organisms/services/services-section/tests/__snapshots__/ServicesSection.spec.ts.snap +1 -1
- package/app/layouts/default.vue +2 -0
- package/app/pages/banner-video.vue +238 -0
- package/app/pages/grid-stack.vue +252 -0
- package/app/pages/index.vue +5 -0
- package/app/pages/ui/scroll-reveal-image.vue +483 -0
- package/app/types/types.services.ts +1 -0
- package/package.json +1 -1
- package/app/assets/styles/extends-layer/srcdev-components/components/_display-prompt-core.css +0 -94
- package/app/assets/styles/extends-layer/srcdev-components/components/_display-toast.css +0 -5
- package/app/assets/styles/extends-layer/srcdev-components/components/_expanding-panel.css +0 -37
- package/app/assets/styles/extends-layer/srcdev-components/components/index.css +0 -3
- package/app/assets/styles/extends-layer/srcdev-components/index.css +0 -1
- package/app/assets/styles/extends-layer/srcdev-forms/components/_form-fieldset.css +0 -38
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-button.css +0 -67
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-checkbox-radio-core.css +0 -87
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-checkbox-radio-options-button.css +0 -74
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-checkbox-radio-with-label.css +0 -14
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-description.css +0 -13
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-error.css +0 -54
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-label.css +0 -21
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-select.css +0 -143
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-text.css +0 -190
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-textarea.css +0 -96
- package/app/assets/styles/extends-layer/srcdev-forms/components/_input-toggle-switch.css +0 -68
- package/app/assets/styles/extends-layer/srcdev-forms/components/index.css +0 -12
- package/app/assets/styles/extends-layer/srcdev-forms/index.css +0 -2
- package/app/assets/styles/extends-layer/srcdev-forms/setup/_generic.css +0 -21
- package/app/assets/styles/extends-layer/srcdev-forms/setup/index.css +0 -3
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/_default.css +0 -13
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/_large.css +0 -12
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/_medium.css +0 -12
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/_small.css +0 -12
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/_x-small.css +0 -11
- package/app/assets/styles/extends-layer/srcdev-forms/setup/sizes/index.css +0 -23
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_default.css +0 -62
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_error.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_ghost.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_info.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_primary.css +0 -66
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_secondary.css +0 -64
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_success.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_tertiary.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/_warning.css +0 -63
- package/app/assets/styles/extends-layer/srcdev-forms/setup/themes/index.css +0 -7
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="tag"
|
|
4
|
+
class="banner-video"
|
|
5
|
+
:class="elementClasses"
|
|
6
|
+
:data-depth="depth"
|
|
7
|
+
:style="{
|
|
8
|
+
'--_aspect-ratio': aspectRatio,
|
|
9
|
+
'--_align-self': verticalPosition,
|
|
10
|
+
'--_justify-self': horizontalPosition,
|
|
11
|
+
}"
|
|
12
|
+
>
|
|
13
|
+
<video
|
|
14
|
+
:key="src"
|
|
15
|
+
ref="videoEl"
|
|
16
|
+
class="video"
|
|
17
|
+
autoplay
|
|
18
|
+
muted
|
|
19
|
+
loop
|
|
20
|
+
playsinline
|
|
21
|
+
preload="auto"
|
|
22
|
+
:poster="poster"
|
|
23
|
+
:style="{ objectFit: props.objectFit }"
|
|
24
|
+
@loadeddata="handleLoadedData"
|
|
25
|
+
@canplay="handleCanPlay"
|
|
26
|
+
>
|
|
27
|
+
<source :src="src" type="video/mp4" />
|
|
28
|
+
</video>
|
|
29
|
+
<NuxtImg
|
|
30
|
+
class="fallback"
|
|
31
|
+
:src="poster"
|
|
32
|
+
:alt="alt"
|
|
33
|
+
:width="imgWidth"
|
|
34
|
+
:height="imgHeight"
|
|
35
|
+
loading="eager"
|
|
36
|
+
decoding="async"
|
|
37
|
+
:style="{ objectFit: props.objectFit, objectPosition: imgObjectPosition }"
|
|
38
|
+
/>
|
|
39
|
+
</component>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
interface Props {
|
|
44
|
+
/** HTML element to render as the root. Defaults to `section` for landmark semantics. */
|
|
45
|
+
tag?: "section" | "div" | "header" | "main" | "article";
|
|
46
|
+
/** Path to the video source file (mp4). */
|
|
47
|
+
src: string;
|
|
48
|
+
/** Path to the fallback/poster image. Used as video poster and as the visible fallback
|
|
49
|
+
* when the video cannot play or when the user prefers reduced motion. */
|
|
50
|
+
poster: string;
|
|
51
|
+
alt?: string;
|
|
52
|
+
/** Intrinsic width of the poster image — required for NuxtImg optimisation. */
|
|
53
|
+
imgWidth?: number;
|
|
54
|
+
/** Intrinsic height of the poster image — required for NuxtImg optimisation. */
|
|
55
|
+
imgHeight?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Depth tier controlling the responsive max-height via a `clamp()` scale.
|
|
58
|
+
* Each tier maps to a `--theme-banner-video-max-height-{depth}` CSS token that
|
|
59
|
+
* consuming pages can override. Defaults to `"md"`.
|
|
60
|
+
*/
|
|
61
|
+
depth?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
62
|
+
/**
|
|
63
|
+
* CSS aspect-ratio of the banner container (e.g. `"16/9"`, `"21/9"`, `"4/3"`).
|
|
64
|
+
* Provides the intrinsic height that `max-height` caps at larger viewport widths.
|
|
65
|
+
*/
|
|
66
|
+
aspectRatio?: string;
|
|
67
|
+
/** How the video and fallback image fill the banner frame. Defaults to `"cover"`. */
|
|
68
|
+
objectFit?: "cover" | "contain" | "fill" | "none" | "scale-down";
|
|
69
|
+
/** Vertical crop position within the banner. Maps to `align-self` on the video and `object-position` on the fallback image. Defaults to `"center"`. */
|
|
70
|
+
verticalPosition?: "start" | "center" | "end";
|
|
71
|
+
/** Horizontal crop position within the banner. Maps to `object-position` on the fallback image. Defaults to `"center"`. */
|
|
72
|
+
horizontalPosition?: "start" | "center" | "end";
|
|
73
|
+
styleClassPassthrough?: string | string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
77
|
+
tag: "div",
|
|
78
|
+
alt: "",
|
|
79
|
+
imgWidth: 1920,
|
|
80
|
+
imgHeight: 1080,
|
|
81
|
+
depth: "md",
|
|
82
|
+
aspectRatio: "21/9",
|
|
83
|
+
objectFit: "cover",
|
|
84
|
+
verticalPosition: "center",
|
|
85
|
+
horizontalPosition: "center",
|
|
86
|
+
styleClassPassthrough: () => [],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const positionKeywordMap = {
|
|
90
|
+
vertical: { start: "top", center: "center", end: "bottom" },
|
|
91
|
+
horizontal: { start: "left", center: "center", end: "right" },
|
|
92
|
+
} as const;
|
|
93
|
+
|
|
94
|
+
const imgObjectPosition = computed(
|
|
95
|
+
() =>
|
|
96
|
+
`${positionKeywordMap.horizontal[props.horizontalPosition]} ${positionKeywordMap.vertical[props.verticalPosition]}`
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
100
|
+
|
|
101
|
+
watch(
|
|
102
|
+
() => props.styleClassPassthrough,
|
|
103
|
+
() => resetElementClasses(props.styleClassPassthrough)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const videoEl = shallowRef<HTMLVideoElement | null>(null);
|
|
107
|
+
|
|
108
|
+
const tryPlay = async () => {
|
|
109
|
+
const v = videoEl.value;
|
|
110
|
+
if (!v) return;
|
|
111
|
+
try {
|
|
112
|
+
// Ensure muted stays true — required for programmatic autoplay in all browsers
|
|
113
|
+
v.muted = true;
|
|
114
|
+
await v.play();
|
|
115
|
+
} catch {
|
|
116
|
+
// Autoplay blocked or interrupted — fallback image will remain visible
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const kickOffLoad = async () => {
|
|
121
|
+
await nextTick();
|
|
122
|
+
const v = videoEl.value;
|
|
123
|
+
if (!v) return;
|
|
124
|
+
// Force the media element to (re)read its source child and begin fetching
|
|
125
|
+
v.load();
|
|
126
|
+
// Attempt immediate play; loadeddata/canplay handlers will retry once data arrives
|
|
127
|
+
void tryPlay();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const handleLoadedData = () => {
|
|
131
|
+
void tryPlay();
|
|
132
|
+
};
|
|
133
|
+
const handleCanPlay = () => {
|
|
134
|
+
void tryPlay();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Runs on mount AND whenever src changes (covers route-change re-use edge cases)
|
|
138
|
+
watch(
|
|
139
|
+
() => props.src,
|
|
140
|
+
() => {
|
|
141
|
+
void kickOffLoad();
|
|
142
|
+
},
|
|
143
|
+
{ immediate: true, flush: "post" }
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Extra safety: when the component becomes active again (e.g. returning via keep-alive)
|
|
147
|
+
onActivated(() => {
|
|
148
|
+
void kickOffLoad();
|
|
149
|
+
});
|
|
150
|
+
</script>
|
|
151
|
+
|
|
152
|
+
<style lang="css">
|
|
153
|
+
@layer components {
|
|
154
|
+
.banner-video {
|
|
155
|
+
--_max-height: var(--theme-banner-video-max-height, clamp(28rem, 38vw, 56rem));
|
|
156
|
+
|
|
157
|
+
display: grid;
|
|
158
|
+
grid-template-areas: "media";
|
|
159
|
+
aspect-ratio: var(--_aspect-ratio, 16/9);
|
|
160
|
+
max-height: var(--_max-height);
|
|
161
|
+
width: 100%;
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
|
|
164
|
+
&[data-depth="xs"] {
|
|
165
|
+
--_max-height: var(--theme-banner-video-max-height-xs, clamp(12rem, 15vw, 24rem));
|
|
166
|
+
}
|
|
167
|
+
&[data-depth="sm"] {
|
|
168
|
+
--_max-height: var(--theme-banner-video-max-height-sm, clamp(18rem, 22vw, 36rem));
|
|
169
|
+
}
|
|
170
|
+
&[data-depth="md"] {
|
|
171
|
+
--_max-height: var(--theme-banner-video-max-height-md, clamp(28rem, 38vw, 56rem));
|
|
172
|
+
}
|
|
173
|
+
&[data-depth="lg"] {
|
|
174
|
+
--_max-height: var(--theme-banner-video-max-height-lg, clamp(40rem, 52vw, 72rem));
|
|
175
|
+
}
|
|
176
|
+
&[data-depth="xl"] {
|
|
177
|
+
--_max-height: var(--theme-banner-video-max-height-xl, clamp(52rem, 65vw, 90rem));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.video {
|
|
181
|
+
grid-area: media;
|
|
182
|
+
display: block;
|
|
183
|
+
width: 100%;
|
|
184
|
+
height: auto;
|
|
185
|
+
min-height: 100%;
|
|
186
|
+
align-self: var(--_align-self, center);
|
|
187
|
+
justify-self: var(--_justify-self, center);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.fallback {
|
|
191
|
+
grid-area: media;
|
|
192
|
+
display: none;
|
|
193
|
+
width: 100%;
|
|
194
|
+
height: 100%;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@media (prefers-reduced-motion: reduce) {
|
|
198
|
+
.video {
|
|
199
|
+
display: none;
|
|
200
|
+
}
|
|
201
|
+
.fallback {
|
|
202
|
+
display: block;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
</style>
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import BannerVideo from "../BannerVideo.vue";
|
|
2
|
+
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof BannerVideo> = {
|
|
5
|
+
title: "Atoms/Media/BannerVideo",
|
|
6
|
+
component: BannerVideo,
|
|
7
|
+
argTypes: {
|
|
8
|
+
src: {
|
|
9
|
+
control: "text",
|
|
10
|
+
description: "Path to the mp4 video source file",
|
|
11
|
+
table: { category: "Media" },
|
|
12
|
+
},
|
|
13
|
+
poster: {
|
|
14
|
+
control: "text",
|
|
15
|
+
description: "Path to the fallback/poster image — shown before video loads, on error, and when prefers-reduced-motion is active",
|
|
16
|
+
table: { category: "Media" },
|
|
17
|
+
},
|
|
18
|
+
alt: {
|
|
19
|
+
control: "text",
|
|
20
|
+
description: "Alt text for the fallback image",
|
|
21
|
+
table: { category: "Media" },
|
|
22
|
+
},
|
|
23
|
+
imgWidth: {
|
|
24
|
+
control: { type: "number" },
|
|
25
|
+
description: "Intrinsic width of the poster image — required for NuxtImg optimisation",
|
|
26
|
+
table: { category: "Media" },
|
|
27
|
+
},
|
|
28
|
+
imgHeight: {
|
|
29
|
+
control: { type: "number" },
|
|
30
|
+
description: "Intrinsic height of the poster image — required for NuxtImg optimisation",
|
|
31
|
+
table: { category: "Media" },
|
|
32
|
+
},
|
|
33
|
+
tag: {
|
|
34
|
+
control: "select",
|
|
35
|
+
options: ["section", "div", "header", "main", "article"],
|
|
36
|
+
description: "HTML element rendered as the root",
|
|
37
|
+
table: { category: "Markup" },
|
|
38
|
+
},
|
|
39
|
+
depth: {
|
|
40
|
+
control: "select",
|
|
41
|
+
options: ["xs", "sm", "md", "lg", "xl"],
|
|
42
|
+
description: "Responsive max-height tier. Each maps to a clamp() scale; override via --theme-banner-video-max-height-{depth}",
|
|
43
|
+
table: { category: "Layout" },
|
|
44
|
+
},
|
|
45
|
+
aspectRatio: {
|
|
46
|
+
control: "text",
|
|
47
|
+
description: "CSS aspect-ratio of the banner container (e.g. '16/9', '21/9', '4/3')",
|
|
48
|
+
table: { category: "Layout" },
|
|
49
|
+
},
|
|
50
|
+
objectFit: {
|
|
51
|
+
control: "select",
|
|
52
|
+
options: ["cover", "contain", "fill", "none", "scale-down"],
|
|
53
|
+
description: "How the video and fallback image fill the banner frame",
|
|
54
|
+
table: { category: "Appearance" },
|
|
55
|
+
},
|
|
56
|
+
verticalPosition: {
|
|
57
|
+
control: "select",
|
|
58
|
+
options: ["start", "center", "end"],
|
|
59
|
+
description: "Vertical crop position — start (top), center, or end (bottom). Maps to align-self on the video and object-position on the fallback image",
|
|
60
|
+
table: { category: "Appearance" },
|
|
61
|
+
},
|
|
62
|
+
horizontalPosition: {
|
|
63
|
+
control: "select",
|
|
64
|
+
options: ["start", "center", "end"],
|
|
65
|
+
description: "Horizontal crop position — start (left), center, or end (right). Maps to object-position on the fallback image",
|
|
66
|
+
table: { category: "Appearance" },
|
|
67
|
+
},
|
|
68
|
+
styleClassPassthrough: {
|
|
69
|
+
table: { disable: true },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
parameters: {
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
component:
|
|
76
|
+
"A full-width banner that plays a muted, looping mp4 video. The poster image is shown before the video loads, when the video fails to play, and whenever the user has `prefers-reduced-motion: reduce` set. The banner is sized via `aspect-ratio` with a responsive `max-height` driven by the `depth` tier (`xs` → `xl`). Each tier uses a `clamp()` scale and exposes a `--theme-banner-video-max-height-{depth}` token for consuming pages to override. `objectFit` controls how media fills the frame. `verticalPosition` and `horizontalPosition` control the crop focal point.",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default meta;
|
|
83
|
+
type Story = StoryObj<typeof BannerVideo>;
|
|
84
|
+
|
|
85
|
+
export const Default: Story = {
|
|
86
|
+
args: {
|
|
87
|
+
src: "/images/banners/video/lake-banner.mp4",
|
|
88
|
+
poster: "/images/banners/video/lake-banner.jpg",
|
|
89
|
+
alt: "A serene lake landscape",
|
|
90
|
+
imgWidth: 1920,
|
|
91
|
+
imgHeight: 1080,
|
|
92
|
+
tag: "section",
|
|
93
|
+
depth: "md",
|
|
94
|
+
aspectRatio: "21/9",
|
|
95
|
+
objectFit: "cover",
|
|
96
|
+
verticalPosition: "center",
|
|
97
|
+
horizontalPosition: "center",
|
|
98
|
+
},
|
|
99
|
+
parameters: {
|
|
100
|
+
docs: {
|
|
101
|
+
description: {
|
|
102
|
+
story: "Default props. The lake video plays muted and looped at a 21/9 aspect ratio with the md depth tier (clamp 28rem → 56rem).",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const Compact: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
...Default.args,
|
|
111
|
+
depth: "xs",
|
|
112
|
+
verticalPosition: "end",
|
|
113
|
+
},
|
|
114
|
+
parameters: {
|
|
115
|
+
docs: {
|
|
116
|
+
description: {
|
|
117
|
+
story: "xs depth tier (clamp 12rem → 24rem) — a thin strip banner. verticalPosition: 'end' crops to the bottom of the frame.",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const Shallow: Story = {
|
|
124
|
+
args: {
|
|
125
|
+
...Default.args,
|
|
126
|
+
depth: "sm",
|
|
127
|
+
},
|
|
128
|
+
parameters: {
|
|
129
|
+
docs: {
|
|
130
|
+
description: {
|
|
131
|
+
story: "sm depth tier (clamp 18rem → 36rem) — a shorter banner suitable for secondary sections.",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const Deep: Story = {
|
|
138
|
+
args: {
|
|
139
|
+
...Default.args,
|
|
140
|
+
depth: "lg",
|
|
141
|
+
aspectRatio: "21/9",
|
|
142
|
+
},
|
|
143
|
+
parameters: {
|
|
144
|
+
docs: {
|
|
145
|
+
description: {
|
|
146
|
+
story: "lg depth tier (clamp 40rem → 72rem) — a tall cinematic banner with more vertical presence on large screens.",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const Hero: Story = {
|
|
153
|
+
args: {
|
|
154
|
+
...Default.args,
|
|
155
|
+
depth: "xl",
|
|
156
|
+
aspectRatio: "16/9",
|
|
157
|
+
},
|
|
158
|
+
parameters: {
|
|
159
|
+
docs: {
|
|
160
|
+
description: {
|
|
161
|
+
story: "xl depth tier (clamp 52rem → 90rem) — near full-screen hero. Pair with a 16/9 ratio to maximise coverage.",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const Standard169: Story = {
|
|
168
|
+
args: {
|
|
169
|
+
...Default.args,
|
|
170
|
+
aspectRatio: "16/9",
|
|
171
|
+
depth: "md",
|
|
172
|
+
},
|
|
173
|
+
parameters: {
|
|
174
|
+
docs: {
|
|
175
|
+
description: {
|
|
176
|
+
story: "Standard 16/9 aspect ratio with md depth — matches a typical video's native ratio.",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|