@xenterprises/nuxt-x-marketing 0.0.23 → 1.0.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/.claude/settings.local.json +22 -0
- package/.playground/app.config.ts +44 -45
- package/.playground/app.vue +263 -0
- package/.playground/components/AppFooter.vue +103 -0
- package/.playground/components/AppHeader.vue +41 -0
- package/.playground/components/CodeExample.vue +70 -0
- package/.playground/components/ComponentPlayground.vue +117 -0
- package/.playground/components/DocsHeader.vue +19 -0
- package/.playground/layouts/default.vue +13 -3
- package/.playground/layouts/docs-layout.vue +161 -0
- package/.playground/layouts/docs.vue +11 -0
- package/.playground/package-lock.json +6 -0
- package/.playground/pages/blog/author.vue +145 -0
- package/.playground/pages/blog/card.vue +149 -0
- package/.playground/pages/blog/cta.vue +131 -0
- package/.playground/pages/blog/detail.vue +379 -0
- package/.playground/pages/blog/list.vue +261 -0
- package/.playground/pages/blog/navigation.vue +228 -0
- package/.playground/pages/blog/sidebar.vue +279 -0
- package/.playground/pages/config.vue +289 -0
- package/.playground/pages/content/comparison.vue +253 -0
- package/.playground/pages/content/cta.vue +245 -0
- package/.playground/pages/content/features.vue +217 -0
- package/.playground/pages/content/newsletter-section.vue +260 -0
- package/.playground/pages/content/newsletter.vue +200 -0
- package/.playground/pages/content/pricing.vue +335 -0
- package/.playground/pages/content/testimonials.vue +188 -0
- package/.playground/pages/hero.vue +257 -0
- package/.playground/pages/index.vue +379 -2
- package/.playground/pages/layout/footer.vue +210 -0
- package/.playground/pages/layout/legal-footer.vue +121 -0
- package/.playground/pages/layout/navbar.vue +285 -0
- package/.playground/pages/layout/section.vue +266 -0
- package/.playground/pages/layout/x-legal.vue +199 -0
- package/.playground/pages/modals/feature-modal.vue +320 -0
- package/.playground/pages/modals/image-lightbox.vue +277 -0
- package/.playground/pages/modals/video-modal.vue +222 -0
- package/.playground/pages/notifications/announcement-bar.vue +164 -0
- package/.playground/pages/notifications/cookie-banner.vue +210 -0
- package/.playground/pages/notifications/cookie-toast.vue +143 -0
- package/.playground/pages/notifications/gdpr.vue +276 -0
- package/.playground/pages/notifications/social-proof-toast.vue +255 -0
- package/.playground/pages/ui/badge.vue +161 -0
- package/.playground/pages/ui/bg-pattern.vue +177 -0
- package/.playground/pages/ui/glass-card.vue +179 -0
- package/.playground/pages/ui/glow-divider.vue +179 -0
- package/.playground/pages/ui/promo-card.vue +259 -0
- package/.playground/pages/ui/section-stitch.vue +411 -0
- package/.playground/pages/utilities/back-to-top.vue +153 -0
- package/.playground/pages/utilities/popup-chat.vue +224 -0
- package/README.md +799 -164
- package/app/app.config.ts +88 -2
- package/app/app.vue +106 -11
- package/app/assets/css/x-marketing.css +668 -0
- package/app/components/X/Footer/XLegal/index.vue +63 -26
- package/app/components/X/Footer/index.vue +89 -89
- package/app/components/X/Header/App.vue +63 -0
- package/app/components/X/Mark/Blog/Author.vue +170 -0
- package/app/components/X/Mark/Blog/CTANewsletter.vue +197 -0
- package/app/components/X/Mark/Blog/Card.vue +117 -0
- package/app/components/X/Mark/Blog/Detail.vue +240 -0
- package/app/components/X/Mark/Blog/List.vue +156 -0
- package/app/components/X/Mark/Blog/Navigation.vue +100 -0
- package/app/components/X/Mark/Blog/Sidebar.vue +160 -0
- package/app/components/X/Mark/Button/BackToTop.vue +99 -0
- package/app/components/X/Mark/CTA/index.vue +294 -0
- package/app/components/X/Mark/Card/Glass.vue +64 -0
- package/app/components/X/Mark/Card/Promo.vue +164 -0
- package/app/components/X/Mark/Divider/Glow.vue +113 -0
- package/app/components/X/Mark/Features/index.vue +194 -0
- package/app/components/X/Mark/Hero/index.vue +217 -0
- package/app/components/X/Mark/Layout/AnnouncementBar.vue +139 -0
- package/app/components/X/Mark/Layout/Footer.vue +160 -0
- package/app/components/X/Mark/Layout/FooterLegal.vue +42 -0
- package/app/components/X/Mark/Layout/Navbar.vue +267 -0
- package/app/components/X/Mark/Modal/Chat.vue +151 -0
- package/app/components/X/Mark/Modal/Feature.vue +197 -0
- package/app/components/X/Mark/Modal/Image.vue +226 -0
- package/app/components/X/Mark/Modal/Video.vue +203 -0
- package/app/components/X/Mark/Modal/index.vue +133 -0
- package/app/components/X/Mark/Newsletter/Form.vue +174 -0
- package/app/components/X/Mark/Pattern/Bg.vue +86 -0
- package/app/components/X/Mark/Pricing/Comparison.vue +126 -0
- package/app/components/X/Mark/Pricing/Plans.vue +181 -0
- package/app/components/X/Mark/Privacy/CookieBanner.vue +112 -0
- package/app/components/X/Mark/Privacy/CookieToast.vue +43 -0
- package/app/components/X/Mark/Privacy/GDPR.vue +245 -0
- package/app/components/X/Mark/Section/Newsletter.vue +183 -0
- package/app/components/X/Mark/Section/Stitch.vue +156 -0
- package/app/components/X/Mark/Section/index.vue +151 -0
- package/app/components/X/Mark/SocialProof/Badge.vue +102 -0
- package/app/components/X/Mark/SocialProof/Testimonials.vue +326 -0
- package/app/components/X/Mark/SocialProof/Toast.vue +136 -0
- package/app/components/X/Mark/_archive/Pattern/Background.vue +83 -0
- package/app/components/X/Mark/_archive/Pattern/CheckBg.vue +35 -0
- package/app/components/X/Mark/_archive/Pattern/CircuitBg.vue +41 -0
- package/app/components/X/Mark/_archive/Pattern/DotsBg.vue +35 -0
- package/app/components/X/Mark/_archive/Pattern/GlowDivider.vue +86 -0
- package/app/components/X/Mark/_archive/Pattern/HexBg.vue +35 -0
- package/app/components/X/Mark/_archive/Pattern/PlaidBg.vue +47 -0
- package/app/components/X/Mark/_archive/Pattern/SectionStitch.vue +168 -0
- package/app/components/X/Mark/_archive/Pattern/WaveBg.vue +41 -0
- package/app/components/X/_archive/Newsletter/Form.vue +123 -0
- package/app/components/X/_archive/Newsletter/Section.vue +131 -0
- package/app/composables/useParallax.ts +124 -0
- package/app/composables/useScrollReveal.ts +92 -0
- package/app/pages/index.vue +210 -0
- package/app/plugins/marketing.client.ts +13 -0
- package/app/types/marketing.d.ts +268 -0
- package/componentList.json +511 -0
- package/nuxt.config.ts +6 -13
- package/package.json +21 -9
- package/.editorconfig +0 -12
- package/app/assets/css/main.css +0 -2
- package/app/components/X/Marketing/Sections/EmailSubscription/index.vue +0 -129
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Transition
|
|
3
|
+
enter-active-class="transition-all duration-500 ease-out"
|
|
4
|
+
:enter-from-class="enterFromClass"
|
|
5
|
+
leave-active-class="transition-all duration-300 ease-in"
|
|
6
|
+
:leave-to-class="leaveToClass"
|
|
7
|
+
>
|
|
8
|
+
<aside
|
|
9
|
+
v-if="currentNotification"
|
|
10
|
+
class="fixed z-40"
|
|
11
|
+
:class="placementClasses"
|
|
12
|
+
>
|
|
13
|
+
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-lg border border-gray-200 dark:border-gray-800 p-4 flex items-center gap-3">
|
|
14
|
+
<!-- Avatar -->
|
|
15
|
+
<img
|
|
16
|
+
v-if="currentNotification.avatar"
|
|
17
|
+
:src="currentNotification.avatar"
|
|
18
|
+
:alt="currentNotification.name"
|
|
19
|
+
class="w-10 h-10 rounded-full object-cover"
|
|
20
|
+
/>
|
|
21
|
+
<div
|
|
22
|
+
v-else
|
|
23
|
+
class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-900 flex items-center justify-center"
|
|
24
|
+
>
|
|
25
|
+
<span class="text-primary-600 dark:text-primary-400 font-medium">
|
|
26
|
+
{{ currentNotification.name.charAt(0) }}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- Content -->
|
|
31
|
+
<div>
|
|
32
|
+
<p class="text-sm text-gray-900 dark:text-white">
|
|
33
|
+
<span class="font-medium">{{ currentNotification.name }}</span>
|
|
34
|
+
{{ currentNotification.action }}
|
|
35
|
+
</p>
|
|
36
|
+
<p class="text-xs text-gray-500 mt-0.5">
|
|
37
|
+
{{ currentNotification.time }}
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Verified badge -->
|
|
42
|
+
<UIcon
|
|
43
|
+
name="i-lucide-badge-check"
|
|
44
|
+
class="w-5 h-5 text-green-500 flex-shrink-0"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</aside>
|
|
48
|
+
</Transition>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script setup>
|
|
52
|
+
const props = defineProps({
|
|
53
|
+
/** List of notifications to cycle through */
|
|
54
|
+
notifications: {
|
|
55
|
+
type: Array,
|
|
56
|
+
required: true,
|
|
57
|
+
validator: (arr) => arr.every((n) => n.name && n.action && n.time),
|
|
58
|
+
},
|
|
59
|
+
/** Time between notifications (ms) */
|
|
60
|
+
interval: {
|
|
61
|
+
type: Number,
|
|
62
|
+
default: 4000,
|
|
63
|
+
},
|
|
64
|
+
/** Delay before first notification (ms) */
|
|
65
|
+
initialDelay: {
|
|
66
|
+
type: Number,
|
|
67
|
+
default: 5000,
|
|
68
|
+
},
|
|
69
|
+
/** How long each notification shows (ms) */
|
|
70
|
+
displayDuration: {
|
|
71
|
+
type: Number,
|
|
72
|
+
default: 3000,
|
|
73
|
+
},
|
|
74
|
+
/** Toast placement */
|
|
75
|
+
placement: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: 'topRight',
|
|
78
|
+
validator: (v) => ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'].includes(v),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const placementClasses = computed(() => {
|
|
83
|
+
const placements = {
|
|
84
|
+
topLeft: 'top-6 left-6',
|
|
85
|
+
topRight: 'top-6 right-6',
|
|
86
|
+
bottomLeft: 'bottom-6 left-6',
|
|
87
|
+
bottomRight: 'bottom-6 right-6',
|
|
88
|
+
};
|
|
89
|
+
return placements[props.placement];
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const enterFromClass = computed(() => {
|
|
93
|
+
if (props.placement.includes('Left')) {
|
|
94
|
+
return '-translate-x-full opacity-0';
|
|
95
|
+
}
|
|
96
|
+
return 'translate-x-full opacity-0';
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const leaveToClass = computed(() => {
|
|
100
|
+
if (props.placement.includes('Left')) {
|
|
101
|
+
return '-translate-x-full opacity-0';
|
|
102
|
+
}
|
|
103
|
+
return 'translate-x-full opacity-0';
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const currentNotification = ref(null);
|
|
107
|
+
const currentIndex = ref(0);
|
|
108
|
+
let showTimeout = null;
|
|
109
|
+
let hideTimeout = null;
|
|
110
|
+
let cycleTimeout = null;
|
|
111
|
+
|
|
112
|
+
const showNext = () => {
|
|
113
|
+
if (props.notifications.length === 0) return;
|
|
114
|
+
|
|
115
|
+
currentNotification.value = props.notifications[currentIndex.value];
|
|
116
|
+
currentIndex.value = (currentIndex.value + 1) % props.notifications.length;
|
|
117
|
+
|
|
118
|
+
hideTimeout = setTimeout(() => {
|
|
119
|
+
currentNotification.value = null;
|
|
120
|
+
|
|
121
|
+
cycleTimeout = setTimeout(showNext, props.interval - props.displayDuration);
|
|
122
|
+
}, props.displayDuration);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
onMounted(() => {
|
|
126
|
+
if (process.client && props.notifications.length > 0) {
|
|
127
|
+
showTimeout = setTimeout(showNext, props.initialDelay);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
onUnmounted(() => {
|
|
132
|
+
if (showTimeout) clearTimeout(showTimeout);
|
|
133
|
+
if (hideTimeout) clearTimeout(hideTimeout);
|
|
134
|
+
if (cycleTimeout) clearTimeout(cycleTimeout);
|
|
135
|
+
});
|
|
136
|
+
</script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="absolute inset-0 pointer-events-none"
|
|
4
|
+
:style="{
|
|
5
|
+
opacity: opacity,
|
|
6
|
+
backgroundImage: `url('${patternSvg}')`,
|
|
7
|
+
backgroundSize: size
|
|
8
|
+
}"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
/** Pattern type */
|
|
15
|
+
pattern: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: 'dots',
|
|
18
|
+
validator: (v) => ['dots', 'grid', 'diagonal', 'topography', 'circuit', 'waves'].includes(v),
|
|
19
|
+
},
|
|
20
|
+
/** Pattern opacity (0-1) */
|
|
21
|
+
opacity: {
|
|
22
|
+
type: Number,
|
|
23
|
+
default: 0.05,
|
|
24
|
+
},
|
|
25
|
+
/** Background size */
|
|
26
|
+
size: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: 'auto',
|
|
29
|
+
},
|
|
30
|
+
/** Pattern color */
|
|
31
|
+
color: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: 'currentColor',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const patterns = {
|
|
38
|
+
dots: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
39
|
+
<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
40
|
+
<circle cx="2" cy="2" r="1" fill="${color}"/>
|
|
41
|
+
</svg>
|
|
42
|
+
`)}`,
|
|
43
|
+
|
|
44
|
+
grid: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
45
|
+
<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
|
46
|
+
<path d="M0 0h40v40H0z" fill="none" stroke="${color}" stroke-width="0.5"/>
|
|
47
|
+
</svg>
|
|
48
|
+
`)}`,
|
|
49
|
+
|
|
50
|
+
diagonal: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
51
|
+
<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
|
52
|
+
<path d="M0 40L40 0" stroke="${color}" stroke-width="0.5"/>
|
|
53
|
+
</svg>
|
|
54
|
+
`)}`,
|
|
55
|
+
|
|
56
|
+
topography: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
57
|
+
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
58
|
+
<path d="M0 50 Q25 30 50 50 T100 50" stroke="${color}" fill="none" stroke-width="0.5"/>
|
|
59
|
+
<path d="M0 70 Q25 50 50 70 T100 70" stroke="${color}" fill="none" stroke-width="0.5"/>
|
|
60
|
+
<path d="M0 30 Q25 10 50 30 T100 30" stroke="${color}" fill="none" stroke-width="0.5"/>
|
|
61
|
+
</svg>
|
|
62
|
+
`)}`,
|
|
63
|
+
|
|
64
|
+
circuit: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
65
|
+
<svg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
|
|
66
|
+
<path d="M30 0v20M30 40v20M0 30h20M40 30h20" stroke="${color}" stroke-width="0.5" fill="none"/>
|
|
67
|
+
<circle cx="30" cy="30" r="3" stroke="${color}" fill="none" stroke-width="0.5"/>
|
|
68
|
+
</svg>
|
|
69
|
+
`)}`,
|
|
70
|
+
|
|
71
|
+
waves: (color) => `data:image/svg+xml,${encodeURIComponent(`
|
|
72
|
+
<svg width="100" height="20" viewBox="0 0 100 20" xmlns="http://www.w3.org/2000/svg">
|
|
73
|
+
<path d="M0 10 Q25 0 50 10 T100 10" stroke="${color}" fill="none" stroke-width="0.5"/>
|
|
74
|
+
</svg>
|
|
75
|
+
`)}`,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const patternSvg = computed(() => {
|
|
79
|
+
// Use fully opaque color - opacity is controlled by the wrapper element
|
|
80
|
+
const colorValue = props.color === 'currentColor' ? 'rgb(0,0,0)' : props.color;
|
|
81
|
+
return patterns[props.pattern]?.(colorValue) || patterns.dots(colorValue);
|
|
82
|
+
});
|
|
83
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 transition-transform duration-100 xParallax"
|
|
5
|
+
data-parallax-speed="0.4"
|
|
6
|
+
:style="{
|
|
7
|
+
opacity: opacity,
|
|
8
|
+
backgroundImage: patternSvg
|
|
9
|
+
}"
|
|
10
|
+
/>
|
|
11
|
+
<div class="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-[#0a0a0a] to-transparent z-10" />
|
|
12
|
+
<div class="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0a0a0a] to-transparent z-10" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
/** Pattern opacity (0-1) */
|
|
19
|
+
opacity: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0.5,
|
|
22
|
+
},
|
|
23
|
+
/** Pattern color */
|
|
24
|
+
color: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: '#164e63',
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const patternSvg = computed(() => {
|
|
31
|
+
const encodedColor = encodeURIComponent(props.color)
|
|
32
|
+
return `url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cpattern id="diag" x="0" y="0" width="60" height="60" patternUnits="userSpaceOnUse"%3E%3Cline x1="0" y1="60" x2="60" y2="0" stroke="${encodedColor}" stroke-width="1" opacity="0.4"/%3E%3C/pattern%3E%3Crect width="60" height="60" fill="url(%23diag)"/%3E%3C/svg%3E')`
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 transition-transform duration-100 xParallax"
|
|
5
|
+
data-parallax-speed="0.4"
|
|
6
|
+
:style="{
|
|
7
|
+
opacity: opacity,
|
|
8
|
+
backgroundImage: patternSvg
|
|
9
|
+
}"
|
|
10
|
+
/>
|
|
11
|
+
<div class="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-[#0a0a0a] to-transparent z-10" />
|
|
12
|
+
<div class="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0a0a0a] to-transparent z-10" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
/** Pattern opacity (0-1) */
|
|
19
|
+
opacity: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0.35,
|
|
22
|
+
},
|
|
23
|
+
/** Primary pattern color */
|
|
24
|
+
color: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: '#164e63',
|
|
27
|
+
},
|
|
28
|
+
/** Secondary pattern color */
|
|
29
|
+
colorSecondary: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: '#1e3a8a',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const patternSvg = computed(() => {
|
|
36
|
+
const encodedColor = encodeURIComponent(props.color)
|
|
37
|
+
const encodedColorSecondary = encodeURIComponent(props.colorSecondary)
|
|
38
|
+
return `url('data:image/svg+xml,%3Csvg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"%3E%3Cpattern id="circuit" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse"%3E%3Cpath d="M0 50 H30 V20 H50" fill="none" stroke="${encodedColor}" stroke-width="1"/%3E%3Cpath d="M50 80 V50 H80 V30" fill="none" stroke="${encodedColorSecondary}" stroke-width="1"/%3E%3Ccircle cx="30" cy="50" r="3" fill="${encodedColor}"/%3E%3Ccircle cx="50" cy="20" r="3" fill="${encodedColor}"/%3E%3Ccircle cx="50" cy="80" r="3" fill="${encodedColorSecondary}"/%3E%3Ccircle cx="80" cy="30" r="3" fill="${encodedColorSecondary}"/%3E%3C/pattern%3E%3Crect width="100" height="100" fill="url(%23circuit)"/%3E%3C/svg%3E')`
|
|
39
|
+
})
|
|
40
|
+
</script>
|
|
41
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 transition-transform duration-100 xParallax"
|
|
5
|
+
data-parallax-speed="0.4"
|
|
6
|
+
:style="{
|
|
7
|
+
opacity: opacity,
|
|
8
|
+
backgroundImage: patternSvg
|
|
9
|
+
}"
|
|
10
|
+
/>
|
|
11
|
+
<div class="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-[#0a0a0a] to-transparent z-10" />
|
|
12
|
+
<div class="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0a0a0a] to-transparent z-10" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
/** Pattern opacity (0-1) */
|
|
19
|
+
opacity: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0.5,
|
|
22
|
+
},
|
|
23
|
+
/** Pattern color */
|
|
24
|
+
color: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: '#164e63',
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const patternSvg = computed(() => {
|
|
31
|
+
const encodedColor = encodeURIComponent(props.color)
|
|
32
|
+
return `url('data:image/svg+xml,%3Csvg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"%3E%3Cpattern id="dots" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse"%3E%3Ccircle cx="20" cy="20" r="2" fill="${encodedColor}" opacity="0.6"/%3E%3C/pattern%3E%3Crect width="40" height="40" fill="url(%23dots)"/%3E%3C/svg%3E')`
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative h-px" :class="spacingClasses">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 bg-gradient-to-r from-transparent to-transparent"
|
|
5
|
+
:class="lineClasses"
|
|
6
|
+
/>
|
|
7
|
+
<div
|
|
8
|
+
v-if="glow"
|
|
9
|
+
class="absolute inset-0 bg-gradient-to-r from-transparent to-transparent blur-sm"
|
|
10
|
+
:class="glowClasses"
|
|
11
|
+
/>
|
|
12
|
+
<div
|
|
13
|
+
v-if="glow && intensity === 'strong'"
|
|
14
|
+
class="absolute -inset-1 bg-gradient-to-r from-transparent to-transparent blur-xl"
|
|
15
|
+
:class="glowXlClasses"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
/** Show glow effect */
|
|
23
|
+
glow: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: true,
|
|
26
|
+
},
|
|
27
|
+
/** Glow color preset */
|
|
28
|
+
color: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: 'primary',
|
|
31
|
+
validator: (v) => ['primary', 'secondary', 'neutral', 'cyan', 'blue', 'purple', 'white'].includes(v),
|
|
32
|
+
},
|
|
33
|
+
/** Glow strength */
|
|
34
|
+
intensity: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: 'medium',
|
|
37
|
+
validator: (v) => ['subtle', 'medium', 'strong'].includes(v),
|
|
38
|
+
},
|
|
39
|
+
/** Vertical margin */
|
|
40
|
+
spacing: {
|
|
41
|
+
type: String,
|
|
42
|
+
default: 'my-16',
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const spacingClasses = computed(() => props.spacing)
|
|
47
|
+
|
|
48
|
+
const lineClasses = computed(() => {
|
|
49
|
+
const colors = {
|
|
50
|
+
primary: 'via-primary-500/50',
|
|
51
|
+
secondary: 'via-secondary-500/50',
|
|
52
|
+
neutral: 'via-neutral-500/30',
|
|
53
|
+
cyan: 'via-cyan-500/50',
|
|
54
|
+
blue: 'via-blue-500/50',
|
|
55
|
+
purple: 'via-purple-500/50',
|
|
56
|
+
white: 'via-white/30',
|
|
57
|
+
}
|
|
58
|
+
return colors[props.color]
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const glowClasses = computed(() => {
|
|
62
|
+
const colors = {
|
|
63
|
+
primary: 'via-primary-400',
|
|
64
|
+
secondary: 'via-secondary-400',
|
|
65
|
+
neutral: 'via-neutral-400',
|
|
66
|
+
cyan: 'via-cyan-400',
|
|
67
|
+
blue: 'via-blue-400',
|
|
68
|
+
purple: 'via-purple-400',
|
|
69
|
+
white: 'via-white',
|
|
70
|
+
}
|
|
71
|
+
return colors[props.color]
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const glowXlClasses = computed(() => {
|
|
75
|
+
const colors = {
|
|
76
|
+
primary: 'via-primary-500/20',
|
|
77
|
+
secondary: 'via-secondary-500/20',
|
|
78
|
+
neutral: 'via-neutral-500/20',
|
|
79
|
+
cyan: 'via-cyan-500/20',
|
|
80
|
+
blue: 'via-blue-500/20',
|
|
81
|
+
purple: 'via-purple-500/20',
|
|
82
|
+
white: 'via-white/20',
|
|
83
|
+
}
|
|
84
|
+
return colors[props.color]
|
|
85
|
+
})
|
|
86
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 transition-transform duration-100 xParallax"
|
|
5
|
+
data-parallax-speed="0.4"
|
|
6
|
+
:style="{
|
|
7
|
+
opacity: opacity,
|
|
8
|
+
backgroundImage: patternSvg
|
|
9
|
+
}"
|
|
10
|
+
/>
|
|
11
|
+
<div class="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-[#0a0a0a] to-transparent z-10" />
|
|
12
|
+
<div class="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0a0a0a] to-transparent z-10" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
/** Pattern opacity (0-1) */
|
|
19
|
+
opacity: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0.4,
|
|
22
|
+
},
|
|
23
|
+
/** Pattern color */
|
|
24
|
+
color: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: '#164e63',
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const patternSvg = computed(() => {
|
|
31
|
+
const encodedColor = encodeURIComponent(props.color)
|
|
32
|
+
return `url('data:image/svg+xml,%3Csvg width="60" height="52" viewBox="0 0 60 52" xmlns="http://www.w3.org/2000/svg"%3E%3Cpattern id="hex" x="0" y="0" width="60" height="52" patternUnits="userSpaceOnUse"%3E%3Cpath d="M30 0 L45 13 L45 39 L30 52 L15 39 L15 13 Z" fill="none" stroke="${encodedColor}" stroke-width="1" opacity="0.5"/%3E%3C/pattern%3E%3Crect width="60" height="52" fill="url(%23hex)"/%3E%3C/svg%3E')`
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
|
3
|
+
<div
|
|
4
|
+
class="absolute inset-0 transition-transform duration-100 xParallax"
|
|
5
|
+
data-parallax-speed="0.4"
|
|
6
|
+
:style="{
|
|
7
|
+
opacity: opacity,
|
|
8
|
+
backgroundImage: patternSvg
|
|
9
|
+
}"
|
|
10
|
+
/>
|
|
11
|
+
<div class="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-[#0a0a0a] to-transparent z-10" />
|
|
12
|
+
<div class="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0a0a0a] to-transparent z-10" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
/** Pattern opacity (0-1) */
|
|
19
|
+
opacity: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0.6,
|
|
22
|
+
},
|
|
23
|
+
/** Primary pattern color */
|
|
24
|
+
color: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: '#164e63',
|
|
27
|
+
},
|
|
28
|
+
/** Secondary pattern color */
|
|
29
|
+
colorSecondary: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: '#1e3a8a',
|
|
32
|
+
},
|
|
33
|
+
/** Background color */
|
|
34
|
+
bgColor: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: '#0a0a0a',
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const patternSvg = computed(() => {
|
|
41
|
+
const encodedColor = encodeURIComponent(props.color)
|
|
42
|
+
const encodedColorSecondary = encodeURIComponent(props.colorSecondary)
|
|
43
|
+
const encodedBgColor = encodeURIComponent(props.bgColor)
|
|
44
|
+
return `url('data:image/svg+xml,%3Csvg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"%3E%3Cdefs%3E%3Cpattern id="plaid" x="0" y="0" width="200" height="200" patternUnits="userSpaceOnUse"%3E%3Crect width="200" height="200" fill="${encodedBgColor}"/%3E%3Cline x1="0" y1="0" x2="200" y2="200" stroke="${encodedColor}" stroke-width="4" opacity="0.7"/%3E%3Cline x1="80" y1="0" x2="280" y2="200" stroke="${encodedColorSecondary}" stroke-width="4" opacity="0.6"/%3E%3Cline x1="160" y1="0" x2="360" y2="200" stroke="${encodedColor}" stroke-width="4" opacity="0.7"/%3E%3Cline x1="-80" y1="0" x2="120" y2="200" stroke="${encodedColorSecondary}" stroke-width="4" opacity="0.6"/%3E%3Cline x1="200" y1="0" x2="0" y2="200" stroke="${encodedColor}" stroke-width="4" opacity="0.65"/%3E%3Cline x1="280" y1="0" x2="80" y2="200" stroke="${encodedColorSecondary}" stroke-width="4" opacity="0.55"/%3E%3Cline x1="120" y1="0" x2="-80" y2="200" stroke="${encodedColor}" stroke-width="4" opacity="0.65"/%3E%3Cline x1="40" y1="0" x2="-160" y2="200" stroke="${encodedColorSecondary}" stroke-width="4" opacity="0.55"/%3E%3C/pattern%3E%3C/defs%3E%3Crect width="200" height="200" fill="url(%23plaid)"/%3E%3C/svg%3E')`
|
|
45
|
+
})
|
|
46
|
+
</script>
|
|
47
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[
|
|
4
|
+
'relative w-full overflow-hidden',
|
|
5
|
+
heightClasses
|
|
6
|
+
]"
|
|
7
|
+
>
|
|
8
|
+
<!-- Angled Divider -->
|
|
9
|
+
<svg
|
|
10
|
+
v-if="variant === 'angle'"
|
|
11
|
+
class="absolute inset-0 w-full h-full"
|
|
12
|
+
preserveAspectRatio="none"
|
|
13
|
+
viewBox="0 0 100 100"
|
|
14
|
+
>
|
|
15
|
+
<polygon
|
|
16
|
+
:fill="topColor"
|
|
17
|
+
:points="anglePoints.top"
|
|
18
|
+
/>
|
|
19
|
+
<polygon
|
|
20
|
+
:fill="bottomColor"
|
|
21
|
+
:points="anglePoints.bottom"
|
|
22
|
+
/>
|
|
23
|
+
</svg>
|
|
24
|
+
|
|
25
|
+
<!-- Wave Divider -->
|
|
26
|
+
<svg
|
|
27
|
+
v-else-if="variant === 'wave'"
|
|
28
|
+
class="absolute inset-0 w-full h-full"
|
|
29
|
+
preserveAspectRatio="none"
|
|
30
|
+
viewBox="0 0 1200 120"
|
|
31
|
+
>
|
|
32
|
+
<path
|
|
33
|
+
:fill="topColor"
|
|
34
|
+
d="M0,0 L1200,0 L1200,40 Q900,120 600,60 Q300,0 0,60 L0,0 Z"
|
|
35
|
+
/>
|
|
36
|
+
<path
|
|
37
|
+
:fill="bottomColor"
|
|
38
|
+
d="M0,120 L1200,120 L1200,40 Q900,120 600,60 Q300,0 0,60 L0,120 Z"
|
|
39
|
+
/>
|
|
40
|
+
</svg>
|
|
41
|
+
|
|
42
|
+
<!-- Curve Divider -->
|
|
43
|
+
<svg
|
|
44
|
+
v-else-if="variant === 'curve'"
|
|
45
|
+
class="absolute inset-0 w-full h-full"
|
|
46
|
+
preserveAspectRatio="none"
|
|
47
|
+
viewBox="0 0 100 100"
|
|
48
|
+
>
|
|
49
|
+
<path
|
|
50
|
+
:fill="topColor"
|
|
51
|
+
d="M0,0 L100,0 L100,50 Q50,100 0,50 Z"
|
|
52
|
+
/>
|
|
53
|
+
<path
|
|
54
|
+
:fill="bottomColor"
|
|
55
|
+
d="M0,100 L100,100 L100,50 Q50,100 0,50 Z"
|
|
56
|
+
/>
|
|
57
|
+
</svg>
|
|
58
|
+
|
|
59
|
+
<!-- Triangle Divider -->
|
|
60
|
+
<svg
|
|
61
|
+
v-else-if="variant === 'triangle'"
|
|
62
|
+
class="absolute inset-0 w-full h-full"
|
|
63
|
+
preserveAspectRatio="none"
|
|
64
|
+
viewBox="0 0 100 100"
|
|
65
|
+
>
|
|
66
|
+
<polygon
|
|
67
|
+
:fill="topColor"
|
|
68
|
+
points="0,0 100,0 100,100 50,60 0,100"
|
|
69
|
+
/>
|
|
70
|
+
<polygon
|
|
71
|
+
:fill="bottomColor"
|
|
72
|
+
points="0,100 50,60 100,100"
|
|
73
|
+
/>
|
|
74
|
+
</svg>
|
|
75
|
+
|
|
76
|
+
<!-- Zigzag Divider -->
|
|
77
|
+
<svg
|
|
78
|
+
v-else-if="variant === 'zigzag'"
|
|
79
|
+
class="absolute inset-0 w-full h-full"
|
|
80
|
+
preserveAspectRatio="none"
|
|
81
|
+
viewBox="0 0 100 20"
|
|
82
|
+
>
|
|
83
|
+
<polygon
|
|
84
|
+
:fill="topColor"
|
|
85
|
+
points="0,0 100,0 100,10 90,20 80,10 70,20 60,10 50,20 40,10 30,20 20,10 10,20 0,10"
|
|
86
|
+
/>
|
|
87
|
+
<polygon
|
|
88
|
+
:fill="bottomColor"
|
|
89
|
+
points="0,20 10,20 20,10 30,20 40,10 50,20 60,10 70,20 80,10 90,20 100,10 100,20"
|
|
90
|
+
/>
|
|
91
|
+
</svg>
|
|
92
|
+
|
|
93
|
+
<!-- Glow line overlay -->
|
|
94
|
+
<div
|
|
95
|
+
v-if="glow"
|
|
96
|
+
class="absolute inset-x-0 top-1/2 -translate-y-1/2 h-px"
|
|
97
|
+
:style="{
|
|
98
|
+
background: `linear-gradient(90deg, transparent 0%, ${glowColor} 50%, transparent 100%)`,
|
|
99
|
+
boxShadow: `0 0 20px ${glowColor}`
|
|
100
|
+
}"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</template>
|
|
104
|
+
|
|
105
|
+
<script setup>
|
|
106
|
+
const props = defineProps({
|
|
107
|
+
/** Stitch variant */
|
|
108
|
+
variant: {
|
|
109
|
+
type: String,
|
|
110
|
+
default: 'angle',
|
|
111
|
+
validator: (v) => ['angle', 'wave', 'curve', 'triangle', 'zigzag'].includes(v),
|
|
112
|
+
},
|
|
113
|
+
/** Top section color (CSS color) */
|
|
114
|
+
topColor: {
|
|
115
|
+
type: String,
|
|
116
|
+
default: 'var(--color-neutral-100)',
|
|
117
|
+
},
|
|
118
|
+
/** Bottom section color (CSS color) */
|
|
119
|
+
bottomColor: {
|
|
120
|
+
type: String,
|
|
121
|
+
default: 'var(--color-neutral-50)',
|
|
122
|
+
},
|
|
123
|
+
/** Height preset */
|
|
124
|
+
height: {
|
|
125
|
+
type: String,
|
|
126
|
+
default: 'md',
|
|
127
|
+
validator: (v) => ['sm', 'md', 'lg', 'xl'].includes(v),
|
|
128
|
+
},
|
|
129
|
+
/** Flip the direction */
|
|
130
|
+
flip: {
|
|
131
|
+
type: Boolean,
|
|
132
|
+
default: false,
|
|
133
|
+
},
|
|
134
|
+
/** Add glow line at center */
|
|
135
|
+
glow: {
|
|
136
|
+
type: Boolean,
|
|
137
|
+
default: false,
|
|
138
|
+
},
|
|
139
|
+
/** Glow line color */
|
|
140
|
+
glowColor: {
|
|
141
|
+
type: String,
|
|
142
|
+
default: 'var(--x-glow-primary)',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const heightClasses = computed(() => {
|
|
147
|
+
const heights = {
|
|
148
|
+
sm: 'h-12 md:h-16',
|
|
149
|
+
md: 'h-16 md:h-24',
|
|
150
|
+
lg: 'h-24 md:h-32',
|
|
151
|
+
xl: 'h-32 md:h-48',
|
|
152
|
+
};
|
|
153
|
+
return heights[props.height];
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const anglePoints = computed(() => {
|
|
157
|
+
if (props.flip) {
|
|
158
|
+
return {
|
|
159
|
+
top: '0,0 100,0 100,100 0,0',
|
|
160
|
+
bottom: '0,0 100,100 0,100',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
top: '0,0 100,0 0,100',
|
|
165
|
+
bottom: '0,100 100,0 100,100',
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
</script>
|