diff --git a/crates/mdbook-core/src/config.rs b/crates/mdbook-core/src/config.rs index 44de130301..d4ae36aafa 100644 --- a/crates/mdbook-core/src/config.rs +++ b/crates/mdbook-core/src/config.rs @@ -436,6 +436,8 @@ pub struct HtmlConfig { pub smart_punctuation: bool, /// Support for definition lists. pub definition_lists: bool, + /// Support for admonitions. + pub admonitions: bool, /// Should mathjax be enabled? pub mathjax_support: bool, /// Additional CSS stylesheets to include in the rendered page's ``. @@ -504,6 +506,7 @@ impl Default for HtmlConfig { preferred_dark_theme: None, smart_punctuation: true, definition_lists: true, + admonitions: true, mathjax_support: false, additional_css: Vec::new(), additional_js: Vec::new(), diff --git a/crates/mdbook-html/front-end/css/general.css b/crates/mdbook-html/front-end/css/general.css index eca0e9da3b..df8a3efe2b 100644 --- a/crates/mdbook-html/front-end/css/general.css +++ b/crates/mdbook-html/front-end/css/general.css @@ -156,6 +156,8 @@ blockquote { border-block-end: .1em solid var(--quote-border); } +/* TODO: Remove .warning in a future version of mdbook, it is replaced by +blockquote tags. */ .warning { margin: 20px; padding: 0 20px; @@ -324,3 +326,83 @@ dd > p { space before the definition. */ margin-top: 0; } + +/* Remove some excess space from the bottom. */ +.blockquote-tag p:last-child { + margin-bottom: 2px; +} + +.blockquote-tag { + /* Add some padding to make the vertical bar a little taller than the text.*/ + padding: 2px 0px 2px 20px; + /* Add a solid color bar on the left side. */ + border-inline-start-style: solid; + border-inline-start-width: 4px; + /* Disable the background color from normal blockquotes . */ + background-color: inherit; + /* Disable border blocks from blockquotes. */ + border-block-start: none; + border-block-end: none; +} + +.blockquote-tag-title svg { + fill: currentColor; + /* Add space between the icon and the title. */ + margin-right: 8px; +} + +.blockquote-tag-note { + border-inline-start-color: var(--blockquote-note-color); +} + +.blockquote-tag-tip { + border-inline-start-color: var(--blockquote-tip-color); +} + +.blockquote-tag-important { + border-inline-start-color: var(--blockquote-important-color); +} + +.blockquote-tag-warning { + border-inline-start-color: var(--blockquote-warning-color); +} + +.blockquote-tag-caution { + border-inline-start-color: var(--blockquote-caution-color); +} + +.blockquote-tag-note .blockquote-tag-title { + color: var(--blockquote-note-color); +} + +.blockquote-tag-tip .blockquote-tag-title { + color: var(--blockquote-tip-color); +} + +.blockquote-tag-important .blockquote-tag-title { + color: var(--blockquote-important-color); +} + +.blockquote-tag-warning .blockquote-tag-title { + color: var(--blockquote-warning-color); +} + +.blockquote-tag-caution .blockquote-tag-title { + color: var(--blockquote-caution-color); +} + +.blockquote-tag-title { + /* Slightly increase the weight for more emphasis. */ + font-weight: 600; + /* Vertically center the icon with the text. */ + display: flex; + align-items: center; + /* Remove default large margins for a more compact display. */ + margin: 2px 0 8px 0; +} + +.blockquote-tag-title .fa-svg { + fill: currentColor; + /* Add some space between the icon and the text. */ + margin-right: 8px; +} diff --git a/crates/mdbook-html/front-end/css/variables.css b/crates/mdbook-html/front-end/css/variables.css index ae287821c0..b715d672ed 100644 --- a/crates/mdbook-html/front-end/css/variables.css +++ b/crates/mdbook-html/front-end/css/variables.css @@ -67,6 +67,12 @@ --footnote-highlight: #2668a6; --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #74b9ff; + --blockquote-tip-color: #09ca09; + --blockquote-important-color: #d3abff; + --blockquote-warning-color: #f0b72f; + --blockquote-caution-color: #f21424; } .coal { @@ -120,6 +126,12 @@ --footnote-highlight: #4079ae; --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #08ae08; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #d91b29; } .light, html:not(.js) { @@ -173,6 +185,12 @@ --footnote-highlight: #7e7eff; --overlay-bg: rgba(200, 200, 205, 0.4); + + --blockquote-note-color: #0969da; + --blockquote-tip-color: #008000; + --blockquote-important-color: #8250df; + --blockquote-warning-color: #9a6700; + --blockquote-caution-color: #b52731; } .navy { @@ -226,6 +244,12 @@ --footnote-highlight: #4079ae; --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #09ca09; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #f21424; } .rust { @@ -277,6 +301,12 @@ --footnote-highlight: #d3a17a; --overlay-bg: rgba(150, 150, 150, 0.25); + + --blockquote-note-color: #023b95; + --blockquote-tip-color: #007700; + --blockquote-important-color: #8250df; + --blockquote-warning-color: #603700; + --blockquote-caution-color: #aa1721; } @media (prefers-color-scheme: dark) { @@ -331,5 +361,11 @@ --footnote-highlight: #4079ae; --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #08ae08; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #d91b29; } } diff --git a/crates/mdbook-html/src/html/admonitions.rs b/crates/mdbook-html/src/html/admonitions.rs new file mode 100644 index 0000000000..e2bd4c0611 --- /dev/null +++ b/crates/mdbook-html/src/html/admonitions.rs @@ -0,0 +1,26 @@ +use pulldown_cmark::BlockQuoteKind; + +// This icon is from GitHub, MIT License, see https://github.com/primer/octicons +const ICON_NOTE: &str = r#""#; + +// This icon is from GitHub, MIT License, see https://github.com/primer/octicons +const ICON_TIP: &str = r#""#; + +// This icon is from GitHub, MIT License, see https://github.com/primer/octicons +const ICON_IMPORTANT: &str = r#""#; + +// This icon is from GitHub, MIT License, see https://github.com/primer/octicons +const ICON_WARNING: &str = r#""#; + +// This icon is from GitHub, MIT License, see https://github.com/primer/octicons +const ICON_CAUTION: &str = r#""#; + +pub(crate) fn select_tag(kind: BlockQuoteKind) -> (&'static str, &'static str, &'static str) { + match kind { + BlockQuoteKind::Note => ("note", ICON_NOTE, "Note"), + BlockQuoteKind::Tip => ("tip", ICON_TIP, "Tip"), + BlockQuoteKind::Important => ("important", ICON_IMPORTANT, "Important"), + BlockQuoteKind::Warning => ("warning", ICON_WARNING, "Warning"), + BlockQuoteKind::Caution => ("caution", ICON_CAUTION, "Caution"), + } +} diff --git a/crates/mdbook-html/src/html/mod.rs b/crates/mdbook-html/src/html/mod.rs index af1306ae23..8a70700f7e 100644 --- a/crates/mdbook-html/src/html/mod.rs +++ b/crates/mdbook-html/src/html/mod.rs @@ -16,6 +16,7 @@ use mdbook_core::config::{HtmlConfig, RustEdition}; use mdbook_markdown::{MarkdownOptions, new_cmark_parser}; use std::path::{Path, PathBuf}; +mod admonitions; mod hide_lines; mod print; mod serialize; @@ -51,6 +52,7 @@ impl<'a> HtmlRenderOptions<'a> { let mut markdown_options = MarkdownOptions::default(); markdown_options.smart_punctuation = config.smart_punctuation; markdown_options.definition_lists = config.definition_lists; + markdown_options.admonitions = config.admonitions; HtmlRenderOptions { markdown_options, path, diff --git a/crates/mdbook-html/src/html/tree.rs b/crates/mdbook-html/src/html/tree.rs index 2efce9678a..ba441f6f91 100644 --- a/crates/mdbook-html/src/html/tree.rs +++ b/crates/mdbook-html/src/html/tree.rs @@ -15,9 +15,7 @@ use html5ever::{LocalName, QualName}; use indexmap::IndexMap; use mdbook_core::config::RustEdition; use mdbook_core::static_regex; -use pulldown_cmark::{ - Alignment, BlockQuoteKind, CodeBlockKind, CowStr, Event, LinkType, Tag, TagEnd, -}; +use pulldown_cmark::{Alignment, CodeBlockKind, CowStr, Event, LinkType, Tag, TagEnd}; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::ops::Deref; @@ -407,14 +405,27 @@ where Tag::BlockQuote(kind) => { let mut b = Element::new("blockquote"); if let Some(kind) = kind { - let class = match kind { - BlockQuoteKind::Note => "markdown-alert-note", - BlockQuoteKind::Tip => "markdown-alert-tip", - BlockQuoteKind::Important => "markdown-alert-important", - BlockQuoteKind::Warning => "markdown-alert-warning", - BlockQuoteKind::Caution => "markdown-alert-caution", - }; + let (class_kind, icon, text) = super::admonitions::select_tag(kind); + let class = format!("blockquote-tag blockquote-tag-{class_kind}"); b.insert_attr("class", class.into()); + self.push(Node::Element(b)); + + let mut title = Element::new("p"); + title.insert_attr("class", "blockquote-tag-title".into()); + self.push(Node::Element(title)); + + let mut svg = Element::new("svg"); + svg.insert_attr("viewbox", "0 0 16 16".into()); + svg.insert_attr("width", "18".into()); + svg.insert_attr("height", "18".into()); + self.push(Node::Element(svg)); + self.append_html(icon); + self.pop(); + + self.append(Node::Text(text.into())); + + self.pop(); + return; } b } diff --git a/crates/mdbook-markdown/src/lib.rs b/crates/mdbook-markdown/src/lib.rs index 0f490ee1f8..339674a4e2 100644 --- a/crates/mdbook-markdown/src/lib.rs +++ b/crates/mdbook-markdown/src/lib.rs @@ -28,6 +28,10 @@ pub struct MarkdownOptions { /// /// This is `true` by default. pub definition_lists: bool, + /// Enables admonitions. + /// + /// This is `true` by default. + pub admonitions: bool, } impl Default for MarkdownOptions { @@ -35,6 +39,7 @@ impl Default for MarkdownOptions { MarkdownOptions { smart_punctuation: true, definition_lists: true, + admonitions: true, } } } @@ -53,5 +58,8 @@ pub fn new_cmark_parser<'text>(text: &'text str, options: &MarkdownOptions) -> P if options.definition_lists { opts.insert(Options::ENABLE_DEFINITION_LIST); } + if options.admonitions { + opts.insert(Options::ENABLE_GFM); + } Parser::new_ext(text, opts) } diff --git a/guide/src/format/configuration/renderers.md b/guide/src/format/configuration/renderers.md index 6e9ccda7c1..ec2ea09243 100644 --- a/guide/src/format/configuration/renderers.md +++ b/guide/src/format/configuration/renderers.md @@ -99,6 +99,7 @@ default-theme = "light" preferred-dark-theme = "navy" smart-punctuation = true definition-lists = true +admonitions = true mathjax-support = false additional-css = ["custom.css", "custom2.css"] additional-js = ["custom.js"] @@ -127,6 +128,7 @@ The following configuration options are available: See [Smart Punctuation](../markdown.md#smart-punctuation). Defaults to `true`. - **definition-lists:** Enables [definition lists](../markdown.md#definition-lists). Defaults to `true`. +- **admonitions:** Enables [admonitions](../markdown.md#admonitions). Defaults to `true`. - **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to `false`. - **additional-css:** If you need to slightly change the appearance of your book diff --git a/guide/src/format/markdown.md b/guide/src/format/markdown.md index f6f58a891f..60c62ac3c9 100644 --- a/guide/src/format/markdown.md +++ b/guide/src/format/markdown.md @@ -270,3 +270,46 @@ term B Terms are clickable just like headers, which will set the browser's URL to point directly to that term. See the [definition lists spec](https://github.com/pulldown-cmark/pulldown-cmark/blob/HEAD/pulldown-cmark/specs/definition_lists.txt) for more information on the specifics of the syntax. See the [Wikipedia guidelines for glossaries](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Glossaries#General_guidelines_for_writing_glossaries) for some guidelines on how to write a glossary. + +### Admonitions + +An admonition is a special type of callout or notice block used to highlight important information. It is written as a blockquote with a special tag on the first line. + +```md +> [!NOTE] +> General information or additional context. + +> [!TIP] +> A helpful suggestion or best practice. + +> [!IMPORTANT] +> Key information that shouldn't be missed. + +> [!WARNING] +> Critical information that highlights a potential risk. + +> [!CAUTION] +> Information about potential issues that require caution. +``` + +These will render as: + +> [!NOTE] +> General information or additional context. + +> [!TIP] +> A helpful suggestion or best practice. + +> [!IMPORTANT] +> Key information that shouldn't be missed. + +> [!WARNING] +> Critical information that highlights a potential risk. + +> [!CAUTION] +> Information about potential issues that require caution. + +This feature is enabled by default. +To disable it, see the [`output.html.admonitions`] config option. + +[`output.html.admonitions`]: configuration/renderers.md#html-renderer-options diff --git a/guide/src/format/mdbook.md b/guide/src/format/mdbook.md index 4243627fd1..8b0e7314de 100644 --- a/guide/src/format/mdbook.md +++ b/guide/src/format/mdbook.md @@ -338,32 +338,6 @@ HTML tags with class `hidden` will not be shown. -### `class="warning"` - -To make a warning or similar note stand out, wrap it in a warning div. - -```html -
- -This is a bad thing that you should pay attention to. - -Warning blocks should be used sparingly in documentation, to avoid "warning -fatigue," where people are trained to ignore them because they usually don't -matter for what they're doing. - -
-``` - -
- -This is a bad thing that you should pay attention to. - -Warning blocks should be used sparingly in documentation, to avoid "warning -fatigue," where people are trained to ignore them because they usually don't -matter for what they're doing. - -
- ## Font-Awesome icons mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com) diff --git a/tests/testsuite/markdown.rs b/tests/testsuite/markdown.rs index b2aae75cbd..98fde906ce 100644 --- a/tests/testsuite/markdown.rs +++ b/tests/testsuite/markdown.rs @@ -159,3 +159,16 @@ fn definition_lists() { file!["markdown/definition_lists/expected_disabled/definition_lists.html"], ); } + +#[test] +fn admonitions() { + BookTest::from_dir("markdown/admonitions") + .check_all_main_files() + .run("build", |cmd| { + cmd.env("MDBOOK_OUTPUT__HTML__ADMONITIONS", "false"); + }) + .check_main_file( + "book/admonitions.html", + file!["markdown/admonitions/expected_disabled/admonitions.html"], + ); +} diff --git a/tests/testsuite/markdown/admonitions/book.toml b/tests/testsuite/markdown/admonitions/book.toml new file mode 100644 index 0000000000..770792f67a --- /dev/null +++ b/tests/testsuite/markdown/admonitions/book.toml @@ -0,0 +1,2 @@ +[book] +title = "admonitions" diff --git a/tests/testsuite/markdown/admonitions/expected/admonitions.html b/tests/testsuite/markdown/admonitions/expected/admonitions.html new file mode 100644 index 0000000000..8bbf4c879e --- /dev/null +++ b/tests/testsuite/markdown/admonitions/expected/admonitions.html @@ -0,0 +1,26 @@ +

Admonitions

+
+

Note

+

This is a note.

+

There are multiple paragraphs.

+
+
+

Tip

+

This is a tip.

+
+
+

Important

+

This is important.

+
+
+

Warning

+

This is a warning.

+
+
+

Caution

+

This is a caution.

+
+
+

[!UNKNOWN] +This is an unknown tag.

+
\ No newline at end of file diff --git a/tests/testsuite/markdown/admonitions/expected_disabled/admonitions.html b/tests/testsuite/markdown/admonitions/expected_disabled/admonitions.html new file mode 100644 index 0000000000..3bb296ed5b --- /dev/null +++ b/tests/testsuite/markdown/admonitions/expected_disabled/admonitions.html @@ -0,0 +1,26 @@ +

Admonitions

+
+

[!NOTE] +This is a note.

+

There are multiple paragraphs.

+
+
+

[!TIP] +This is a tip.

+
+
+

[!IMPORTANT] +This is important.

+
+
+

[!WARNING] +This is a warning.

+
+
+

[!CAUTION] +This is a caution.

+
+
+

[!UNKNOWN] +This is an unknown tag.

+
\ No newline at end of file diff --git a/tests/testsuite/markdown/admonitions/src/SUMMARY.md b/tests/testsuite/markdown/admonitions/src/SUMMARY.md new file mode 100644 index 0000000000..58da0f6c7f --- /dev/null +++ b/tests/testsuite/markdown/admonitions/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [Admonitions](./admonitions.md) diff --git a/tests/testsuite/markdown/admonitions/src/admonitions.md b/tests/testsuite/markdown/admonitions/src/admonitions.md new file mode 100644 index 0000000000..0bf6526fe0 --- /dev/null +++ b/tests/testsuite/markdown/admonitions/src/admonitions.md @@ -0,0 +1,21 @@ +# Admonitions + +> [!NOTE] +> This is a note. +> +> There are multiple paragraphs. + +> [!TIP] +> This is a tip. + +> [!IMPORTANT] +> This is important. + +> [!WARNING] +> This is a warning. + +> [!CAUTION] +> This is a caution. + +> [!UNKNOWN] +> This is an unknown tag.