@xenterprises/nuxt-x-marketing 0.0.24 → 1.0.1
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/.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/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 +471 -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 +515 -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 +669 -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 +187 -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/CodeBlock/index.vue +69 -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 +181 -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 +14 -8
- package/package.json +24 -10
- package/.editorconfig +0 -12
- package/app/assets/css/main.css +0 -9
- package/app/components/X/Marketing/Sections/EmailSubscription/index.vue +0 -129
|
@@ -0,0 +1,181 @@
|
|
|
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
|
|
14
|
+
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"
|
|
15
|
+
>
|
|
16
|
+
<!-- Avatar -->
|
|
17
|
+
<img
|
|
18
|
+
v-if="currentNotification.avatar"
|
|
19
|
+
:src="currentNotification.avatar"
|
|
20
|
+
:alt="currentNotification.name"
|
|
21
|
+
class="w-10 h-10 rounded-full object-cover"
|
|
22
|
+
/>
|
|
23
|
+
<div
|
|
24
|
+
v-else
|
|
25
|
+
class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-900 flex items-center justify-center"
|
|
26
|
+
>
|
|
27
|
+
<span class="text-primary-600 dark:text-primary-400 font-medium">
|
|
28
|
+
{{ currentNotification.name.charAt(0) }}
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Content -->
|
|
33
|
+
<div>
|
|
34
|
+
<p class="text-sm text-gray-900 dark:text-white">
|
|
35
|
+
<span class="font-medium">{{ currentNotification.name }}</span>
|
|
36
|
+
{{ currentNotification.action }}
|
|
37
|
+
</p>
|
|
38
|
+
<p class="text-xs text-gray-500 mt-0.5">
|
|
39
|
+
{{ currentNotification.time }}
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- Status icon -->
|
|
44
|
+
<UIcon
|
|
45
|
+
v-if="showIcon && iconName"
|
|
46
|
+
:name="iconName"
|
|
47
|
+
:class="iconClasses"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</aside>
|
|
51
|
+
</Transition>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<script setup>
|
|
55
|
+
const props = defineProps({
|
|
56
|
+
/** List of notifications to cycle through */
|
|
57
|
+
notifications: {
|
|
58
|
+
type: Array,
|
|
59
|
+
required: true,
|
|
60
|
+
validator: (arr) => arr.every((n) => n.name && n.action && n.time),
|
|
61
|
+
},
|
|
62
|
+
/** Time between notifications (ms) */
|
|
63
|
+
interval: {
|
|
64
|
+
type: Number,
|
|
65
|
+
default: 4000,
|
|
66
|
+
},
|
|
67
|
+
/** Delay before first notification (ms) */
|
|
68
|
+
initialDelay: {
|
|
69
|
+
type: Number,
|
|
70
|
+
default: 5000,
|
|
71
|
+
},
|
|
72
|
+
/** How long each notification shows (ms) */
|
|
73
|
+
displayDuration: {
|
|
74
|
+
type: Number,
|
|
75
|
+
default: 3000,
|
|
76
|
+
},
|
|
77
|
+
/** Toast placement */
|
|
78
|
+
placement: {
|
|
79
|
+
type: String,
|
|
80
|
+
default: "topRight",
|
|
81
|
+
validator: (v) =>
|
|
82
|
+
["topLeft", "topRight", "bottomLeft", "bottomRight"].includes(v),
|
|
83
|
+
},
|
|
84
|
+
/** Toast type - determines icon and color */
|
|
85
|
+
type: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: "success",
|
|
88
|
+
validator: (v) => ["success", "error", "warning", "info"].includes(v),
|
|
89
|
+
},
|
|
90
|
+
/** Whether to show the status icon */
|
|
91
|
+
showIcon: {
|
|
92
|
+
type: Boolean,
|
|
93
|
+
default: true,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const placementClasses = computed(() => {
|
|
98
|
+
const placements = {
|
|
99
|
+
topLeft: "top-6 left-6",
|
|
100
|
+
topRight: "top-6 right-6",
|
|
101
|
+
bottomLeft: "bottom-6 left-6",
|
|
102
|
+
bottomRight: "bottom-6 right-6",
|
|
103
|
+
};
|
|
104
|
+
return placements[props.placement];
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const iconConfig = {
|
|
108
|
+
success: {
|
|
109
|
+
name: "i-lucide-badge-check",
|
|
110
|
+
color: "text-green-500",
|
|
111
|
+
},
|
|
112
|
+
error: {
|
|
113
|
+
name: "i-lucide-x-circle",
|
|
114
|
+
color: "text-red-500",
|
|
115
|
+
},
|
|
116
|
+
warning: {
|
|
117
|
+
name: "i-lucide-alert-triangle",
|
|
118
|
+
color: "text-yellow-500",
|
|
119
|
+
},
|
|
120
|
+
info: {
|
|
121
|
+
name: "i-lucide-info",
|
|
122
|
+
color: "text-blue-500",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const iconName = computed(() => {
|
|
127
|
+
if (!props.showIcon) return null;
|
|
128
|
+
return iconConfig[props.type]?.name || null;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const iconClasses = computed(() => {
|
|
132
|
+
const baseClasses = "w-5 h-5 flex-shrink-0";
|
|
133
|
+
const colorClass = iconConfig[props.type]?.color || "text-gray-500";
|
|
134
|
+
return `${baseClasses} ${colorClass}`;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const enterFromClass = computed(() => {
|
|
138
|
+
if (props.placement.includes("Left")) {
|
|
139
|
+
return "-translate-x-full opacity-0";
|
|
140
|
+
}
|
|
141
|
+
return "translate-x-full opacity-0";
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const leaveToClass = computed(() => {
|
|
145
|
+
if (props.placement.includes("Left")) {
|
|
146
|
+
return "-translate-x-full opacity-0";
|
|
147
|
+
}
|
|
148
|
+
return "translate-x-full opacity-0";
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const currentNotification = ref(null);
|
|
152
|
+
const currentIndex = ref(0);
|
|
153
|
+
let showTimeout = null;
|
|
154
|
+
let hideTimeout = null;
|
|
155
|
+
let cycleTimeout = null;
|
|
156
|
+
|
|
157
|
+
const showNext = () => {
|
|
158
|
+
if (props.notifications.length === 0) return;
|
|
159
|
+
|
|
160
|
+
currentNotification.value = props.notifications[currentIndex.value];
|
|
161
|
+
currentIndex.value = (currentIndex.value + 1) % props.notifications.length;
|
|
162
|
+
|
|
163
|
+
hideTimeout = setTimeout(() => {
|
|
164
|
+
currentNotification.value = null;
|
|
165
|
+
|
|
166
|
+
cycleTimeout = setTimeout(showNext, props.interval - props.displayDuration);
|
|
167
|
+
}, props.displayDuration);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
onMounted(() => {
|
|
171
|
+
if (process.client && props.notifications.length > 0) {
|
|
172
|
+
showTimeout = setTimeout(showNext, props.initialDelay);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
onUnmounted(() => {
|
|
177
|
+
if (showTimeout) clearTimeout(showTimeout);
|
|
178
|
+
if (hideTimeout) clearTimeout(hideTimeout);
|
|
179
|
+
if (cycleTimeout) clearTimeout(cycleTimeout);
|
|
180
|
+
});
|
|
181
|
+
</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
|
+
|