@eox/pages-theme-eox 1.9.0 → 2.0.0-alpha.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/README.md +13 -0
- package/cypress/support/mocks/vitepress.js +8 -1
- package/cypress.config.js +5 -0
- package/package.json +1 -1
- package/src/Layout.vue +58 -1
- package/src/authors.data.js +14 -0
- package/src/components/BlogList.vue +636 -0
- package/src/components/BlogPost.vue +450 -0
- package/src/components/FeatureCard/Index.vue +370 -26
- package/src/components/HeroSection.vue +1 -1
- package/src/components/SectionHeader.cy.js +54 -0
- package/src/components/SectionHeader.vue +115 -0
- package/src/components/TagList.vue +293 -0
- package/src/components/TeamPage.vue +333 -0
- package/src/components/TestimonialsSection.cy.js +79 -0
- package/src/components/TestimonialsSection.vue +265 -0
- package/src/index.js +12 -0
- package/src/pages/author/[author].md +4 -0
- package/src/pages/author/[author].paths.js +60 -0
- package/src/pages/blog.md +4 -0
- package/src/pages/tag/[tag].md +4 -0
- package/src/pages/tag/[tag].paths.js +49 -0
- package/src/pages/tag.md +4 -0
- package/src/pages/team.md +4 -0
- package/src/posts.data.js +96 -0
- package/src/style.css +160 -0
- package/src/tagUtils.js +148 -0
- package/src/team.data.js +1 -1
- package/src/vitepressConfig.mjs +97 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="blog-landing max-w-[1200px] mx-auto px-6 md:px-8">
|
|
3
|
+
<!-- Page Header -->
|
|
4
|
+
<div class="blog-landing-header" role="banner">
|
|
5
|
+
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
|
6
|
+
<div>
|
|
7
|
+
<h1 class="blog-landing-title">
|
|
8
|
+
EOX Blog
|
|
9
|
+
</h1>
|
|
10
|
+
<p class="blog-landing-subtitle">
|
|
11
|
+
Insights, updates, and deep dives from our geospatial experts.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<!-- Active Filter Banner -->
|
|
17
|
+
<div
|
|
18
|
+
v-if="activeTag || activeAuthor"
|
|
19
|
+
class="active-filter-banner"
|
|
20
|
+
>
|
|
21
|
+
<span class="filter-label">Active filter:</span>
|
|
22
|
+
<span
|
|
23
|
+
v-if="activeTag"
|
|
24
|
+
:style="getTagStyle(activeTag)"
|
|
25
|
+
class="active-filter-tag shadow-sm inline-flex items-center gap-1.5"
|
|
26
|
+
>
|
|
27
|
+
{{ activeTag }}
|
|
28
|
+
</span>
|
|
29
|
+
<span
|
|
30
|
+
v-if="activeAuthor"
|
|
31
|
+
class="active-filter-author shadow-sm inline-flex items-center gap-1.5"
|
|
32
|
+
>
|
|
33
|
+
Author: {{ getAuthorName(activeAuthor) }}
|
|
34
|
+
</span>
|
|
35
|
+
<button
|
|
36
|
+
@click="clearFilters"
|
|
37
|
+
class="clear-filter-btn"
|
|
38
|
+
>
|
|
39
|
+
<i class="mdi mdi-close-circle"></i>
|
|
40
|
+
<span>Clear Filter</span>
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- Grid of Blog Posts -->
|
|
46
|
+
<div class="blog-post-layout">
|
|
47
|
+
<!-- Main Content Column -->
|
|
48
|
+
<div class="blog-content-col">
|
|
49
|
+
<div v-if="paginatedPosts.length" class="posts-grid">
|
|
50
|
+
<FeatureCard
|
|
51
|
+
v-for="post in paginatedPosts"
|
|
52
|
+
:key="post.url"
|
|
53
|
+
:title="post.title"
|
|
54
|
+
:content="post.excerpt"
|
|
55
|
+
:image="post.coverImage ? getCoverImageUrl(post.coverImage) : undefined"
|
|
56
|
+
:date="post.date"
|
|
57
|
+
:authors="post.authors"
|
|
58
|
+
:chips="post.tags ? post.tags.map(t => ({ text: t })) : []"
|
|
59
|
+
:link="{
|
|
60
|
+
text: 'Read more',
|
|
61
|
+
href: post.url,
|
|
62
|
+
target: '_self'
|
|
63
|
+
}"
|
|
64
|
+
variant="cover"
|
|
65
|
+
class="h-full bg-surface-container-low"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Empty State -->
|
|
70
|
+
<div
|
|
71
|
+
v-else
|
|
72
|
+
class="text-center py-20 bg-surface-container-low rounded-2xl border border-slate-100/50 max-w-xl mx-auto my-12"
|
|
73
|
+
>
|
|
74
|
+
<i class="mdi mdi-text-search text-5xl text-slate-300 mb-4 block"></i>
|
|
75
|
+
<h3 class="text-xl font-bold text-slate-900 mb-2">No posts found</h3>
|
|
76
|
+
<p class="text-slate-500 mb-6">
|
|
77
|
+
We couldn't find any articles matching your filters.
|
|
78
|
+
</p>
|
|
79
|
+
<button
|
|
80
|
+
@click="clearFilters"
|
|
81
|
+
class="px-5 py-2.5 text-sm font-semibold text-white bg-primary hover:bg-primary/90 rounded-xl transition-colors shadow-sm"
|
|
82
|
+
>
|
|
83
|
+
Clear Filters
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- Pagination -->
|
|
88
|
+
<nav
|
|
89
|
+
v-if="totalPages > 1"
|
|
90
|
+
class="mt-16 flex justify-center items-center gap-2"
|
|
91
|
+
>
|
|
92
|
+
<button
|
|
93
|
+
@click="changePage(currentPage - 1)"
|
|
94
|
+
:disabled="currentPage === 1"
|
|
95
|
+
class="p-2.5 rounded-xl border border-slate-200 text-slate-600 hover:bg-slate-50 disabled:opacity-40 disabled:hover:bg-transparent disabled:cursor-not-allowed transition-colors"
|
|
96
|
+
>
|
|
97
|
+
<span class="sr-only">Previous</span>
|
|
98
|
+
<i class="mdi mdi-chevron-left text-lg"></i>
|
|
99
|
+
</button>
|
|
100
|
+
|
|
101
|
+
<div class="flex items-center gap-1.5">
|
|
102
|
+
<button
|
|
103
|
+
v-for="page in visiblePages"
|
|
104
|
+
:key="page"
|
|
105
|
+
@click="changePage(page)"
|
|
106
|
+
:class="[
|
|
107
|
+
currentPage === page
|
|
108
|
+
? 'bg-primary text-white border-primary shadow-sm'
|
|
109
|
+
: 'border-slate-200 text-slate-600 hover:bg-slate-50',
|
|
110
|
+
]"
|
|
111
|
+
class="min-w-[40px] h-[40px] px-3 font-semibold text-sm rounded-xl border flex items-center justify-center transition-all duration-150"
|
|
112
|
+
>
|
|
113
|
+
{{ page }}
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<button
|
|
118
|
+
@click="changePage(currentPage + 1)"
|
|
119
|
+
:disabled="currentPage === totalPages"
|
|
120
|
+
class="p-2.5 rounded-xl border border-slate-200 text-slate-600 hover:bg-slate-50 disabled:opacity-40 disabled:hover:bg-transparent disabled:cursor-not-allowed transition-colors"
|
|
121
|
+
>
|
|
122
|
+
<span class="sr-only">Next</span>
|
|
123
|
+
<i class="mdi mdi-chevron-right text-lg"></i>
|
|
124
|
+
</button>
|
|
125
|
+
</nav>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- Sidebar Column -->
|
|
129
|
+
<aside class="blog-sidebar-col">
|
|
130
|
+
<!-- Search widget -->
|
|
131
|
+
<div class="sidebar-widget">
|
|
132
|
+
<h3 class="sidebar-title">
|
|
133
|
+
Search
|
|
134
|
+
</h3>
|
|
135
|
+
<div class="search-container">
|
|
136
|
+
<VPNavBarSearch class="search" />
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- Hot Tags -->
|
|
141
|
+
<div class="sidebar-widget">
|
|
142
|
+
<div class="sidebar-title-container">
|
|
143
|
+
<h3 class="sidebar-title-inline">
|
|
144
|
+
Hot tags
|
|
145
|
+
</h3>
|
|
146
|
+
<a href="/tag" class="see-all-link">
|
|
147
|
+
<span>see all</span>
|
|
148
|
+
<i class="mdi mdi-arrow-right"></i>
|
|
149
|
+
</a>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="sidebar-tags-container">
|
|
152
|
+
<a
|
|
153
|
+
v-for="tag in calculatedHotTags"
|
|
154
|
+
:key="tag.name"
|
|
155
|
+
:href="`/tag/${tag.name}/`"
|
|
156
|
+
:style="getTagStyle(tag.name)"
|
|
157
|
+
class="sidebar-tag"
|
|
158
|
+
>
|
|
159
|
+
{{ tag.name }} ({{ tag.count }})
|
|
160
|
+
</a>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</aside>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</template>
|
|
167
|
+
|
|
168
|
+
<script setup>
|
|
169
|
+
import { ref, computed, onMounted, watch } from "vue";
|
|
170
|
+
import { useRouter, useData } from "vitepress";
|
|
171
|
+
import { data as posts } from "../posts.data.js";
|
|
172
|
+
import { data as authorsList } from "../authors.data.js";
|
|
173
|
+
import { getTagColor, getTagStyle } from "../tagUtils.js";
|
|
174
|
+
import { VPNavBarSearch } from "vitepress/theme";
|
|
175
|
+
import FeatureCard from "./FeatureCard/Index.vue";
|
|
176
|
+
|
|
177
|
+
// Dynamic Hot Tags
|
|
178
|
+
const calculatedHotTags = computed(() => {
|
|
179
|
+
const tagCounts = {};
|
|
180
|
+
posts.forEach((p) => {
|
|
181
|
+
if (Array.isArray(p.tags)) {
|
|
182
|
+
p.tags.forEach((t) => {
|
|
183
|
+
const clean = t.trim();
|
|
184
|
+
if (clean) {
|
|
185
|
+
tagCounts[clean] = (tagCounts[clean] || 0) + 1;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
return Object.entries(tagCounts)
|
|
191
|
+
.map(([name, count]) => ({
|
|
192
|
+
name,
|
|
193
|
+
count,
|
|
194
|
+
color: getTagColor(name),
|
|
195
|
+
}))
|
|
196
|
+
.sort((a, b) => b.count - a.count)
|
|
197
|
+
.slice(0, 10);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const router = useRouter();
|
|
201
|
+
const { params } = useData();
|
|
202
|
+
|
|
203
|
+
const activeTag = ref("");
|
|
204
|
+
const activeAuthor = ref("");
|
|
205
|
+
const currentPage = ref(1);
|
|
206
|
+
const postsPerPage = 9;
|
|
207
|
+
|
|
208
|
+
// Form cover image URL correctly (prefixing filename with /images/)
|
|
209
|
+
function getCoverImageUrl(coverImage) {
|
|
210
|
+
if (!coverImage) return "";
|
|
211
|
+
if (coverImage.startsWith("/") || coverImage.startsWith("http")) {
|
|
212
|
+
return coverImage;
|
|
213
|
+
}
|
|
214
|
+
return `/images/${coverImage}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const tagFromParam = computed(() => {
|
|
218
|
+
return params.value?.tag || "";
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const authorFromParam = computed(() => {
|
|
222
|
+
return params.value?.author || "";
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Reactively parse query filters on client side
|
|
226
|
+
function updateFilters() {
|
|
227
|
+
if (typeof window === "undefined") return;
|
|
228
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
229
|
+
activeTag.value = tagFromParam.value || urlParams.get("tag") || "";
|
|
230
|
+
activeAuthor.value = authorFromParam.value || urlParams.get("author") || "";
|
|
231
|
+
currentPage.value = 1; // Reset to page 1 on filter change
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
onMounted(() => {
|
|
235
|
+
updateFilters();
|
|
236
|
+
window.addEventListener("popstate", updateFilters);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Watch router path/search changes
|
|
240
|
+
watch(() => router.route.path, updateFilters);
|
|
241
|
+
watch(tagFromParam, (newVal) => {
|
|
242
|
+
activeTag.value = newVal;
|
|
243
|
+
});
|
|
244
|
+
watch(authorFromParam, (newVal) => {
|
|
245
|
+
activeAuthor.value = newVal;
|
|
246
|
+
});
|
|
247
|
+
if (typeof window !== "undefined") {
|
|
248
|
+
watch(() => window.location.search, updateFilters);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Filter posts
|
|
252
|
+
const filteredPosts = computed(() => {
|
|
253
|
+
return posts.filter((post) => {
|
|
254
|
+
// Filter by tag
|
|
255
|
+
const tagToFilter = activeTag.value || tagFromParam.value;
|
|
256
|
+
if (tagToFilter) {
|
|
257
|
+
const matchTag = post.tags.some(
|
|
258
|
+
(t) => t.toLowerCase().trim() === tagToFilter.toLowerCase().trim(),
|
|
259
|
+
);
|
|
260
|
+
if (!matchTag) return false;
|
|
261
|
+
}
|
|
262
|
+
// Filter by author
|
|
263
|
+
const authorToFilter = activeAuthor.value || authorFromParam.value;
|
|
264
|
+
if (authorToFilter) {
|
|
265
|
+
const matchAuthor = post.authors.some(
|
|
266
|
+
(a) => a.toLowerCase().trim() === authorToFilter.toLowerCase().trim(),
|
|
267
|
+
);
|
|
268
|
+
if (!matchAuthor) return false;
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Paginated posts
|
|
275
|
+
const paginatedPosts = computed(() => {
|
|
276
|
+
const startIndex = (currentPage.value - 1) * postsPerPage;
|
|
277
|
+
return filteredPosts.value.slice(startIndex, startIndex + postsPerPage);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Total pages
|
|
281
|
+
const totalPages = computed(() => {
|
|
282
|
+
return Math.ceil(filteredPosts.value.length / postsPerPage);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Page navigation helper
|
|
286
|
+
function changePage(page) {
|
|
287
|
+
if (page < 1 || page > totalPages.value) return;
|
|
288
|
+
currentPage.value = page;
|
|
289
|
+
if (typeof window !== "undefined") {
|
|
290
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Generate list of page numbers to render
|
|
295
|
+
const visiblePages = computed(() => {
|
|
296
|
+
const pages = [];
|
|
297
|
+
const maxPages = 5;
|
|
298
|
+
let start = Math.max(1, currentPage.value - Math.floor(maxPages / 2));
|
|
299
|
+
let end = Math.min(totalPages.value, start + maxPages - 1);
|
|
300
|
+
if (end - start + 1 < maxPages) {
|
|
301
|
+
start = Math.max(1, end - maxPages + 1);
|
|
302
|
+
}
|
|
303
|
+
for (let i = start; i <= end; i++) {
|
|
304
|
+
pages.push(i);
|
|
305
|
+
}
|
|
306
|
+
return pages;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Format publication date
|
|
310
|
+
function formatDate(dateStr) {
|
|
311
|
+
if (!dateStr) return "";
|
|
312
|
+
const date = new Date(dateStr);
|
|
313
|
+
return date.toLocaleDateString("en-US", {
|
|
314
|
+
year: "numeric",
|
|
315
|
+
month: "long",
|
|
316
|
+
day: "numeric",
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Get author name
|
|
321
|
+
function getAuthorName(authorId) {
|
|
322
|
+
const auth = authorsList.find((a) => a.id === authorId);
|
|
323
|
+
return auth ? auth.name : authorId;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function clearFilters() {
|
|
327
|
+
router.go("/blog");
|
|
328
|
+
}
|
|
329
|
+
</script>
|
|
330
|
+
|
|
331
|
+
<style scoped>
|
|
332
|
+
.blog-landing {
|
|
333
|
+
width: 100%;
|
|
334
|
+
max-width: 1200px !important;
|
|
335
|
+
margin-left: auto !important;
|
|
336
|
+
margin-right: auto !important;
|
|
337
|
+
padding-left: 1rem !important;
|
|
338
|
+
padding-right: 1rem !important;
|
|
339
|
+
box-sizing: border-box !important;
|
|
340
|
+
padding-top: calc(var(--vp-nav-height) + 28px) !important;
|
|
341
|
+
padding-bottom: 48px !important;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@media (min-width: 640px) {
|
|
345
|
+
.blog-landing {
|
|
346
|
+
padding-left: 1.5rem !important;
|
|
347
|
+
padding-right: 1.5rem !important;
|
|
348
|
+
padding-top: calc(var(--vp-nav-height) + 40px) !important;
|
|
349
|
+
padding-bottom: 64px !important;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@media (min-width: 960px) {
|
|
354
|
+
.blog-landing {
|
|
355
|
+
padding-left: 2rem !important;
|
|
356
|
+
padding-right: 2rem !important;
|
|
357
|
+
padding-top: calc(var(--vp-nav-height) + 58px) !important;
|
|
358
|
+
padding-bottom: 96px !important;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.blog-landing-header {
|
|
363
|
+
margin-bottom: 2rem;
|
|
364
|
+
border-bottom: 1px solid #f1f5f9; /* slate-100 */
|
|
365
|
+
padding-bottom: 1.5rem !important;
|
|
366
|
+
padding-top: 0 !important;
|
|
367
|
+
padding-left: 0 !important;
|
|
368
|
+
padding-right: 0 !important;
|
|
369
|
+
background: transparent !important;
|
|
370
|
+
min-height: auto !important;
|
|
371
|
+
height: auto !important;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.blog-landing-title {
|
|
375
|
+
font-family: "Inter", sans-serif;
|
|
376
|
+
font-style: normal;
|
|
377
|
+
font-weight: 700;
|
|
378
|
+
font-size: 28px;
|
|
379
|
+
line-height: 36px;
|
|
380
|
+
letter-spacing: 0.5px;
|
|
381
|
+
color: #020617; /* slate-950 */
|
|
382
|
+
margin-top: 0;
|
|
383
|
+
margin-bottom: 0.5rem;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
@media (min-width: 768px) {
|
|
387
|
+
.blog-landing-title {
|
|
388
|
+
font-size: 32px;
|
|
389
|
+
line-height: 40px;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.blog-landing-subtitle {
|
|
394
|
+
font-family: "Inter", sans-serif;
|
|
395
|
+
font-style: normal;
|
|
396
|
+
font-weight: 400;
|
|
397
|
+
font-size: 16px;
|
|
398
|
+
line-height: 24px;
|
|
399
|
+
letter-spacing: 0.5px;
|
|
400
|
+
color: #64748b; /* slate-500 */
|
|
401
|
+
max-width: 620px;
|
|
402
|
+
margin: 0;
|
|
403
|
+
padding: 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.posts-grid {
|
|
407
|
+
display: flex;
|
|
408
|
+
flex-direction: column;
|
|
409
|
+
gap: 2.5rem;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
:deep(.card-cover-link) {
|
|
413
|
+
max-height: 280px !important;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.blog-post-layout {
|
|
417
|
+
display: grid;
|
|
418
|
+
grid-template-columns: 1fr;
|
|
419
|
+
gap: 2.5rem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.blog-content-col {
|
|
423
|
+
width: 100%;
|
|
424
|
+
min-width: 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.blog-sidebar-col {
|
|
428
|
+
width: 100%;
|
|
429
|
+
display: flex;
|
|
430
|
+
flex-direction: column;
|
|
431
|
+
gap: 2.5rem;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@media (min-width: 1024px) {
|
|
435
|
+
.blog-post-layout {
|
|
436
|
+
grid-template-columns: minmax(0, 1fr) 280px;
|
|
437
|
+
gap: 4rem;
|
|
438
|
+
align-items: start;
|
|
439
|
+
}
|
|
440
|
+
.blog-sidebar-col {
|
|
441
|
+
position: sticky !important;
|
|
442
|
+
align-self: start !important;
|
|
443
|
+
top: calc(var(--vp-nav-height) + 2rem) !important;
|
|
444
|
+
max-height: calc(100vh - var(--vp-nav-height) - 4rem);
|
|
445
|
+
overflow-y: auto;
|
|
446
|
+
scrollbar-width: none; /* Hide scrollbar for cleaner look */
|
|
447
|
+
}
|
|
448
|
+
.blog-sidebar-col::-webkit-scrollbar {
|
|
449
|
+
display: none;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.sidebar-widget {
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
gap: 0.75rem;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.sidebar-title {
|
|
460
|
+
font-size: 0.75rem;
|
|
461
|
+
font-weight: 600;
|
|
462
|
+
text-transform: uppercase;
|
|
463
|
+
letter-spacing: 0.05em;
|
|
464
|
+
color: #64748b; /* slate-500 */
|
|
465
|
+
border-bottom: 1px solid #f1f5f9; /* slate-100 */
|
|
466
|
+
padding-bottom: 0.5rem;
|
|
467
|
+
margin: 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.sidebar-title-container {
|
|
471
|
+
display: flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
justify-content: space-between;
|
|
474
|
+
border-bottom: 1px solid #f1f5f9; /* slate-100 */
|
|
475
|
+
padding-bottom: 0.5rem;
|
|
476
|
+
margin: 0;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.sidebar-title-inline {
|
|
480
|
+
font-size: 0.75rem;
|
|
481
|
+
font-weight: 600;
|
|
482
|
+
text-transform: uppercase;
|
|
483
|
+
letter-spacing: 0.05em;
|
|
484
|
+
color: #64748b; /* slate-500 */
|
|
485
|
+
margin: 0;
|
|
486
|
+
padding: 0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.see-all-link {
|
|
490
|
+
font-size: 0.75rem;
|
|
491
|
+
font-weight: 600;
|
|
492
|
+
color: var(--vp-c-brand, #004170);
|
|
493
|
+
text-decoration: none;
|
|
494
|
+
display: inline-flex;
|
|
495
|
+
align-items: center;
|
|
496
|
+
gap: 0.25rem;
|
|
497
|
+
transition: opacity 0.2s;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.see-all-link:hover {
|
|
501
|
+
text-decoration: underline;
|
|
502
|
+
opacity: 0.8;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.see-all-link i {
|
|
506
|
+
font-size: 0.75rem;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.sidebar-tags-container {
|
|
510
|
+
display: flex !important;
|
|
511
|
+
flex-wrap: wrap !important;
|
|
512
|
+
gap: 0.5rem !important;
|
|
513
|
+
margin-top: 0.25rem !important;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.sidebar-tag {
|
|
517
|
+
display: inline-flex !important;
|
|
518
|
+
align-items: center !important;
|
|
519
|
+
font-size: 0.75rem !important;
|
|
520
|
+
font-weight: 600 !important;
|
|
521
|
+
padding: 0.25rem 0.75rem !important;
|
|
522
|
+
border-radius: 4px !important;
|
|
523
|
+
text-decoration: none !important;
|
|
524
|
+
line-height: 1.2 !important;
|
|
525
|
+
transition: opacity 0.2s ease, transform 0.1s ease !important;
|
|
526
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* Active Filter Banner Styles */
|
|
530
|
+
.active-filter-banner {
|
|
531
|
+
display: flex;
|
|
532
|
+
flex-wrap: wrap;
|
|
533
|
+
align-items: center;
|
|
534
|
+
gap: 0.75rem;
|
|
535
|
+
background-color: var(--surface-container-low, #f8fafc);
|
|
536
|
+
padding: 0.75rem 1.25rem !important;
|
|
537
|
+
border-radius: 0.75rem;
|
|
538
|
+
border: 1px solid rgba(226, 232, 240, 0.8); /* slate-200/80 */
|
|
539
|
+
margin-top: 1.5rem;
|
|
540
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.filter-label {
|
|
544
|
+
font-size: 0.875rem; /* text-sm */
|
|
545
|
+
color: #64748b; /* slate-500 */
|
|
546
|
+
font-weight: 500;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.active-filter-tag {
|
|
550
|
+
display: inline-flex !important;
|
|
551
|
+
align-items: center !important;
|
|
552
|
+
font-size: 0.75rem !important;
|
|
553
|
+
font-weight: 700 !important;
|
|
554
|
+
padding: 0.25rem 0.75rem !important;
|
|
555
|
+
border-radius: 4px !important;
|
|
556
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
557
|
+
line-height: 1.2 !important;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.active-filter-author {
|
|
561
|
+
display: inline-flex !important;
|
|
562
|
+
align-items: center !important;
|
|
563
|
+
font-size: 0.75rem !important;
|
|
564
|
+
font-weight: 700 !important;
|
|
565
|
+
padding: 0.25rem 0.75rem !important;
|
|
566
|
+
border-radius: 4px !important;
|
|
567
|
+
background-color: var(--primary-container) !important;
|
|
568
|
+
color: var(--primary) !important;
|
|
569
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
570
|
+
line-height: 1.2 !important;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/* Clear Filter Button Reset & Style */
|
|
574
|
+
.clear-filter-btn {
|
|
575
|
+
/* Complete reset of standard button styling */
|
|
576
|
+
background: none !important;
|
|
577
|
+
background-color: transparent !important;
|
|
578
|
+
border: none !important;
|
|
579
|
+
outline: none !important;
|
|
580
|
+
box-shadow: none !important;
|
|
581
|
+
height: auto !important;
|
|
582
|
+
min-height: auto !important;
|
|
583
|
+
width: auto !important;
|
|
584
|
+
padding: 0 !important;
|
|
585
|
+
margin: 0 !important;
|
|
586
|
+
margin-left: 0.5rem !important;
|
|
587
|
+
|
|
588
|
+
display: inline-flex !important;
|
|
589
|
+
align-items: center !important;
|
|
590
|
+
gap: 0.375rem !important;
|
|
591
|
+
font-size: 0.8125rem !important; /* 13px */
|
|
592
|
+
font-weight: 600 !important;
|
|
593
|
+
color: #94a3b8 !important; /* slate-400 */
|
|
594
|
+
cursor: pointer !important;
|
|
595
|
+
transition: color 0.15s ease !important;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.clear-filter-btn:hover {
|
|
599
|
+
color: #475569 !important; /* slate-600 */
|
|
600
|
+
background: none !important;
|
|
601
|
+
background-color: transparent !important;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.clear-filter-btn i {
|
|
605
|
+
font-size: 1rem !important;
|
|
606
|
+
color: inherit !important;
|
|
607
|
+
vertical-align: middle !important;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.sidebar-tag:hover {
|
|
611
|
+
opacity: 0.8 !important;
|
|
612
|
+
transform: translateY(-1px) !important;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.search-container {
|
|
616
|
+
margin-top: 0.75rem;
|
|
617
|
+
margin-bottom: 0.5rem;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
:deep(.VPNavBarSearch) {
|
|
621
|
+
padding-left: 0 !important;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.line-clamp-2 {
|
|
625
|
+
display: -webkit-box;
|
|
626
|
+
-webkit-line-clamp: 2;
|
|
627
|
+
-webkit-box-orient: vertical;
|
|
628
|
+
overflow: hidden;
|
|
629
|
+
}
|
|
630
|
+
.line-clamp-3 {
|
|
631
|
+
display: -webkit-box;
|
|
632
|
+
-webkit-line-clamp: 3;
|
|
633
|
+
-webkit-box-orient: vertical;
|
|
634
|
+
overflow: hidden;
|
|
635
|
+
}
|
|
636
|
+
</style>
|