diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ff1c402a..db4a23d9 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -100,11 +100,11 @@ function sidebarGuide() { text: 'Customization', collapsed: false, items: [ + { text: 'Using a Custom Theme', link: '/guide/custom-theme' }, { text: 'Extending the Default Theme', link: '/guide/extending-default-theme' }, - { text: 'Building a Custom Theme', link: '/guide/custom-theme' }, { text: 'Build-Time Data Loading', link: '/guide/data-loading' }, { text: 'Connecting to a CMS', link: '/guide/cms' } ] diff --git a/docs/guide/custom-theme.md b/docs/guide/custom-theme.md index ebe596fc..edde3d24 100644 --- a/docs/guide/custom-theme.md +++ b/docs/guide/custom-theme.md @@ -1 +1,222 @@ -# Building a Custom Theme +# Using a Custom Theme + +## Theme Resolving + +You can enable a custom theme by creating a `.vitepress/theme/index.js` or `.vitepress/theme/index.ts` file (the "theme entry file"): + +``` +. +├─ docs # project root +│ ├─ .vitepress +│ │ ├─ theme +│ │ │ └─ index.js # theme entry +│ │ └─ config.js # config file +│ └─ index.md +└─ package.json +``` + +VitePress will always use the custom theme instead of the default theme when it detects presence of a theme entry file. You can, however, [extend the default theme](./extending-default-theme) to perform advanced customizations on top of it. + +## Theme Interface + +A VitePress custom theme is defined as an object with the following interface: + +```ts +interface Theme { + /** + * Root layout component for every page + * @required + */ + Layout: Component + /** + * Enhance Vue app instance + * @optional + */ + enhanceApp?: (ctx: EnhanceAppContext) => Awaitable + /** + * Extend another theme, calling its `enhanceApp` before ours + * @optional + */ + extends?: Theme +} + +interface EnhanceAppContext { + app: App // Vue app instance + router: Router // VitePress router instance + siteData: Ref // Site-level metadata +} +``` + +The theme entry file should export the theme as its default export: + +```js +// .vitepress/theme/index.js + +// You can directly import Vue files in the theme entry +// VitePress is pre-configured with @vitejs/plugin-vue. +import Layout from './Layout.vue' + +export default { + Layout, + enhanceApp({ app, router, siteData }) { + // ... + } +} +``` + +The default export is the only contract for a custom theme, and only the `Layout` property is required. So technically, a VitePress theme can be as simple as a single Vue component. + +Inside your layout component, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions). + +## Building a Layout + +The most basic layout component needs to contain a [``](/reference/runtime-api#content) component: + +```vue + + +``` + +The above layout simply renders every page's markdown as HTML. The first improvement we can add is to handle 404 errors: + +```vue{1-4,9-12} + + + +``` + +The [`useData()`](/reference/runtime-api#usedata) helper provides us with all the runtime data we need to conditionally render different layouts. One of the other data we can access is the current page's frontmatter. We can leverage this to allow the end user to control the layout in each page. For example, the user can indicate the page should use a special home page layout with: + +```md +--- +layout: home +--- +``` + +And we can adjust our theme to handle this: + +```vue{3,12-14} + + + +``` + +You can, of course, split the layout into more components: + +```vue{3-5,12-15} + + + +``` + +Consult the [Runtime API Reference](/reference/runtime-api) for everything available in theme components. In addition, you can leverage [Build-Time Data Loading](./data-loading) to generate data-driven layout - for example, a page that lists all blog posts in the current project. + +## Distributing a Custom Theme + +The easiest way to distribute a custom theme is by providing it as a [template repository on GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository). + +If you wish to distribute the theme as an npm package, follow these steps: + +1. Export the theme object as the default export in your package entry. + +2. If applicable, export your theme config type definition as `ThemeConfig`. + +3. If your theme requires adjusting the VitePress config, export that config under a package sub-path (e.g. `my-theme/config`) so the user can extend it. + +4. Document the theme config options (both via config file and frontmatter). + +5. Provide clear instructions on how to consume your theme (see below). + +## Consuming a Custom Theme + +To consume an external theme, import and re-export it from the custom theme entry: + +```js +// .vitepress/theme/index.js +import Theme from 'awesome-vitepress-theme' + +export default Theme +``` + +If the theme needs to be extended: + +```js +// .vitepress/theme/index.js +import Theme from 'awesome-vitepress-theme' + +export default { + extends: Theme, + enhanceApp(ctx) { + // ... + } +} +``` + +If the theme requires special VitePress config, you will need to also extend it in your own config: + +```ts +// .vitepress/theme/config.ts +import baseConfig from 'awesome-vitepress-theme/config' + +export default { + // extend theme base config (if needed) + extends: baseConfig +} +``` + +Finally, if the theme provides types for its theme config: + +```ts +// .vitepress/theme/config.ts +import baseConfig from 'awesome-vitepress-theme/config' +import { defineConfigWithTheme } from 'vitepress' +import type { ThemeConfig } from 'awesome-vitepress-theme' + +export default defineConfigWithTheme({ + extends: baseConfig, + themeConfig: { + // Type is `ThemeConfig` + } +}) +``` diff --git a/docs/guide/extending-default-theme.md b/docs/guide/extending-default-theme.md index 2e48007a..88647cec 100644 --- a/docs/guide/extending-default-theme.md +++ b/docs/guide/extending-default-theme.md @@ -1,97 +1,40 @@ # Extending the Default Theme -VitePress comes with its default theme providing many features out of the box. You can check out the full features in the [Default Theme Config Overview](/reference/default-theme-config). +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. -There are several cases where you may want to extend the default theme: +However, there are a number of cases where configuration alone won't be enough. For example: -1. You want to modify the Vue app, for example register global components; -2. You want to tweak the CSS styling; -3. You want to inject custom content into the theme via layout slots. +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. -## Using a Custom Theme +These advanced customizations will require using a custom theme that "extends" the default theme. -You can enable a custom theme by adding the `.vitepress/theme/index.js` or `.vitepress/theme/index.ts` file (the "theme entry file"). +:::tip +Before proceeding, make sure to first read [Using a Custom Theme](./custom-theme) to understand how custom themes work. +::: -``` -. -├─ docs -│ ├─ .vitepress -│ │ ├─ theme -│ │ │ └─ index.js -│ │ └─ config.js -│ └─ index.md -└─ package.json -``` - -A VitePress custom theme is simply an object containing four properties and is defined as follows: - -```ts -interface Theme { - Layout: Component // Vue 3 component - NotFound?: Component - enhanceApp?: (ctx: EnhanceAppContext) => Awaitable - setup?: () => void -} - -interface EnhanceAppContext { - app: App // Vue 3 app instance - router: Router // VitePress router instance - siteData: Ref -} -``` +## Customizing CSS -The theme entry file should export the theme as its default export: +The default theme CSS is customizable by overriding root level CSS variables: ```js // .vitepress/theme/index.js -import Layout from './Layout.vue' - -export default { - // root component to wrap each page - Layout, - - // this is a Vue 3 functional component - NotFound: () => 'custom 404', - - enhanceApp({ app, router, siteData }) { - // app is the Vue 3 app instance from `createApp()`. - // router is VitePress' custom router. `siteData` is - // a `ref` of current site-level metadata. - }, - - setup() { - // this function will be executed inside VitePressApp's - // setup hook. all composition APIs are available here. - } -} -``` - -...where the `Layout` component could look like this: - -```vue - - +export default DefaultTheme ``` -The default export is the only contract for a custom theme. Inside your custom theme, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions). - -To distribute a theme, simply export the object in your package entry. To consume an external theme, import and re-export it from the custom theme entry: - -```js -// .vitepress/theme/index.js -import Theme from 'awesome-vitepress-theme' - -export default Theme +```css +/* .vitepress/theme/custom.css */ +:root { + --vp-c-brand: #646cff; + --vp-c-brand-light: #747bff; +} ``` -## Extending the Default Theme - -If you want to extend and customize the default theme, you can import it from `vitepress/theme` and augment it in a custom theme entry. Here are some examples of common customizations: +See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css) that can be overridden. ## Registering Global Components @@ -100,11 +43,8 @@ If you want to extend and customize the default theme, you can import it from `v import DefaultTheme from 'vitepress/theme' export default { - ...DefaultTheme, + extends: DefaultTheme, enhanceApp(ctx) { - // extend default theme custom behaviour. - DefaultTheme.enhanceApp(ctx) - // register your custom global components ctx.app.component('MyGlobalComponent' /* ... */) } @@ -113,28 +53,6 @@ export default { 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. -## Customizing CSS - -The default theme CSS is customizable by overriding root level CSS variables: - -```js -// .vitepress/theme/index.js -import DefaultTheme from 'vitepress/theme' -import './custom.css' - -export default DefaultTheme -``` - -```css -/* .vitepress/theme/custom.css */ -:root { - --vp-c-brand: #646cff; - --vp-c-brand-light: #747bff; -} -``` - -See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/clien/reference/default-theme-default/styles/vars.css) that can be overridden. - ## Layout Slots The default theme's `` 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: diff --git a/docs/reference/runtime-api.md b/docs/reference/runtime-api.md index 4240fdc1..823074c8 100644 --- a/docs/reference/runtime-api.md +++ b/docs/reference/runtime-api.md @@ -4,7 +4,7 @@ VitePress offers several built-in APIs to let you access app data. VitePress als The helper methods are globally importable from `vitepress` and are typically used in custom theme Vue components. However, they are also usable inside `.md` pages because markdown files are compiled into Vue [Single-File Components](https://vuejs.org/guide/scaling-up/sfc.html). -Methods that start with `use*` indicates that it is a [Vue 3 Composition API](https://vuejs.org/guide/introduction.html#composition-api) function that can only be used inside `setup()` or `