@xenterprises/nuxt-x-marketing 1.5.2 → 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 +25 -0
- package/app/components/X/Mark/Hero/index.vue +130 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,31 @@ 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
|
+
|
|
46
|
+
## [1.6.0] - 2026-10-12
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **`XMarkHero` `scrimWidth` prop** (opt-in, `overlay="scrim-left"` only). CSS length such as
|
|
51
|
+
`"40%"` or `"36rem"`. On md+ the scrim is 90% black up to `scrimWidth`, then fades out over a
|
|
52
|
+
further ~34% of the width so the photo on the right stays vivid. Below md it darkens a 90% band
|
|
53
|
+
from 18% to 82% of the hero height (35% at the top and bottom edges) instead of a flat 90%.
|
|
54
|
+
Without `scrimWidth`, `scrim-left` and every other default render byte-identical to 1.5.2.
|
|
55
|
+
Keep the whole text column inside the 90% zone: white title over 90% black on a pure-white
|
|
56
|
+
pixel is 17.8:1, white/80 subtitle 11.6:1; outside the zone contrast drops fast.
|
|
57
|
+
|
|
33
58
|
## [1.5.2] - 2026-10-12
|
|
34
59
|
|
|
35
60
|
### Fixed
|
|
@@ -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,23 @@
|
|
|
40
60
|
|
|
41
61
|
<!-- Overlay -->
|
|
42
62
|
<div
|
|
43
|
-
v-if="overlay !== 'none'"
|
|
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
|
+
/>
|
|
74
|
+
<!-- scrim-left + scrimWidth (opt-in, 1.6.0) -->
|
|
75
|
+
<div
|
|
76
|
+
v-else-if="softScrim"
|
|
77
|
+
class="absolute inset-0 x-hero-scrim-w"
|
|
78
|
+
:style="{ '--x-scrim-w': scrimWidth }"
|
|
79
|
+
/>
|
|
47
80
|
</div>
|
|
48
81
|
|
|
49
82
|
<!-- Content -->
|
|
@@ -121,11 +154,19 @@
|
|
|
121
154
|
|
|
122
155
|
<script setup>
|
|
123
156
|
const props = defineProps({
|
|
124
|
-
/**
|
|
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
|
+
*/
|
|
125
161
|
img: {
|
|
126
162
|
type: Object,
|
|
127
163
|
default: null,
|
|
128
|
-
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")))),
|
|
129
170
|
},
|
|
130
171
|
/** Background video object { src: string, poster?: string } */
|
|
131
172
|
video: {
|
|
@@ -196,6 +237,29 @@ const props = defineProps({
|
|
|
196
237
|
type: String,
|
|
197
238
|
default: "",
|
|
198
239
|
},
|
|
240
|
+
/**
|
|
241
|
+
* scrim-left only, opt-in. CSS length for the 90% black zone, e.g. "40%" or "36rem".
|
|
242
|
+
* When set: desktop (md+) keeps 90% up to this width, then a long soft fade so the
|
|
243
|
+
* photo on the right stays vivid; mobile darkens a band behind the text instead of
|
|
244
|
+
* a flat 90% over the whole hero. Keep the text column inside the 90% zone.
|
|
245
|
+
* Unset (default): scrim-left renders exactly as in 1.5.x.
|
|
246
|
+
*/
|
|
247
|
+
scrimWidth: {
|
|
248
|
+
type: String,
|
|
249
|
+
default: "",
|
|
250
|
+
validator: (v) => v === "" || /^\d+(\.\d+)?(%|px|rem|em|vw)$/.test(v),
|
|
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
|
+
},
|
|
199
263
|
/** Add top padding for non-transparent nav */
|
|
200
264
|
includeNavPadding: {
|
|
201
265
|
type: Boolean,
|
|
@@ -221,6 +285,14 @@ const overlayClass = computed(() => {
|
|
|
221
285
|
return overlays[props.overlay] || "";
|
|
222
286
|
});
|
|
223
287
|
|
|
288
|
+
// scrim-left with scrimWidth renders a separate overlay element; without it the
|
|
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
|
+
|
|
294
|
+
const softScrim = computed(() => props.overlay === "scrim-left" && !!props.scrimWidth);
|
|
295
|
+
|
|
224
296
|
const heightClass = computed(() => {
|
|
225
297
|
const heights = {
|
|
226
298
|
full: "min-h-[calc(100svh-var(--ui-header-height))]",
|
|
@@ -248,7 +320,8 @@ const verticalAlignClasses = computed(() => {
|
|
|
248
320
|
center: "items-center",
|
|
249
321
|
bottom: "items-end pb-24",
|
|
250
322
|
};
|
|
251
|
-
|
|
323
|
+
const base = aligns[props.verticalAlign];
|
|
324
|
+
return mobileBottom.value ? `${base} max-md:items-end max-md:pb-10` : base;
|
|
252
325
|
});
|
|
253
326
|
|
|
254
327
|
const contentAlignmentClasses = computed(() => {
|
|
@@ -260,3 +333,56 @@ const contentAlignmentClasses = computed(() => {
|
|
|
260
333
|
return aligns[props.align];
|
|
261
334
|
});
|
|
262
335
|
</script>
|
|
336
|
+
|
|
337
|
+
<style>
|
|
338
|
+
/* scrim-left + scrimWidth (1.6.0). Mobile: 90% band behind the text column,
|
|
339
|
+
* lighter at the top and bottom edges of the hero. */
|
|
340
|
+
.x-hero-scrim-w {
|
|
341
|
+
background-image: linear-gradient(
|
|
342
|
+
to bottom,
|
|
343
|
+
rgb(0 0 0 / 0.35) 0%,
|
|
344
|
+
rgb(0 0 0 / 0.9) 18%,
|
|
345
|
+
rgb(0 0 0 / 0.9) 82%,
|
|
346
|
+
rgb(0 0 0 / 0.35) 100%
|
|
347
|
+
);
|
|
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
|
+
}
|
|
373
|
+
/* md+: 90% up to --x-scrim-w, then a long eased fade to clear. */
|
|
374
|
+
@media (min-width: 768px) {
|
|
375
|
+
.x-hero-scrim-w {
|
|
376
|
+
background-image: linear-gradient(
|
|
377
|
+
to right,
|
|
378
|
+
rgb(0 0 0 / 0.9) 0,
|
|
379
|
+
rgb(0 0 0 / 0.9) var(--x-scrim-w),
|
|
380
|
+
rgb(0 0 0 / 0.72) calc(var(--x-scrim-w) + 4%),
|
|
381
|
+
rgb(0 0 0 / 0.45) calc(var(--x-scrim-w) + 10%),
|
|
382
|
+
rgb(0 0 0 / 0.2) calc(var(--x-scrim-w) + 18%),
|
|
383
|
+
rgb(0 0 0 / 0.06) calc(var(--x-scrim-w) + 26%),
|
|
384
|
+
rgb(0 0 0 / 0) calc(var(--x-scrim-w) + 34%)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
</style>
|