
Research
Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.
remark-flexible-markers
Advanced tools
Remark plugin to add custom mark element with customizable properties in markdown
A robust Next.js newsletter Next.js Weekly is sponsoring me 💖

A warm thanks 🙌 to @ErfanEbrahimnia, @recepkyk, and @LSeaburg for the support 💖
This package is a unified (remark) plugin to add custom <mark> element with customizable properties in markdown.
unified is a project that transforms content with abstract syntax trees (ASTs) using the new parser micromark. remark adds support for markdown to unified. mdast is the Markdown Abstract Syntax Tree (AST) which is a specification for representing markdown in a syntax tree.
This plugin is a remark plugin that transforms the mdast.
remark-flexible-markers is useful if you want to add a custom <mark> element in markdown for providing marked or highlighted text, with custom tag name, custom class name, custom color classification, and also additional properties. You can easily create <mark> element with remark-flexible-markers.
This package is suitable for ESM only. In Node.js (version 16+), install with npm:
npm install remark-flexible-markers
or
yarn add remark-flexible-markers
== around the content==marked content==
[!IMPORTANT] The
==in the begining part of representation specifies there is NOcolorspecification.
=[classification key]= at the beginning and == at the end=r=marked content with red classification==
[!IMPORTANT] The
=r=at the beginning part of representation specifies thecolorspecification is"red".
Say we have the following file, example.md, which consists some flexible markers.
==marked content==
=r=marked content==
And our module, example.js, looks as follows:
import { read } from "to-vfile";
import remark from "remark";
import gfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import remarkFlexibleMarkers from "remark-flexible-markers";
main();
async function main() {
const file = await remark()
.use(gfm)
.use(remarkFlexibleMarkers)
.use(remarkRehype)
.use(rehypeStringify)
.process(await read("example.md"));
console.log(String(file));
}
Now, running node example.js yields:
<p>
<mark class="flexible-marker flexible-marker-default">marked content</mark>
<mark class="flexible-marker flexible-marker-red">marked content</mark>
</p>
Without remark-flexible-markers, you’d get:
<p>==marked content==
=r=marked content==</p>
[!CAUTION] The double equity signs must be adjacent to the content.
The content must be wrapped with double equity signs, not singular at any side.
More than one classification is not allowed.
Here are some bad usage, and will not work.
==text with bad wrapped=
=text with bad wrapped==
== text with unwanted space==
==text with unwanted space ==
=ab=text with more than one classification==
As of version ^1.2.0, remark-flexible-markers can handle also the syntax containing other markdown phrases like strong, emphasis, link etc. For example:
==**marked bold content**==
==_marked italic content_==
==[marked link](https://google.com)==
<p>
<mark class="flexible-marker flexible-marker-default">
<strong>marked bold content</strong>
</mark>
</p>
<p>
<mark class="flexible-marker flexible-marker-default">
<em>marked italic content</em>
</mark>
</p>
<p>
<mark class="flexible-marker flexible-marker-default">
<a href="https://google.com">marked link</a>
</mark>
</p>
All options are optional and some of them have default values.
interface Properties {
[PropertyName: string]: boolean | number | string | null | undefined | Array<string | number>;
}
type TagNameFunction = (color?: string) => string;
type ClassNameFunction = (color?: string) => string[];
type PropertyFunction = (color?: string) => Omit<Properties, 'className'> & { className?: never };
use(remarkFlexibleMarkers, {
dictionary?: Dictionary; // explained in the options section
markerTagName?: string | TagNameFunction; // default is "mark"
markerClassName?: string | ClassNameFunction; // default is "flexible-marker"
markerProperties?: PropertyFunction;
equalityOperator?: string;
actionForEmptyContent?: "keep" | "remove" | "marker"; // // default is "marker"
} as FlexibleMarkerOptions);
dictionaryIt is a key, value option for providing color classification for the mark node.
The dictionary is opinionated, by default.
type Key = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m"
| "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
type Dictionary = Partial<Record<Key, string>>;
const dictionary: Dictionary = {
a: "amber",
b: "blue",
c: "cyan",
d: "brown",
e: "espresso",
f: "fuchsia",
g: "green",
h: "hotpink",
i: "indigo",
j: "jade",
k: "kiwi",
l: "lime",
m: "magenta",
n: "navyblue",
o: "orange",
p: "purple",
q: "pink",
r: "red",
s: "silver",
t: "teal",
u: "umber",
v: "violet",
w: "white",
x: "gray",
y: "yellow",
z: "black",
};
You can override the dictionary entries.
use(remarkFlexibleMarkers, {
dictionary: {
w: "wall"
},
});
Now, it is overriden for only w key, and the color classification will be wall instead of default one white.
=w=marked content==
<p>
<mark class="remark-marker remark-marker-wall">marked content</mark>
</p>
markerTagNameIt is a string or a callback (color?: string) => string option for providing custom HTML tag name for mark nodes.
By default, it is mark which is well known HTML element for highlighting the texts.
use(remarkFlexibleMarkers, {
markerTagName: "span",
});
Now, the element tag names will be span.
<span class="...">marked content</span>
The option can take also a callback function, which has an optional argument color, and returns string representing the custom tag name.
use(remarkFlexibleMarkers, {
markerTagName: (color) => color ?? "yellow",
});
Now, the element tag names will be the color name.
==marked content==
=r=marked content==
<p>
<yellow class="...">marked content</yellow>
<red class="...">marked content</red>
</p>
markerClassNameIt is a string or a callback (color?: string) => string[] option for providing custom class name for the mark node.
By default, it is flexible-marker, and all mark nodes' classnames will contain flexible-marker.
A mark node contains also a secondary class name representing the color specification which starts with the flexible-marker- and ends with the color specification, like flexible-marker-red or flexible-marker-blue. If there is no color classification, then the secondary class name will be flexible-marker-default.
If a mark syntax in the document has no content, and would wanted to be an empty marker, the class name will contain flexible-marker-empty, additionally.
use(remarkFlexibleMarkers, {
markerClassName: "remark-marker",
});
Now, the mark nodes will have remark-marker as a className, and the secondary class names will start with remark-marker-.
==marked content==
=r=marked content==
<p>
<mark class="remark-marker remark-marker-default">marked content</mark>
<mark class="remark-marker remark-marker-red">marked content</mark>
</p>
The option can take also a callback function, which has an optional argument color, and returns array of strings representing class names.
use(remarkFlexibleMarkers, {
markerClassName: (color) => {
return [`marker-${color ?? "yellow"}`]
},
});
Now, the element class names will contain only one class name like marker-yellow, marker-red etc.
==marked content==
=r=marked content==
<p>
<mark class="marker-yellow">marked content</mark>
<mark class="marker-red">marked content</mark>
</p>
[!WARNING] If you use the
markerClassNameoption as a callback function, it is your responsibility to define class names, primary or secondary in an array.
markerPropertiesIt is a callback (color?: string) => Record<string, unknown> & { className?: never } option to set additional properties for the mark node.
The callback function that takes the color as optional argument and returns object which is going to be used for adding additional properties into the mark node.
The className key is forbidden and effectless in the returned object.
use(remarkFlexibleMarkers, {
markerProperties(color) {
return {
["data-color"]: color,
};
},
});
Now, the mark nodes which have a color classification will contain data-color property.
==marked content==
=r=marked content==
<p>
<mark class="flexible-marker flexible-marker-default">marked content</mark>
<mark class="flexible-marker flexible-marker-red" data-color="red">marked content</mark>
</p>
equalityOperatorIt is a string option in order not to confuse with mathematical equality operator like if a == b, then ....
If there is a space around double equality in a mathematical text, there is no problem, since the plugin will not match with these.
If a == b and c == d then the theorem is right. --> will not cause any problem.
But, if there is NO space around double equality in a mathematical text, the plugin assumes they are marker but actually not.
If a==b and c==d then the theorem is right. --> will cause the plugin match a marker <mark>b and c</mark>, unwantedly.
In order the plugin to handle this kind of mathematical expressions correctly, there is equalityOperator option.
use(remarkFlexibleMarkers, {
equalityOperator: "=:=",
});
If a=:=b and c=:=d then the theorem is right
Now, the plugin is going to convert the =:= into == as should be.
<p>If a==b and c==d then the theorem is right</p>
By default, the option is undefined, which means no check happens.
actionForEmptyContentIt is a union "keep" | "remove" | "mark" option to handle marker syntax with empty content in a markdown document.
By default, it is mark, meaningly the plugin will create an empty <mark> node for marker syntax with empty content.
I don't know what could be a reason you use empty markers, but anyway I wanted to handle it. Here is an example marker syntax with empty content.
====, == ==, =r===, =r= ==
You have three options to handle marker syntax with empty content.
keep will keep the syntax as it is.use(remarkFlexibleMarkers, {
actionForEmptyContent: "keep",
});
will produce:
<p>====, == ==, =r===, =r= ==</p>
remove will remove the mark syntax with empty content.use(remarkFlexibleMarkers, {
actionForEmptyContent: "remove",
});
will produce:
<p>, , , </p>
mark will create an empty <mark> node.use(remarkFlexibleMarkers, {
actionForEmptyContent: "mark", // actually, it is default
});
will produce <mark> nodes with additional class name "flexible-marker-empty".
<p>
<mark class="flexible-marker flexible-marker-default flexible-marker-empty"></mark>,
<mark class="flexible-marker flexible-marker-default flexible-marker-empty"></mark>,
<mark class="flexible-marker flexible-marker-red flexible-marker-empty"></mark>,
<mark class="flexible-marker flexible-marker-red flexible-marker-empty"></mark>
</p>
Here is ==marked content==
Here is =r=marked content with r classification==
Here are **==marked bold content==** and ==**marked bold content**==
### ==marked content in headings==
use(remarkFlexibleMarkers);
is going to produce as default:
<p>
Here is
<mark class="flexible-marker flexible-marker-default">marked content</mark>
</p>
<p>
Here is
<mark class="flexible-marker flexible-marker-red">marked content with r classification</mark>
</p>
<p>
Here are
<strong>
<mark class="flexible-marker flexible-marker-default">marked bold content</mark>
</strong>
and
<mark class="flexible-marker flexible-marker-default">
<strong>marked bold content</strong>
</mark>
</p>
<h3>
<mark class="flexible-marker flexible-marker-default">marked content in headings</mark>
</h3>
use(remarkFlexibleMarkers, {
dictionary: {
r: "rain",
},
markerClassName: "custom-marker",
markerTagName: "span",
markerProperties(color) {
return {
["data-color"]: color,
};
},
});
is going to produce:
<p>
Here is
<span class="custom-marker custom-marker-default">marked content</span>
</p>
<p>
Here is
<span class="custom-marker custom-marker-rain" data-color="rain">marked content with r classification</span>
</p>
<p>
Here are
<strong>
<span class="custom-marker custom-marker-default">marked bold content</span>
</strong>
and
<span class="custom-marker custom-marker-default">
<strong>marked bold content</strong>
</span>
</p>
<h3>
<span class="custom-marker custom-marker-default">marked content in headings</span>
</h3>
[!TIP] You can use the marker syntax in the tables, headings, lists, blockquotes etc. You can have a look at the test files in the github repo for detailed examples.
This plugin only modifies the mdast (markdown abstract syntax tree) as explained.
This package is fully typed with TypeScript. The plugin options' type is exported as FlexibleMarkerOptions.
This plugin works with unified version 6+ and remark version 7+. It is compatible with mdx version 2+.
Use of remark-flexible-markers does not involve rehype (hast) or user content so there are no openings for cross-site scripting (XSS) attacks.
I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.
If you find remark-flexible-markers or any of my projects is useful and helpful, please consider supporting my work. Your sponsorship means a lot to me and keeps these projects alive and updated! 💖
My sponsors are going to be featured at the very top of the page and proudly displayed on my Sponsor Wall.
Thank you for supporting open source! 🙌
remark-flexible-code-titles
– Remark plugin to add titles or/and containers for the code blocks with customizable propertiesremark-flexible-containers
– Remark plugin to add custom containers with customizable properties in markdownremark-ins
– Remark plugin to add ins element in markdownremark-flexible-paragraphs
– Remark plugin to add custom paragraphs with customizable properties in markdownremark-flexible-markers
– Remark plugin to add custom mark element with customizable properties in markdownremark-flexible-toc
– Remark plugin to expose the table of contents via vfile.data or via an option referenceremark-mdx-remove-esm
– Remark plugin to remove import and/or export statements (mdxjsEsm)remark-mdx-remove-expressions
– Remark plugin to remove MDX expressions within curlybraces {} in MDX contentrehype-pre-language
– Rehype plugin to add language information as a property to pre elementrehype-highlight-code-lines
– Rehype plugin to add line numbers to code blocks and allow highlighting of desired code linesrehype-code-meta
– Rehype plugin to copy code.data.meta to code.properties.metastringrehype-image-toolkit
– Rehype plugin to enhance Markdown image syntax ![]() and Markdown/MDX media elements (<img>, <audio>, <video>) by auto-linking bracketed or parenthesized image URLs, wrapping them in <figure> with optional captions, unwrapping images/videos/audio from paragraph, parsing directives in title for styling and adding attributes, and dynamically converting images into <video> or <audio> elements based on file extension.recma-mdx-escape-missing-components
– Recma plugin to set the default value () => null for the Components in MDX in case of missing or not provided so as not to throw an errorrecma-mdx-change-props
– Recma plugin to change the props parameter into the _props in the function _createMdxContent(props) {/* */} in the compiled source in order to be able to use {props.foo} like expressions. It is useful for the next-mdx-remote or next-mdx-remote-client users in nextjs applications.recma-mdx-change-imports
– Recma plugin to convert import declarations for assets and media with relative links into variable declarations with string URLs, enabling direct asset URL resolution in compiled MDX.recma-mdx-import-media
– Recma plugin to turn media relative paths into import declarations for both markdown and html syntax in MDX.recma-mdx-import-react
– Recma plugin to ensure getting React instance from the arguments and to make the runtime props {React, jsx, jsxs, jsxDev, Fragment} is available in the dynamically imported components in the compiled source of MDX.recma-mdx-html-override
– Recma plugin to allow selected raw HTML elements to be overridden via MDX components.recma-mdx-interpolate
– Recma plugin to enable interpolation of identifiers wrapped in curly braces within the alt, src, href, and title attributes of markdown link and image syntax in MDX.I also build low-level utilities and plugins for the Unified ecosystem that can be used across Remark, Rehype, Recma, and other unist-based abstract syntax trees (ASTs).
unist-util-find-between
– Unist utility to find the nodes between two nodes.unified-log-tree
– Unified plugin to log abstract syntax trees (ASTs) for debugging without mutating.MIT License © ipikuka
FAQs
Remark plugin to add custom mark element with customizable properties in markdown
The npm package remark-flexible-markers receives a total of 13,635 weekly downloads. As such, remark-flexible-markers popularity was classified as popular.
We found that remark-flexible-markers demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.