@xenterprises/nuxt-x-marketing 1.0.0 → 1.0.2

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.
Files changed (49) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/.playground/app.vue +12 -6
  3. package/.playground/pages/blog/detail.vue +341 -249
  4. package/.playground/pages/index.vue +2 -2
  5. package/.playground/pages/layout/x-legal.vue +12 -12
  6. package/.playground/pages/notifications/social-proof-toast.vue +330 -70
  7. package/LICENSE +60 -0
  8. package/README.md +210 -64
  9. package/app/app.config.ts +2 -72
  10. package/app/assets/css/x-marketing.css +1 -0
  11. package/app/components/X/Footer/index.vue +3 -4
  12. package/app/components/X/Mark/Affiliate/ComparisonTable.vue +382 -0
  13. package/app/components/X/Mark/Affiliate/Disclosure.vue +138 -0
  14. package/app/components/X/Mark/Affiliate/ProductCard.vue +431 -0
  15. package/app/components/X/Mark/Affiliate/ProductDetail.vue +410 -0
  16. package/app/components/X/Mark/Affiliate/ProductGrid.vue +101 -0
  17. package/app/components/X/Mark/Blog/Author.vue +26 -20
  18. package/app/components/X/Mark/Blog/Card.vue +28 -15
  19. package/app/components/X/Mark/Blog/Detail.vue +82 -127
  20. package/app/components/X/Mark/Blog/List.vue +17 -16
  21. package/app/components/X/Mark/CTA/index.vue +74 -70
  22. package/app/components/X/Mark/Card/Promo.vue +149 -139
  23. package/app/components/X/Mark/CodeBlock/index.vue +69 -0
  24. package/app/components/X/Mark/Directory/CategoryHero.vue +124 -0
  25. package/app/components/X/Mark/Directory/ListingCard.vue +336 -0
  26. package/app/components/X/Mark/Directory/ListingDetail.vue +306 -0
  27. package/app/components/X/Mark/Directory/ListingGrid.vue +190 -0
  28. package/app/components/X/Mark/Directory/SubmitCTA.vue +201 -0
  29. package/app/components/X/Mark/FAQ/index.vue +191 -0
  30. package/app/components/X/Mark/Features/index.vue +87 -35
  31. package/app/components/X/Mark/Hero/index.vue +41 -38
  32. package/app/components/X/Mark/Layout/AnnouncementBar.vue +20 -28
  33. package/app/components/X/Mark/Modal/Chat.vue +124 -124
  34. package/app/components/X/Mark/Modal/Video.vue +46 -36
  35. package/app/components/X/Mark/Pricing/Comparison.vue +76 -21
  36. package/app/components/X/Mark/Pricing/Plans.vue +75 -25
  37. package/app/components/X/Mark/SocialProof/Testimonials.vue +89 -21
  38. package/app/components/X/Mark/SocialProof/Toast.vue +155 -102
  39. package/app/components/X/X/Footer/index.vue +101 -0
  40. package/app/components/X/X/Header/Nav/index.vue +62 -0
  41. package/app/components/X/X/Header/index.vue +72 -0
  42. package/app/components/X/X/Legal/index.vue +94 -0
  43. package/app/composables/useParallax.ts +32 -19
  44. package/app/composables/useScrollReveal.ts +20 -13
  45. package/app/pages/index.vue +230 -28
  46. package/componentList.json +1856 -238
  47. package/nuxt.config.ts +12 -3
  48. package/package.json +5 -2
  49. package/screenshots/playground-home.png +0 -0
@@ -3,7 +3,7 @@
3
3
  :class="[
4
4
  'relative overflow-hidden rounded-2xl',
5
5
  paddingClasses,
6
- bgColorClasses
6
+ bgColorClasses,
7
7
  ]"
8
8
  >
9
9
  <!-- Background Pattern -->
@@ -15,10 +15,7 @@
15
15
  />
16
16
 
17
17
  <!-- Background Image -->
18
- <div
19
- v-if="resolvedBg.image"
20
- class="absolute inset-0"
21
- >
18
+ <div v-if="resolvedBg.image" class="absolute inset-0">
22
19
  <img
23
20
  :src="resolvedBg.image"
24
21
  :alt="resolvedBg.imageAlt"
@@ -80,22 +77,22 @@ const props = defineProps({
80
77
  /** Eyebrow text above title */
81
78
  eyebrow: {
82
79
  type: String,
83
- default: '',
80
+ default: "",
84
81
  },
85
82
  /** CTA title */
86
83
  title: {
87
84
  type: String,
88
- required: true,
85
+ default: "Ready to get started?",
89
86
  },
90
87
  /** CTA description */
91
88
  description: {
92
89
  type: String,
93
- default: '',
90
+ default: "",
94
91
  },
95
92
  /** Optional icon */
96
93
  icon: {
97
94
  type: String,
98
- default: '',
95
+ default: "",
99
96
  },
100
97
  /** Action buttons */
101
98
  buttons: {
@@ -106,8 +103,8 @@ const props = defineProps({
106
103
  /** Layout variant */
107
104
  variant: {
108
105
  type: String,
109
- default: 'default',
110
- validator: (v) => ['default', 'centered', 'inline', 'stacked'].includes(v),
106
+ default: "default",
107
+ validator: (v) => ["default", "centered", "inline", "stacked"].includes(v),
111
108
  },
112
109
  /**
113
110
  * Background configuration object or string shorthand
@@ -122,31 +119,31 @@ const props = defineProps({
122
119
  */
123
120
  bg: {
124
121
  type: [Object, String],
125
- default: 'subtle',
122
+ default: "subtle",
126
123
  },
127
124
  /** Size */
128
125
  size: {
129
126
  type: String,
130
- default: 'md',
131
- validator: (v) => ['sm', 'md', 'lg'].includes(v),
127
+ default: "md",
128
+ validator: (v) => ["sm", "md", "lg"].includes(v),
132
129
  },
133
130
  });
134
131
 
135
132
  // Default bg values
136
133
  const bgDefaults = {
137
- variant: 'subtle',
138
- color: '',
139
- image: '',
140
- imageAlt: '',
141
- imageOverlay: '',
142
- imagePosition: 'object-center',
143
- pattern: '',
134
+ variant: "subtle",
135
+ color: "",
136
+ image: "",
137
+ imageAlt: "",
138
+ imageOverlay: "",
139
+ imagePosition: "object-center",
140
+ pattern: "",
144
141
  patternOpacity: 0.05,
145
142
  };
146
143
 
147
144
  // Resolve bg prop (supports string shorthand for variant or full object)
148
145
  const resolvedBg = computed(() => {
149
- if (typeof props.bg === 'string') {
146
+ if (typeof props.bg === "string") {
150
147
  return { ...bgDefaults, variant: props.bg };
151
148
  }
152
149
  return { ...bgDefaults, ...props.bg };
@@ -154,14 +151,17 @@ const resolvedBg = computed(() => {
154
151
 
155
152
  // Check if background is "bold" (dark background requiring light text)
156
153
  const isBoldBg = computed(() => {
157
- return ['bold', 'gradient'].includes(resolvedBg.value.variant) || resolvedBg.value.image;
154
+ return (
155
+ ["bold", "gradient"].includes(resolvedBg.value.variant) ||
156
+ resolvedBg.value.image
157
+ );
158
158
  });
159
159
 
160
160
  const paddingClasses = computed(() => {
161
161
  const sizes = {
162
- sm: 'p-6',
163
- md: 'p-8 md:p-12',
164
- lg: 'p-12 md:p-16',
162
+ sm: "p-6",
163
+ md: "p-8 md:p-12",
164
+ lg: "p-12 md:p-16",
165
165
  };
166
166
  return sizes[props.size];
167
167
  });
@@ -171,124 +171,128 @@ const bgColorClasses = computed(() => {
171
171
  if (resolvedBg.value.color) return resolvedBg.value.color;
172
172
 
173
173
  const variants = {
174
- subtle: 'bg-neutral-100 dark:bg-neutral-900',
175
- bold: 'bg-primary-600 dark:bg-primary-700',
176
- gradient: 'bg-gradient-to-br from-primary-600 to-primary-800',
177
- none: '',
174
+ subtle: "bg-neutral-100 dark:bg-neutral-900",
175
+ bold: "bg-primary-600 dark:bg-primary-700",
176
+ gradient: "bg-gradient-to-br from-primary-600 to-primary-800",
177
+ none: "",
178
178
  };
179
179
  return variants[resolvedBg.value.variant] || variants.subtle;
180
180
  });
181
181
 
182
182
  const bgImageOverlayClass = computed(() => {
183
183
  const overlays = {
184
- light: 'bg-black/30',
185
- heavy: 'bg-black/60',
186
- gradient: 'bg-gradient-to-t from-black/80 via-black/40 to-transparent',
184
+ light: "bg-black/30",
185
+ heavy: "bg-black/60",
186
+ gradient: "bg-gradient-to-t from-black/80 via-black/40 to-transparent",
187
187
  };
188
- return overlays[resolvedBg.value.imageOverlay] || '';
188
+ return overlays[resolvedBg.value.imageOverlay] || "";
189
189
  });
190
190
 
191
191
  const containerClasses = computed(() => {
192
192
  const layouts = {
193
- default: 'flex flex-col md:flex-row md:items-center md:justify-between gap-6',
194
- centered: 'text-center max-w-2xl mx-auto',
195
- inline: 'flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4',
196
- stacked: 'flex flex-col items-center text-center gap-6',
193
+ default:
194
+ "flex flex-col md:flex-row md:items-center md:justify-between gap-6",
195
+ centered: "text-center max-w-2xl mx-auto",
196
+ inline:
197
+ "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4",
198
+ stacked: "flex flex-col items-center text-center gap-6",
197
199
  };
198
200
  return layouts[props.variant];
199
201
  });
200
202
 
201
203
  const iconContainerClasses = computed(() => {
202
204
  return [
203
- 'flex items-center justify-center rounded-xl',
204
- props.size === 'lg' ? 'w-16 h-16' : 'w-12 h-12',
205
- isBoldBg.value ? 'bg-white/20' : 'bg-primary-100 dark:bg-primary-900/50',
205
+ "flex items-center justify-center rounded-xl",
206
+ props.size === "lg" ? "w-16 h-16" : "w-12 h-12",
207
+ isBoldBg.value ? "bg-white/20" : "bg-primary-100 dark:bg-primary-900/50",
206
208
  ];
207
209
  });
208
210
 
209
211
  const iconClasses = computed(() => {
210
212
  return [
211
- props.size === 'lg' ? 'w-8 h-8' : 'w-6 h-6',
212
- isBoldBg.value ? 'text-white' : 'text-primary-500',
213
+ props.size === "lg" ? "w-8 h-8" : "w-6 h-6",
214
+ isBoldBg.value ? "text-white" : "text-primary-500",
213
215
  ];
214
216
  });
215
217
 
216
218
  const contentClasses = computed(() => {
217
- if (props.variant === 'default') {
218
- return 'flex-1';
219
+ if (props.variant === "default") {
220
+ return "flex-1";
219
221
  }
220
- return '';
222
+ return "";
221
223
  });
222
224
 
223
225
  const eyebrowClasses = computed(() => {
224
226
  return [
225
- 'xText-eyebrow mb-2',
226
- isBoldBg.value ? 'text-white/70' : 'text-primary-600 dark:text-primary-400',
227
+ "xText-eyebrow mb-2",
228
+ isBoldBg.value ? "text-white/70" : "text-primary-600 dark:text-primary-400",
227
229
  ];
228
230
  });
229
231
 
230
232
  const titleClasses = computed(() => {
231
233
  const sizeClasses = {
232
- sm: 'text-lg font-semibold',
233
- md: 'text-xl font-semibold',
234
- lg: 'xText-headline',
234
+ sm: "text-lg font-semibold",
235
+ md: "text-xl font-semibold",
236
+ lg: "xText-headline",
235
237
  };
236
238
  return [
237
239
  sizeClasses[props.size],
238
- isBoldBg.value ? 'text-white' : 'text-neutral-900 dark:text-white',
240
+ isBoldBg.value ? "text-white" : "text-neutral-900 dark:text-white",
239
241
  ];
240
242
  });
241
243
 
242
244
  const descriptionClasses = computed(() => {
243
245
  return [
244
- 'mt-2',
245
- props.size === 'sm' ? 'text-sm' : 'text-base',
246
- isBoldBg.value ? 'text-white/80' : 'text-neutral-600 dark:text-neutral-400',
246
+ "mt-2",
247
+ props.size === "sm" ? "text-sm" : "text-base",
248
+ isBoldBg.value ? "text-white/80" : "text-neutral-600 dark:text-neutral-400",
247
249
  ];
248
250
  });
249
251
 
250
252
  const buttonsClasses = computed(() => {
251
253
  const layouts = {
252
- default: 'flex flex-wrap gap-3',
253
- centered: 'flex flex-wrap justify-center gap-3 mt-6',
254
- inline: 'flex flex-wrap gap-3',
255
- stacked: 'flex flex-wrap justify-center gap-3',
254
+ default: "flex flex-wrap gap-3",
255
+ centered: "flex flex-wrap justify-center gap-3 mt-6",
256
+ inline: "flex flex-wrap gap-3",
257
+ stacked: "flex flex-wrap justify-center gap-3",
256
258
  };
257
259
  return layouts[props.variant];
258
260
  });
259
261
 
260
262
  const getButtonColor = (button, index) => {
261
263
  // Allow explicit color override
262
- if (button.color && button.color !== 'white') return button.color;
264
+ if (button.color && button.color !== "white") return button.color;
263
265
  if (index === 0) {
264
266
  // Use neutral instead of white (not a valid Nuxt UI v4 color)
265
267
  // Custom classes handle the actual styling
266
- return isBoldBg.value ? 'neutral' : 'primary';
268
+ return isBoldBg.value ? "neutral" : "primary";
267
269
  }
268
- return 'neutral';
270
+ return "neutral";
269
271
  };
270
272
 
271
273
  const getButtonClass = (button, index) => {
272
- const isOutline = button.variant === 'outline' || (index > 0 && !button.variant);
273
- const isGhost = button.variant === 'ghost';
274
- const isSolid = button.variant === 'solid' || (index === 0 && !button.variant);
274
+ const isOutline =
275
+ button.variant === "outline" || (index > 0 && !button.variant);
276
+ const isGhost = button.variant === "ghost";
277
+ const isSolid =
278
+ button.variant === "solid" || (index === 0 && !button.variant);
275
279
 
276
280
  // Solid buttons on bold/gradient backgrounds - white bg with dark text
277
281
  if (isBoldBg.value && isSolid) {
278
- return 'bg-white text-neutral-900 hover:bg-white/90';
282
+ return "bg-white text-neutral-900 hover:bg-white/90";
279
283
  }
280
284
  // Outline buttons on bold/gradient backgrounds - transparent with white border
281
285
  if (isBoldBg.value && isOutline) {
282
- return 'bg-white/10 backdrop-blur-sm border-white/40 hover:bg-white/20 hover:border-white/60 text-white';
286
+ return "bg-white/10 backdrop-blur-sm border-white/40 hover:bg-white/20 hover:border-white/60 text-white";
283
287
  }
284
288
  // Ghost buttons on bold/gradient backgrounds - transparent with white text
285
289
  if (isBoldBg.value && isGhost) {
286
- return 'text-white hover:bg-white/10';
290
+ return "text-white hover:bg-white/10";
287
291
  }
288
292
  // Outline buttons on subtle backgrounds
289
293
  if (!isBoldBg.value && isOutline) {
290
- return 'border-neutral-400 dark:border-neutral-600 hover:border-neutral-500 dark:hover:border-neutral-500';
294
+ return "border-neutral-400 dark:border-neutral-600 hover:border-neutral-500 dark:hover:border-neutral-500";
291
295
  }
292
- return '';
296
+ return "";
293
297
  };
294
298
  </script>
@@ -1,164 +1,174 @@
1
1
  <template>
2
- <component
3
- :is="to ? 'NuxtLink' : 'article'"
4
- :to="to"
5
- :class="[
6
- 'group relative aspect-[9/16] rounded-2xl overflow-hidden cursor-pointer block',
7
- { 'xHover-lift': hoverable }
8
- ]"
9
- @click="!to && $emit('click')"
10
- >
11
- <!-- Background Image -->
12
- <img
13
- :src="img.src"
14
- :alt="img.alt || ''"
15
- class="absolute inset-0 w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.08]"
16
- />
2
+ <component
3
+ :is="to ? 'NuxtLink' : 'article'"
4
+ :to="to"
5
+ :class="[
6
+ 'group relative aspect-[9/16] rounded-2xl overflow-hidden cursor-pointer block',
7
+ { 'xHover-lift': hoverable },
8
+ ]"
9
+ @click="!to && $emit('click')"
10
+ >
11
+ <!-- Background Image -->
12
+ <img
13
+ :src="img.src"
14
+ :alt="img.alt || ''"
15
+ class="absolute inset-0 w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.08]"
16
+ />
17
17
 
18
- <!-- Overlay -->
19
- <div
20
- class="absolute inset-0"
21
- :class="overlayClasses"
22
- />
18
+ <!-- Overlay -->
19
+ <div class="absolute inset-0" :class="overlayClasses" />
23
20
 
24
- <!-- Content -->
25
- <div
26
- :class="[
27
- 'absolute inset-0 p-6 flex flex-col',
28
- alignmentClasses
29
- ]"
30
- >
31
- <!-- Badge -->
32
- <span
33
- v-if="badge"
34
- class="inline-flex self-start px-3 py-1 rounded-full text-xs font-medium bg-white/20 text-white backdrop-blur-sm"
35
- >
36
- {{ badge }}
37
- </span>
21
+ <!-- Content -->
22
+ <div :class="['absolute inset-0 p-6 flex flex-col', alignmentClasses]">
23
+ <!-- Badge -->
24
+ <span
25
+ v-if="badge"
26
+ class="inline-flex self-start px-3 py-1 rounded-full text-xs font-medium bg-white/20 text-white backdrop-blur-sm"
27
+ >
28
+ {{ badge }}
29
+ </span>
38
30
 
39
- <!-- Spacer -->
40
- <div class="flex-1" />
31
+ <!-- Spacer -->
32
+ <div class="flex-1" />
41
33
 
42
- <!-- Text Content -->
43
- <div class="space-y-3">
44
- <h3 :class="['font-semibold text-white', titleSizeClass]">
45
- {{ title }}
46
- </h3>
34
+ <!-- Text Content -->
35
+ <div class="space-y-3">
36
+ <h3 :class="['font-semibold text-white', titleSizeClass]">
37
+ {{ title }}
38
+ </h3>
47
39
 
48
- <div v-if="$slots.default" class="text-white/80 text-sm">
49
- <slot />
50
- </div>
40
+ <div v-if="$slots.default" class="text-white/80 text-sm">
41
+ <slot />
42
+ </div>
51
43
 
52
- <div
53
- v-if="buttonLabel && to"
54
- class="mt-4 inline-flex items-center justify-center px-4 py-2 rounded-md text-sm font-medium bg-white text-neutral-900 transition-all duration-300 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-white/50"
55
- >
56
- {{ buttonLabel }}
57
- </div>
44
+ <div
45
+ v-if="buttonLabel && to"
46
+ class="mt-4 inline-flex items-center justify-center px-4 py-2 rounded-md text-sm font-medium bg-white text-neutral-900 transition-all duration-300 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-white/50"
47
+ >
48
+ {{ buttonLabel }}
49
+ </div>
58
50
 
59
- <button
60
- v-if="buttonLabel && !to"
61
- class="mt-4 inline-flex items-center justify-center px-4 py-2 rounded-md text-sm font-medium bg-white text-neutral-900 transition-all duration-300 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-white/50"
62
- >
63
- {{ buttonLabel }}
64
- </button>
65
- </div>
66
- </div>
67
- </component>
51
+ <button
52
+ v-if="buttonLabel && !to"
53
+ class="mt-4 inline-flex items-center justify-center px-4 py-2 rounded-md text-sm font-medium bg-white text-neutral-900 transition-all duration-300 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-white/50"
54
+ >
55
+ {{ buttonLabel }}
56
+ </button>
57
+ </div>
58
+ </div>
59
+ </component>
68
60
  </template>
69
61
 
70
62
  <script setup>
71
- defineEmits(['click']);
63
+ defineEmits(["click"]);
72
64
 
73
65
  const props = defineProps({
74
- /** Navigation URL (uses NuxtLink) */
75
- to: {
76
- type: String,
77
- default: '',
78
- },
79
- /** Image object { src: string, alt?: string } */
80
- img: {
81
- type: Object,
82
- required: true,
83
- validator: (v) => v && typeof v.src === 'string',
84
- },
85
- /** Card title */
86
- title: {
87
- type: String,
88
- required: true,
89
- },
90
- /** Optional badge text */
91
- badge: {
92
- type: String,
93
- default: '',
94
- },
95
- /** Button object { label: string, color?: string, variant?: string } */
96
- button: {
97
- type: Object,
98
- default: null,
99
- validator: (v) => !v || (v.label !== undefined),
100
- },
101
- /** Content alignment */
102
- align: {
103
- type: String,
104
- default: 'bottomLeft',
105
- validator: (v) => ['bottomLeft', 'bottomRight', 'bottomCenter', 'topLeft'].includes(v),
106
- },
107
- /** Overlay style */
108
- overlay: {
109
- type: String,
110
- default: 'gradient',
111
- validator: (v) => ['light', 'heavy', 'gradient'].includes(v),
112
- },
113
- /** Enable lift on hover */
114
- hoverable: {
115
- type: Boolean,
116
- default: true,
117
- },
118
- /** Title text size (Tailwind sizes) */
119
- titleSize: {
120
- type: String,
121
- default: 'xl',
122
- validator: (v) => ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl'].includes(v),
123
- },
66
+ /** Navigation URL (uses NuxtLink) */
67
+ to: {
68
+ type: String,
69
+ default: "",
70
+ },
71
+ /** Image object { src: string, alt?: string } */
72
+ img: {
73
+ type: Object,
74
+ default: () => ({
75
+ src: "",
76
+ alt: "Promo image",
77
+ }),
78
+ validator: (v) => !v || typeof v.src === "string",
79
+ },
80
+ /** Card title */
81
+ title: {
82
+ type: String,
83
+ default: "Promo Card Title",
84
+ },
85
+ /** Optional badge text */
86
+ badge: {
87
+ type: String,
88
+ default: "",
89
+ },
90
+ /** Button object { label: string, color?: string, variant?: string } */
91
+ button: {
92
+ type: Object,
93
+ default: null,
94
+ validator: (v) => !v || v.label !== undefined,
95
+ },
96
+ /** Content alignment */
97
+ align: {
98
+ type: String,
99
+ default: "bottomLeft",
100
+ validator: (v) =>
101
+ ["bottomLeft", "bottomRight", "bottomCenter", "topLeft"].includes(v),
102
+ },
103
+ /** Overlay style */
104
+ overlay: {
105
+ type: String,
106
+ default: "gradient",
107
+ validator: (v) => ["light", "heavy", "gradient"].includes(v),
108
+ },
109
+ /** Enable lift on hover */
110
+ hoverable: {
111
+ type: Boolean,
112
+ default: true,
113
+ },
114
+ /** Title text size (Tailwind sizes) */
115
+ titleSize: {
116
+ type: String,
117
+ default: "xl",
118
+ validator: (v) =>
119
+ [
120
+ "xs",
121
+ "sm",
122
+ "base",
123
+ "lg",
124
+ "xl",
125
+ "2xl",
126
+ "3xl",
127
+ "4xl",
128
+ "5xl",
129
+ "6xl",
130
+ "7xl",
131
+ "8xl",
132
+ ].includes(v),
133
+ },
124
134
  });
125
135
 
126
- const buttonLabel = computed(() => props.button?.label || '');
136
+ const buttonLabel = computed(() => props.button?.label || "");
127
137
 
128
138
  const overlayClasses = computed(() => {
129
- const overlays = {
130
- light: 'bg-black/30',
131
- heavy: 'bg-black/60',
132
- gradient: 'bg-gradient-to-t from-black/80 via-black/30 to-transparent',
133
- };
134
- return overlays[props.overlay];
139
+ const overlays = {
140
+ light: "bg-black/30",
141
+ heavy: "bg-black/60",
142
+ gradient: "bg-gradient-to-t from-black/80 via-black/30 to-transparent",
143
+ };
144
+ return overlays[props.overlay];
135
145
  });
136
146
 
137
147
  const alignmentClasses = computed(() => {
138
- const alignments = {
139
- bottomLeft: 'justify-end items-start text-left',
140
- bottomRight: 'justify-end items-end text-right',
141
- bottomCenter: 'justify-end items-center text-center',
142
- topLeft: 'justify-start items-start text-left',
143
- };
144
- return alignments[props.align];
148
+ const alignments = {
149
+ bottomLeft: "justify-end items-start text-left",
150
+ bottomRight: "justify-end items-end text-right",
151
+ bottomCenter: "justify-end items-center text-center",
152
+ topLeft: "justify-start items-start text-left",
153
+ };
154
+ return alignments[props.align];
145
155
  });
146
156
 
147
157
  const titleSizeClass = computed(() => {
148
- const sizes = {
149
- xs: 'text-xs',
150
- sm: 'text-sm',
151
- base: 'text-base',
152
- lg: 'text-lg',
153
- xl: 'text-xl',
154
- '2xl': 'text-2xl',
155
- '3xl': 'text-3xl',
156
- '4xl': 'text-4xl',
157
- '5xl': 'text-5xl',
158
- '6xl': 'text-6xl',
159
- '7xl': 'text-7xl',
160
- '8xl': 'text-8xl',
161
- };
162
- return sizes[props.titleSize] || 'text-xl';
158
+ const sizes = {
159
+ xs: "text-xs",
160
+ sm: "text-sm",
161
+ base: "text-base",
162
+ lg: "text-lg",
163
+ xl: "text-xl",
164
+ "2xl": "text-2xl",
165
+ "3xl": "text-3xl",
166
+ "4xl": "text-4xl",
167
+ "5xl": "text-5xl",
168
+ "6xl": "text-6xl",
169
+ "7xl": "text-7xl",
170
+ "8xl": "text-8xl",
171
+ };
172
+ return sizes[props.titleSize] || "text-xl";
163
173
  });
164
174
  </script>