diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md
index 7dccbe7e..25a7ee66 100644
--- a/docs/guide/markdown.md
+++ b/docs/guide/markdown.md
@@ -263,6 +263,42 @@ Wraps in a
})
```
+## GitHub-flavored Alerts
+
+VitePress also supports [GitHub-flavored alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) to render as callouts. They will be rendered the same as the [custom containers](#custom-containers).
+
+```md
+> [!NOTE]
+> Highlights information that users should take into account, even when skimming.
+
+> [!TIP]
+> Optional information to help a user be more successful.
+
+> [!IMPORTANT]
+> Crucial information necessary for users to succeed.
+
+> [!WARNING]
+> Critical content demanding immediate user attention due to potential risks.
+
+> [!CAUTION]
+> Negative potential consequences of an action.
+```
+
+> [!NOTE]
+> Highlights information that users should take into account, even when skimming.
+
+> [!TIP]
+> Optional information to help a user be more successful.
+
+> [!IMPORTANT]
+> Crucial information necessary for users to succeed.
+
+> [!WARNING]
+> Critical content demanding immediate user attention due to potential risks.
+
+> [!CAUTION]
+> Negative potential consequences of an action.
+
## Syntax Highlighting in Code Blocks
VitePress uses [Shikiji](https://github.com/antfu/shikiji) (an improved version of [Shiki](https://shiki.matsu.io/)) to highlight language syntax in Markdown code blocks, using coloured text. Shiki 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:
diff --git a/src/client/theme-default/styles/components/custom-block.css b/src/client/theme-default/styles/components/custom-block.css
index 7af89807..a960381c 100644
--- a/src/client/theme-default/styles/components/custom-block.css
+++ b/src/client/theme-default/styles/components/custom-block.css
@@ -27,6 +27,26 @@
background-color: var(--vp-custom-block-info-code-bg);
}
+.custom-block.note {
+ border-color: var(--vp-custom-block-note-border);
+ color: var(--vp-custom-block-note-text);
+ background-color: var(--vp-custom-block-note-bg);
+}
+
+.custom-block.note a,
+.custom-block.note code {
+ color: var(--vp-c-brand-1);
+}
+
+.custom-block.note a:hover,
+.custom-block.note a:hover > code {
+ color: var(--vp-c-brand-2);
+}
+
+.custom-block.note code {
+ background-color: var(--vp-custom-block-note-code-bg);
+}
+
.custom-block.tip {
border-color: var(--vp-custom-block-tip-border);
color: var(--vp-custom-block-tip-text);
@@ -47,6 +67,26 @@
background-color: var(--vp-custom-block-tip-code-bg);
}
+.custom-block.important {
+ border-color: var(--vp-custom-block-important-border);
+ color: var(--vp-custom-block-important-text);
+ background-color: var(--vp-custom-block-important-bg);
+}
+
+.custom-block.important a,
+.custom-block.important code {
+ color: var(--vp-c-important-1);
+}
+
+.custom-block.important a:hover,
+.custom-block.important a:hover > code {
+ color: var(--vp-c-important-2);
+}
+
+.custom-block.important code {
+ background-color: var(--vp-custom-block-important-code-bg);
+}
+
.custom-block.warning {
border-color: var(--vp-custom-block-warning-border);
color: var(--vp-custom-block-warning-text);
@@ -87,6 +127,26 @@
background-color: var(--vp-custom-block-danger-code-bg);
}
+.custom-block.caution {
+ border-color: var(--vp-custom-block-caution-border);
+ color: var(--vp-custom-block-caution-text);
+ background-color: var(--vp-custom-block-caution-bg);
+}
+
+.custom-block.caution a,
+.custom-block.caution code {
+ color: var(--vp-c-caution-1);
+}
+
+.custom-block.caution a:hover,
+.custom-block.caution a:hover > code {
+ color: var(--vp-c-caution-2);
+}
+
+.custom-block.caution code {
+ background-color: var(--vp-custom-block-caution-code-bg);
+}
+
.custom-block.details {
border-color: var(--vp-custom-block-details-border);
color: var(--vp-custom-block-details-text);
diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css
index d85304cd..80525155 100644
--- a/src/client/theme-default/styles/vars.css
+++ b/src/client/theme-default/styles/vars.css
@@ -54,6 +54,11 @@
--vp-c-indigo-3: #5672cd;
--vp-c-indigo-soft: rgba(100, 108, 255, 0.14);
+ --vp-c-purple-1: #6f42c1;
+ --vp-c-purple-2: #7e4cc9;
+ --vp-c-purple-3: #8e5cd9;
+ --vp-c-purple-soft: rgba(159, 122, 234, 0.14);
+
--vp-c-green-1: #18794e;
--vp-c-green-2: #299764;
--vp-c-green-3: #30a46c;
@@ -83,6 +88,11 @@
--vp-c-indigo-3: #3e63dd;
--vp-c-indigo-soft: rgba(100, 108, 255, 0.16);
+ --vp-c-purple-1: #c8abfa;
+ --vp-c-purple-2: #a879e6;
+ --vp-c-purple-3: #8e5cd9;
+ --vp-c-purple-soft: rgba(159, 122, 234, 0.16);
+
--vp-c-green-1: #3dd68c;
--vp-c-green-2: #30a46c;
--vp-c-green-3: #298459;
@@ -215,11 +225,21 @@
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
+ --vp-c-note-1: var(--vp-c-brand-1);
+ --vp-c-note-2: var(--vp-c-brand-2);
+ --vp-c-note-3: var(--vp-c-brand-3);
+ --vp-c-note-soft: var(--vp-c-brand-soft);
+
--vp-c-success-1: var(--vp-c-green-1);
--vp-c-success-2: var(--vp-c-green-2);
--vp-c-success-3: var(--vp-c-green-3);
--vp-c-success-soft: var(--vp-c-green-soft);
+ --vp-c-important-1: var(--vp-c-purple-1);
+ --vp-c-important-2: var(--vp-c-purple-2);
+ --vp-c-important-3: var(--vp-c-purple-3);
+ --vp-c-important-soft: var(--vp-c-purple-soft);
+
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
@@ -229,6 +249,11 @@
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
+
+ --vp-c-caution-1: var(--vp-c-red-1);
+ --vp-c-caution-2: var(--vp-c-red-2);
+ --vp-c-caution-3: var(--vp-c-red-3);
+ --vp-c-caution-soft: var(--vp-c-red-soft);
}
/**
@@ -394,11 +419,21 @@
--vp-custom-block-info-bg: var(--vp-c-default-soft);
--vp-custom-block-info-code-bg: var(--vp-c-default-soft);
+ --vp-custom-block-note-border: transparent;
+ --vp-custom-block-note-text: var(--vp-c-text-1);
+ --vp-custom-block-note-bg: var(--vp-c-default-soft);
+ --vp-custom-block-note-code-bg: var(--vp-c-default-soft);
+
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-tip-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-tip-soft);
+ --vp-custom-block-important-border: transparent;
+ --vp-custom-block-important-text: var(--vp-c-text-1);
+ --vp-custom-block-important-bg: var(--vp-c-important-soft);
+ --vp-custom-block-important-code-bg: var(--vp-c-important-soft);
+
--vp-custom-block-warning-border: transparent;
--vp-custom-block-warning-text: var(--vp-c-text-1);
--vp-custom-block-warning-bg: var(--vp-c-warning-soft);
@@ -409,6 +444,11 @@
--vp-custom-block-danger-bg: var(--vp-c-danger-soft);
--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);
+ --vp-custom-block-caution-border: transparent;
+ --vp-custom-block-caution-text: var(--vp-c-text-1);
+ --vp-custom-block-caution-bg: var(--vp-c-caution-soft);
+ --vp-custom-block-caution-code-bg: var(--vp-c-caution-soft);
+
--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);
diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts
index 5b6978c5..bf81f38d 100644
--- a/src/node/markdown/markdown.ts
+++ b/src/node/markdown/markdown.ts
@@ -35,6 +35,7 @@ import { lineNumberPlugin } from './plugins/lineNumbers'
import { linkPlugin } from './plugins/link'
import { preWrapperPlugin } from './plugins/preWrapper'
import { snippetPlugin } from './plugins/snippet'
+import { gitHubAlertsPlugin } from './plugins/githubAlerts'
export type { Header } from '../shared'
@@ -208,6 +209,7 @@ export const createMarkdownRenderer = async (
.use(preWrapperPlugin, { hasSingleTheme })
.use(snippetPlugin, srcDir)
.use(containerPlugin, { hasSingleTheme }, options.container)
+ .use(gitHubAlertsPlugin, options.container)
.use(imagePlugin, options.image)
.use(
linkPlugin,
diff --git a/src/node/markdown/plugins/containers.ts b/src/node/markdown/plugins/containers.ts
index 2a70e217..5008850b 100644
--- a/src/node/markdown/plugins/containers.ts
+++ b/src/node/markdown/plugins/containers.ts
@@ -129,8 +129,11 @@ function createCodeGroup(options: Options): ContainerArgs {
export interface ContainerOptions {
infoLabel?: string
+ noteLabel?: string
tipLabel?: string
warningLabel?: string
dangerLabel?: string
detailsLabel?: string
+ importantLabel?: string
+ cautionLabel?: string
}
diff --git a/src/node/markdown/plugins/githubAlerts.ts b/src/node/markdown/plugins/githubAlerts.ts
new file mode 100644
index 00000000..fd084f59
--- /dev/null
+++ b/src/node/markdown/plugins/githubAlerts.ts
@@ -0,0 +1,62 @@
+import type MarkdownIt from 'markdown-it'
+import type { ContainerOptions } from './containers'
+
+const markerRE =
+ /^\[\!(TIP|NOTE|INFO|IMPORTANT|WARNING|CAUTION|DANGER)\]([^\n\r]*)/i
+
+export const gitHubAlertsPlugin = (
+ md: MarkdownIt,
+ options?: ContainerOptions
+) => {
+ const titleMark = {
+ tip: options?.tipLabel || 'TIP',
+ note: options?.noteLabel || 'NOTE',
+ info: options?.infoLabel || 'INFO',
+ important: options?.importantLabel || 'IMPORTANT',
+ warning: options?.warningLabel || 'WARNING',
+ caution: options?.cautionLabel || 'CAUTION',
+ danger: options?.dangerLabel || 'DANGER'
+ } as Record
+
+ md.core.ruler.after('block', 'github-alerts', (state) => {
+ const tokens = state.tokens
+ for (let i = 0; i < tokens.length; i++) {
+ if (tokens[i].type === 'blockquote_open') {
+ const open = tokens[i]
+ const startIndex = i
+ while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length)
+ i += 1
+ const close = tokens[i]
+ const endIndex = i
+ const firstContent = tokens
+ .slice(startIndex, endIndex + 1)
+ .find((token) => token.type === 'inline')
+ if (!firstContent) continue
+ const match = firstContent.content.match(markerRE)
+ if (!match) continue
+ const type = match[1].toLowerCase()
+ const title = match[2].trim() || titleMark[type] || capitalize(type)
+ firstContent.content = firstContent.content
+ .slice(match[0].length)
+ .trimStart()
+ open.type = 'github_alert_open'
+ open.tag = 'div'
+ open.meta = {
+ title,
+ type
+ }
+ close.type = 'github_alert_close'
+ close.tag = 'div'
+ }
+ }
+ })
+ md.renderer.rules.github_alert_open = function (tokens, idx) {
+ const { title, type } = tokens[idx].meta
+ const attrs = ''
+ return `${title}
\n`
+ }
+}
+
+function capitalize(str: string) {
+ return str.charAt(0).toUpperCase() + str.slice(1)
+}