From e51d2a8c4922caf2619183c97d11c2e69337680e Mon Sep 17 00:00:00 2001 From: Kia King Ishii Date: Sun, 22 May 2022 16:27:51 +0900 Subject: [PATCH] feat: add custom container stylings --- docs/.vitepress/config.ts | 6 +- docs/config/frontmatter-configs.md | 24 + docs/guide/frontmatter.md | 38 ++ docs/guide/markdown-extensions.md | 413 ++++++++++++++++++ docs/snippets/snippet-with-region.js | 7 + docs/snippets/snippet.js | 3 + src/client/theme-default/index.ts | 5 +- .../styles/components/custom-block.css | 66 +++ .../styles/{ => components}/vp-doc.css | 43 +- .../styles/{ => components}/vp-sponsor.css | 0 src/client/theme-default/styles/vars.css | 112 +++-- src/node/markdown/plugins/containers.ts | 4 +- 12 files changed, 673 insertions(+), 48 deletions(-) create mode 100644 docs/guide/frontmatter.md create mode 100644 docs/guide/markdown-extensions.md create mode 100644 docs/snippets/snippet-with-region.js create mode 100644 docs/snippets/snippet.js create mode 100644 src/client/theme-default/styles/components/custom-block.css rename src/client/theme-default/styles/{ => components}/vp-doc.css (93%) rename src/client/theme-default/styles/{ => components}/vp-sponsor.css (100%) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index e2aa7800..09f2a04b 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -52,7 +52,11 @@ function sidebarGuide() { return [ { text: 'Introduction', - items: [{ text: 'What is VitePress?', link: '/guide/what-is-vitepress' }] + items: [ + { text: 'What is VitePress?', link: '/guide/what-is-vitepress' }, + { text: 'Markdown Extensions', link: '/guide/markdown-extensions' }, + { text: 'Frontmatter', link: '/guide/frontmatter' } + ] }, { text: 'Migrations', diff --git a/docs/config/frontmatter-configs.md b/docs/config/frontmatter-configs.md index ed1f5f09..b11a5cf2 100644 --- a/docs/config/frontmatter-configs.md +++ b/docs/config/frontmatter-configs.md @@ -52,6 +52,30 @@ description: VitePress --- ``` +### head + +- Type: `Head[]` + +Specify extra head tags to be injected: + +```yaml +--- +head: + - - meta + - name: description + content: hello + - - meta + - name: keywords + content: super duper SEO +--- +``` + +```ts +type Head = + | [string, Record] + | [string, Record, string] +``` + ## layout - Type: `doc | home | page` diff --git a/docs/guide/frontmatter.md b/docs/guide/frontmatter.md new file mode 100644 index 00000000..4f25ac96 --- /dev/null +++ b/docs/guide/frontmatter.md @@ -0,0 +1,38 @@ +# Frontmatter + +Any Markdown file that contains a YAML frontmatter block will be processed by [gray-matter](https://github.com/jonschlinkert/gray-matter). The frontmatter must be at the top of the Markdown file, and must take the form of valid YAML set between triple-dashed lines. Example: + +```md +--- +title: Docs with VitePress +editLink: true +--- +``` + +Between the triple-dashed lines, you can set [predefined variables](../config/frontmatter-configs), or even create custom ones of your own. These variables can be used via the special $frontmatter variable. + +Here’s an example of how you could use it in your Markdown file: + +```md +--- +title: Docs with VitePress +editLink: true +--- + +# {{ $frontmatter.title }} + +Guide content +``` + +## Alternative frontmatter Formats + +VitePress also supports JSON frontmatter syntax, starting and ending in curly braces: + +```json +--- +{ + "title": "Blogging Like a Hacker", + "editLink": true +} +--- +``` diff --git a/docs/guide/markdown-extensions.md b/docs/guide/markdown-extensions.md new file mode 100644 index 00000000..dbeefe11 --- /dev/null +++ b/docs/guide/markdown-extensions.md @@ -0,0 +1,413 @@ +# Markdown Extensions + +VitePress comes with built in Markdown Extensions. + +## Header Anchors + +Headers automatically get anchor links applied. Rendering of anchors can be configured using the `markdown.anchor` option. + +## Links + +Both internal and external links gets special treatments. + +### Internal Links + +Internal links are converted to router link for SPA navigation. Also, every `index.md` contained in each sub-directory will automatically be converted to `index.html`, with corresponding URL `/`. + +For example, given the following directory structure: + +``` +. +├─ index.md +├─ foo +│ ├─ index.md +│ ├─ one.md +│ └─ two.md +└─ bar + ├─ index.md + ├─ three.md + └─ four.md +``` + +And providing you are in `foo/one.md`: + +```md +[Home](/) +[foo](/foo/) +[foo heading](./#heading) +[bar - three](../bar/three) +[bar - three](../bar/three.md) +[bar - four](../bar/four.html) +``` + +### Page Suffix + +Pages and internal links get generated with the `.html` suffix by default. + +### External Links + +Outbound links automatically get `target="_blank" rel="noopener noreferrer"`: + +- [vuejs.org](https://vuejs.org) +- [VitePress on GitHub](https://github.com/vuejs/vitepress) + +## Frontmatter + +[YAML frontmatter](https://jekyllrb.com/docs/front-matter/) is supported out of the box: + +```yaml +--- +title: Blogging Like a Hacker +lang: en-US +--- +``` + +This data will be available to the rest of the page, along with all custom and theming components. + +For more details, see [Frontmatter](./frontmatter). + +## GitHub-Style Tables + +**Input** + +``` +| Tables | Are | Cool | +| ------------- |:-------------:| -----:| +| col 3 is | right-aligned | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | +``` + +**Output** + +| Tables | Are | Cool | +| ------------- | :-----------: | -----: | +| col 3 is | right-aligned | \$1600 | +| col 2 is | centered | \$12 | +| zebra stripes | are neat | \$1 | + +## Emoji :tada: + +**Input** + +``` +:tada: :100: +``` + +**Output** + +:tada: :100: + +A [list of all emojis](https://github.com/markdown-it/markdown-it-emoji/blob/master/lib/data/full.json) is available. + +## Table of Contents + +**Input** + +``` +[[toc]] +``` + +**Output** + +[[toc]] + +Rendering of the TOC can be configured using the `markdown.toc` option. + +## Custom Containers + +Custom containers can be defined by their types, titles, and contents. + +### Default Title + +**Input** + +```md +::: info +This is an info box. +::: + +::: tip +This is a tip. +::: + +::: warning +This is a warning. +::: + +::: danger +This is a dangerous warning. +::: + +::: details +This is a details block. +::: +``` + +**Output** + +::: info +This is an info box. +::: + +::: tip +This is a tip. +::: + +::: warning +This is a dangerous warning. +::: + +::: danger +This is a dangerous warning. +::: + +::: details +This is a details block. +::: + +### Custom Title + +You may set custom title by appending the text right after the "type" of the container. + +**Input** + +````md +::: danger STOP +Danger zone, do not proceed +::: + +::: details Click me to view the code +```js +console.log('Hello, VitePress!') +``` +::: +```` + +**Output** + +::: danger STOP +Danger zone, do not proceed +::: + +::: details Click me to view the code +```js +console.log('Hello, VitePress!') +``` +::: + +## Syntax Highlighting in Code Blocks + +VitePress uses [Prism](https://prismjs.com) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block: + +**Input** + +```` +```js +export default { + name: 'MyComponent', + // ... +} +``` +```` + +**Output** + +```js +export default { + name: 'MyComponent' + // ... +} +``` + +**Input** + +```` +```html +
    +
  • + {{ todo.text }} +
  • +
+``` +```` + +**Output** + +```html +
    +
  • {{ todo.text }}
  • +
+``` + +A [list of valid languages](https://prismjs.com/#languages-list) is available on Prism’s site. + +## Line Highlighting in Code Blocks + +**Input** + +```` +```js{4} +export default { + data () { + return { + msg: 'Highlighted!' + } + } +} +``` +```` + +**Output** + +```js{4} +export default { + data () { + return { + msg: 'Highlighted!' + } + } +} +``` + +In addition to a single line, you can also specify multiple single lines, ranges, or both: + +- Line ranges: for example `{5-8}`, `{3-10}`, `{10-17}` +- Multiple single lines: for example `{4,7,9}` +- Line ranges and single lines: for example `{4,7-13,16,23-27,40}` + +**Input** + +```` +```js{1,4,6-7} +export default { // Highlighted + data () { + return { + msg: `Highlighted! + This line isn't highlighted, + but this and the next 2 are.`, + motd: 'VitePress is awesome', + lorem: 'ipsum', + } + } +} +``` +```` + +**Output** + +```js{1,4,6-8} +export default { // Highlighted + data () { + return { + msg: `Highlighted! + This line isn't highlighted, + but this and the next 2 are.`, + motd: 'VitePress is awesome', + lorem: 'ipsum', + } + } +} +``` + +## Line Numbers + +You can enable line numbers for each code blocks via config: + +```js +module.exports = { + markdown: { + lineNumbers: true + } +} +``` + +## Import Code Snippets + +You can import code snippets from existing files via following syntax: + +```md +<<< @/filepath +``` + +It also supports [line highlighting](#line-highlighting-in-code-blocks): + +```md +<<< @/filepath{highlightLines} +``` + +**Input** + +```md +<<< @/snippets/snippet.js{2} +``` + +**Code file** + + + +<<< @/snippets/snippet.js + + + +**Output** + + + +<<< @/snippets/snippet.js{2} + + + +::: tip +The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured. +::: + +You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding) to only include the corresponding part of the code file. You can provide a custom region name after a `#` following the filepath (`snippet` by default): + +**Input** + +```md +<<< @/snippets/snippet-with-region.js{1} +``` + +**Code file** + + + +<<< @/snippets/snippet-with-region.js + + + +**Output** + + + +<<< @/snippets/snippet-with-region.js#snippet{1} + + + +## Advanced Configuration + +VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`: + +```js +const anchor = require('markdown-it-anchor') + +module.exports = { + markdown: { + // options for markdown-it-anchor + // https://github.com/valeriangalliat/markdown-it-anchor#permalinks + anchor: { + permalink: anchor.permalink.headerLink() + }, + + // options for markdown-it-table-of-contents + toc: { includeLevel: [1, 2] }, + + config: (md) => { + // use more markdown-it plugins! + md.use(require('markdown-it-xxx')) + } + } +} +``` diff --git a/docs/snippets/snippet-with-region.js b/docs/snippets/snippet-with-region.js new file mode 100644 index 00000000..9c7faaeb --- /dev/null +++ b/docs/snippets/snippet-with-region.js @@ -0,0 +1,7 @@ +// #region snippet +function foo() { + // .. +} +// #endregion snippet + +export default foo diff --git a/docs/snippets/snippet.js b/docs/snippets/snippet.js new file mode 100644 index 00000000..575039d1 --- /dev/null +++ b/docs/snippets/snippet.js @@ -0,0 +1,3 @@ +export default function () { + // .. +} diff --git a/src/client/theme-default/index.ts b/src/client/theme-default/index.ts index ca744086..712f89bb 100644 --- a/src/client/theme-default/index.ts +++ b/src/client/theme-default/index.ts @@ -2,8 +2,9 @@ import './styles/fonts.css' import './styles/vars.css' import './styles/base.css' import './styles/utils.css' -import './styles/vp-doc.css' -import './styles/vp-sponsor.css' +import './styles/components/custom-block.css' +import './styles/components/vp-doc.css' +import './styles/components/vp-sponsor.css' import { Theme } from 'vitepress' import Layout from './Layout.vue' diff --git a/src/client/theme-default/styles/components/custom-block.css b/src/client/theme-default/styles/components/custom-block.css new file mode 100644 index 00000000..0239b2b5 --- /dev/null +++ b/src/client/theme-default/styles/components/custom-block.css @@ -0,0 +1,66 @@ +.custom-block { + border: 1px solid transparent; + border-radius: 8px; + padding: 16px 16px 8px; + line-height: 20px; + font-size: 14px; + color: var(--vp-c-text-2); +} + +.custom-block.info { + border-color: var(--vp-custom-block-info-border); + color: var(--vp-custom-block-info-text); + background-color: var(--vp-custom-block-info-bg); +} + +.custom-block.tip { + border-color: var(--vp-custom-block-tip-border); + color: var(--vp-custom-block-tip-text); + background-color: var(--vp-custom-block-tip-bg); +} + +.custom-block.warning { + border-color: var(--vp-custom-block-warning-border); + color: var(--vp-custom-block-warning-text); + background-color: var(--vp-custom-block-warning-bg); +} + +.custom-block.danger { + border-color: var(--vp-custom-block-danger-border); + color: var(--vp-custom-block-danger-text); + background-color: var(--vp-custom-block-danger-bg); +} + +.custom-block.details { + border-color: var(--vp-custom-block-details-border); + color: var(--vp-custom-block-details-text); + background-color: var(--vp-custom-block-details-bg); +} + +.custom-block-title { + font-weight: 700; +} + +.custom-block p + p { + margin: 8px 0; +} + +.custom-block.details summary { + margin: 0 0 8px; + font-weight: 700; +} + +.custom-block.details summary + p { + margin: 8px 0; +} + +.custom-block a { + color: inherit; + font-weight: 600; + text-decoration: underline; + transition: opacity .25s; +} + +.custom-block a:hover { + opacity: 0.6; +} diff --git a/src/client/theme-default/styles/vp-doc.css b/src/client/theme-default/styles/components/vp-doc.css similarity index 93% rename from src/client/theme-default/styles/vp-doc.css rename to src/client/theme-default/styles/components/vp-doc.css index e664afbc..644def57 100644 --- a/src/client/theme-default/styles/vp-doc.css +++ b/src/client/theme-default/styles/components/vp-doc.css @@ -115,18 +115,27 @@ .vp-doc ul, .vp-doc ol { + padding-left: 1.25rem; margin: 16px 0; } .vp-doc ul { - padding-left: 1.25rem; list-style: disc; } +.vp-doc ol { + list-style: decimal; +} + .vp-doc li + li { margin-top: 8px; } +.vp-doc li > ol, +.vp-doc li > ul { + margin: 8px 0 0; +} + /** * Table * -------------------------------------------------------------------------- */ @@ -177,6 +186,38 @@ border-top: 1px solid var(--vp-c-divider-light); } +/** + * Custom Block + * -------------------------------------------------------------------------- */ + +.vp-doc .custom-block { + margin: 16px 0; +} + +.vp-doc .custom-block p { + margin: 8px 0; + line-height: 24px; +} + +.vp-doc .custom-block p:first-child { + margin: 0; +} + +.vp-doc .custom-block a { + color: inherit; + font-weight: 600; + text-decoration: underline; + transition: opacity .25s; +} + +.vp-doc .custom-block a:hover { + opacity: 0.6; +} + +.vp-doc .custom-block div[class*='language-'] { + margin: 8px 0; +} + /** * Code * -------------------------------------------------------------------------- */ diff --git a/src/client/theme-default/styles/vp-sponsor.css b/src/client/theme-default/styles/components/vp-sponsor.css similarity index 100% rename from src/client/theme-default/styles/vp-sponsor.css rename to src/client/theme-default/styles/components/vp-sponsor.css diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css index 0ec0581d..0fa023e4 100644 --- a/src/client/theme-default/styles/vars.css +++ b/src/client/theme-default/styles/vars.css @@ -43,12 +43,6 @@ --vp-c-text-dark-3: rgba(235, 235, 235, 0.38); --vp-c-text-dark-4: rgba(235, 235, 235, 0.18); - --vp-c-green: #42b883; - --vp-c-green-light: #42d392; - --vp-c-green-lighter: #35eb9a; - --vp-c-green-dark: #33a06f; - --vp-c-green-darker: #155f3e; - --vp-c-indigo: #213547; --vp-c-indigo-soft: #476582; --vp-c-indigo-light: #aac8e4; @@ -56,29 +50,26 @@ --vp-c-indigo-dark: #1d2f3f; --vp-c-indigo-darker: #14212e; - --vp-c-blue: #3b8eed; - --vp-c-blue-light: #549ced; - --vp-c-blue-lighter: #50a2ff; - --vp-c-blue-dark: #3468a3; - --vp-c-blue-darker: #255489; + --vp-c-green: #42b883; + --vp-c-green-light: #42d392; + --vp-c-green-lighter: #35eb9a; + --vp-c-green-dark: #33a06f; + --vp-c-green-darker: #155f3e; + --vp-c-green-dimm: rgba(66, 184, 131, 0.08); --vp-c-yellow: #ffc517; --vp-c-yellow-light: #ffe417; --vp-c-yellow-lighter: #ffff17; --vp-c-yellow-dark: #e0ad15; - --vp-c-yellow-darker: #bc9112; + --vp-c-yellow-darker: #ad850e; + --vp-c-yellow-dimm: rgba(255, 197, 23, 0.08); --vp-c-red: #ed3c50; --vp-c-red-light: #f43771; --vp-c-red-lighter: #fd1d7c; --vp-c-red-dark: #cd2d3f; --vp-c-red-darker: #ab2131; - - --vp-c-purple: #de41e0; - --vp-c-purple-light: #e936eb; - --vp-c-purple-lighter: #f616f8; - --vp-c-purple-dark: #823c83; - --vp-c-purple-darker: #602960; + --vp-c-red-dimm: rgba(237, 60, 80, 0.08); } /** @@ -188,30 +179,6 @@ --vp-layout-max-width: 1376px; } -/** - * Component: Nav - * -------------------------------------------------------------------------- */ - -:root { - --vp-nav-height: var(--vp-nav-height-mobile); - --vp-nav-height-mobile: 56px; - --vp-nav-height-desktop: 72px; -} - -@media (min-width: 960px) { - :root { - --vp-nav-height: var(--vp-nav-height-desktop); - } -} - -/** - * Component: Sidebar - * -------------------------------------------------------------------------- */ - -:root { - --vp-sidebar-width: 272px; -} - /** * Component: Code * -------------------------------------------------------------------------- */ @@ -289,6 +256,67 @@ --vp-button-sponsor-text: var(--vp-c-text-dark-2); } +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-info-border: var(--vp-c-divider-light); + --vp-custom-block-info-text: var(--vp-c-text-2); + --vp-custom-block-info-bg: var(--vp-c-white-soft); + + --vp-custom-block-tip-border: var(--vp-c-green); + --vp-custom-block-tip-text: var(--vp-c-green-darker); + --vp-custom-block-tip-bg: var(--vp-c-green-dimm); + + --vp-custom-block-warning-border: var(--vp-c-yellow); + --vp-custom-block-warning-text: var(--vp-c-yellow-darker); + --vp-custom-block-warning-bg: var(--vp-c-yellow-dimm); + + --vp-custom-block-danger-border: var(--vp-c-red); + --vp-custom-block-danger-text: var(--vp-c-red-darker); + --vp-custom-block-danger-bg: var(--vp-c-red-dimm); + + --vp-custom-block-details-border: var(--vp-custom-block-info-border); + --vp-custom-block-details-text: var(--vp-custom-block-info-text); + --vp-custom-block-details-bg: var(--vp-custom-block-info-bg); +} + +.dark { + --vp-custom-block-info-border: var(--vp-c-divider-light); + --vp-custom-block-info-bg: var(--vp-c-black-mute); + + --vp-custom-block-tip-text: var(--vp-c-green); + + --vp-custom-block-warning-text: var(--vp-c-yellow); + + --vp-custom-block-danger-text: var(--vp-c-red); +} + +/** + * Component: Nav + * -------------------------------------------------------------------------- */ + +:root { + --vp-nav-height: var(--vp-nav-height-mobile); + --vp-nav-height-mobile: 56px; + --vp-nav-height-desktop: 72px; +} + +@media (min-width: 960px) { + :root { + --vp-nav-height: var(--vp-nav-height-desktop); + } +} + +/** + * Component: Sidebar + * -------------------------------------------------------------------------- */ + +:root { + --vp-sidebar-width: 272px; +} + /** * Component: Home * -------------------------------------------------------------------------- */ diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts index 9e3caa23..def665b2 100644 --- a/src/node/markdown/plugins/containers.ts +++ b/src/node/markdown/plugins/containers.ts @@ -6,7 +6,7 @@ export const containerPlugin = (md: MarkdownIt) => { md.use(...createContainer('tip', 'TIP')) .use(...createContainer('info', 'INFO')) .use(...createContainer('warning', 'WARNING')) - .use(...createContainer('danger', 'WARNING')) + .use(...createContainer('danger', 'DANGER')) .use(...createContainer('details', 'Details')) // explicitly escape Vue syntax .use(container, 'v-pre', { @@ -34,7 +34,7 @@ function createContainer(klass: string, defaultTitle: string): ContainerArgs { if (token.nesting === 1) { if (klass === 'details') { return `
${ - info ? `${info}` : '' + info ? `${info}` : `Details` }\n` } return `

${