diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index ab1e2115c93902a5cbbf4f8931ab69a82da31f8a..b2068c510c541402910696557df9ff514ea53586 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -15,6 +15,7 @@ module.exports = {
'/assets',
'/using-vue',
'/config',
+ '/default-theme',
'/theming',
'/deploy'
]
diff --git a/docs/README.md b/docs/README.md
index b6c8d25e615970ba64a53e2b12fd77bb43fcaa35..99a78a223d56c8f3c98fb1fae616ea5f85afa94c 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -8,15 +8,15 @@ navTitle: Getting Started
## Why
-- Writing first: minimal setup, all you need is a markdown file.
+- **Writing First**: minimal setup, all you need is a markdown file.
-- Vue-powered: the layout system is a Vue app. It's also pre-rendered into static HTML. And you can register and use Vue components inside markdown content.
+- **Vue-powered**: Use custom Vue components inside markdown, and develop custom themes using Vue single file components.
-- Flexible: develop with full power of webpack (hot-reload, pre-processors support), generate SEO-friendly static HTML, and works as an SPA after initial page load.
+- **Great Dev Experience**: enjoy the same enjoyable development experience of a Vue app. Leverage the full power of webpack (hot-reload, pre-processors), generate SEO-friendly static HTML, and works as an SPA after initial page load.
-- Optimized for docs: many built-in markdown extensions and default theme features for writing great documentation.
+- **Optimized for Docs**: many [built-in markdown extensions](./markdown.md) and default theme features for writing great documentation.
-- GitHub friendly: pages can link to each other using relative links that ends in `.md`, auto-generates GitHub link and page edit links if a repo is provided.
+- **GitHub Friendly**: source markdown files can link to each other using relative links that ends in `.md` so they are also readable on GitHub, auto-generates page edit links if a repo is provided.
## Quickstart
diff --git a/docs/config.md b/docs/config.md
index 68ce4016f0372e55dfc590c14e970f76c372e8fc..c1b2f548241c65b198bafca804d09a2d986c3563 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -20,6 +20,8 @@
### themeConfig
+Also see [config options for the default theme](./default-theme.md).
+
## Markdown
### markdown.anchor
diff --git a/docs/default-theme.md b/docs/default-theme.md
new file mode 100644
index 0000000000000000000000000000000000000000..f36984da393f3255d2f97fc759cba2c0b0c0db6f
--- /dev/null
+++ b/docs/default-theme.md
@@ -0,0 +1 @@
+# Default Theme Configuration
diff --git a/docs/theming.md b/docs/theming.md
index 29235422b030523e7519cdf3fa779ffada945433..960686a858941c4e23397f25338b4af5975db224 100644
--- a/docs/theming.md
+++ b/docs/theming.md
@@ -1,4 +1,4 @@
-# Theming
+# Custom Themes
VuePress uses Vue single file components for custom themes. To use a custom layout, create a `.vuepress/theme` directory in your docs root, and then create a `Layout.vue` file:
diff --git a/docs/using-vue.md b/docs/using-vue.md
index a472bdf1fdd5348f705256ab545911daca6c43d7..a147f4854ce4fbd2ec63acb5dcd797bf3e949dda 100644
--- a/docs/using-vue.md
+++ b/docs/using-vue.md
@@ -42,18 +42,32 @@ The compiled component does not have any private data but do have access to the
**Output**
-
{{ $page }}
+``` json
+{
+ "path": "/using-vue.html",
+ "title": "Using Vue in Markdown",
+ "frontmatter": {}
+}
+```
## Escaping
By default, fenced code blocks are automatically wrapped with `v-pre`. If you want to display raw mustaches or Vue-specific syntax inside inline code snippets or plain text, you need to wrap a paragraph with the `v-pre` custom container:
+**Input**
+
``` markdown
::: v-pre
`{{ This will be displayed as-is }}`
:::
```
+**Output**
+
+::: v-pre
+`{{ This will be displayed as-is }}`
+:::
+
## Using Components
Any `*.vue` file found in `.vuepress/components` are automatically registered as global async components. For example:
@@ -77,7 +91,7 @@ Inside any markdown file you can then directly use the components (names are inf
-::: warning
+::: warning IMPORTANT
Make sure a custom component's names either contains a hyphen or is in PascalCase. Otherwise it will be treated as an inline element and wrapped inside a `` tag, which will lead to hydration mismatch because `
` does not allow block elements to be placed inside it.
:::
diff --git a/lib/app/index.dev.html b/lib/app/index.dev.html
index d3ef49ee6bb5916345e336b301985894f0867749..d27b7a25fadd249ba8f4807b4cf8d6c3930fb655 100644
--- a/lib/app/index.dev.html
+++ b/lib/app/index.dev.html
@@ -2,6 +2,7 @@
+
diff --git a/lib/app/index.ssr.html b/lib/app/index.ssr.html
index 474281551d805ca6d1b417a24c7dfe1a706e4b25..d61f12366ee78e637adba224208f024a781b9273 100644
--- a/lib/app/index.ssr.html
+++ b/lib/app/index.ssr.html
@@ -2,6 +2,7 @@
+
{{ title }}
{{{ userHeadTags }}}
{{{ pageMeta }}}
diff --git a/lib/default-theme/Layout.vue b/lib/default-theme/Layout.vue
index 624b7bb884b4cb519ea0cdcff81c8449a4a968a4..1776121fa679755ae14f314fbb88b05f92c8210d 100644
--- a/lib/default-theme/Layout.vue
+++ b/lib/default-theme/Layout.vue
@@ -1,12 +1,14 @@
diff --git a/lib/default-theme/theme.stylus b/lib/default-theme/theme.stylus
index 7866ce2fd7d90afe74d8aea5d25a05a63a382e7d..ca3cf6c489c33a48cccdc1380b0e035226fd4241 100644
--- a/lib/default-theme/theme.stylus
+++ b/lib/default-theme/theme.stylus
@@ -1,20 +1,45 @@
-.theme-container
+body
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
- padding 0 30px
font-size 16px
color #2c3e50
-.markdown
- max-width 800px
- margin-left 300px
- margin-bottom 30px
+.page
+ padding-left 300px
.sidebar
+ width 300px
position fixed
margin 0
- top 30px
+ top 0
+ left 0
+ bottom 0
+ padding 2em 0
+ box-sizing border-box
+ border-right 1px solid #eaecef
+ overflow-y scroll
+ ul
+ padding 0
+ margin 0
+ list-style-type none
+ a
+ display inline-block
+ color #2c3e50
+ border-left 4px solid transparent
+ padding 0.25em
+ padding-left 1.25em
+ &:hover
+ color darken(#41b883, 5%)
+ &.router-link-exact-active
+ font-weight 600
+ color darken(#41b883, 5%)
+ border-left-color darken(#41b883, 5%)
+
+.markdown
+ max-width 720px
+ margin 2em auto
+ padding 0 2em
blockquote
font-size 1.25em
@@ -64,7 +89,7 @@ code
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
p, ul, ol
- line-height 1.5
+ line-height 1.7
p, h1, h2, h3, h4, h5, h6
code
@@ -81,19 +106,19 @@ pre, pre[class*="language-"]
background-color #2d2d2d
color white
line-height 1.4
- border-radius 5px
- padding 1.3rem 1.575rem
+ border-radius 6px
+ padding 1.4em 1.6em
white-space pre-wrap
word-break break-word
overflow auto
code
- font-size .88em
+ font-size 0.85em
.highlighted-line
background-color #14161a
display block
- margin 0 -1.575rem
- padding 0.2rem 1.575rem 0
+ margin 0 -1.6em
+ padding 0.2em 1.6em 0
.custom-block
.custom-block-title
@@ -105,7 +130,7 @@ pre, pre[class*="language-"]
border-left-style solid
margin 1em 0
&.tip
- background-color #f1f3f5
+ background-color #f3f5f7
border-color #42b983
&.warning
background-color rgba(255,229,100,.3)
@@ -125,3 +150,27 @@ p
padding 1rem
border 1px solid #ddd
border-radius 4px
+
+@media (max-width: 879px)
+ body
+ font-size 15px
+ .sidebar
+ font-size 14px
+ width 250px
+ .page
+ padding-left 250px
+ .markdown
+ margin 1.5em 0
+ padding 0 1.5em
+
+@media (max-width: 639px)
+ body
+ font-size 15px
+ .sidebar
+ width 275px
+ display none
+ .page
+ padding-left 0
+ .markdown
+ margin 1em 0
+ padding 0 1em
diff --git a/lib/prepare.js b/lib/prepare.js
index 5f59534a4a8481bbe6bef1562454eb253606e928..b1c2a19fb4078b1f82970e1b2c1beee5256afb49 100644
--- a/lib/prepare.js
+++ b/lib/prepare.js
@@ -4,6 +4,7 @@ const globby = require('globby')
const mkdirp = require('mkdirp')
const yaml = require('yaml-front-matter')
const tempPath = path.resolve(__dirname, 'app/.temp')
+const { inferTitle } = require('./util')
mkdirp(tempPath)
@@ -114,9 +115,9 @@ async function resolveOptions (sourceDir) {
// extract yaml frontmatter
const frontmatter = yaml.loadFront(content)
// infer title
- const titleMatch = frontmatter.__content.trim().match(/^#+\s+(.*)/)
- if (titleMatch) {
- data.title = titleMatch[1]
+ const title = inferTitle(frontmatter)
+ if (title) {
+ data.title = title
}
delete frontmatter.__content
if (Object.keys(frontmatter).length) {
diff --git a/lib/util.js b/lib/util.js
index 108004befa4aa69bb7e7a21dd0f3f97a97f67ab6..b4e14120e93fe4004e65b8b5675c01320be84fe5 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -24,3 +24,13 @@ exports.applyUserWebpackConfig = function (userConfig, config, isServer) {
}
return config
}
+
+exports.inferTitle = function (frontmatter) {
+ if (frontmatter.title) {
+ return frontmatter.title
+ }
+ const match = frontmatter.__content.trim().match(/^#+\s+(.*)/)
+ if (match) {
+ return match[1]
+ }
+}
diff --git a/lib/webpack/markdownLoader.js b/lib/webpack/markdownLoader.js
index 0dad3d5c5103143ceb06a60b6c44d130d9e01b9e..870ca4c0c8e234bbc7767e97b9bdcd6c869c5d15 100644
--- a/lib/webpack/markdownLoader.js
+++ b/lib/webpack/markdownLoader.js
@@ -1,24 +1,33 @@
const { EventEmitter } = require('events')
const { getOptions } = require('loader-utils')
-const frontmatter = require('yaml-front-matter')
+const yaml = require('yaml-front-matter')
+const { inferTitle } = require('../util')
-const frontmatterCache = new Map()
+const cache = new Map()
module.exports = function (src) {
const { markdown } = getOptions(this)
- const parsed = frontmatter.loadFront(src)
- const content = parsed.__content
- delete parsed.__content
- // diff frontmatter, since it's not going to be part of the returned
- // component, changes in frontmatter do not trigger hot updates
- const serializedData = JSON.stringify(parsed)
- const cachedData = frontmatterCache.get(this.resourcePath)
- if (cachedData != null && cachedData !== serializedData) {
+ const frontmatter = yaml.loadFront(src)
+ const inferredTitle = inferTitle(frontmatter)
+ const content = frontmatter.__content
+ delete frontmatter.__content
+
+ // diff frontmatter and title, since they are not going to be part of the
+ // returned component, changes in frontmatter do not trigger proper updates
+ const cachedData = cache.get(this.resourcePath)
+ if (cachedData && (
+ cachedData.inferredTitle !== inferredTitle ||
+ JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter)
+ )) {
// frontmatter changed... need to do a full reload
module.exports.frontmatterEmitter.emit('update')
}
- frontmatterCache.set(this.resourcePath, serializedData)
+
+ cache.set(this.resourcePath, {
+ frontmatter,
+ inferredTitle
+ })
const { html, hoistedTags } = markdown.renderWithHoisting(content)
return (