mirror of https://github.com/vuejs/vitepress
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
342 lines
8.5 KiB
342 lines
8.5 KiB
1 year ago
|
---
|
||
|
outline: deep
|
||
|
---
|
||
|
|
||
2 years ago
|
# Extending the Default Theme
|
||
|
|
||
2 years ago
|
VitePress' default theme is optimized for documentation, and can be customized. Consult the [Default Theme Config Overview](../reference/default-theme-config) for a comprehensive list of options.
|
||
2 years ago
|
|
||
2 years ago
|
However, there are a number of cases where configuration alone won't be enough. For example:
|
||
2 years ago
|
|
||
2 years ago
|
1. You need to tweak the CSS styling;
|
||
|
2. You need to modify the Vue app instance, for example to register global components;
|
||
|
3. You need to inject custom content into the theme via layout slots.
|
||
2 years ago
|
|
||
2 years ago
|
These advanced customizations will require using a custom theme that "extends" the default theme.
|
||
2 years ago
|
|
||
1 year ago
|
::: tip
|
||
2 years ago
|
Before proceeding, make sure to first read [Using a Custom Theme](./custom-theme) to understand how custom themes work.
|
||
|
:::
|
||
2 years ago
|
|
||
2 years ago
|
## Customizing CSS
|
||
2 years ago
|
|
||
2 years ago
|
The default theme CSS is customizable by overriding root level CSS variables:
|
||
2 years ago
|
|
||
|
```js
|
||
|
// .vitepress/theme/index.js
|
||
2 years ago
|
import DefaultTheme from 'vitepress/theme'
|
||
|
import './custom.css'
|
||
2 years ago
|
|
||
2 years ago
|
export default DefaultTheme
|
||
2 years ago
|
```
|
||
|
|
||
2 years ago
|
```css
|
||
|
/* .vitepress/theme/custom.css */
|
||
|
:root {
|
||
1 year ago
|
--vp-c-brand-1: #646cff;
|
||
|
--vp-c-brand-2: #747bff;
|
||
2 years ago
|
}
|
||
2 years ago
|
```
|
||
|
|
||
2 years ago
|
See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css) that can be overridden.
|
||
2 years ago
|
|
||
2 years ago
|
## Using Different Fonts
|
||
|
|
||
|
VitePress uses [Inter](https://rsms.me/inter/) as the default font, and will include the fonts in the build output. The font is also auto preloaded in production. However, this may not be desirable if you want to use a different main font.
|
||
|
|
||
|
To avoid including Inter in the build output, import the theme from `vitepress/theme-without-fonts` instead:
|
||
|
|
||
|
```js
|
||
|
// .vitepress/theme/index.js
|
||
|
import DefaultTheme from 'vitepress/theme-without-fonts'
|
||
|
import './my-fonts.css'
|
||
|
|
||
|
export default DefaultTheme
|
||
|
```
|
||
|
|
||
|
```css
|
||
|
/* .vitepress/theme/custom.css */
|
||
|
:root {
|
||
|
--vp-font-family-base: /* normal text font */
|
||
|
--vp-font-family-mono: /* code font */
|
||
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
::: warning
|
||
1 year ago
|
If you are using optional components like the [Team Page](../reference/default-theme-team-page) components, make sure to also import them from `vitepress/theme-without-fonts`!
|
||
2 years ago
|
:::
|
||
|
|
||
1 year ago
|
If your font is a local file referenced via `@font-face`, it will be processed as an asset and included under `.vitepress/dist/assets` with hashed filename. To preload this file, use the [transformHead](../reference/site-config#transformhead) build hook:
|
||
2 years ago
|
|
||
|
```js
|
||
|
// .vitepress/config.js
|
||
|
export default {
|
||
|
transformHead({ assets }) {
|
||
|
// adjust the regex accordingly to match your font
|
||
|
const myFontFile = assets.find(file => /font-name\.\w+\.woff2/)
|
||
|
if (myFontFile) {
|
||
|
return [
|
||
2 years ago
|
[
|
||
|
'link',
|
||
|
{
|
||
|
rel: 'preload',
|
||
|
href: myFontFile,
|
||
|
as: 'font',
|
||
|
type: 'font/woff2',
|
||
|
crossorigin: ''
|
||
|
}
|
||
|
]
|
||
2 years ago
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
2 years ago
|
## Registering Global Components
|
||
|
|
||
|
```js
|
||
|
// .vitepress/theme/index.js
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
|
||
1 year ago
|
/** @type {import('vitepress').Theme} */
|
||
2 years ago
|
export default {
|
||
2 years ago
|
extends: DefaultTheme,
|
||
1 year ago
|
enhanceApp({ app }) {
|
||
2 years ago
|
// register your custom global components
|
||
1 year ago
|
app.component('MyGlobalComponent' /* ... */)
|
||
2 years ago
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
If you're using TypeScript:
|
||
|
```ts
|
||
|
// .vitepress/theme/index.ts
|
||
|
import type { Theme } from 'vitepress'
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
|
||
|
export default {
|
||
1 year ago
|
extends: DefaultTheme,
|
||
1 year ago
|
enhanceApp({ app }) {
|
||
1 year ago
|
// register your custom global components
|
||
1 year ago
|
app.component('MyGlobalComponent' /* ... */)
|
||
1 year ago
|
}
|
||
|
} satisfies Theme
|
||
|
```
|
||
|
|
||
2 years ago
|
Since we are using Vite, you can also leverage Vite's [glob import feature](https://vitejs.dev/guide/features.html#glob-import) to auto register a directory of components.
|
||
|
|
||
|
## Layout Slots
|
||
|
|
||
|
The default theme's `<Layout/>` component has a few slots that can be used to inject content at certain locations of the page. Here's an example of injecting a component into the before outline:
|
||
|
|
||
|
```js
|
||
|
// .vitepress/theme/index.js
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
import MyLayout from './MyLayout.vue'
|
||
|
|
||
|
export default {
|
||
1 year ago
|
extends: DefaultTheme,
|
||
2 years ago
|
// override the Layout with a wrapper component that
|
||
|
// injects the slots
|
||
|
Layout: MyLayout
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```vue
|
||
|
<!--.vitepress/theme/MyLayout.vue-->
|
||
|
<script setup>
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
|
||
|
const { Layout } = DefaultTheme
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<Layout>
|
||
|
<template #aside-outline-before>
|
||
|
My custom sidebar top content
|
||
|
</template>
|
||
|
</Layout>
|
||
|
</template>
|
||
|
```
|
||
|
|
||
|
Or you could use render function as well.
|
||
|
|
||
|
```js
|
||
|
// .vitepress/theme/index.js
|
||
|
import { h } from 'vue'
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
import MyComponent from './MyComponent.vue'
|
||
|
|
||
|
export default {
|
||
1 year ago
|
extends: DefaultTheme,
|
||
2 years ago
|
Layout() {
|
||
|
return h(DefaultTheme.Layout, null, {
|
||
|
'aside-outline-before': () => h(MyComponent)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Full list of slots available in the default theme layout:
|
||
|
|
||
|
- When `layout: 'doc'` (default) is enabled via frontmatter:
|
||
2 years ago
|
- `doc-top`
|
||
|
- `doc-bottom`
|
||
2 years ago
|
- `doc-footer-before`
|
||
|
- `doc-before`
|
||
|
- `doc-after`
|
||
|
- `sidebar-nav-before`
|
||
|
- `sidebar-nav-after`
|
||
|
- `aside-top`
|
||
|
- `aside-bottom`
|
||
|
- `aside-outline-before`
|
||
|
- `aside-outline-after`
|
||
|
- `aside-ads-before`
|
||
|
- `aside-ads-after`
|
||
|
- When `layout: 'home'` is enabled via frontmatter:
|
||
|
- `home-hero-before`
|
||
10 months ago
|
- `home-hero-info-before`
|
||
2 years ago
|
- `home-hero-info`
|
||
10 months ago
|
- `home-hero-info-after`
|
||
10 months ago
|
- `home-hero-actions-after`
|
||
2 years ago
|
- `home-hero-image`
|
||
|
- `home-hero-after`
|
||
|
- `home-features-before`
|
||
|
- `home-features-after`
|
||
2 years ago
|
- When `layout: 'page'` is enabled via frontmatter:
|
||
|
- `page-top`
|
||
|
- `page-bottom`
|
||
2 years ago
|
- On not found (404) page:
|
||
|
- `not-found`
|
||
2 years ago
|
- Always:
|
||
|
- `layout-top`
|
||
|
- `layout-bottom`
|
||
|
- `nav-bar-title-before`
|
||
|
- `nav-bar-title-after`
|
||
|
- `nav-bar-content-before`
|
||
|
- `nav-bar-content-after`
|
||
|
- `nav-screen-content-before`
|
||
|
- `nav-screen-content-after`
|
||
2 years ago
|
|
||
1 year ago
|
## Using View Transitions API
|
||
|
|
||
|
### On Appearance Toggle
|
||
|
|
||
|
You can extend the default theme to provide a custom transition when the color mode is toggled. An example:
|
||
|
|
||
|
```vue
|
||
|
<!-- .vitepress/theme/Layout.vue -->
|
||
|
|
||
|
<script setup lang="ts">
|
||
|
import { useData } from 'vitepress'
|
||
|
import DefaultTheme from 'vitepress/theme'
|
||
|
import { nextTick, provide } from 'vue'
|
||
|
|
||
|
const { isDark } = useData()
|
||
|
|
||
|
const enableTransitions = () =>
|
||
|
'startViewTransition' in document &&
|
||
|
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||
|
|
||
|
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||
|
if (!enableTransitions()) {
|
||
|
isDark.value = !isDark.value
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const clipPath = [
|
||
|
`circle(0px at ${x}px ${y}px)`,
|
||
|
`circle(${Math.hypot(
|
||
|
Math.max(x, innerWidth - x),
|
||
|
Math.max(y, innerHeight - y)
|
||
|
)}px at ${x}px ${y}px)`
|
||
|
]
|
||
|
|
||
|
await document.startViewTransition(async () => {
|
||
|
isDark.value = !isDark.value
|
||
|
await nextTick()
|
||
|
}).ready
|
||
|
|
||
|
document.documentElement.animate(
|
||
|
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
|
||
|
{
|
||
|
duration: 300,
|
||
|
easing: 'ease-in',
|
||
|
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
|
||
|
}
|
||
|
)
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<DefaultTheme.Layout />
|
||
|
</template>
|
||
|
|
||
|
<style>
|
||
|
::view-transition-old(root),
|
||
|
::view-transition-new(root) {
|
||
|
animation: none;
|
||
|
mix-blend-mode: normal;
|
||
|
}
|
||
|
|
||
|
::view-transition-old(root),
|
||
|
.dark::view-transition-new(root) {
|
||
|
z-index: 1;
|
||
|
}
|
||
|
|
||
|
::view-transition-new(root),
|
||
|
.dark::view-transition-old(root) {
|
||
|
z-index: 9999;
|
||
|
}
|
||
|
|
||
|
.VPSwitchAppearance {
|
||
|
width: 22px !important;
|
||
|
}
|
||
|
|
||
|
.VPSwitchAppearance .check {
|
||
|
transform: none !important;
|
||
|
}
|
||
|
</style>
|
||
|
```
|
||
|
|
||
|
Result (**warning!**: flashing colors, sudden movements, bright lights):
|
||
|
|
||
|
<details>
|
||
|
<summary>Demo</summary>
|
||
|
|
||
|
![Appearance Toggle Transition Demo](/appearance-toggle-transition.webp)
|
||
|
|
||
|
</details>
|
||
|
|
||
|
Refer [Chrome Docs](https://developer.chrome.com/docs/web-platform/view-transitions/) from more details on view transitions.
|
||
|
|
||
|
### On Route Change
|
||
|
|
||
|
Coming soon.
|
||
|
|
||
2 years ago
|
## Overriding Internal Components
|
||
|
|
||
|
You can use Vite's [aliases](https://vitejs.dev/config/shared-options.html#resolve-alias) to replace default theme components with your custom ones:
|
||
|
|
||
|
```ts
|
||
|
import { fileURLToPath, URL } from 'node:url'
|
||
|
import { defineConfig } from 'vitepress'
|
||
|
|
||
|
export default defineConfig({
|
||
|
vite: {
|
||
|
resolve: {
|
||
|
alias: [
|
||
|
{
|
||
|
find: /^.*\/VPNavBar\.vue$/,
|
||
|
replacement: fileURLToPath(
|
||
|
new URL('./components/CustomNavBar.vue', import.meta.url)
|
||
|
)
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
```
|
||
|
|
||
|
To know the exact name of the component refer [our source code](https://github.com/vuejs/vitepress/tree/main/src/client/theme-default/components). Since the components are internal, there is a slight chance their name is updated between minor releases.
|