@eox/pages-theme-eox 1.9.0 → 2.0.0-alpha.10
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/.gitlab-ci.yml +4 -1
- package/README.md +88 -11
- package/cypress/support/mocks/authors.data.js +14 -0
- package/cypress/support/mocks/helpers.js +65 -4
- package/cypress/support/mocks/vitepress.js +8 -1
- package/cypress.config.js +14 -0
- package/package.json +3 -1
- package/src/Layout.vue +69 -1
- package/src/authors.data.js +14 -0
- package/src/components/BlogList.vue +655 -0
- package/src/components/BlogPost.vue +472 -0
- package/src/components/CTASection.vue +18 -4
- package/src/components/FeatureCard/Index.vue +462 -31
- package/src/components/FeatureCard/Poster.vue +25 -3
- package/src/components/FeatureSection.cy.js +16 -0
- package/src/components/FeatureSection.vue +3 -8
- package/src/components/FeaturesGallery.vue +34 -2
- package/src/components/Footer.cy.js +52 -2
- package/src/components/Footer.vue +17 -5
- package/src/components/HeroSection.cy.js +20 -0
- package/src/components/HeroSection.vue +140 -25
- package/src/components/ImageCarousel.vue +11 -2
- package/src/components/ImageElement.vue +16 -4
- package/src/components/ImageGallery.vue +29 -4
- package/src/components/LogoSection.vue +26 -3
- package/src/components/MobileNavDropdown.vue +13 -3
- package/src/components/NavBar.cy.js +15 -0
- package/src/components/NavBar.vue +149 -21
- package/src/components/NavDropdown.vue +12 -2
- package/src/components/OpenPositions.vue +292 -0
- package/src/components/SectionHeader.cy.js +54 -0
- package/src/components/SectionHeader.vue +115 -0
- package/src/components/TagList.vue +311 -0
- package/src/components/TeamPage.vue +347 -0
- package/src/components/TestimonialsSection.cy.js +76 -0
- package/src/components/TestimonialsSection.vue +368 -0
- package/src/components/TextSection.cy.js +115 -0
- package/src/components/TextSection.vue +396 -0
- package/src/helpers.js +182 -37
- package/src/helpers.test.js +181 -0
- package/src/index.js +16 -0
- package/src/pages/author/[author].md +5 -0
- package/src/pages/author/[author].paths.js +60 -0
- package/src/pages/blog.md +5 -0
- package/src/pages/tag/[tag].md +5 -0
- package/src/pages/tag/[tag].paths.js +49 -0
- package/src/pages/tag.md +5 -0
- package/src/pages/team.md +5 -0
- package/src/posts.data.js +134 -0
- package/src/style.css +389 -8
- package/src/tagUtils.js +148 -0
- package/src/team.data.js +1 -1
- package/src/vitepressConfig.mjs +101 -1
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="blog-post-page VPDoc max-w-[1000px] mx-auto px-6 md:px-8">
|
|
3
|
+
<div class="blog-post-layout">
|
|
4
|
+
<!-- Main Article Column -->
|
|
5
|
+
<div class="blog-content-col">
|
|
6
|
+
<!-- Cover Image -->
|
|
7
|
+
<div
|
|
8
|
+
v-if="coverImage"
|
|
9
|
+
class="mb-8 rounded-2xl overflow-hidden shadow-sm max-h-[550px] bg-slate-50 border border-slate-100"
|
|
10
|
+
>
|
|
11
|
+
<img
|
|
12
|
+
:src="getCoverImageUrl(coverImage)"
|
|
13
|
+
:alt="frontmatter.title"
|
|
14
|
+
class="w-full h-full object-cover max-h-[550px]"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Title -->
|
|
19
|
+
<h1 class="blog-post-title">
|
|
20
|
+
{{ frontmatter.title }}
|
|
21
|
+
</h1>
|
|
22
|
+
|
|
23
|
+
<!-- Metadata Row (Date & Authors) and Tags Underneath -->
|
|
24
|
+
<div class="mb-8 border-b border-slate-100 pb-6">
|
|
25
|
+
<div class="blog-metadata">
|
|
26
|
+
<!-- Date Span -->
|
|
27
|
+
<span v-if="frontmatter.date" class="metadata-item">
|
|
28
|
+
<i class="mdi mdi-clock-outline metadata-icon"></i>
|
|
29
|
+
<time :datetime="frontmatter.date" class="metadata-text">
|
|
30
|
+
{{ formatDate(frontmatter.date) }}
|
|
31
|
+
</time>
|
|
32
|
+
</span>
|
|
33
|
+
|
|
34
|
+
<!-- Author Span -->
|
|
35
|
+
<span v-if="authors && authors.length" class="metadata-item">
|
|
36
|
+
<i class="mdi mdi-account-outline metadata-icon"></i>
|
|
37
|
+
<span class="metadata-text label-text">By</span>
|
|
38
|
+
<span
|
|
39
|
+
v-for="(authorId, idx) in authors"
|
|
40
|
+
:key="authorId"
|
|
41
|
+
class="author-item"
|
|
42
|
+
>
|
|
43
|
+
<a
|
|
44
|
+
:href="withBase(`/author/${authorId}/`)"
|
|
45
|
+
class="author-link-active"
|
|
46
|
+
>
|
|
47
|
+
{{ getAuthorName(authorId) }}
|
|
48
|
+
</a>
|
|
49
|
+
<span v-if="idx < authors.length - 1" class="author-separator"
|
|
50
|
+
>,</span
|
|
51
|
+
>
|
|
52
|
+
</span>
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Tags right underneath -->
|
|
57
|
+
<div
|
|
58
|
+
v-if="frontmatter.tags && frontmatter.tags.length"
|
|
59
|
+
class="post-tags-container"
|
|
60
|
+
>
|
|
61
|
+
<a
|
|
62
|
+
v-for="tag in frontmatter.tags"
|
|
63
|
+
:key="tag"
|
|
64
|
+
:href="withBase(`/tag/${tag}/`)"
|
|
65
|
+
:style="getTagStyle(tag)"
|
|
66
|
+
class="post-tag"
|
|
67
|
+
>
|
|
68
|
+
{{ tag }}
|
|
69
|
+
</a>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- Main Content -->
|
|
74
|
+
<div class="vp-doc prose prose-slate max-w-none">
|
|
75
|
+
<Content />
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Sidebar Column -->
|
|
80
|
+
<div class="blog-sidebar-col">
|
|
81
|
+
<!-- Table of Contents (On This Page) -->
|
|
82
|
+
<div v-if="headers && headers.length" class="toc-container">
|
|
83
|
+
<VPDocAsideOutline />
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- Hot Tags -->
|
|
87
|
+
<div class="sidebar-widget">
|
|
88
|
+
<h3 class="sidebar-title">Hot tags</h3>
|
|
89
|
+
<div class="sidebar-tags-container">
|
|
90
|
+
<a
|
|
91
|
+
v-for="tag in calculatedHotTags"
|
|
92
|
+
:key="tag.name"
|
|
93
|
+
:href="withBase(`/tag/${tag.name}/`)"
|
|
94
|
+
:style="getTagStyle(tag.name)"
|
|
95
|
+
class="sidebar-tag"
|
|
96
|
+
>
|
|
97
|
+
{{ tag.name }} ({{ tag.count }})
|
|
98
|
+
</a>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<!-- Recent Posts -->
|
|
103
|
+
<div class="sidebar-widget">
|
|
104
|
+
<h3 class="sidebar-title">Recent posts</h3>
|
|
105
|
+
<div class="space-y-4">
|
|
106
|
+
<div
|
|
107
|
+
v-for="post in calculatedRecentPosts"
|
|
108
|
+
:key="post.url"
|
|
109
|
+
class="flex flex-col gap-1 border-b border-slate-100 last:border-none pb-3 last:pb-0"
|
|
110
|
+
>
|
|
111
|
+
<a
|
|
112
|
+
:href="withBase(post.url)"
|
|
113
|
+
class="text-sm font-semibold text-slate-900 hover:text-primary leading-snug transition-colors line-clamp-2"
|
|
114
|
+
>
|
|
115
|
+
{{ post.title }}
|
|
116
|
+
</a>
|
|
117
|
+
<time :datetime="post.date" class="text-xs text-slate-500">
|
|
118
|
+
{{ formatDate(post.date) }}
|
|
119
|
+
</time>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</template>
|
|
127
|
+
|
|
128
|
+
<script setup>
|
|
129
|
+
import { useData, withBase } from "vitepress";
|
|
130
|
+
import { computed } from "vue";
|
|
131
|
+
import { data as posts } from "../posts.data.js";
|
|
132
|
+
import { data as authorsList } from "../authors.data.js";
|
|
133
|
+
import { getTagColor, getTagStyle } from "../tagUtils.js";
|
|
134
|
+
import VPDocAsideOutline from "vitepress/dist/client/theme-default/components/VPDocAsideOutline.vue";
|
|
135
|
+
|
|
136
|
+
const { frontmatter, page } = useData();
|
|
137
|
+
|
|
138
|
+
// Set default outline level to deep for blog posts so h3 headers are resolved
|
|
139
|
+
if (frontmatter.value && frontmatter.value.outline === undefined) {
|
|
140
|
+
frontmatter.value.outline = "deep";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Extract headers
|
|
144
|
+
const headers = computed(() => {
|
|
145
|
+
return page.value.headers || [];
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Form cover image URL correctly (prefixing filename with /images/)
|
|
149
|
+
function getCoverImageUrl(coverImage) {
|
|
150
|
+
if (!coverImage) return "";
|
|
151
|
+
if (coverImage.startsWith("http") || coverImage.startsWith("//")) {
|
|
152
|
+
return coverImage;
|
|
153
|
+
}
|
|
154
|
+
const base = withBase("/");
|
|
155
|
+
if (base && base !== "/" && coverImage.startsWith(base)) {
|
|
156
|
+
return coverImage;
|
|
157
|
+
}
|
|
158
|
+
const path = coverImage.startsWith("/")
|
|
159
|
+
? coverImage
|
|
160
|
+
: `/images/${coverImage}`;
|
|
161
|
+
return withBase(path);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Format publication date
|
|
165
|
+
function formatDate(dateStr) {
|
|
166
|
+
if (!dateStr) return "";
|
|
167
|
+
const date = new Date(dateStr);
|
|
168
|
+
return date.toLocaleDateString("en-US", {
|
|
169
|
+
year: "numeric",
|
|
170
|
+
month: "long",
|
|
171
|
+
day: "numeric",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Extract authors
|
|
176
|
+
const authors = computed(() => {
|
|
177
|
+
const author = frontmatter.value.author || "";
|
|
178
|
+
return Array.isArray(author) ? author : [author];
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Get author full name
|
|
182
|
+
function getAuthorName(authorId) {
|
|
183
|
+
const auth = authorsList.find((a) => a.id === authorId);
|
|
184
|
+
return auth ? auth.name : authorId;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Cover image path
|
|
188
|
+
const coverImage = computed(() => {
|
|
189
|
+
return frontmatter.value.coverImage || frontmatter.value.image || "";
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Dynamic Recent Posts (excluding current page)
|
|
193
|
+
const calculatedRecentPosts = computed(() => {
|
|
194
|
+
const rel = page.value.relativePath || "";
|
|
195
|
+
const current = (
|
|
196
|
+
"/" + rel.replace(/(^|\/)index\.md$/, "$1").replace(/\.md$/, "")
|
|
197
|
+
)
|
|
198
|
+
.replace(/\/$/, "")
|
|
199
|
+
.replace(/\.html$/, "");
|
|
200
|
+
|
|
201
|
+
return posts
|
|
202
|
+
.filter((p) => {
|
|
203
|
+
const pUrl = (p.url || "").replace(/\/$/, "").replace(/\.html$/, "");
|
|
204
|
+
return pUrl !== current && p.title !== frontmatter.value.title;
|
|
205
|
+
})
|
|
206
|
+
.slice(0, 5);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Dynamic Hot Tags
|
|
210
|
+
const calculatedHotTags = computed(() => {
|
|
211
|
+
const tagCounts = {};
|
|
212
|
+
posts.forEach((p) => {
|
|
213
|
+
if (Array.isArray(p.tags)) {
|
|
214
|
+
p.tags.forEach((t) => {
|
|
215
|
+
const clean = t.trim();
|
|
216
|
+
if (clean) {
|
|
217
|
+
tagCounts[clean] = (tagCounts[clean] || 0) + 1;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return Object.entries(tagCounts)
|
|
223
|
+
.map(([name, count]) => ({
|
|
224
|
+
name,
|
|
225
|
+
count,
|
|
226
|
+
color: getTagColor(name),
|
|
227
|
+
}))
|
|
228
|
+
.sort((a, b) => b.count - a.count)
|
|
229
|
+
.slice(0, 10);
|
|
230
|
+
});
|
|
231
|
+
</script>
|
|
232
|
+
|
|
233
|
+
<style scoped>
|
|
234
|
+
.blog-post-page {
|
|
235
|
+
width: 100% !important;
|
|
236
|
+
max-width: 1000px !important;
|
|
237
|
+
margin-left: auto !important;
|
|
238
|
+
margin-right: auto !important;
|
|
239
|
+
padding-left: 1.5rem !important;
|
|
240
|
+
padding-right: 1.5rem !important;
|
|
241
|
+
box-sizing: border-box !important;
|
|
242
|
+
padding-top: calc(var(--vp-nav-height) + 0.5rem) !important;
|
|
243
|
+
padding-bottom: 2rem !important;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@media (min-width: 768px) {
|
|
247
|
+
.blog-post-page {
|
|
248
|
+
padding-left: 2rem !important;
|
|
249
|
+
padding-right: 2rem !important;
|
|
250
|
+
padding-top: calc(var(--vp-nav-height) + 1.5rem) !important;
|
|
251
|
+
padding-bottom: 4rem !important;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.blog-post-layout {
|
|
256
|
+
display: grid;
|
|
257
|
+
grid-template-columns: 1fr;
|
|
258
|
+
gap: 2.5rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.blog-content-col {
|
|
262
|
+
width: 100%;
|
|
263
|
+
min-width: 0;
|
|
264
|
+
/* Prevent Beercss style inheritance */
|
|
265
|
+
background: transparent !important;
|
|
266
|
+
box-shadow: none !important;
|
|
267
|
+
border: none !important;
|
|
268
|
+
padding: 0 !important;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.blog-sidebar-col {
|
|
272
|
+
width: 100%;
|
|
273
|
+
display: flex;
|
|
274
|
+
flex-direction: column;
|
|
275
|
+
gap: 2.5rem;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (min-width: 1024px) {
|
|
279
|
+
.blog-post-layout {
|
|
280
|
+
grid-template-columns: minmax(0, 1fr) 280px;
|
|
281
|
+
gap: 4rem;
|
|
282
|
+
align-items: start;
|
|
283
|
+
}
|
|
284
|
+
.blog-sidebar-col {
|
|
285
|
+
position: sticky !important;
|
|
286
|
+
align-self: start !important;
|
|
287
|
+
top: calc(var(--vp-nav-height) + 2rem) !important;
|
|
288
|
+
max-height: calc(100vh - var(--vp-nav-height) - 4rem);
|
|
289
|
+
overflow-y: auto;
|
|
290
|
+
scrollbar-width: none; /* Hide scrollbar for cleaner look */
|
|
291
|
+
}
|
|
292
|
+
.blog-sidebar-col::-webkit-scrollbar {
|
|
293
|
+
display: none;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.blog-post-title {
|
|
298
|
+
font-family: "Inter", sans-serif;
|
|
299
|
+
font-style: normal;
|
|
300
|
+
font-weight: 700;
|
|
301
|
+
font-size: 28px;
|
|
302
|
+
line-height: 36px;
|
|
303
|
+
letter-spacing: 0.5px;
|
|
304
|
+
color: #020617; /* slate-950 */
|
|
305
|
+
margin-top: 0;
|
|
306
|
+
margin-bottom: 1.5rem;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@media (min-width: 768px) {
|
|
310
|
+
.blog-post-title {
|
|
311
|
+
font-size: 36px;
|
|
312
|
+
line-height: 44px;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.blog-metadata {
|
|
317
|
+
display: flex;
|
|
318
|
+
flex-wrap: wrap;
|
|
319
|
+
align-items: center;
|
|
320
|
+
gap: 1.5rem;
|
|
321
|
+
margin-bottom: 1rem;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.metadata-item {
|
|
325
|
+
display: inline-flex;
|
|
326
|
+
align-items: center;
|
|
327
|
+
gap: 0.5rem;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.metadata-icon {
|
|
331
|
+
font-size: 1.1rem !important;
|
|
332
|
+
color: #64748b;
|
|
333
|
+
display: inline-flex;
|
|
334
|
+
align-items: center;
|
|
335
|
+
justify-content: center;
|
|
336
|
+
height: 100%;
|
|
337
|
+
transform: translateY(
|
|
338
|
+
0.5px
|
|
339
|
+
); /* Micro-adjustment for perfect vertical alignment */
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.metadata-text {
|
|
343
|
+
font-size: 0.875rem;
|
|
344
|
+
color: #64748b;
|
|
345
|
+
line-height: 1.2;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.label-text {
|
|
349
|
+
margin-right: -0.125rem;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.author-item {
|
|
353
|
+
display: inline-flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.author-link-active {
|
|
358
|
+
font-size: 0.875rem;
|
|
359
|
+
font-weight: 600;
|
|
360
|
+
color: var(--vp-c-brand, #004170) !important;
|
|
361
|
+
text-decoration: none;
|
|
362
|
+
transition:
|
|
363
|
+
color 0.15s ease,
|
|
364
|
+
text-decoration 0.15s ease;
|
|
365
|
+
line-height: 1.2;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.author-link-active:hover {
|
|
369
|
+
color: var(--vp-c-brand-next, #002d50) !important;
|
|
370
|
+
text-decoration: underline !important;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.author-separator {
|
|
374
|
+
color: #64748b;
|
|
375
|
+
margin-right: 0.25rem;
|
|
376
|
+
font-size: 0.875rem;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.sidebar-widget {
|
|
380
|
+
display: flex;
|
|
381
|
+
flex-direction: column;
|
|
382
|
+
gap: 0.75rem;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.sidebar-title {
|
|
386
|
+
font-size: 0.75rem;
|
|
387
|
+
font-weight: 600;
|
|
388
|
+
text-transform: uppercase;
|
|
389
|
+
letter-spacing: 0.05em;
|
|
390
|
+
color: #64748b; /* slate-500 */
|
|
391
|
+
border-bottom: 1px solid #f1f5f9; /* slate-100 */
|
|
392
|
+
padding-bottom: 0.5rem;
|
|
393
|
+
margin: 0;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.sidebar-tags-container {
|
|
397
|
+
display: flex !important;
|
|
398
|
+
flex-wrap: wrap !important;
|
|
399
|
+
gap: 0.5rem !important;
|
|
400
|
+
margin-top: 0.25rem !important;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.sidebar-tag {
|
|
404
|
+
display: inline-flex !important;
|
|
405
|
+
align-items: center !important;
|
|
406
|
+
font-size: 0.75rem !important;
|
|
407
|
+
font-weight: 600 !important;
|
|
408
|
+
padding: 0.25rem 0.75rem !important;
|
|
409
|
+
border-radius: 4px !important;
|
|
410
|
+
text-decoration: none !important;
|
|
411
|
+
line-height: 1.2 !important;
|
|
412
|
+
transition:
|
|
413
|
+
opacity 0.2s ease,
|
|
414
|
+
transform 0.1s ease !important;
|
|
415
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.sidebar-tag:hover {
|
|
419
|
+
opacity: 0.8 !important;
|
|
420
|
+
transform: translateY(-1px) !important;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.post-tags-container {
|
|
424
|
+
display: flex !important;
|
|
425
|
+
flex-wrap: wrap !important;
|
|
426
|
+
gap: 0.5rem !important;
|
|
427
|
+
margin-top: 0.75rem !important;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.post-tag {
|
|
431
|
+
display: inline-flex !important;
|
|
432
|
+
align-items: center !important;
|
|
433
|
+
font-size: 0.75rem !important;
|
|
434
|
+
font-weight: 600 !important;
|
|
435
|
+
padding: 0.25rem 0.75rem !important;
|
|
436
|
+
border-radius: 4px !important;
|
|
437
|
+
text-decoration: none !important;
|
|
438
|
+
line-height: 1.2 !important;
|
|
439
|
+
transition:
|
|
440
|
+
opacity 0.2s ease,
|
|
441
|
+
transform 0.1s ease !important;
|
|
442
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.post-tag:hover {
|
|
446
|
+
opacity: 0.8 !important;
|
|
447
|
+
transform: translateY(-1px) !important;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Force VPDocAsideOutline to be visible on all screen sizes in our custom layout */
|
|
451
|
+
:deep(.VPDocAsideOutline) {
|
|
452
|
+
display: block !important;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
:deep(.VPDocAsideOutline.has-outline) {
|
|
456
|
+
display: block !important;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.line-clamp-2 {
|
|
460
|
+
display: -webkit-box;
|
|
461
|
+
-webkit-line-clamp: 2;
|
|
462
|
+
-webkit-box-orient: vertical;
|
|
463
|
+
overflow: hidden;
|
|
464
|
+
}
|
|
465
|
+
</style>
|
|
466
|
+
|
|
467
|
+
<style>
|
|
468
|
+
/* Global override for main tag to ensure position: sticky is never blocked on blog pages */
|
|
469
|
+
main {
|
|
470
|
+
overflow: visible !important;
|
|
471
|
+
}
|
|
472
|
+
</style>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
:class="
|
|
5
5
|
dark !== undefined
|
|
6
6
|
? 'primary primary-gradient-bg'
|
|
7
|
-
: 'surface-container-
|
|
7
|
+
: 'surface-container-lowest'
|
|
8
8
|
"
|
|
9
9
|
>
|
|
10
10
|
<div class="large-space"></div>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
v-if="primaryButton"
|
|
19
19
|
class="button extra small-margin responsive-mobile"
|
|
20
20
|
:class="dark !== undefined ? 'surface' : 'primary'"
|
|
21
|
-
:href="
|
|
21
|
+
:href="resolvedPrimaryLink"
|
|
22
22
|
@click="
|
|
23
23
|
trackEvent(['CTA', 'Click', 'CTA Section Primary', primaryButton])
|
|
24
24
|
"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<a
|
|
28
28
|
v-if="secondaryButton"
|
|
29
29
|
class="button extra small-margin secondary responsive-mobile"
|
|
30
|
-
:href="
|
|
30
|
+
:href="resolvedSecondaryLink"
|
|
31
31
|
@click="
|
|
32
32
|
trackEvent(['CTA', 'Click', 'CTA Section Secondary', secondaryButton])
|
|
33
33
|
"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
v-if="altButton"
|
|
38
38
|
class="button border extra small-margin responsive-mobile"
|
|
39
39
|
:class="dark !== undefined ? 'white-text' : ''"
|
|
40
|
-
:href="
|
|
40
|
+
:href="resolvedAltLink"
|
|
41
41
|
@click="trackEvent(['CTA', 'Click', 'CTA Section Alt', altButton])"
|
|
42
42
|
>{{ altButton }}</a
|
|
43
43
|
>
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
</template>
|
|
50
50
|
|
|
51
51
|
<script setup>
|
|
52
|
+
import { computed } from "vue";
|
|
53
|
+
import { withBase } from "vitepress";
|
|
52
54
|
import { trackEvent } from "../helpers";
|
|
53
55
|
const props = defineProps([
|
|
54
56
|
"dark",
|
|
@@ -61,6 +63,18 @@ const props = defineProps([
|
|
|
61
63
|
"tagline",
|
|
62
64
|
"title",
|
|
63
65
|
]);
|
|
66
|
+
|
|
67
|
+
function resolveLink(link) {
|
|
68
|
+
if (!link) return "";
|
|
69
|
+
if (link.startsWith("http") || link.startsWith("//")) return link;
|
|
70
|
+
const base = withBase("/");
|
|
71
|
+
if (base && base !== "/" && link.startsWith(base)) return link;
|
|
72
|
+
return link.startsWith("/") ? withBase(link) : withBase("/" + link);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const resolvedPrimaryLink = computed(() => resolveLink(props.primaryLink));
|
|
76
|
+
const resolvedSecondaryLink = computed(() => resolveLink(props.secondaryLink));
|
|
77
|
+
const resolvedAltLink = computed(() => resolveLink(props.altLink));
|
|
64
78
|
</script>
|
|
65
79
|
|
|
66
80
|
<style>
|