Skip to content

Commit 781e2b9

Browse files
Updating theme
1 parent 717852c commit 781e2b9

12 files changed

Lines changed: 769 additions & 175 deletions

File tree

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"css.lint.unknownAtRules": "ignore",
3+
"scss.lint.unknownAtRules": "ignore",
4+
"less.lint.unknownAtRules": "ignore"
5+
}

docs/.vitepress/config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { copyFileSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
22
import { dirname, join, relative, resolve } from "path";
33
import { defineConfig, type HeadConfig } from "vitepress";
4+
import { extendConfig } from "@voidzero-dev/vitepress-theme/config";
45
import { tabsMarkdownPlugin } from "vitepress-plugin-tabs";
56

67
function loadEnvVar(key: string): string | undefined {
@@ -45,7 +46,7 @@ const searchConfig =
4546
}
4647
: { provider: "local" as const };
4748

48-
export default defineConfig({
49+
const config = defineConfig({
4950
title: "Plane",
5051
description: "Modern project management software",
5152
cleanUrls: true,
@@ -195,11 +196,12 @@ export default defineConfig({
195196
],
196197

197198
themeConfig: {
199+
variant: "voidzero",
198200
logo: {
199201
light: "https://media.docs.plane.so/logo/new-logo-white.png",
200202
dark: "https://media.docs.plane.so/logo/new-logo-dark.png",
201203
},
202-
siteTitle: "",
204+
siteTitle: "Plane",
203205

204206
outline: {
205207
level: [2, 3],
@@ -224,6 +226,7 @@ export default defineConfig({
224226
{
225227
text: "Sign in",
226228
link: "https://app.plane.so/sign-in",
229+
noIcon: true,
227230
},
228231
],
229232

@@ -682,3 +685,5 @@ export default defineConfig({
682685
},
683686
},
684687
});
688+
689+
export default extendConfig(config);

docs/.vitepress/theme/Layout.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import { computed, useSlots } from "vue";
3+
import VoidZeroTheme from "@voidzero-dev/vitepress-theme";
4+
5+
const BaseLayout = VoidZeroTheme.Layout;
6+
const slots = useSlots();
7+
const forwardSlotNames = computed(() => Object.keys(slots) as string[]);
8+
</script>
9+
10+
<template>
11+
<BaseLayout>
12+
<template v-for="name in forwardSlotNames" :key="name" #[name]="data">
13+
<slot :name="name" v-bind="data || {}" />
14+
</template>
15+
</BaseLayout>
16+
</template>

docs/.vitepress/theme/components/Card.vue

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@ import * as LucideIcons from "lucide-vue-next";
55
const props = defineProps({
66
title: String,
77
icon: String,
8+
/** Doc path or full URL */
89
href: String,
10+
/** Alias for `href` (common in markdown) */
11+
link: String,
12+
/** When set, used instead of the default slot for body text */
13+
description: String,
14+
/** Bottom link label (shown with →). Home feature cards only. */
15+
cta: String,
916
});
1017
18+
const resolvedHref = computed(() => props.link || props.href || "#");
19+
1120
// Custom SVG icons for brands
1221
const customSvgIcons = {
1322
asana: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18.559 13.605a5.158 5.158 0 1 0 0 10.317 5.158 5.158 0 0 0 0-10.317Zm-13.401.001a5.158 5.158 0 1 0 0 10.315 5.158 5.158 0 0 0 0-10.315Zm11.858-6.448a5.158 5.158 0 1 1-10.316 0 5.158 5.158 0 0 1 10.316 0Z" fill="#F06A6A"/></svg>`,
@@ -44,18 +53,24 @@ const IconComponent = computed(() => {
4453
</script>
4554

4655
<template>
47-
<a :href="href" class="card-link">
48-
<div v-if="icon" class="card-icon">
49-
<!-- Render custom SVG if available -->
56+
<a
57+
:href="resolvedHref"
58+
class="card-link"
59+
:class="{ 'card-link--with-cta': cta }"
60+
>
61+
<div v-if="icon" class="card-icon" aria-hidden="true">
5062
<div v-if="customSvgIcons[icon]" v-html="customSvgIcons[icon]"></div>
51-
<!-- Otherwise render Lucide icon -->
52-
<component v-else :is="IconComponent" :size="24" />
63+
<component v-else :is="IconComponent" :size="24" stroke-width="1.5" />
5364
</div>
5465
<h3 class="card-title">
5566
{{ title }}
5667
</h3>
57-
<p class="card-description">
68+
<p v-if="description" class="card-description">
69+
{{ description }}
70+
</p>
71+
<p v-else class="card-description">
5872
<slot />
5973
</p>
74+
<span v-if="cta" class="card-cta">{{ cta }} →</span>
6075
</a>
6176
</template>

docs/.vitepress/theme/components/CardGroup.vue

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
<script setup>
2-
defineProps({
2+
import { computed } from "vue";
3+
4+
const props = defineProps({
35
cols: {
46
type: [Number, String],
57
default: 2,
68
},
9+
/** Alias for `cols` (e.g. `<CardGroup columns="3">`) */
10+
columns: {
11+
type: [Number, String],
12+
default: undefined,
13+
},
714
});
15+
16+
const columnCount = computed(() => props.columns ?? props.cols);
817
</script>
918
1019
<template>
1120
<div
1221
class="card-group"
1322
:class="{
14-
'card-group-2': cols == 2,
15-
'card-group-3': cols == 3,
16-
'card-group-4': cols == 4,
23+
'card-group-2': columnCount == 2,
24+
'card-group-3': columnCount == 3,
25+
'card-group-4': columnCount == 4,
1726
}"
1827
>
1928
<slot />

docs/.vitepress/theme/index.ts

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
1-
import DefaultTheme from "vitepress/theme";
21
import type { Theme } from "vitepress";
2+
import type { ThemeContext } from "@voidzero-dev/vitepress-theme";
3+
import VoidZeroTheme from "@voidzero-dev/vitepress-theme";
4+
import { themeContextKey } from "@voidzero-dev/vitepress-theme";
35
import { onMounted, watch, nextTick } from "vue";
46
import { useRoute } from "vitepress";
57
import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client";
68
import mediumZoom from "medium-zoom";
79
import Card from "./components/Card.vue";
810
import CardGroup from "./components/CardGroup.vue";
911
import Tags from "./components/Tags.vue";
12+
import Layout from "./Layout.vue";
1013
import "./style.css";
1114

15+
/**
16+
* OSSHeader (used on doc pages) injects this context for the bar logo — *not* `themeConfig.logo`.
17+
* The `viteplus` entry in the package overwrites it with "Vite+" assets; we use the base
18+
* `VoidZeroTheme` and provide Plane branding here.
19+
*/
20+
const planeThemeContext: ThemeContext = {
21+
/* OSSHeader renders logoDark in light mode and logoLight in dark mode. */
22+
logoDark: "https://media.docs.plane.so/logo/new-logo-white.png",
23+
logoLight: "https://media.docs.plane.so/logo/new-logo-dark.png",
24+
logoAlt: "Plane",
25+
footerBg: "https://media.docs.plane.so/logo/og-docs.webp",
26+
monoIcon: "https://media.docs.plane.so/logo/favicon-32x32.png",
27+
};
28+
1229
/**
1330
* Handles tab activation based on URL hash
1431
*/
@@ -73,13 +90,52 @@ function updateHashOnTabClick(event: Event) {
7390
}
7491
}
7592

93+
/**
94+
* Move "Sign in" CTA to the right utility area (between search and theme toggle).
95+
* The upstream OSSHeader renders nav links on the left; we remap only this CTA.
96+
*/
97+
function moveSignInToUtilityArea() {
98+
if (typeof document === "undefined") return;
99+
100+
const signInLink = document.querySelector(
101+
'.docs-layout header .VPNavBarMenu a.VPLink[href*="sign-in"]',
102+
) as HTMLAnchorElement | null;
103+
if (!signInLink) return;
104+
105+
const appearanceToggle = document.querySelector(
106+
".docs-layout header .VPNavBarAppearance",
107+
) as HTMLElement | null;
108+
109+
// Desktop (xl+): insert before theme toggle inside the utilities row.
110+
if (
111+
appearanceToggle?.parentElement &&
112+
signInLink.parentElement !== appearanceToggle.parentElement
113+
) {
114+
signInLink.classList.add("sign-in-relocated");
115+
appearanceToggle.parentElement.insertBefore(signInLink, appearanceToggle);
116+
return;
117+
}
118+
119+
// Tablet fallback (lg-xl): keep it in right controls row before the extra menu.
120+
const extraMenu = document.querySelector(
121+
".docs-layout header .VPNavBarExtra",
122+
) as HTMLElement | null;
123+
if (extraMenu?.parentElement && signInLink.parentElement !== extraMenu.parentElement) {
124+
signInLink.classList.add("sign-in-relocated");
125+
extraMenu.parentElement.insertBefore(signInLink, extraMenu);
126+
}
127+
}
128+
76129
export default {
77-
extends: DefaultTheme,
78-
enhanceApp({ app }) {
79-
enhanceAppWithTabs(app);
80-
app.component("Card", Card);
81-
app.component("CardGroup", CardGroup);
82-
app.component("Tags", Tags);
130+
extends: VoidZeroTheme,
131+
Layout,
132+
enhanceApp(ctx) {
133+
ctx.app.provide(themeContextKey, planeThemeContext);
134+
VoidZeroTheme.enhanceApp(ctx);
135+
enhanceAppWithTabs(ctx.app);
136+
ctx.app.component("Card", Card);
137+
ctx.app.component("CardGroup", CardGroup);
138+
ctx.app.component("Tags", Tags);
83139
},
84140
setup() {
85141
if (typeof window === "undefined") return;
@@ -95,6 +151,7 @@ export default {
95151
setTimeout(() => {
96152
handleTabHash();
97153
setupTabHashUpdates();
154+
moveSignInToUtilityArea();
98155
}, 100);
99156

100157
// Listen for hash changes
@@ -112,6 +169,7 @@ export default {
112169
zoom.attach(":not(a) > img:not(.VPImage)");
113170
handleTabHash();
114171
setupTabHashUpdates();
172+
moveSignInToUtilityArea();
115173
});
116174
},
117175
);

0 commit comments

Comments
 (0)