Remark plugin to render callouts with directives
npm install @microflash/remark-callout-directives


remark plugin to render callouts with directives
- What’s this?
- When should I use this?
- Prerequisites
- Install
- Use
- API
- Options
- Default options
- Themes
- Examples
- Example: callout with custom title
- Example: callout with markdown title
- Example: custom callouts
- Example: configure aliases
- Example: configure element type
- Example: global and local element type configuration
- Example: override the defaults
- Example: remove the hint icon
- Example: nested callouts
- Example: collapsible callouts
- Example: using a theme
- License
This package is a unified (remark) plugin to add support for callouts and admonitions using the directives. It depends on remark-directive which must be included before this plugin.
Callouts are used to provide additional information related to a topic under discussion or draw out attention to potential possibilities. They are widely used in documentation by popular libraries, frameworks, and applications (such as Starlight, Obsidian, Bear and so on). Use this plugin if you need something similar.
You should import remark-directive before this plugin for callouts to work.
This package is ESM only.
In Node.js (version 16.0+), install with npm:
``sh`
npm install @microflash/remark-callout-directives
In Deno, with esm.sh:
`js`
import remarkCalloutDirectives from "https://esm.sh/@microflash/remark-callout-directives"
In browsers, with esm.sh:
`html`
Say we have the following file example.md:
`mdsyntax
:::note
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
`html`
The default export is remarkCalloutDirectives.
You can provide the following options. All of them are optional.
- aliases: a map of aliases to an existing callout typecallouts
- : an object containing the callout definitions, each definition containing the following properties:title
- : the display title of the callouthint
- : optional SVG icon representing the callout typetagName
- : optional HTML tag name for the callout wrapper (overrides the global tagName configuration)tagName
- : global HTML tag name used for wrapping the callout (default: aside)generate(title, children, prefs)
- : optional function that creates the MDAST representation of a callout body. This function receives three inputs:title
- : the callout title (can be plaintext or markdown)children
- : array of MDAST nodes representing the content inside the calloutprefs
- : preferences for generating the callout. The default generate function supports the following preferences:hint
- : optional SVG icon representing the callout typecollapsible
- : whether the callout is collapsible (default: false), only true when tagName is detailsshowHint
- : whether to display the hint icon (default: true)
You can implement your own generate function to completely control the layout of the callout body. An example of such an implementation is available under Vitepress theme.
By default, the following callouts and aliases are preconfigured.
To style the callouts, import a theme from themes folder.
#### GitHub
#### Microflash
#### VitePress
For more advanced customizations, take a look at the existing themes and remix your own.
Say we have the following file example.md:
`mdsyntax
:::warn{title="Hold on there"}
Some content with _Markdown_ .`
:::
Running example.js will yield:
`html`
Say we have the following file example.md:
`mdsyntax
:::warn{title="Hold on _there_!"}
Some content with _Markdown_ .`
:::
Running example.js will yield:
`html`
You can add your own callouts as well. Say we have the following file example.md:
`mdsyntax
:::shoutout{title="Well done!"}
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
callouts: {
shoutout: {
title: "Shoutout",
hint:
}
}
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running example.js will yield:
`html`
Say we have the following file example.md:
`mdsyntax
:::danger
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
aliases: {
danger: "deter"
}
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
`html`
By default, a callout is rendered as an aside. You can override this behavior by providing is property on demand.
Say we have the following file example.md:
`mdsyntax
:::assert{is="blockquote"}
Some content with _Markdown_ .`
:::
Running the example.js yields:
` Some content with Markdown html`
Info
syntax.
You can override this behavior for all instances of a specific callout by providing a tagName for that callout.
Say we have the following file example.md:
`mdsyntax
:::assert
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
callouts: {
assert: {
tagName: "div"
}
}
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
` Some content with Markdown html`
Info
syntax.
You can override the element type of all callouts by providing a tagName.
Say we have the following file example.md:
`mdsyntax
:::assert
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
tagName: "div"
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
` Some content with Markdown html`
Info
syntax.
You can mix the tagName and is configurations globally and specifically for a callout.
Say we have the following file example.md:
`mdsyntax
:::tip{is="blockquote"}
Some content with _Markdown_ .
:::
:::assert
Some content with _Markdown_ syntax.
:::
:::note
Some content with _Markdown_ syntax.`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
tagName: "div",
callouts: {
note: {
tagName: "aside"
}
}
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
` Some content with Markdown html
Success
syntax.
Some content with Markdown syntax.
`
You can override the defaults by passing your own preferences; they will be merged on top of the default values.
Say we have the following file example.md:
`mdsyntax
:::commend
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, {
callouts: {
commend: {
title: "Tip",
hint:
}
}
})
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
`html`
You can remove the hint icon using the showHint="false" property on a callout.
Say we have the following file example.md:
`mdsyntax
:::note{showHint="false"}
Some content with _Markdown_ .`
:::
And our module example.js looks as follows:
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Running that with node example.js yields:
`html`
You can nest the callouts within each other. Make sure to add additional colons : to disambiguate them.
Say we have the following file example.md:
`md
::::warn
Critical content demanding immediate user attention due to potential risks.
:::note
Nested information relevant to this context.
:::
::::
`
Running this with node example.js yields:
`html`
You can make a callout collapsible by setting the tagName or is property to details. By default, such a callout is collapsed but you can initialize it as open using open property.
Say we have the following file example.md:
`md
:::deter{is="details"}
This is a collapsed callout.
:::
:::commend{is="details" open}
This is a collapsible callout that is open by default.
:::
`
Running this with node example.js yields:
` This is a collapsed callout.html
Danger
This is a collapsible callout that is open by default.
Tip
`
Say, you want to use the GitHub theme.
First, import the options for this theme and pass it to the plugin as follows.
`js
import { read } from "to-vfile"
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkDirective from "remark-directive"
import remarkCalloutDirectives from "@microflash/remark-callout-directives"
import githubCalloutOptions from "@microflash/remark-callout-directives/config/github"
import remarkRehype from "remark-rehype"
import rehypeStringify from "rehype-stringify"
main()
async function main() {
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkCalloutDirectives, githubCalloutOptions)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(await read("example.md"))
console.log(String(file))
}
`
Finally, import the CSS file. If you've an entrypoint file in your application, you can import the CSS as follows.
`js`
import "@microflash/remark-callout-directives/theme/github"
// or using URL import
import "https://unpkg.com/@microflash/remark-callout-directives/src/themes/github/index.css"
If you're bundling the CSS files using a bundler, you can import the CSS in your main CSS file containing other imports.
`css`
/ other imports... /
@import "@microflash/remark-callout-directives/theme/github";
If you're using Sass, you can import the CSS in your main Sass file.
`scss`
// other Sass imports
@use "@microflash/remark-callout-directives/theme/github";
You can also import the CSS file directly in browsers, with unpkg.com or jsdelivr.net:
`html``