@xenterprises/nuxt-x-marketing 1.6.0 → 1.7.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/CHANGELOG.md +13 -0
- package/app/components/X/Mark/Hero/index.vue +79 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
30
30
|
section and its sample product data were dropped with them. Every other section of the
|
|
31
31
|
demo page is unchanged.
|
|
32
32
|
|
|
33
|
+
## [1.7.0] - 2026-10-12
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **`XMarkHero` `scrimMobile="bottom"`** (opt-in, `overlay="scrim-left"` only). Below md the scrim
|
|
38
|
+
becomes a bottom gradient (90% black over the bottom 55%, fading to clear by 70%) and the text is
|
|
39
|
+
bottom-aligned (`max-md:items-end max-md:pb-10`), so the top of the photo stays clear. md+ is
|
|
40
|
+
unchanged (with `scrimWidth`, the 1.6.0 soft scrim; without, the scrim-left gradient). Keep the
|
|
41
|
+
title and subtitle inside the bottom 55% of the hero on mobile.
|
|
42
|
+
- **`XMarkHero` `img.sources`**: `[{ srcset, type, media?, sizes? }]` renders
|
|
43
|
+
`<picture><source>...<img>` (e.g. AVIF, then WebP, with `img.src` as the fallback).
|
|
44
|
+
Without `sources` the output is byte-identical to 1.6.0.
|
|
45
|
+
|
|
33
46
|
## [1.6.0] - 2026-10-12
|
|
34
47
|
|
|
35
48
|
### Added
|
|
@@ -26,6 +26,26 @@
|
|
|
26
26
|
data-parallax-speed="0.3"
|
|
27
27
|
/>
|
|
28
28
|
|
|
29
|
+
<!-- Image Background with format sources (opt-in, 1.7.0) -->
|
|
30
|
+
<picture v-else-if="img?.src && img.sources?.length">
|
|
31
|
+
<source
|
|
32
|
+
v-for="(source, i) in img.sources"
|
|
33
|
+
:key="i"
|
|
34
|
+
:srcset="source.srcset"
|
|
35
|
+
:type="source.type"
|
|
36
|
+
:media="source.media"
|
|
37
|
+
:sizes="source.sizes"
|
|
38
|
+
>
|
|
39
|
+
<img
|
|
40
|
+
:src="img.src"
|
|
41
|
+
:alt="img.alt || ''"
|
|
42
|
+
fetchpriority="high"
|
|
43
|
+
class="absolute inset-0 w-full h-full object-cover xParallax"
|
|
44
|
+
:style="imagePositionStyle"
|
|
45
|
+
data-parallax-speed="0.3"
|
|
46
|
+
>
|
|
47
|
+
</picture>
|
|
48
|
+
|
|
29
49
|
<!-- Image Background -->
|
|
30
50
|
<img
|
|
31
51
|
v-else-if="img?.src"
|
|
@@ -40,10 +60,17 @@
|
|
|
40
60
|
|
|
41
61
|
<!-- Overlay -->
|
|
42
62
|
<div
|
|
43
|
-
v-if="overlay !== 'none' && !softScrim"
|
|
63
|
+
v-if="overlay !== 'none' && !softScrim && !mobileBottom"
|
|
44
64
|
class="absolute inset-0"
|
|
45
65
|
:class="overlayClass"
|
|
46
66
|
/>
|
|
67
|
+
<!-- scrim-left + scrimMobile="bottom" (opt-in, 1.7.0) -->
|
|
68
|
+
<div
|
|
69
|
+
v-else-if="mobileBottom"
|
|
70
|
+
class="absolute inset-0 x-hero-scrim-mb"
|
|
71
|
+
:class="scrimWidth ? 'x-hero-scrim-w' : 'x-hero-scrim-55'"
|
|
72
|
+
v-bind="scrimWidth ? { style: { '--x-scrim-w': scrimWidth } } : {}"
|
|
73
|
+
/>
|
|
47
74
|
<!-- scrim-left + scrimWidth (opt-in, 1.6.0) -->
|
|
48
75
|
<div
|
|
49
76
|
v-else-if="softScrim"
|
|
@@ -127,11 +154,19 @@
|
|
|
127
154
|
|
|
128
155
|
<script setup>
|
|
129
156
|
const props = defineProps({
|
|
130
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* Background image object { src: string, alt?: string, sources?: Array<{ srcset, type, media?, sizes? }> }.
|
|
159
|
+
* With sources (1.7.0) it renders <picture><source>...<img>, e.g. AVIF then WebP with src as fallback.
|
|
160
|
+
*/
|
|
131
161
|
img: {
|
|
132
162
|
type: Object,
|
|
133
163
|
default: null,
|
|
134
|
-
validator: (v) =>
|
|
164
|
+
validator: (v) =>
|
|
165
|
+
!v ||
|
|
166
|
+
(typeof v.src === "string" &&
|
|
167
|
+
(v.sources === undefined ||
|
|
168
|
+
(Array.isArray(v.sources) &&
|
|
169
|
+
v.sources.every((x) => x && typeof x.srcset === "string")))),
|
|
135
170
|
},
|
|
136
171
|
/** Background video object { src: string, poster?: string } */
|
|
137
172
|
video: {
|
|
@@ -214,6 +249,17 @@ const props = defineProps({
|
|
|
214
249
|
default: "",
|
|
215
250
|
validator: (v) => v === "" || /^\d+(\.\d+)?(%|px|rem|em|vw)$/.test(v),
|
|
216
251
|
},
|
|
252
|
+
/**
|
|
253
|
+
* scrim-left only, opt-in (1.7.0). "bottom": below md the scrim becomes a bottom
|
|
254
|
+
* gradient behind the text (90% over the bottom 55%, clear from 70% up) and the text
|
|
255
|
+
* is bottom-aligned, so the top of the photo stays clear. md+ is unchanged.
|
|
256
|
+
* Empty (default) keeps 1.6.0 output.
|
|
257
|
+
*/
|
|
258
|
+
scrimMobile: {
|
|
259
|
+
type: String,
|
|
260
|
+
default: "",
|
|
261
|
+
validator: (v) => ["", "bottom"].includes(v),
|
|
262
|
+
},
|
|
217
263
|
/** Add top padding for non-transparent nav */
|
|
218
264
|
includeNavPadding: {
|
|
219
265
|
type: Boolean,
|
|
@@ -241,6 +287,10 @@ const overlayClass = computed(() => {
|
|
|
241
287
|
|
|
242
288
|
// scrim-left with scrimWidth renders a separate overlay element; without it the
|
|
243
289
|
// 1.5.x overlay element renders unchanged (no extra class or style attribute).
|
|
290
|
+
const mobileBottom = computed(
|
|
291
|
+
() => props.overlay === "scrim-left" && props.scrimMobile === "bottom",
|
|
292
|
+
);
|
|
293
|
+
|
|
244
294
|
const softScrim = computed(() => props.overlay === "scrim-left" && !!props.scrimWidth);
|
|
245
295
|
|
|
246
296
|
const heightClass = computed(() => {
|
|
@@ -270,7 +320,8 @@ const verticalAlignClasses = computed(() => {
|
|
|
270
320
|
center: "items-center",
|
|
271
321
|
bottom: "items-end pb-24",
|
|
272
322
|
};
|
|
273
|
-
|
|
323
|
+
const base = aligns[props.verticalAlign];
|
|
324
|
+
return mobileBottom.value ? `${base} max-md:items-end max-md:pb-10` : base;
|
|
274
325
|
});
|
|
275
326
|
|
|
276
327
|
const contentAlignmentClasses = computed(() => {
|
|
@@ -295,6 +346,30 @@ const contentAlignmentClasses = computed(() => {
|
|
|
295
346
|
rgb(0 0 0 / 0.35) 100%
|
|
296
347
|
);
|
|
297
348
|
}
|
|
349
|
+
/* scrimMobile="bottom" (1.7.0), below md: bottom gradient behind bottom-aligned
|
|
350
|
+
* text, top of the photo clear. md+ falls through to the rules below. */
|
|
351
|
+
@media (max-width: 767.98px) {
|
|
352
|
+
.x-hero-scrim-mb {
|
|
353
|
+
background-image: linear-gradient(
|
|
354
|
+
to top,
|
|
355
|
+
rgb(0 0 0 / 0.9) 0%,
|
|
356
|
+
rgb(0 0 0 / 0.9) 55%,
|
|
357
|
+
rgb(0 0 0 / 0.5) 62%,
|
|
358
|
+
rgb(0 0 0 / 0) 70%
|
|
359
|
+
) !important;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/* scrimMobile without scrimWidth: md+ matches the 1.5.x scrim-left gradient. */
|
|
363
|
+
@media (min-width: 768px) {
|
|
364
|
+
.x-hero-scrim-55 {
|
|
365
|
+
background-image: linear-gradient(
|
|
366
|
+
to right,
|
|
367
|
+
rgb(0 0 0 / 0.9) 0%,
|
|
368
|
+
rgb(0 0 0 / 0.9) 55%,
|
|
369
|
+
rgb(0 0 0 / 0.3) 100%
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
298
373
|
/* md+: 90% up to --x-scrim-w, then a long eased fade to clear. */
|
|
299
374
|
@media (min-width: 768px) {
|
|
300
375
|
.x-hero-scrim-w {
|