@amulet-laboratories/rig-nuxt 0.3.1 → 0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amulet-laboratories/rig-nuxt",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Nuxt module for @amulet-laboratories/rig — auto-imports, tree-shaking, SSR-safe",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "peerDependencies": {
18
18
  "@amulet-laboratories/rig": ">=0.4.0",
19
- "nuxt": "^3.0.0",
19
+ "nuxt": "^3.21.6",
20
20
  "@nuxt/content": "^3.0.0",
21
21
  "@sentry/vue": "^9.0.0"
22
22
  },
@@ -34,4 +34,4 @@
34
34
  "devDependencies": {
35
35
  "@nuxt/kit": "^3.0.0"
36
36
  }
37
- }
37
+ }
package/src/module.ts CHANGED
@@ -259,7 +259,6 @@ export default defineNuxtModule<NuxtRigOptions>({
259
259
  addImports({ name: 'useDetailView', from: '@amulet-laboratories/rig' })
260
260
  addImports({ name: 'useFormatDate', from: '@amulet-laboratories/rig' })
261
261
  addImports({ name: 'useBreadcrumbs', from: '@amulet-laboratories/rig' })
262
- addImports({ name: 'useFathom', from: '@amulet-laboratories/rig' })
263
262
  }
264
263
 
265
264
  // ── Content features (authority-site runtime) ──
@@ -267,6 +266,11 @@ export default defineNuxtModule<NuxtRigOptions>({
267
266
 
268
267
  const contentOpts: ContentOptions = options.content === true ? {} : options.content
269
268
 
269
+ // Authority-site analytics composable. Gated to content consumers so it
270
+ // does not shadow an app's own `useFathom` auto-import (e.g. QuizSort's
271
+ // quiz-specific analytics live in its local composables/useFathom.ts).
272
+ addImports({ name: 'useFathom', from: '@amulet-laboratories/rig' })
273
+
270
274
  // Content components
271
275
  const contentComponents: Array<{ name: string; filePath: string }> = [
272
276
  {
@@ -301,6 +305,30 @@ export default defineNuxtModule<NuxtRigOptions>({
301
305
  name: 'ProductCardWrapper',
302
306
  filePath: resolve('./runtime/components/content/ProductCardWrapper.vue'),
303
307
  },
308
+ {
309
+ name: 'CategoryIcon',
310
+ filePath: resolve('./runtime/components/content/CategoryIcon.vue'),
311
+ },
312
+ {
313
+ name: 'ContentHomePage',
314
+ filePath: resolve('./runtime/components/content/ContentHomePage.vue'),
315
+ },
316
+ {
317
+ name: 'ContentComparePage',
318
+ filePath: resolve('./runtime/components/content/ContentComparePage.vue'),
319
+ },
320
+ {
321
+ name: 'ContentCompareIndexPage',
322
+ filePath: resolve('./runtime/components/content/ContentCompareIndexPage.vue'),
323
+ },
324
+ {
325
+ name: 'ContentBestForPersonaPage',
326
+ filePath: resolve('./runtime/components/content/ContentBestForPersonaPage.vue'),
327
+ },
328
+ {
329
+ name: 'ContentBestForIndexPage',
330
+ filePath: resolve('./runtime/components/content/ContentBestForIndexPage.vue'),
331
+ },
304
332
  {
305
333
  name: 'AffiliateDisclosure',
306
334
  filePath: resolve('./runtime/components/content/AffiliateDisclosure.vue'),
@@ -309,6 +337,10 @@ export default defineNuxtModule<NuxtRigOptions>({
309
337
  name: 'ContentNewsletterSignup',
310
338
  filePath: resolve('./runtime/components/content/NewsletterSignup.vue'),
311
339
  },
340
+ {
341
+ name: 'NewsletterSignup',
342
+ filePath: resolve('./runtime/components/content/NewsletterSignup.vue'),
343
+ },
312
344
  { name: 'QuizPromo', filePath: resolve('./runtime/components/content/QuizPromo.vue') },
313
345
  {
314
346
  name: 'QuizEmbedWrapper',
@@ -10,6 +10,12 @@ interface Props {
10
10
  updatedAt?: string
11
11
  category?: string
12
12
  timeToRead?: number
13
+ featuredImage?: {
14
+ src: string
15
+ alt: string
16
+ width?: number
17
+ height?: number
18
+ }
13
19
  }
14
20
 
15
21
  const props = defineProps<Props>()
@@ -46,6 +52,16 @@ const formattedUpdatedDate = computed(() => {
46
52
  </div>
47
53
  <h1 data-rig-article-header-title>{{ title }}</h1>
48
54
  <p v-if="description" data-rig-article-header-description>{{ description }}</p>
55
+ <NuxtImg
56
+ v-if="featuredImage"
57
+ :src="featuredImage.src"
58
+ :alt="featuredImage.alt"
59
+ :width="featuredImage.width ?? 1200"
60
+ :height="featuredImage.height ?? 630"
61
+ fetchpriority="high"
62
+ loading="eager"
63
+ data-rig-article-header-image
64
+ />
49
65
  <div v-if="publishedAt || updatedAt" data-rig-article-header-byline>
50
66
  <time v-if="publishedAt" :datetime="publishedAt">
51
67
  {{ formattedDate }}
@@ -0,0 +1,453 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ category: string
6
+ size?: number
7
+ }>()
8
+
9
+ const iconSize = computed(() => props.size ?? 24)
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ v-if="category === 'brewing-guides'"
15
+ :width="iconSize"
16
+ :height="iconSize"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ stroke-width="1.5"
21
+ stroke-linecap="round"
22
+ stroke-linejoin="round"
23
+ aria-hidden="true"
24
+ data-rig-category-icon
25
+ >
26
+ <path d="M17 8h1a4 4 0 1 1 0 8h-1" />
27
+ <path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V8z" />
28
+ <line x1="6" y1="2" x2="6" y2="4" />
29
+ <line x1="10" y1="2" x2="10" y2="4" />
30
+ <line x1="14" y1="2" x2="14" y2="4" />
31
+ </svg>
32
+ <svg
33
+ v-else-if="category === 'equipment-reviews'"
34
+ :width="iconSize"
35
+ :height="iconSize"
36
+ viewBox="0 0 24 24"
37
+ fill="none"
38
+ stroke="currentColor"
39
+ stroke-width="1.5"
40
+ stroke-linecap="round"
41
+ stroke-linejoin="round"
42
+ aria-hidden="true"
43
+ data-rig-category-icon
44
+ >
45
+ <path
46
+ d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
47
+ />
48
+ </svg>
49
+ <svg
50
+ v-else-if="category === 'beans-and-blends'"
51
+ :width="iconSize"
52
+ :height="iconSize"
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke="currentColor"
56
+ stroke-width="1.5"
57
+ stroke-linecap="round"
58
+ stroke-linejoin="round"
59
+ aria-hidden="true"
60
+ data-rig-category-icon
61
+ >
62
+ <path
63
+ d="M12 2C9.24 2 7 4.24 7 7c0 3.18 2.45 5.47 5 8 2.55-2.53 5-4.82 5-8 0-2.76-2.24-5-5-5z"
64
+ />
65
+ <path d="M12 10V7" />
66
+ <path d="M12 15c-2.76 0-5 2.24-5 5s2.24 4 5 4 5-1.24 5-4-2.24-5-5-5z" />
67
+ </svg>
68
+ <svg
69
+ v-else-if="category === 'routines'"
70
+ :width="iconSize"
71
+ :height="iconSize"
72
+ viewBox="0 0 24 24"
73
+ fill="none"
74
+ stroke="currentColor"
75
+ stroke-width="1.5"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ aria-hidden="true"
79
+ data-rig-category-icon
80
+ >
81
+ <path
82
+ d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"
83
+ />
84
+ <path d="M9 18h6" />
85
+ <path d="M10 22h4" />
86
+ </svg>
87
+ <svg
88
+ v-else-if="category === 'ingredients'"
89
+ :width="iconSize"
90
+ :height="iconSize"
91
+ viewBox="0 0 24 24"
92
+ fill="none"
93
+ stroke="currentColor"
94
+ stroke-width="1.5"
95
+ stroke-linecap="round"
96
+ stroke-linejoin="round"
97
+ aria-hidden="true"
98
+ data-rig-category-icon
99
+ >
100
+ <path
101
+ d="M10 2v7.527a2 2 0 0 1-.211.896L4.72 20.55a1 1 0 0 0 .9 1.45h12.76a1 1 0 0 0 .9-1.45l-5.069-10.127A2 2 0 0 1 14 9.527V2"
102
+ />
103
+ <path d="M8.5 2h7" />
104
+ <path d="M7 16h10" />
105
+ </svg>
106
+ <svg
107
+ v-else-if="category === 'style-guides'"
108
+ :width="iconSize"
109
+ :height="iconSize"
110
+ viewBox="0 0 24 24"
111
+ fill="none"
112
+ stroke="currentColor"
113
+ stroke-width="1.5"
114
+ stroke-linecap="round"
115
+ stroke-linejoin="round"
116
+ aria-hidden="true"
117
+ data-rig-category-icon
118
+ >
119
+ <circle cx="13.5" cy="6.5" r="2.5" />
120
+ <circle cx="19" cy="13" r="2" />
121
+ <circle cx="6" cy="12" r="3" />
122
+ <path d="M12 22s8-4 8-10.4A7.6 7.6 0 0 0 12 4a7.6 7.6 0 0 0-8 7.6C4 18 12 22 12 22z" />
123
+ </svg>
124
+ <svg
125
+ v-else-if="category === 'room-guides'"
126
+ :width="iconSize"
127
+ :height="iconSize"
128
+ viewBox="0 0 24 24"
129
+ fill="none"
130
+ stroke="currentColor"
131
+ stroke-width="1.5"
132
+ stroke-linecap="round"
133
+ stroke-linejoin="round"
134
+ aria-hidden="true"
135
+ data-rig-category-icon
136
+ >
137
+ <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
138
+ <polyline points="9 22 9 12 15 12 15 22" />
139
+ </svg>
140
+ <svg
141
+ v-else-if="category === 'best-of'"
142
+ :width="iconSize"
143
+ :height="iconSize"
144
+ viewBox="0 0 24 24"
145
+ fill="none"
146
+ stroke="currentColor"
147
+ stroke-width="1.5"
148
+ stroke-linecap="round"
149
+ stroke-linejoin="round"
150
+ aria-hidden="true"
151
+ data-rig-category-icon
152
+ >
153
+ <path d="M6 9H4.5a2.5 2.5 0 0 1 0-5C7 4 7 7 7 7" />
154
+ <path d="M18 9h1.5a2.5 2.5 0 0 0 0-5C17 4 17 7 17 7" />
155
+ <path d="M4 22h16" />
156
+ <path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22" />
157
+ <path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22" />
158
+ <path d="M18 2H6v7a6 6 0 0 0 12 0V2Z" />
159
+ </svg>
160
+ <svg
161
+ v-else-if="category === 'comparisons'"
162
+ :width="iconSize"
163
+ :height="iconSize"
164
+ viewBox="0 0 24 24"
165
+ fill="none"
166
+ stroke="currentColor"
167
+ stroke-width="1.5"
168
+ stroke-linecap="round"
169
+ stroke-linejoin="round"
170
+ aria-hidden="true"
171
+ data-rig-category-icon
172
+ >
173
+ <path d="M16 3h5v5" />
174
+ <path d="M8 3H3v5" />
175
+ <path d="M12 22V8" />
176
+ <path d="M21 3l-9 9" />
177
+ <path d="M3 3l9 9" />
178
+ </svg>
179
+ <svg
180
+ v-else-if="category === 'mechanics'"
181
+ :width="iconSize"
182
+ :height="iconSize"
183
+ viewBox="0 0 24 24"
184
+ fill="none"
185
+ stroke="currentColor"
186
+ stroke-width="1.5"
187
+ stroke-linecap="round"
188
+ stroke-linejoin="round"
189
+ aria-hidden="true"
190
+ data-rig-category-icon
191
+ >
192
+ <circle cx="12" cy="12" r="3" />
193
+ <path
194
+ d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"
195
+ />
196
+ </svg>
197
+ <svg
198
+ v-else-if="category === 'guides'"
199
+ :width="iconSize"
200
+ :height="iconSize"
201
+ viewBox="0 0 24 24"
202
+ fill="none"
203
+ stroke="currentColor"
204
+ stroke-width="1.5"
205
+ stroke-linecap="round"
206
+ stroke-linejoin="round"
207
+ aria-hidden="true"
208
+ data-rig-category-icon
209
+ >
210
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
211
+ <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
212
+ </svg>
213
+ <svg
214
+ v-else-if="category === 'recommendations'"
215
+ :width="iconSize"
216
+ :height="iconSize"
217
+ viewBox="0 0 24 24"
218
+ fill="none"
219
+ stroke="currentColor"
220
+ stroke-width="1.5"
221
+ stroke-linecap="round"
222
+ stroke-linejoin="round"
223
+ aria-hidden="true"
224
+ data-rig-category-icon
225
+ >
226
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
227
+ <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
228
+ <path d="M6 8h2" />
229
+ <path d="M6 12h2" />
230
+ <path d="M16 8h2" />
231
+ <path d="M16 12h2" />
232
+ </svg>
233
+ <svg
234
+ v-else-if="category === 'genre-guides'"
235
+ :width="iconSize"
236
+ :height="iconSize"
237
+ viewBox="0 0 24 24"
238
+ fill="none"
239
+ stroke="currentColor"
240
+ stroke-width="1.5"
241
+ stroke-linecap="round"
242
+ stroke-linejoin="round"
243
+ aria-hidden="true"
244
+ data-rig-category-icon
245
+ >
246
+ <circle cx="12" cy="12" r="10" />
247
+ <polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" />
248
+ </svg>
249
+ <svg
250
+ v-else-if="category === 'device-reviews'"
251
+ :width="iconSize"
252
+ :height="iconSize"
253
+ viewBox="0 0 24 24"
254
+ fill="none"
255
+ stroke="currentColor"
256
+ stroke-width="1.5"
257
+ stroke-linecap="round"
258
+ stroke-linejoin="round"
259
+ aria-hidden="true"
260
+ data-rig-category-icon
261
+ >
262
+ <rect x="4" y="2" width="16" height="20" rx="2" />
263
+ <line x1="8" y1="6" x2="16" y2="6" />
264
+ <line x1="8" y1="10" x2="16" y2="10" />
265
+ <line x1="8" y1="14" x2="12" y2="14" />
266
+ <circle cx="12" cy="19" r="1" fill="currentColor" />
267
+ </svg>
268
+ <svg
269
+ v-else-if="category === 'reading-guides'"
270
+ :width="iconSize"
271
+ :height="iconSize"
272
+ viewBox="0 0 24 24"
273
+ fill="none"
274
+ stroke="currentColor"
275
+ stroke-width="1.5"
276
+ stroke-linecap="round"
277
+ stroke-linejoin="round"
278
+ aria-hidden="true"
279
+ data-rig-category-icon
280
+ >
281
+ <path d="M12 2L2 7l10 5 10-5-10-5z" />
282
+ <path d="M2 17l10 5 10-5" />
283
+ <path d="M2 12l10 5 10-5" />
284
+ </svg>
285
+ <svg
286
+ v-else-if="category === 'dog-guides'"
287
+ :width="iconSize"
288
+ :height="iconSize"
289
+ viewBox="0 0 24 24"
290
+ fill="none"
291
+ stroke="currentColor"
292
+ stroke-width="1.5"
293
+ stroke-linecap="round"
294
+ stroke-linejoin="round"
295
+ aria-hidden="true"
296
+ data-rig-category-icon
297
+ >
298
+ <path
299
+ d="M10 5.172C10 3.782 8.423 2.679 6.5 3c-2.823.47-4.113 6.006-4 7 .137 1.217 1.5 2 2.5 2s2-.5 3-1l1-1"
300
+ />
301
+ <path
302
+ d="M14.267 5.172c0-1.39 1.577-2.493 3.5-2.172 2.823.47 4.113 6.006 4 7-.137 1.217-1.5 2-2.5 2s-2-.5-3-1l-1-1"
303
+ />
304
+ <path d="M8 14v.5" />
305
+ <path d="M16 14v.5" />
306
+ <path d="M11.25 16.25h1.5L12 17l-.75-.75z" />
307
+ <path
308
+ d="M4.42 11.247A13.152 13.152 0 0 0 4 14.556C4 18.728 7.582 21 12 21s8-2.272 8-6.444a11.7 11.7 0 0 0-.42-3.31"
309
+ />
310
+ </svg>
311
+ <svg
312
+ v-else-if="category === 'cat-guides'"
313
+ :width="iconSize"
314
+ :height="iconSize"
315
+ viewBox="0 0 24 24"
316
+ fill="none"
317
+ stroke="currentColor"
318
+ stroke-width="1.5"
319
+ stroke-linecap="round"
320
+ stroke-linejoin="round"
321
+ aria-hidden="true"
322
+ data-rig-category-icon
323
+ >
324
+ <path d="M12 5c-2.76 0-5 2.24-5 5v6a4 4 0 0 0 4 4h2a4 4 0 0 0 4-4v-6c0-2.76-2.24-5-5-5z" />
325
+ <path d="M7 5L5 2" />
326
+ <path d="M17 5l2-3" />
327
+ <circle cx="10" cy="11" r="1" fill="currentColor" />
328
+ <circle cx="14" cy="11" r="1" fill="currentColor" />
329
+ <path d="M10 14h4" />
330
+ <path d="M12 14v2" />
331
+ </svg>
332
+ <svg
333
+ v-else-if="category === 'care'"
334
+ :width="iconSize"
335
+ :height="iconSize"
336
+ viewBox="0 0 24 24"
337
+ fill="none"
338
+ stroke="currentColor"
339
+ stroke-width="1.5"
340
+ stroke-linecap="round"
341
+ stroke-linejoin="round"
342
+ aria-hidden="true"
343
+ data-rig-category-icon
344
+ >
345
+ <path
346
+ d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3.332.88-4.5 2.17A6.42 6.42 0 0 0 7.5 3 5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7 7-7z"
347
+ />
348
+ </svg>
349
+ <svg
350
+ v-else-if="category === 'education'"
351
+ :width="iconSize"
352
+ :height="iconSize"
353
+ viewBox="0 0 24 24"
354
+ fill="none"
355
+ stroke="currentColor"
356
+ stroke-width="1.5"
357
+ stroke-linecap="round"
358
+ stroke-linejoin="round"
359
+ aria-hidden="true"
360
+ data-rig-category-icon
361
+ >
362
+ <path d="M22 10v6M2 10l10-5 10 5-10 5z" />
363
+ <path d="M6 12v5c0 1.66 2.69 3 6 3s6-1.34 6-3v-5" />
364
+ </svg>
365
+ <svg
366
+ v-else-if="category === 'organization'"
367
+ :width="iconSize"
368
+ :height="iconSize"
369
+ viewBox="0 0 24 24"
370
+ fill="none"
371
+ stroke="currentColor"
372
+ stroke-width="1.5"
373
+ stroke-linecap="round"
374
+ stroke-linejoin="round"
375
+ aria-hidden="true"
376
+ data-rig-category-icon
377
+ >
378
+ <rect x="3" y="3" width="7" height="7" />
379
+ <rect x="14" y="3" width="7" height="7" />
380
+ <rect x="3" y="14" width="7" height="7" />
381
+ <rect x="14" y="14" width="7" height="7" />
382
+ </svg>
383
+ <svg
384
+ v-else-if="category === 'reviews'"
385
+ :width="iconSize"
386
+ :height="iconSize"
387
+ viewBox="0 0 24 24"
388
+ fill="none"
389
+ stroke="currentColor"
390
+ stroke-width="1.5"
391
+ stroke-linecap="round"
392
+ stroke-linejoin="round"
393
+ aria-hidden="true"
394
+ data-rig-category-icon
395
+ >
396
+ <polygon
397
+ points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
398
+ />
399
+ </svg>
400
+ <svg
401
+ v-else-if="category === 'buying-guides'"
402
+ :width="iconSize"
403
+ :height="iconSize"
404
+ viewBox="0 0 24 24"
405
+ fill="none"
406
+ stroke="currentColor"
407
+ stroke-width="1.5"
408
+ stroke-linecap="round"
409
+ stroke-linejoin="round"
410
+ aria-hidden="true"
411
+ data-rig-category-icon
412
+ >
413
+ <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" />
414
+ <line x1="3" y1="6" x2="21" y2="6" />
415
+ <path d="M16 10a4 4 0 0 1-8 0" />
416
+ </svg>
417
+ <svg
418
+ v-else-if="category === 'culture'"
419
+ :width="iconSize"
420
+ :height="iconSize"
421
+ viewBox="0 0 24 24"
422
+ fill="none"
423
+ stroke="currentColor"
424
+ stroke-width="1.5"
425
+ stroke-linecap="round"
426
+ stroke-linejoin="round"
427
+ aria-hidden="true"
428
+ data-rig-category-icon
429
+ >
430
+ <path
431
+ d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"
432
+ />
433
+ <path d="M18 14h-8" />
434
+ <path d="M15 18h-5" />
435
+ <path d="M10 6h8v4h-8V6z" />
436
+ </svg>
437
+ <svg
438
+ v-else
439
+ :width="iconSize"
440
+ :height="iconSize"
441
+ viewBox="0 0 24 24"
442
+ fill="none"
443
+ stroke="currentColor"
444
+ stroke-width="1.5"
445
+ stroke-linecap="round"
446
+ stroke-linejoin="round"
447
+ aria-hidden="true"
448
+ data-rig-category-icon
449
+ >
450
+ <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
451
+ <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
452
+ </svg>
453
+ </template>
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import {
4
+ useRuntimeConfig,
5
+ useSiteConfig,
6
+ useSeoMeta,
7
+ useSchemaOrg,
8
+ defineWebPage,
9
+ defineBreadcrumb,
10
+ navigateTo,
11
+ } from '#imports'
12
+
13
+ const runtimeConfig = useRuntimeConfig()
14
+ const personaSlugs = runtimeConfig.contentPersonaSlugs.split(',')
15
+
16
+ const personaLabels = computed(() =>
17
+ personaSlugs.map((slug: string) => ({
18
+ slug,
19
+ label: slug
20
+ .split('-')
21
+ .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))
22
+ .join(' '),
23
+ })),
24
+ )
25
+
26
+ const siteConfig = useSiteConfig()
27
+
28
+ useSeoMeta({
29
+ title: `Best For You | ${siteConfig.name}`,
30
+ description: `Personalized product picks based on your quiz results. Find the best ${siteConfig.name} recommendations for your type.`,
31
+ })
32
+
33
+ useSchemaOrg([
34
+ defineWebPage({ name: `Best For You | ${siteConfig.name}` }),
35
+ defineBreadcrumb({
36
+ itemListElement: [
37
+ { name: 'Home', item: '/', position: 1 },
38
+ { name: 'Best For', item: '/best-for', position: 2 },
39
+ ],
40
+ }),
41
+ ])
42
+ </script>
43
+
44
+ <template>
45
+ <div>
46
+ <ContentBreadcrumbs
47
+ :items="[
48
+ { label: 'Home', path: '/' },
49
+ { label: 'Best For', path: '/best-for' },
50
+ ]"
51
+ />
52
+ <Section
53
+ title="Best For You"
54
+ subtitle="Personalized recommendations based on your quiz results"
55
+ >
56
+ <div v-if="personaLabels.length" data-rig-article-grid>
57
+ <Card
58
+ v-for="persona in personaLabels"
59
+ :key="persona.slug"
60
+ interactive
61
+ role="link"
62
+ :tabindex="0"
63
+ @click="navigateTo(`/best-for/${persona.slug}`)"
64
+ @keydown.enter="navigateTo(`/best-for/${persona.slug}`)"
65
+ >
66
+ <NuxtLink :to="`/best-for/${persona.slug}`" data-rig-article-card-link>
67
+ <span data-rig-persona-badge>Persona</span>
68
+ <span data-rig-article-card-title>{{ persona.label }}</span>
69
+ <span data-rig-article-card-description
70
+ >See our curated product picks for this type.</span
71
+ >
72
+ </NuxtLink>
73
+ </Card>
74
+ </div>
75
+ <p v-else data-rig-empty-state>
76
+ No persona results available yet. Take a quiz to get personalized picks.
77
+ </p>
78
+ </Section>
79
+ </div>
80
+ </template>