@voidzero-dev/vitepress-theme 2.0.0 → 2.1.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.
Files changed (79) hide show
  1. package/package.json +7 -6
  2. package/src/aliases.js +14 -0
  3. package/src/vitepress/assets/clients/clickup.svg +5 -0
  4. package/src/vitepress/assets/clients/stripe.svg +3 -0
  5. package/src/vitepress/components/oss/Footer.vue +4 -21
  6. package/src/vitepress/components/oss/Header.vue +82 -180
  7. package/src/vitepress/components/oss/Sponsors.vue +3 -3
  8. package/src/vitepress/components/oss/TopBanner.vue +20 -79
  9. package/src/vitepress/components/oss/TrustedBy.vue +1 -1
  10. package/src/vitepress/components/vite/Community.vue +3 -3
  11. package/src/vitepress/components/vite/FeatureGrid1.vue +63 -0
  12. package/src/vitepress/components/vite/{FeatureGrid.vue → FeatureGrid2.vue} +8 -10
  13. package/src/vitepress/components/vite/Hero.vue +6 -15
  14. package/src/vitepress/components/vitepress-default/VPDocOutlineItem.vue +2 -2
  15. package/src/vitepress/components/vitepress-default/VPFlyout.vue +1 -1
  16. package/src/vitepress/components/vitepress-default/VPMenuLink.vue +1 -1
  17. package/src/vitepress/components/vitepress-default/VPNavBarMenuLink.vue +1 -1
  18. package/src/vitepress/components/vitepress-default/VPSidebarItem.vue +1 -1
  19. package/src/vitepress/components/vitepress-default/VPSocialLink.vue +1 -2
  20. package/src/vitepress/fonts/APK-Protocol-Semi-Bold.woff2 +0 -0
  21. package/src/vitepress/fonts/inter-italic-cyrillic-ext.woff2 +0 -0
  22. package/src/vitepress/fonts/inter-italic-cyrillic.woff2 +0 -0
  23. package/src/vitepress/fonts/inter-italic-greek-ext.woff2 +0 -0
  24. package/src/vitepress/fonts/inter-italic-greek.woff2 +0 -0
  25. package/src/vitepress/fonts/inter-italic-latin-ext.woff2 +0 -0
  26. package/src/vitepress/fonts/inter-italic-latin.woff2 +0 -0
  27. package/src/vitepress/fonts/inter-italic-vietnamese.woff2 +0 -0
  28. package/src/vitepress/fonts/inter-roman-cyrillic-ext.woff2 +0 -0
  29. package/src/vitepress/fonts/inter-roman-cyrillic.woff2 +0 -0
  30. package/src/vitepress/fonts/inter-roman-greek-ext.woff2 +0 -0
  31. package/src/vitepress/fonts/inter-roman-greek.woff2 +0 -0
  32. package/src/vitepress/fonts/inter-roman-latin-ext.woff2 +0 -0
  33. package/src/vitepress/fonts/inter-roman-latin.woff2 +0 -0
  34. package/src/vitepress/fonts/inter-roman-vietnamese.woff2 +0 -0
  35. package/src/vitepress/index.ts +64 -230
  36. package/src/vitepress/layouts/VPLayout.vue +2 -17
  37. package/src/vitepress/styles/tokens.css +194 -10
  38. package/src/vitepress/types/theme-context.ts +33 -0
  39. package/src/vitepress/assets/clients/beehiiv.svg +0 -30
  40. package/src/vitepress/assets/clients/excalidraw.svg +0 -82
  41. package/src/vitepress/assets/clients/get-your-guide.svg +0 -1
  42. package/src/vitepress/assets/clients/posthog.svg +0 -1
  43. package/src/vitepress/assets/clients/ramp.svg +0 -1
  44. package/src/vitepress/assets/clients/shopee.svg +0 -55
  45. package/src/vitepress/components/vite/FeaturePanel1.vue +0 -41
  46. package/src/vitepress/components/vite/FeaturePanel2.vue +0 -37
  47. package/src/vitepress/components/vite/FeaturePanel3.vue +0 -43
  48. package/src/vitepress/components/vite/FeaturePanel4.vue +0 -46
  49. package/src/vitepress/components/voidzero/Footer.vue +0 -65
  50. package/src/vitepress/components/voidzero/Header.vue +0 -560
  51. package/src/vitepress/components/voidzero/Megamenu.vue +0 -190
  52. package/src/vitepress/components/voidzero/about/CareerCTA.vue +0 -56
  53. package/src/vitepress/components/voidzero/about/Hero.vue +0 -206
  54. package/src/vitepress/components/voidzero/about/Investors.vue +0 -112
  55. package/src/vitepress/components/voidzero/about/TeamGrid.vue +0 -161
  56. package/src/vitepress/components/voidzero/about/TeamSectionHeading.vue +0 -13
  57. package/src/vitepress/components/voidzero/blog/BlogArchive.vue +0 -223
  58. package/src/vitepress/components/voidzero/blog/BlogSingleContent.vue +0 -364
  59. package/src/vitepress/components/voidzero/blog/BlogSingleHero.vue +0 -113
  60. package/src/vitepress/components/voidzero/blog/BlogSingleRelated.vue +0 -92
  61. package/src/vitepress/components/voidzero/blog/FeaturedArticles.vue +0 -146
  62. package/src/vitepress/components/voidzero/blog/types.ts +0 -56
  63. package/src/vitepress/components/voidzero/home/CaseStudySlider.vue +0 -235
  64. package/src/vitepress/components/voidzero/home/CustomersSectionHeading.vue +0 -5
  65. package/src/vitepress/components/voidzero/home/GitHubStats.vue +0 -27
  66. package/src/vitepress/components/voidzero/home/Hero.vue +0 -69
  67. package/src/vitepress/components/voidzero/home/Investors.vue +0 -30
  68. package/src/vitepress/components/voidzero/home/NewsletterCTA.vue +0 -23
  69. package/src/vitepress/components/voidzero/home/OpenSourceSectionHeading.vue +0 -6
  70. package/src/vitepress/components/voidzero/home/OpenSourceSectionProjects.vue +0 -419
  71. package/src/vitepress/components/voidzero/home/Resources.vue +0 -144
  72. package/src/vitepress/components/voidzero/home/Statistics.vue +0 -507
  73. package/src/vitepress/components/voidzero/home/StatisticsSectionHeading.vue +0 -5
  74. package/src/vitepress/components/voidzero/home/TeamCTA.vue +0 -17
  75. package/src/vitepress/components/voidzero/home/TrustedBy.vue +0 -248
  76. package/src/vitepress/components/voidzero/home/VitePlusSectionFeatures.vue +0 -55
  77. package/src/vitepress/components/voidzero/home/VitePlusSectionHeading.vue +0 -17
  78. package/src/vitepress/fonts/KHTeka-Medium.woff2 +0 -0
  79. package/src/vitepress/fonts/KHTeka-Regular.woff2 +0 -0
@@ -1,560 +0,0 @@
1
- <script setup lang="ts">
2
- import {useData, useRoute} from 'vitepress'
3
- import {ref, onMounted, onUnmounted, watch, nextTick, computed} from 'vue'
4
- import 'vue3-carousel/carousel.css'
5
- import Megamenu from './Megamenu.vue'
6
-
7
- interface FeaturedArticle {
8
- title: string
9
- url?: string
10
- link?: string
11
- cover?: string
12
- image?: string
13
- }
14
-
15
- interface GithubStat {
16
- repo: string
17
- stars: number
18
- contributors: number
19
- }
20
-
21
- interface Props {
22
- featuredArticles?: FeaturedArticle[]
23
- githubStats?: GithubStat[]
24
- }
25
-
26
- const props = withDefaults(defineProps<Props>(), {
27
- featuredArticles: () => [],
28
- githubStats: () => []
29
- })
30
-
31
- const {theme} = useData()
32
- const nav = theme.value.nav
33
-
34
- const route = useRoute()
35
-
36
- // Calculate total GitHub stars from all repos
37
- const totalGithubStars = computed(() => {
38
- if (!props.githubStats || props.githubStats.length === 0) {
39
- return 0
40
- }
41
- return props.githubStats.reduce((sum, stat) => sum + stat.stars, 0)
42
- })
43
-
44
- // Format stars for display (e.g., 103.6k)
45
- const formattedStars = computed(() => {
46
- const stars = totalGithubStars.value
47
- if (stars >= 1000000) {
48
- return (stars / 1000000).toFixed(1) + 'M'
49
- } else if (stars >= 1000) {
50
- return (stars / 1000).toFixed(1) + 'k'
51
- }
52
- return stars.toString()
53
- })
54
-
55
- // Megamenu state
56
- const megamenuOpen = ref(false)
57
- const activeMegamenuItem = ref<string | null>(null)
58
- const megamenuRef = ref<HTMLElement | null>(null)
59
- const hoverTimeout = ref<number | null>(null)
60
-
61
- // Mobile menu state
62
- const mobileMenuOpen = ref(false)
63
- const expandedMobileItem = ref<string | null>(null)
64
-
65
- // Close megamenu when clicking outside
66
- const handleClickOutside = (e: MouseEvent) => {
67
- const target = e.target as HTMLElement
68
- if (megamenuRef.value && !megamenuRef.value.contains(target)) {
69
- closeMegamenu()
70
- }
71
- }
72
-
73
- // Handle keyboard navigation
74
- const handleKeydown = (e: KeyboardEvent) => {
75
- if (e.key === 'Escape') {
76
- if (mobileMenuOpen.value) {
77
- closeMobileMenu()
78
- } else if (megamenuOpen.value) {
79
- closeMegamenu()
80
- }
81
- }
82
- }
83
-
84
- // Open megamenu for a specific nav item
85
- const openMegamenu = (navItemLink: string) => {
86
- if (hoverTimeout.value) {
87
- clearTimeout(hoverTimeout.value)
88
- hoverTimeout.value = null
89
- }
90
- activeMegamenuItem.value = navItemLink
91
- megamenuOpen.value = true
92
- }
93
-
94
- // Close megamenu with optional delay
95
- const closeMegamenu = (delay: number = 0) => {
96
- if (delay > 0) {
97
- hoverTimeout.value = window.setTimeout(() => {
98
- megamenuOpen.value = false
99
- activeMegamenuItem.value = null
100
- }, delay)
101
- } else {
102
- if (hoverTimeout.value) {
103
- clearTimeout(hoverTimeout.value)
104
- hoverTimeout.value = null
105
- }
106
- megamenuOpen.value = false
107
- activeMegamenuItem.value = null
108
- }
109
- }
110
-
111
- // Handle nav item hover (desktop)
112
- const handleNavItemMouseEnter = (navItem: any) => {
113
- if (navItem.megamenu) {
114
- openMegamenu(navItem.link)
115
- } else {
116
- closeMegamenu(200)
117
- }
118
- }
119
-
120
- // Handle nav item hover leave
121
- const handleNavItemMouseLeave = () => {
122
- closeMegamenu(200)
123
- }
124
-
125
- // Handle megamenu hover (keep it open)
126
- const handleMegamenuMouseEnter = () => {
127
- if (hoverTimeout.value) {
128
- clearTimeout(hoverTimeout.value)
129
- hoverTimeout.value = null
130
- }
131
- }
132
-
133
- // Handle megamenu leave
134
- const handleMegamenuMouseLeave = () => {
135
- closeMegamenu(200)
136
- }
137
-
138
- // Toggle megamenu on click (mobile)
139
- const toggleMegamenu = (navItem: any) => {
140
- if (navItem.megamenu) {
141
- if (activeMegamenuItem.value === navItem.link) {
142
- closeMegamenu()
143
- } else {
144
- openMegamenu(navItem.link)
145
- }
146
- }
147
- }
148
-
149
- // Body scroll lock for mobile menu
150
- const lockBodyScroll = () => {
151
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
152
- document.body.style.overflow = 'hidden'
153
- document.body.style.position = 'fixed'
154
- document.body.style.width = '100%'
155
- document.body.style.top = '0'
156
- if (scrollbarWidth > 0) {
157
- document.body.style.paddingRight = `${scrollbarWidth}px`
158
- }
159
- }
160
-
161
- const unlockBodyScroll = () => {
162
- document.body.style.overflow = ''
163
- document.body.style.position = ''
164
- document.body.style.width = ''
165
- document.body.style.top = ''
166
- document.body.style.paddingRight = ''
167
- }
168
-
169
- // Toggle mobile menu
170
- const toggleMobileMenu = () => {
171
- mobileMenuOpen.value = !mobileMenuOpen.value
172
- if (mobileMenuOpen.value) {
173
- lockBodyScroll()
174
- expandedMobileItem.value = null
175
- } else {
176
- unlockBodyScroll()
177
- expandedMobileItem.value = null
178
- }
179
- }
180
-
181
- // Easter egg: right-click logo to open Google Drive
182
- const handleLogoRightClick = (event: MouseEvent) => {
183
- event.preventDefault()
184
- window.open('https://drive.google.com/drive/folders/19FVA6toa2elRXRXO16wrmLn1Rsbhj2Te', '_blank')
185
- }
186
-
187
- // Toggle accordion item in mobile menu
188
- const toggleMobileAccordion = (navItemLink: string) => {
189
- if (expandedMobileItem.value === navItemLink) {
190
- expandedMobileItem.value = null
191
- } else {
192
- expandedMobileItem.value = navItemLink
193
- }
194
- }
195
-
196
- // Close mobile menu
197
- const closeMobileMenu = () => {
198
- mobileMenuOpen.value = false
199
- expandedMobileItem.value = null
200
- unlockBodyScroll()
201
- }
202
-
203
- // Focus trap for mobile menu
204
- const focusTrap = {
205
- firstFocusableEl: null as HTMLElement | null,
206
- lastFocusableEl: null as HTMLElement | null,
207
-
208
- activate: () => {
209
- const mobileMenu = document.getElementById('mobile-menu')
210
- if (!mobileMenu) return
211
-
212
- const focusableElements = mobileMenu.querySelectorAll(
213
- 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])'
214
- )
215
-
216
- if (focusableElements.length === 0) return
217
-
218
- focusTrap.firstFocusableEl = focusableElements[0] as HTMLElement
219
- focusTrap.lastFocusableEl = focusableElements[focusableElements.length - 1] as HTMLElement
220
-
221
- focusTrap.firstFocusableEl?.focus()
222
-
223
- document.addEventListener('keydown', focusTrap.handleTabKey)
224
- },
225
-
226
- deactivate: () => {
227
- document.removeEventListener('keydown', focusTrap.handleTabKey)
228
- focusTrap.firstFocusableEl = null
229
- focusTrap.lastFocusableEl = null
230
- },
231
-
232
- handleTabKey: (e: KeyboardEvent) => {
233
- if (e.key !== 'Tab') return
234
-
235
- if (e.shiftKey) {
236
- if (document.activeElement === focusTrap.firstFocusableEl) {
237
- focusTrap.lastFocusableEl?.focus()
238
- e.preventDefault()
239
- }
240
- } else {
241
- if (document.activeElement === focusTrap.lastFocusableEl) {
242
- focusTrap.firstFocusableEl?.focus()
243
- e.preventDefault()
244
- }
245
- }
246
- }
247
- }
248
-
249
- // Activate/deactivate focus trap when mobile menu opens/closes
250
- watch(mobileMenuOpen, (isOpen) => {
251
- if (isOpen) {
252
- nextTick(() => {
253
- focusTrap.activate()
254
- })
255
- } else {
256
- focusTrap.deactivate()
257
- }
258
- })
259
-
260
- onMounted(() => {
261
- document.addEventListener('click', handleClickOutside)
262
- document.addEventListener('keydown', handleKeydown)
263
- })
264
-
265
- onUnmounted(() => {
266
- document.removeEventListener('click', handleClickOutside)
267
- document.removeEventListener('keydown', handleKeydown)
268
- unlockBodyScroll()
269
- if (hoverTimeout.value) {
270
- clearTimeout(hoverTimeout.value)
271
- }
272
- })
273
- </script>
274
-
275
- <template>
276
- <div class="header-wrapper z-50 relative bg-white dark:bg-primary" ref="megamenuRef">
277
- <header class="wrapper px-6 py-5 lg:py-7 flex items-center justify-between relative z-[1000]">
278
- <div class="flex items-center gap-10">
279
- <a href="/" @contextmenu="handleLogoRightClick">
280
- <img class="h-4 block dark:hidden" src="@assets/voidzero/logo.svg" alt="VoidZero"/>
281
- <img class="h-4 hidden dark:block" src="@assets/voidzero/logo-light.svg" alt="VoidZero"/>
282
- </a>
283
-
284
- <!-- Desktop navigation - hidden on mobile -->
285
- <nav class="nav-container hidden md:block">
286
- <ul class="nav">
287
- <li
288
- v-for="navItem in nav"
289
- :key="navItem.link"
290
- @mouseenter="handleNavItemMouseEnter(navItem)"
291
- @mouseleave="handleNavItemMouseLeave"
292
- class="inline-block px-5"
293
- >
294
- <a
295
- :class="{ active: route.path === navItem.link }"
296
- :href="navItem.link"
297
- :target="navItem.link?.startsWith('http') ? '_blank' : navItem.target"
298
- :rel="navItem.link?.startsWith('http') ? 'noopener noreferrer' : undefined"
299
- :aria-expanded="navItem.megamenu && activeMegamenuItem === navItem.link ? 'true' : undefined"
300
- :aria-haspopup="navItem.megamenu ? 'true' : undefined"
301
- :aria-controls="navItem.megamenu ? `megamenu-${navItem.link}` : undefined"
302
- @click="navItem.megamenu && ($event.preventDefault(), toggleMegamenu(navItem))"
303
- >
304
- {{ navItem.text }}
305
- <svg
306
- v-if="navItem.megamenu"
307
- class="inline-block ml-1 size-3 transition-transform"
308
- :class="{ 'rotate-180': activeMegamenuItem === navItem.link }"
309
- xmlns="http://www.w3.org/2000/svg"
310
- viewBox="0 0 12 7"
311
- fill="currentColor"
312
- aria-hidden="true"
313
- >
314
- <path d="M1.41 0L6 4.58 10.59 0 12 1.42l-6 6-6-6z"/>
315
- </svg>
316
- </a>
317
- </li>
318
- </ul>
319
- </nav>
320
- </div>
321
-
322
- <div class="flex items-center gap-6">
323
- <a href="https://github.com/voidzero-dev" target="_blank" rel="noopener noreferrer"
324
- class="hidden lg:flex gap-1.5 items-center">
325
- <img src="@assets/social/github.svg" alt="Star on GitHub" class="size-6 block dark:hidden">
326
- <img src="@assets/social/github-light.svg" alt="Star on GitHub" class="size-6 hidden dark:block">
327
- <span class="text-primary dark:text-grey font-mono text-sm font-medium">{{ formattedStars }}</span>
328
- </a>
329
- <a href="https://viteplus.dev" target="_blank" rel="noopener noreferrer" class="button hidden md:block">
330
- Explore Vite+
331
- </a>
332
-
333
- <!-- Mobile hamburger/close button - Right aligned -->
334
- <button
335
- @click="toggleMobileMenu"
336
- :aria-expanded="mobileMenuOpen"
337
- aria-controls="mobile-menu"
338
- aria-label="Toggle navigation menu"
339
- class="md:hidden p-2 -mr-2 text-primary dark:text-white hover:opacity-70 transition-opacity cursor-pointer"
340
- type="button"
341
- >
342
- <svg v-if="!mobileMenuOpen" class="size-6 block dark:hidden" viewBox="0 0 18 8" xmlns="http://www.w3.org/2000/svg">
343
- <path d="M0 0.75H18" stroke="#08060D" stroke-width="1.5"/>
344
- <path d="M0 6.75H18" stroke="#08060D" stroke-width="1.5"/>
345
- </svg>
346
- <svg v-if="!mobileMenuOpen" class="size-6 hidden dark:block" viewBox="0 0 18 8" xmlns="http://www.w3.org/2000/svg">
347
- <path d="M0 0.75H18" stroke="#FFFFFF" stroke-width="1.5"/>
348
- <path d="M0 6.75H18" stroke="#FFFFFF" stroke-width="1.5"/>
349
- </svg>
350
-
351
- </button>
352
- </div>
353
- </header>
354
-
355
- <!-- Mobile Menu Overlay - Full Screen -->
356
- <div
357
- v-if="mobileMenuOpen"
358
- id="mobile-menu"
359
- role="dialog"
360
- aria-modal="true"
361
- aria-label="Mobile navigation menu"
362
- data-theme="dark"
363
- class="md:hidden fixed inset-0 z-[1001] bg-primary"
364
- >
365
- <section class="wrapper animate-fade-in">
366
-
367
- <!-- Internal Header with Logo and Close Button -->
368
- <div class="w-full pl-5 pr-7 py-5 lg:py-7 flex items-center justify-between">
369
- <a href="/">
370
- <img class="h-4" src="@assets/voidzero/logo-light.svg" alt="VoidZero"/>
371
- </a>
372
- <button
373
- @click="closeMobileMenu"
374
- aria-label="Close navigation menu"
375
- class="p-2 -mr-2 text-white hover:opacity-70 transition-opacity"
376
- type="button"
377
- >
378
- <svg
379
- class="size-6 cursor-pointer"
380
- xmlns="http://www.w3.org/2000/svg"
381
- fill="none"
382
- viewBox="0 0 24 24"
383
- stroke="currentColor"
384
- aria-hidden="true"
385
- >
386
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
387
- </svg>
388
- </button>
389
- </div>
390
-
391
- <!-- Scrollable content container -->
392
- <div
393
- class="overflow-y-auto flex flex-col [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
394
- style="height: calc(100vh - 88px);">
395
- <!-- Navigation Items - Top Section -->
396
- <nav class="flex-1 w-full pt-6 pb-8">
397
- <ul class="space-y-1">
398
- <li v-for="navItem in nav" :key="navItem.link">
399
- <!-- Nav items with megamenu - Accordion trigger -->
400
- <template v-if="navItem.megamenu">
401
- <button
402
- @click="toggleMobileAccordion(navItem.link)"
403
- :aria-expanded="expandedMobileItem === navItem.link"
404
- :aria-controls="`mobile-megamenu-${navItem.link}`"
405
- class="w-full text-left py-3 px-4 text-lg font-sans text-white flex items-center justify-between cursor-pointer"
406
- >
407
- <span>{{ navItem.text }}</span>
408
- <svg class="size-4 transition-transform duration-200"
409
- :class="{ 'rotate-180': expandedMobileItem === navItem.link }" viewBox="0 0 15 8" fill="none"
410
- xmlns="http://www.w3.org/2000/svg">
411
- <path d="M13.8638 0.530325L7.1971 7.19699L0.530438 0.530324" stroke="#F4F3EC" stroke-width="1.5"
412
- stroke-linejoin="round"/>
413
- </svg>
414
-
415
- </button>
416
-
417
- <!-- Accordion Content - Megamenu (instant, no transition) -->
418
- <Transition>
419
- <div
420
- v-if="expandedMobileItem === navItem.link"
421
- :id="`mobile-megamenu-${navItem.link}`"
422
- class="pb-4 pt-2"
423
- >
424
- <Megamenu
425
- :featured-articles="props.featuredArticles"
426
- :on-link-click="closeMobileMenu"
427
- />
428
- </div>
429
- </Transition>
430
- </template>
431
-
432
- <!-- Regular nav items without megamenu -->
433
- <template v-else>
434
- <a
435
- :href="navItem.link"
436
- @click="closeMobileMenu"
437
- :class="{ 'bg-white/10': route.path === navItem.link }"
438
- class="block py-3 px-4 text-lg font-sans text-white"
439
- >
440
- {{ navItem.text }}
441
- </a>
442
- </template>
443
- </li>
444
- </ul>
445
- </nav>
446
-
447
- <!-- Bottom Section - CTA and Social -->
448
- <div class="w-full py-12 border-t border-nickel relative tick-left tick-right mt-auto">
449
- <div class="space-y-12">
450
- <!-- CTA Button -->
451
- <div class="px-6">
452
- <a
453
- href="https://viteplus.dev"
454
- target="_blank"
455
- rel="noopener noreferrer"
456
- class="button button--primary button--white block text-center bg-white text-primary hover:bg-white/90"
457
- @click="closeMobileMenu"
458
- >
459
- <span>Explore Vite+</span>
460
- </a>
461
- </div>
462
-
463
- <!-- Divider -->
464
- <div class="border-t border-nickel tick-left tick-right relative"></div>
465
-
466
- <!-- Social Icons -->
467
- <div class="flex items-center justify-center gap-4 pb-12">
468
- <a
469
- href="https://github.com/voidzero-dev"
470
- target="_blank"
471
- rel="noopener noreferrer"
472
- class="hover:opacity-70 transition-opacity"
473
- @click="closeMobileMenu"
474
- >
475
- <img src="@assets/social/github-light.svg" alt="GitHub" class="size-6">
476
- </a>
477
- <a
478
- href="https://bsky.app/profile/voidzero.dev"
479
- target="_blank"
480
- rel="noopener noreferrer"
481
- class="hover:opacity-70 transition-opacity"
482
- @click="closeMobileMenu"
483
- >
484
- <img src="@assets/social/bluesky-light.svg" alt="Bluesky" class="size-6">
485
- </a>
486
- <a
487
- href="https://x.com/voidzerodev"
488
- target="_blank"
489
- rel="noopener noreferrer"
490
- class="hover:opacity-70 transition-opacity"
491
- @click="closeMobileMenu"
492
- >
493
- <img src="@assets/social/twitter-light.svg" alt="X" class="size-6">
494
- </a>
495
- </div>
496
- </div>
497
- </div>
498
- </div>
499
- </section>
500
- </div>
501
-
502
- <!-- Desktop Megamenu Panel - Full Width (Hidden on mobile) -->
503
- <Transition
504
- enter-active-class="transition-all duration-200 ease-out"
505
- enter-from-class="opacity-0 -translate-y-2"
506
- enter-to-class="opacity-100 translate-y-0"
507
- leave-active-class="transition-all duration-150 ease-in"
508
- leave-from-class="opacity-100 translate-y-0"
509
- leave-to-class="opacity-0 -translate-y-2"
510
- >
511
- <div
512
- v-if="megamenuOpen && !mobileMenuOpen"
513
- :id="`megamenu-${activeMegamenuItem}`"
514
- role="region"
515
- aria-label="Mega menu"
516
- class="hidden md:block absolute top-full left-0 right-0 w-full z-[1000] pt-10 -mt-10"
517
- @mouseenter="handleMegamenuMouseEnter"
518
- @mouseleave="handleMegamenuMouseLeave"
519
- >
520
- <div class="wrapper wrapper--ticks border-t border-b bg-white dark:bg-primary megamenu-shadow">
521
- <div class="mx-28 border-l border-r border-stroke dark:border-nickel">
522
- <Megamenu
523
- :featured-articles="props.featuredArticles"
524
- />
525
- </div>
526
- </div>
527
- </div>
528
- </Transition>
529
- </div>
530
- </template>
531
-
532
- <style scoped>
533
- .megamenu-shadow {
534
- box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;
535
- animation: shadowFadeIn 0.3s ease-out forwards;
536
- }
537
-
538
- @keyframes shadowFadeIn {
539
- from {
540
- box-shadow: rgba(50, 50, 93, 0.1) 0px 25px 50px -20px, rgba(0, 0, 0, 0.15) 0px 15px 30px -30px;
541
- }
542
- to {
543
- box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;
544
- }
545
- }
546
-
547
- @keyframes fadeIn {
548
- from {
549
- opacity: 0;
550
- }
551
- to {
552
- opacity: 1;
553
- }
554
- }
555
-
556
- .animate-fade-in {
557
- animation: fadeIn 300ms ease-out 100ms forwards;
558
- opacity: 0;
559
- }
560
- </style>