@xenterprises/nuxt-x-marketing 1.2.2 → 1.4.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.
@@ -46,7 +46,7 @@
46
46
  </h2>
47
47
  </header>
48
48
 
49
- <XMarkTestimonials :testimonials="testimonials" layout="grid" />
49
+ <XMarkSocialProofTestimonials :testimonials="testimonials" layout="grid" />
50
50
  </XMarkSection>
51
51
 
52
52
  <!-- Pricing Section -->
@@ -63,7 +63,7 @@
63
63
  </p>
64
64
  </header>
65
65
 
66
- <XMarkPricing :plans="pricingPlans" />
66
+ <XMarkPricingPlans :plans="pricingPlans" />
67
67
  </XMarkSection>
68
68
 
69
69
  <!-- CTA Section -->
@@ -80,128 +80,23 @@
80
80
  </UButton>
81
81
  </div>
82
82
  <div class="mt-6 flex flex-wrap justify-center gap-4">
83
- <XMarkBadge preset="no-credit-card" variant="subtle" />
84
- <XMarkBadge preset="cancel-anytime" variant="subtle" />
83
+ <XMarkSocialProofBadge preset="no-credit-card" variant="subtle" />
84
+ <XMarkSocialProofBadge preset="cancel-anytime" variant="subtle" />
85
85
  </div>
86
86
  </div>
87
87
  </XMarkSection>
88
88
 
89
- <!-- ─── Affiliate Components Demo ─── -->
90
-
91
- <!-- Affiliate Disclosure variants -->
92
- <XMarkSection bg="subtle" padding="xl">
93
- <header class="text-center max-w-3xl mx-auto mb-12">
94
- <p class="xText-eyebrow text-primary-500 mb-4">Affiliate Components</p>
95
- <h2
96
- class="xText-headline text-neutral-900 dark:text-white xText-balance"
97
- >
98
- Affiliate &amp; Review Components
99
- </h2>
100
- <p class="mt-4 text-lg text-neutral-600 dark:text-neutral-400">
101
- FTC-compliant disclosures, product cards, grids, detail pages, and
102
- comparison tables.
103
- </p>
104
- </header>
105
-
106
- <!-- Disclosure variants -->
107
- <div class="max-w-3xl mx-auto space-y-4 mb-16">
108
- <h3
109
- class="font-semibold text-neutral-700 dark:text-neutral-300 text-sm uppercase tracking-wider mb-4"
110
- >
111
- Disclosure Variants
112
- </h3>
113
- <XMarkAffiliateDisclosure variant="subtle" />
114
- <XMarkAffiliateDisclosure variant="banner" />
115
- <XMarkAffiliateDisclosure variant="inline" />
116
- </div>
117
-
118
- <!-- Product Cards — all 3 layouts -->
119
- <div class="mb-16">
120
- <h3
121
- class="font-semibold text-neutral-700 dark:text-neutral-300 text-sm uppercase tracking-wider mb-6"
122
- >
123
- Product Card Layouts
124
- </h3>
125
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
126
- <XMarkAffiliateProductCard
127
- :product="sampleProducts[0]"
128
- layout="card"
129
- :show-pros="true"
130
- />
131
- <XMarkAffiliateProductCard
132
- :product="sampleProducts[1]"
133
- layout="card"
134
- :show-pros="true"
135
- :show-cons="true"
136
- />
137
- <XMarkAffiliateProductCard
138
- :product="sampleProducts[2]"
139
- layout="card"
140
- :show-pros="true"
141
- />
142
- </div>
143
- <div class="space-y-4 mb-6">
144
- <XMarkAffiliateProductCard
145
- :product="sampleProducts[0]"
146
- layout="row"
147
- :show-pros="true"
148
- />
149
- <XMarkAffiliateProductCard
150
- :product="sampleProducts[1]"
151
- layout="row"
152
- />
153
- </div>
154
- <div class="space-y-2">
155
- <XMarkAffiliateProductCard
156
- v-for="p in sampleProducts"
157
- :key="p.name"
158
- :product="p"
159
- layout="compact"
160
- />
161
- </div>
162
- </div>
163
-
164
- <!-- Product Grid -->
165
- <div class="mb-16">
166
- <h3
167
- class="font-semibold text-neutral-700 dark:text-neutral-300 text-sm uppercase tracking-wider mb-6"
168
- >
169
- Product Grid (Roundup Post)
170
- </h3>
171
- <XMarkAffiliateProductGrid
172
- :products="sampleProducts"
173
- layout="grid"
174
- :columns="3"
175
- :show-ranking="true"
176
- disclosure="This page contains affiliate links. As an Amazon Associate, we earn from qualifying purchases."
177
- />
178
- </div>
179
-
180
- <!-- Comparison Table -->
181
- <div class="mb-16">
182
- <h3
183
- class="font-semibold text-neutral-700 dark:text-neutral-300 text-sm uppercase tracking-wider mb-6"
184
- >
185
- Comparison Table (X vs Y)
186
- </h3>
187
- <XMarkAffiliateComparisonTable
188
- :products="comparisonProducts"
189
- :specs="comparisonSpecs"
190
- :show-buy-buttons="true"
191
- :highlight-best="true"
192
- button-label="Buy on Amazon"
193
- disclosure="This page contains affiliate links."
194
- />
195
- </div>
196
- </XMarkSection>
197
89
  </div>
198
90
  </template>
199
91
 
200
92
  <script setup>
93
+ const appConfig = useAppConfig();
94
+
201
95
  useSeoMeta({
202
96
  title: "Modern Platform for Teams",
203
97
  description:
204
98
  "Build, launch, and grow your product with our powerful platform.",
99
+ ogUrl: appConfig.xMarketing?.url,
205
100
  });
206
101
 
207
102
  // Features data
@@ -265,103 +160,6 @@ const testimonials = [
265
160
  },
266
161
  ];
267
162
 
268
- // Affiliate sample products
269
- const sampleProducts = [
270
- {
271
- name: "Sony WH-1000XM5",
272
- image:
273
- "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&q=80",
274
- price: "$279.99",
275
- originalPrice: "$349.99",
276
- rating: 4.8,
277
- reviewCount: 12400,
278
- pros: ["Best-in-class ANC", "30hr battery life", "Foldable design"],
279
- cons: ["No IP rating", "Expensive"],
280
- affiliateUrl: "https://amazon.com",
281
- affiliateTag: "mysite-20",
282
- },
283
- {
284
- name: "Bose QuietComfort 45",
285
- image:
286
- "https://images.unsplash.com/photo-1484704849700-f032a568e944?w=400&q=80",
287
- price: "$249.99",
288
- rating: 4.6,
289
- reviewCount: 8900,
290
- pros: ["Comfortable fit", "Great sound quality", "Easy controls"],
291
- cons: ["No multipoint", "Older design"],
292
- affiliateUrl: "https://amazon.com",
293
- affiliateTag: "mysite-20",
294
- },
295
- {
296
- name: "Apple AirPods Max",
297
- image:
298
- "https://images.unsplash.com/photo-1585386959984-a4155224a1ad?w=400&q=80",
299
- price: "$449.00",
300
- rating: 4.7,
301
- reviewCount: 6200,
302
- pros: ["Premium build quality", "Excellent ANC", "Spatial audio"],
303
- cons: ["Very expensive", "Heavy", "No USB-C charging"],
304
- affiliateUrl: "https://amazon.com",
305
- affiliateTag: "mysite-20",
306
- },
307
- ];
308
-
309
- // Comparison table data
310
- const comparisonProducts = [
311
- {
312
- name: "Sony WH-1000XM5",
313
- image:
314
- "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=200&q=80",
315
- price: "$279.99",
316
- affiliateUrl: "https://amazon.com",
317
- affiliateTag: "mysite-20",
318
- specs: {
319
- price: "$279.99",
320
- rating: "4.8/5",
321
- battery: "30 hours",
322
- weight: "250g",
323
- anc: "Yes",
324
- },
325
- },
326
- {
327
- name: "Bose QC45",
328
- image:
329
- "https://images.unsplash.com/photo-1484704849700-f032a568e944?w=200&q=80",
330
- price: "$249.99",
331
- affiliateUrl: "https://amazon.com",
332
- affiliateTag: "mysite-20",
333
- specs: {
334
- price: "$249.99",
335
- rating: "4.6/5",
336
- battery: "24 hours",
337
- weight: "238g",
338
- anc: "Yes",
339
- },
340
- },
341
- {
342
- name: "AirPods Max",
343
- image:
344
- "https://images.unsplash.com/photo-1585386959984-a4155224a1ad?w=200&q=80",
345
- price: "$449.00",
346
- affiliateUrl: "https://amazon.com",
347
- affiliateTag: "mysite-20",
348
- specs: {
349
- price: "$449.00",
350
- rating: "4.7/5",
351
- battery: "20 hours",
352
- weight: "385g",
353
- anc: "Yes",
354
- },
355
- },
356
- ];
357
-
358
- const comparisonSpecs = [
359
- { label: "Price", key: "price" },
360
- { label: "Rating", key: "rating" },
361
- { label: "Battery Life", key: "battery" },
362
- { label: "Weight", key: "weight" },
363
- { label: "ANC", key: "anc" },
364
- ];
365
163
 
366
164
  // Pricing plans data
367
165
  const pricingPlans = [
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Config types for the `xMarketing` app.config.ts namespace.
3
+ *
4
+ * Exported so consumers and sibling layers can reference the shape
5
+ * (config-typing convention, plan 21 §3). This is the *input* contract —
6
+ * every field is optional so site overrides can set any subset.
7
+ */
8
+
9
+ export interface XMarketingNavLink {
10
+ label: string;
11
+ to?: string;
12
+ target?: string;
13
+ children?: XMarketingNavLink[];
14
+ }
15
+
16
+ export interface XMarketingTrackingScript {
17
+ /** Stable ID — used to dedupe re-injection. */
18
+ id: string;
19
+ /** External script URL (e.g. "https://www.googletagmanager.com/gtag/js?id=G-XXX"). */
20
+ src?: string;
21
+ /** Inline script body (no `src` required). */
22
+ inline?: string;
23
+ /** Consent category gate. "necessary" fires immediately; "analytics" / "marketing" gated. */
24
+ category?: "necessary" | "analytics" | "marketing";
25
+ /** Extra attributes applied to the injected <script>. */
26
+ attrs?: Record<string, string>;
27
+ }
28
+
29
+ /**
30
+ * Tracking entry — use the convenience IDs for the common tools
31
+ * (GTM, GA4, Clarity) or `scripts[]` for everything else. Scripts
32
+ * are injected only after the visitor grants consent for the
33
+ * matching `category` (default: "analytics").
34
+ */
35
+ export interface XMarketingTracking {
36
+ /** Google Tag Manager container ID, e.g. "GTM-XXXXXXX". */
37
+ gtmId?: string;
38
+ /** Google Analytics 4 measurement ID, e.g. "G-XXXXXXXX". */
39
+ ga4Id?: string;
40
+ /** Microsoft Clarity project ID, e.g. "abc123def4". */
41
+ clarityId?: string;
42
+ /**
43
+ * Arbitrary scripts (Meta Pixel, Hotjar, Segment, LinkedIn Insight,
44
+ * Pinterest, TikTok, etc.). Each fires once the matching consent
45
+ * category is granted. Use `inline` for hand-written snippets like
46
+ * `gtag('js', new Date())`.
47
+ */
48
+ scripts?: XMarketingTrackingScript[];
49
+ /** Auto-inject configured scripts on consent. Default: true. */
50
+ autoInject?: boolean;
51
+ }
52
+
53
+ export interface XMarketingConfig {
54
+ /** Project name */
55
+ name?: string;
56
+ /** Project URL */
57
+ url?: string;
58
+ config?: {
59
+ markerProjectId?: string;
60
+ emailMarketingNewsletters?: {
61
+ organizationId?: string;
62
+ };
63
+ };
64
+ header?: {
65
+ /** Set false to opt out of the default navbar in the shipped shell. */
66
+ active?: boolean;
67
+ logo?: {
68
+ src?: string;
69
+ srcDark?: string;
70
+ alt?: string;
71
+ };
72
+ nav?: {
73
+ buttons?: {
74
+ label: string;
75
+ to: string;
76
+ icon?: string;
77
+ target?: string;
78
+ variant?: string;
79
+ square?: boolean;
80
+ color?: string;
81
+ }[];
82
+ links?: XMarketingNavLink[];
83
+ };
84
+ };
85
+ footer?: {
86
+ /** Set false to opt out of the default footer in the shipped shell. */
87
+ active?: boolean;
88
+ body?: string;
89
+ bg?: {
90
+ color?: string;
91
+ img?: {
92
+ src?: string;
93
+ alt?: string;
94
+ };
95
+ };
96
+ logo?: {
97
+ src?: string;
98
+ alt?: string;
99
+ };
100
+ socials?: {
101
+ name: string;
102
+ url: string;
103
+ icon: string;
104
+ }[];
105
+ columns?: {
106
+ headerLabel: string;
107
+ links: {
108
+ label: string;
109
+ to: string;
110
+ target?: string;
111
+ }[];
112
+ }[];
113
+ };
114
+ /** Cookie-consent banner in the shipped shell; gates tracking injection. */
115
+ consent?: {
116
+ active?: boolean;
117
+ };
118
+ /** Blog defaults; `active: false` disables the shipped /blog pages (404). */
119
+ blog?: {
120
+ active?: boolean;
121
+ title?: string;
122
+ description?: string;
123
+ meta?: {
124
+ title?: string;
125
+ description?: string;
126
+ };
127
+ };
128
+ /**
129
+ * Tracking + analytics config. Read by
130
+ * `<XMarkPrivacyCookieConsent>` and `useConsentTracking()`.
131
+ * Only fires after the matching consent category is granted.
132
+ */
133
+ tracking?: XMarketingTracking;
134
+ }
@@ -0,0 +1,35 @@
1
+ import { defineContentConfig, defineCollection } from '@nuxt/content'
2
+ import { z } from 'zod'
3
+
4
+ /**
5
+ * Default `blog` collection for marketing sites.
6
+ * Consumers can extend/override this config; markdown lives in the
7
+ * consumer (or playground) under `content/blog/*.md`.
8
+ */
9
+ export default defineContentConfig({
10
+ collections: {
11
+ blog: defineCollection({
12
+ type: 'page',
13
+ source: 'blog/**/*.md',
14
+ schema: z.object({
15
+ title: z.string(),
16
+ description: z.string().optional(),
17
+ date: z.date().optional(),
18
+ author: z.union([
19
+ z.string(),
20
+ z.object({
21
+ name: z.string(),
22
+ avatar: z.string().optional(),
23
+ title: z.string().optional(),
24
+ bio: z.string().optional(),
25
+ }),
26
+ ]).optional(),
27
+ image: z.string().optional(),
28
+ category: z.string().optional(),
29
+ tags: z.array(z.string()).optional(),
30
+ published: z.boolean().default(true),
31
+ readingTime: z.number().optional(),
32
+ }),
33
+ }),
34
+ },
35
+ })
package/nuxt.config.ts CHANGED
@@ -5,10 +5,23 @@ export default defineNuxtConfig({
5
5
  ssr: true,
6
6
  devtools: { enabled: process.env.NODE_ENV === "development" },
7
7
  runtimeConfig: {
8
- public: {
9
-
10
- },
8
+ public: {},
11
9
  },
12
- modules: ["@nuxt/ui"],
10
+ // Blog pages use Nuxt Content (`queryCollection('blog')`). Consumers must
11
+ // install `@nuxt/content` (peer) and provide `content/blog/**/*.md`.
12
+ // Builder.io is no longer used.
13
+ modules: ["@nuxt/ui", "@nuxt/content"],
13
14
  css: [resolve(__dirname, "app/assets/css/x-marketing.css")],
14
- });
15
+ content: {
16
+ build: {
17
+ markdown: {
18
+ highlight: {
19
+ theme: {
20
+ default: "github-light",
21
+ dark: "github-dark",
22
+ },
23
+ },
24
+ },
25
+ },
26
+ },
27
+ });
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@xenterprises/nuxt-x-marketing",
3
3
  "license": "SEE LICENSE IN LICENSE",
4
4
  "type": "module",
5
- "version": "1.2.2",
5
+ "version": "1.4.0",
6
6
  "main": "./nuxt.config.ts",
7
7
  "scripts": {
8
8
  "dev": "nuxi dev .playground",
@@ -10,42 +10,64 @@
10
10
  "build": "nuxt build .playground",
11
11
  "generate": "nuxt generate .playground",
12
12
  "preview": "nuxt preview .playground",
13
+ "typecheck": "nuxt typecheck .playground",
14
+ "lint": "eslint .",
13
15
  "test": "npm run test:unit && npm run test:e2e",
14
16
  "test:unit": "vitest run --config test/vitest.config.js",
15
17
  "test:unit:watch": "vitest --config test/vitest.config.js",
16
18
  "test:unit:coverage": "vitest run --config test/vitest.config.js --coverage",
17
- "test:e2e": "playwright test --config test/playwright.config.js",
18
- "test:e2e:ui": "playwright test --config test/playwright.config.js --ui",
19
- "test:e2e:headed": "playwright test --config test/playwright.config.js --headed",
19
+ "test:run": "vitest run --config test/vitest.config.js",
20
+ "test:coverage": "vitest run --config test/vitest.config.js --coverage",
21
+ "test:e2e": "playwright test --config test/playwright.smoke.config.js",
22
+ "test:e2e:ui": "playwright test --config test/playwright.smoke.config.js --ui",
23
+ "test:e2e:headed": "playwright test --config test/playwright.smoke.config.js --headed",
24
+ "test:e2e:full": "playwright test --config test/playwright.config.js",
20
25
  "test:visual": "playwright test --config test/playwright.config.js visual-regression",
21
- "test:visual:update": "playwright test --config test/playwright.config.js visual-regression --update-snapshots"
26
+ "test:visual:update": "playwright test --config test/playwright.config.js visual-regression --update-snapshots",
27
+ "prepublishOnly": "npm run test:run && npm run typecheck && npm run lint"
22
28
  },
23
29
  "dependencies": {
24
30
  "@marker.io/browser": "^0.20.2",
25
31
  "zod": "^3.23.8"
26
32
  },
27
33
  "peerDependencies": {
28
- "@iconify-json/lucide": ">=1.2.82",
29
- "@nuxt/ui": ">=4.6.1",
30
- "@tailwindcss/typography": ">=0.5.0",
31
- "nuxt": ">=4.0.0",
32
- "vue": ">=3.0.0"
34
+ "@iconify-json/lucide": "^1.2.82",
35
+ "@nuxt/content": "^3.0.0",
36
+ "@nuxt/ui": "^4.6.1",
37
+ "@tailwindcss/typography": "^0.5.0",
38
+ "better-sqlite3": "^11.0.0 || ^12.0.0",
39
+ "nuxt": "^4.0.0",
40
+ "vue": "^3.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "@nuxt/content": {
44
+ "optional": false
45
+ },
46
+ "better-sqlite3": {
47
+ "optional": false
48
+ }
33
49
  },
34
50
  "devDependencies": {
35
51
  "@axe-core/playwright": "^4.8.4",
36
52
  "@iconify-json/lucide": "^1.2.82",
53
+ "@nuxt/content": "^3.8.2",
54
+ "@nuxt/eslint": "^1.15.2",
37
55
  "@nuxt/ui": "^4.6.1",
38
56
  "@playwright/test": "^1.41.1",
39
57
  "@tailwindcss/typography": "^0.5.19",
58
+ "@types/node": "^22.14.1",
40
59
  "@vitejs/plugin-vue": "^5.0.3",
41
60
  "@vitest/coverage-v8": "^2.1.8",
42
61
  "@vue/test-utils": "^2.4.4",
62
+ "better-sqlite3": "^12.5.0",
63
+ "eslint": "^9.39.0",
43
64
  "happy-dom": "^13.3.8",
44
65
  "nuxt": "^4.4.2",
45
66
  "nuxt-shiki": "^0.3.2",
46
67
  "typescript": "^5.6.3",
47
68
  "vitest": "^2.1.8",
48
- "vue": "latest"
69
+ "vue": "^3.5.39",
70
+ "vue-tsc": "^2.2.8"
49
71
  },
50
72
  "publishConfig": {
51
73
  "access": "public"