@voidzero-dev/vitepress-theme 3.3.1 → 4.0.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 CHANGED
@@ -45,9 +45,33 @@ In your `.vitepress/theme/index.ts`:
45
45
  ```ts
46
46
  import type { Theme } from 'vitepress'
47
47
  import Theme from '@voidzero-dev/vitepress-theme'
48
+ import './styles.css'
48
49
 
49
50
  export default {
50
51
  extends: BaseTheme as unknown as Theme,
51
52
  Layout
52
53
  } satisfies Theme
53
54
  ```
55
+
56
+ In the CSS, import the CSS from theme (which imports tailwind):
57
+
58
+ ```css
59
+ /* styles.css */
60
+ @import "@voidzero-dev/vitepress-theme/src/styles/index.css";
61
+
62
+ @source "./**/*.vue";
63
+
64
+ /* Project specific branding colors */
65
+ :root[data-variant="vite"] {
66
+ --color-brand: #6B1EB9;
67
+ }
68
+
69
+ :root.dark:not([data-theme])[data-variant="vite"],
70
+ :root[data-theme="dark"][data-variant="vite"] {
71
+ --color-brand: var(--color-vite);
72
+ }
73
+
74
+ :root[data-theme="light"][data-variant="vite"] {
75
+ --color-brand: #6B1EB9;
76
+ }
77
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidzero-dev/vitepress-theme",
3
- "version": "3.3.1",
3
+ "version": "4.0.1",
4
4
  "description": "Shared VitePress theme for VoidZero projects",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -4,6 +4,7 @@ import { normalizeLink } from '@vp-support/utils'
4
4
  import { ref, computed, onMounted, onUnmounted, watch, nextTick, inject } from 'vue'
5
5
  import { getSearchProvider } from '@vp-support/search-config'
6
6
  import { themeContextKey } from '../../types/theme-context'
7
+ import { EXTERNAL_URL_RE } from '../../support/vitepress-default/shared-utils'
7
8
 
8
9
  // Import VitePress header components
9
10
  import VPNavBarSearch from '@vp-default/VPNavBarSearch.vue'
@@ -49,6 +50,9 @@ const languageMenuOpen = ref(false)
49
50
  // Check if nav item is a dropdown (has children)
50
51
  const isDropdown = (item: any) => 'items' in item && Array.isArray(item.items)
51
52
 
53
+ // Check if a link is external
54
+ const isExternalLink = (link: string) => EXTERNAL_URL_RE.test(link)
55
+
52
56
  // Toggle accordion for dropdown items
53
57
  const toggleAccordion = (index: number) => {
54
58
  if (expandedAccordions.value.has(index)) {
@@ -314,7 +318,12 @@ onUnmounted(() => {
314
318
  <!-- Direct link item -->
315
319
  <li v-if="'link' in childItem">
316
320
  <a :href="normalizeLink(childItem.link)" @click="closeMobileMenu"
317
- :class="{ 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(childItem.link) }"
321
+ :target="isExternalLink(childItem.link) ? '_blank' : undefined"
322
+ :rel="isExternalLink(childItem.link) ? 'noreferrer' : undefined"
323
+ :class="[
324
+ { 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(childItem.link) },
325
+ { 'vp-external-link-icon': isExternalLink(childItem.link) }
326
+ ]"
318
327
  class="block py-2 px-4 text-base font-sans text-grey dark:text-white/80 hover:text-primary dark:hover:text-white">
319
328
  {{ childItem.text }}
320
329
  </a>
@@ -328,7 +337,12 @@ onUnmounted(() => {
328
337
  <ul class="space-y-1">
329
338
  <li v-for="nestedItem in childItem.items" :key="nestedItem.link || nestedItem.text">
330
339
  <a v-if="nestedItem.link" :href="normalizeLink(nestedItem.link)" @click="closeMobileMenu"
331
- :class="{ 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(nestedItem.link) }"
340
+ :target="isExternalLink(nestedItem.link) ? '_blank' : undefined"
341
+ :rel="isExternalLink(nestedItem.link) ? 'noreferrer' : undefined"
342
+ :class="[
343
+ { 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(nestedItem.link) },
344
+ { 'vp-external-link-icon': isExternalLink(nestedItem.link) }
345
+ ]"
332
346
  class="block py-2 px-4 text-base font-sans text-grey dark:text-white/80 hover:text-primary dark:hover:text-white">
333
347
  {{ nestedItem.text }}
334
348
  </a>
@@ -340,7 +354,12 @@ onUnmounted(() => {
340
354
  </template>
341
355
  <!-- Regular link item -->
342
356
  <a v-else-if="navItem.link" :href="normalizeLink(navItem.link)" @click="closeMobileMenu"
343
- :class="{ 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(navItem.link) }"
357
+ :target="isExternalLink(navItem.link) ? '_blank' : undefined"
358
+ :rel="isExternalLink(navItem.link) ? 'noreferrer' : undefined"
359
+ :class="[
360
+ { 'bg-primary/10 dark:bg-white/10': route.path === normalizeLink(navItem.link) },
361
+ { 'vp-external-link-icon': isExternalLink(navItem.link) }
362
+ ]"
344
363
  class="block py-3 px-4 text-lg font-sans text-primary dark:text-white">
345
364
  {{ navItem.text }}
346
365
  </a>
@@ -174,6 +174,7 @@ function scrollToTop() {
174
174
  .outline {
175
175
  padding: 8px 0;
176
176
  background-color: var(--vp-c-bg-soft);
177
+ outline: 1px solid var(--vp-c-border);
177
178
  }
178
179
 
179
180
  .flyout-enter-active {
package/src/index.ts CHANGED
@@ -15,8 +15,6 @@
15
15
 
16
16
  // Unified design system - single import
17
17
  // Contains: tokens → base → docs → marketing (in that order)
18
- import "./styles/index.css";
19
-
20
18
  import type { Theme } from "vitepress";
21
19
  import { watch } from "vue";
22
20
 
package/src/oxc.ts CHANGED
@@ -13,7 +13,7 @@ export default {
13
13
  ctx.app.provide(themeContextKey, {
14
14
  logoDark,
15
15
  logoLight,
16
- logoAlt: "Vite",
16
+ logoAlt: "Oxc",
17
17
  footerBg,
18
18
  monoIcon,
19
19
  });
package/src/rolldown.ts CHANGED
@@ -13,7 +13,7 @@ export default {
13
13
  ctx.app.provide(themeContextKey, {
14
14
  logoDark,
15
15
  logoLight,
16
- logoAlt: "Vite",
16
+ logoAlt: "Rolldown",
17
17
  footerBg,
18
18
  monoIcon,
19
19
  });
@@ -48,60 +48,3 @@
48
48
  :root[data-theme="light"] {
49
49
  --color-brand: var(--color-electric);
50
50
  }
51
-
52
- /* Vite */
53
- :root[data-variant="vite"] {
54
- --color-brand: #6B1EB9;
55
- }
56
-
57
- :root.dark:not([data-theme])[data-variant="vite"],
58
- :root[data-theme="dark"][data-variant="vite"] {
59
- --color-brand: var(--color-vite);
60
- }
61
-
62
- /* Forced light mode brand for Vite */
63
- :root[data-theme="light"][data-variant="vite"] {
64
- --color-brand: #6B1EB9;
65
- }
66
-
67
- /* Vitest */
68
- :root[data-variant="vitest"] {
69
- --color-brand: #008039;
70
- }
71
-
72
- :root.dark:not([data-theme])[data-variant="vitest"],
73
- :root[data-theme="dark"][data-variant="vitest"] {
74
- --color-brand: var(--color-zest);
75
- }
76
-
77
- :root[data-theme="light"][data-variant="vitest"] {
78
- --color-brand: #008039;
79
- }
80
-
81
- /* Rolldown */
82
- :root[data-variant="rolldown"] {
83
- --color-brand: #D44803;
84
- }
85
-
86
- :root.dark:not([data-theme])[data-variant="rolldown"],
87
- :root[data-theme="dark"][data-variant="rolldown"] {
88
- --color-brand: var(--color-fire);
89
- }
90
-
91
- :root[data-theme="light"][data-variant="rolldown"] {
92
- --color-brand: #D44803;
93
- }
94
-
95
- /* OXC */
96
- :root[data-variant="oxc"] {
97
- --color-brand: #0D6A73;
98
- }
99
-
100
- :root.dark:not([data-theme])[data-variant="oxc"],
101
- :root[data-theme="dark"][data-variant="oxc"] {
102
- --color-brand: var(--color-aqua);
103
- }
104
-
105
- :root[data-theme="light"][data-variant="oxc"] {
106
- --color-brand: #0D6A73;
107
- }
@@ -82,11 +82,11 @@
82
82
  border-left: 5px solid transparent;
83
83
  }
84
84
 
85
- [data-theme=dark] .wrapper--ticks::before, [data-theme=dark] .tick-left::before {
85
+ .dark .wrapper--ticks::before, .dark .tick-left::before {
86
86
  border-left-color: var(--color-nickel);
87
87
  }
88
88
 
89
- [data-theme=dark] .wrapper--ticks::after, [data-theme=dark] .tick-right::after {
89
+ .dark .wrapper--ticks::after, .dark .tick-right::after {
90
90
  border-right-color: var(--color-nickel);
91
91
  }
92
92
 
@@ -542,12 +542,12 @@
542
542
  display: inline-block;
543
543
  margin-top: -1px;
544
544
  margin-left: 4px;
545
- width: 11px;
546
- height: 11px;
545
+ width: 12px;
546
+ height: 12px;
547
547
  background: currentColor;
548
548
  color: var(--vp-c-text-3);
549
549
  flex-shrink: 0;
550
- --icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");
550
+ --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.81802 2.81803L9.18198 2.81803L9.18198 9.18199' stroke='black' stroke-width='1.25' stroke-linejoin='round'/%3E%3Cpath d='M9.18213 2.81802L2.81817 9.18198' stroke='black' stroke-width='1.25' stroke-linejoin='round'/%3E%3C/svg%3E");
551
551
  -webkit-mask-image: var(--icon);
552
552
  mask-image: var(--icon);
553
553
  /*rtl:raw:transform: scaleX(-1);*/
package/src/vitest.ts CHANGED
@@ -13,7 +13,7 @@ export default {
13
13
  ctx.app.provide(themeContextKey, {
14
14
  logoDark,
15
15
  logoLight,
16
- logoAlt: "Vite",
16
+ logoAlt: "Vitest",
17
17
  footerBg,
18
18
  monoIcon,
19
19
  });