remark-supersub
A Remark plugin adding pandoc-style superscript and subscript syntax to Markdown.
Repository Health
Technical Analysis
remark-supersub is a lightweight plugin for the unified/remark ecosystem that teaches the Markdown parser to recognize pandoc-style superscript (^text^) and subscript (~text~) syntax, emitting dedicated superscript and subscript node types into the MDAST tree.
When paired with rehype, those nodes stringify directly to semantic <sup> and <sub> HTML elements, making it a drop-in addition to any remark-based Markdown-to-HTML pipeline that needs typographic superscripts and subscripts — ordinal suffixes, chemical formulas, footnote-style markers — without hand-writing raw HTML in source documents.
What You Get
- Two new MDAST node types (
superscriptandsubscript) inserted directly into the parse tree - Zero-config
unified().use(supersub)integration alongside remark-parse and remark-rehype - Automatic HTML stringification to
<sup>/<sub>tags when combined with rehype-stringify - Bundled TypeScript type definitions for the plugin’s transformer and node types
Common Use Cases
- Rendering ordinal number suffixes like 21^st^ century in documentation sites
- Writing chemical formulas such as H
2O in technical or scientific Markdown content - Adding footnote-style superscript markers to blog posts or docs built on remark/rehype
- Extending a static site generator’s Markdown pipeline with pandoc-compatible typography
Under The Hood
Architecture
The plugin exports a single default factory, supersub(), that returns a Transformer matching the standard unified/remark plugin contract. Internally it runs two separate unist-util-visit passes over text nodes — one splitting on ^ to build superscript spans, another splitting on ~ for subscript spans — and replaces each matched text node in place via parent.children.splice. There is no internal layering, configuration surface, or shared state: the entire behavior lives in one small module, so the two visit passes are the only place correctness can drift, and any change to the split-and-splice logic directly changes output for every consumer.
Tech Stack
Written in TypeScript and compiled with a plain tsc build step (no bundler), with a single runtime dependency, unist-util-visit. It targets the wider unified/remark/rehype ecosystem (developed against unified ^10, remark ^14, remark-parse ^10, remark-rehype ^9, and rehype-stringify ^9 in the monorepo’s dev dependencies) and ships compiled JS plus .d.ts files under lib/. The package lives inside a Lerna-managed monorepo alongside sibling remark plugins, each built and published independently.
Code Quality
Tests exist via the ava test runner using snapshot assertions, but the fixture set covers only a single combined case (superscript and subscript together) with no dedicated coverage for edge cases like unmatched delimiters or escaped characters. The code favors TypeScript non-null assertions (parent!, i!) over explicit null handling, and an inline eslint-disable-next-line comment suggests looser type/lint discipline than the presence of TypeScript alone implies. The monorepo also carries two parallel, seemingly redundant test configurations (a Jest config plus the actual ava config used in CI), which is a minor consistency smell. CI runs on GitHub Actions across Node 14 and 15, executing the test suite on every push and pull request.
API Design
The public surface is minimal by design: a single default export registered with one .use(supersub) call and no configuration options. Naming mirrors pandoc’s own ^text^/~text~ convention, so anyone familiar with pandoc superscript/subscript syntax needs no additional learning. Getting started requires only wiring it into an existing unified().use(remark-parse).use(supersub).use(remark-rehype).use(rehype-stringify) chain, which the README documents directly with a runnable example.