npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

remark-transform-blockquote

v1.2.3

Published

customize Markdown blockquote

Downloads

411

Readme

remark-transform-blockquote

turn a blockquote with special marker into a customisable element, similar but not limited to Github Markdown Alerts

MIT npm.badge codecov

Installation

pnpm add -D remark-transform-blockquote # or via npm, yarn, ...

Usage

This code...

import rehypeStringify from 'rehype-stringify';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import remarkTransformBlockquote from 'remark-transform-blockquote';

const output = await unified()
	.use(remarkParse)
	.use(remarkTransformBlockquote, {
		mappings: [
			{
				marker: '!CUSTOM',
				tag: 'section',
				attributes: { class: 'custom-block' },
			},
		],
	})
	.use(remarkRehype)
	.use(rehypeStringify)
	.process('...');

will transform the following input...

> [!CUSTOM]
> This will be a custom block.

...to this output:

<section class="custom-block">
	<p>This will be a custom block.</p>
</section>

Presets

The package allows some presets for common use cases.

  1. Specify preset:

    const output = await unified()
    	.use(remarkParse)
    	.use(remarkTransformBlockquote, { preset: '<preset>' });
  2. Import CSS

    @import 'remark-transform-blockquote/presets/<preset>.css';

Where <preset> is listed in the following sections.

[!NOTE] You may provide additional mappings that will take precedence over the preset's mappings. Be aware that only the first mapping that matches is applied.

Preset: github

Alerts that matches Github Markdown Alerts.

Screenshot of "github" preset

Input:

> [!<VARIANT>]
> ...

Output:

<div class="markdown-alert markdown-alert-<variant>" data-title="<Variant>">...</div>

Where <VARIANT> is one of {NOTE, TIP, IMPORTANT, WARNING, CAUTION}.

CSS Custom Properties

| CSS Variable | Description | Fallback | | --------------------------------- | ---------------------------------------- | -------- | | --alert-padding-block | padding-inline of container | 1rem | | --alert-padding-inline | padding-block of container | 0.5rem | | --alert-margin-block-end | margin-block-end of container | 1rem | | --alert-border-width | border-inline-start-width of container | 0.25em | | --alert-icon-size | width & height of the icon | 1rem | | --alert-header-margin-block-end | margin-block-end of the title and icon | 1rem | | --alert-title-font-weight | color of the title | 500 |

Modifier variables (changed per variant):

| CSS Variable | Description | Fallback | Set to | | ---------------------- | ---------------------------------------- | -------------- | -------------------------------- | | --alert-border-color | border-inline-start-color of container | currentcolor | --alert-<variant>-border-color | | --alert-header-color | color of the title and icon | currentcolor | --alert-<variant>-header-color | | --alert-icon | an url-encoded SVG | | --alert-<variant>-icon |

See presets/github.css for more information.

[!NOTE] The color variables use CSS new light-dark function for minimal light/dark mode support.

To provide customisation, set the CSS variables where appropriate, e.g.

/* my-design-system.css */
:root {
	--alert-icon-size: 1.25rem;
	--alert-note-icon: url('...');
	--alert-success-border-color: green;
	--alert-success-header-color: darkgreen;
	/* ... */
}

Icons

SVG icons are also available should you need to reference / use them. For example:

// assuming vite or some bundler that supports importing SVG.
import svg from 'remark-transform-blockquote/presets/github/icons/note.svg'; // replace with <variant>.svg as needed

Preset: comeau

Sidenotes based on Josh Comeau's Blog.

Screenshot of "comeau" preset

Input:

> [!<VARIANT>]
> ...

Output:

<aside class="md-sidenote md-sidenote-<variant>">
	<div class="md-sidenote-decoration"></div>
	...
</aside>

Where <VARIANT> is one of {INFO, SUCCESS, WARNING}.

CSS Custom Properties

| CSS Variable | Description | Fallback | | ------------------------------- | --------------------------------- | -------- | | --sidenote-margin-block-start | margin-block-start of container | 2rem | | --sidenote-margin-block-end | margin-block-end of container | 4rem | | --sidenote-padding-block | padding-block of container | 1.5rem |

Modifier variables (changed per variant):

| CSS Variable | Description | Set to | | ----------------------------- | ----------------------------- | ------------------------------------- | | --sidenote-icon | an 32x32 url-encoded SVG | --sidenote-<variant>-icon | | --sidenote-decoration-color | color for icon & left border | sidenote-<variant>-decoration-color | | --sidenote-background-color | background color of container | sidenote-<variant>-background-color |

[!NOTE] The color variables use CSS new light-dark function for minimal light/dark mode support.

Responsive variables:

| CSS Variable | Description | Fallback | Fallback (>= 35.1875rem) | | --------------------------- | ------------------------------------- | -------- | ------------------------ | | --sidenote-padding-inline | padding-inline of container | 1rem | 2rem | | --sidenote-margin-inline | negative [margin-inline] of container | 1rem | 2rem |

When you provide custom value for responsive variables, make sure to set them at each breakpoint, e.g.

:root {
	--sidenote-padding-inline: 0.5rem;
	--sidenote-margin-inline: 0.5rem;

	@media (width >= 35.1875rem) {
		--sidenote-padding-inline: 1rem;
		--sidenote-margin-inline: 1rem;
	}
}

See presets/comeau.css for more information.

[!NOTE] For simplicity, this preset does not include some enhancements that Josh has for his component, for example :selection color or contextual colors for codeblocks within.

Icons

SVG icons are also available should you need to reference / use them. For example:

// assuming vite or some bundler that supports importing SVG.
import svg from 'remark-transform-blockquote/presets/comeau/icons/info.svg'; // replace with <variant>.svg as needed

Per-Transformation Attributes via Meta String

Sometimes it is helpful to allow users to customise the final HTML attribute per transformed element. For this, turn on the meta option. For example, using preset:github...

unified.use(remarkTransformBlockquote, {
	preset: 'github',
	meta: true,
});

...user can provide i18n translation for the title:

> [!NOTE] `data-title="Thông tin"`
> "Thông tin" is Vietnamese for "Information"

Meta String

The meta string is an inline code, i.e. `...`, that follows immediately after the marker. Inside, it can contain key-value pairs for string attribute, or standalone strings that will be understood as boolean attributes. Some example:

  • Simple string attribute, no space: [!MARKER] `attr=value`
  • For string attribute with single quote in value, wrap in double quote: [!MARKER] `attr="value with 'single' quote"`
  • For string attribute with double quote in value, wrap in single quote: [!MARKER] `attr='value with "double" quote'`
  • Boolean attributes, implicitly true: [!MARKER] `attr`
  • Boolean attributes with explicit value: [!MARKER] `attr=true attr=false`

Merging Strategy via Prefixes

By default, parsed attributes from meta string will replace existing attributes with the same name in node.data.hProperties. This can be changed by providing a prefix to the attribute name:

  • ^: prepend the value to existing attribute value, e.g. ^class=" prepend",
  • $: append the value to existing attribute value, e.g. $class="append ",
  • #: parsed but skip merging, useful if you want to do some post-processing with hooks, e.g. #attr="internal".

Note that, on boolean attributes, ^ and $ can be used but have no effect. Also, remember to consider adding space when prepending / appending attribute values.

Complex Transformation

Should you need to do more than just change tag name / attributes, you can specify a post hook

unified.use(remarkTransformBlockquote, {
	mappings: [
		{
			marker: '!CUSTOM',
			tag: 'section',
			attributes: { class: 'custom-block' },
			hooks: {
				post: ({ node, index, parent, tree, meta }) => {
					// do something with node, e.g. adding child, changing content, etc.
					// meta is only available if `meta: true` is set in the options.
				},
			},
		},
	],
});

Related Projects / Prior Arts

CONTRIBUTING

See CONTRIBUTING.md for contribution guidelines.

More unified Plugins by Me


built by human, not agents.