remark-substitute
v1.0.0
Published
Remark plugin for inline substitution syntax (:sub[...]{...})
Maintainers
Readme
remark-substitute
A remark plugin for inline substitution using :sub[INITIAL]{REPLACEMENT} syntax.
Why?
Inline substitution enables progressive disclosure of content:
- TL;DR summaries that expand to full explanations
- Spoiler text that reveals on interaction
- Interactive learning with expandable definitions
- Contextual detail — brief by default, detailed on demand
The syntax is explicit and unambiguous, integrating cleanly with Markdown parsing.
Install
npm install remark-substituteUsage
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkSubstitute from 'remark-substitute'
import remarkStringify from 'remark-stringify'
const result = await unified()
.use(remarkParse)
.use(remarkSubstitute)
.use(remarkStringify)
.process('The :sub[TL;DR]{full explanation here} summarizes the point.')Markdown Syntax
Basic: :sub[click]{revealed text}
Inline Markdown works: :sub[**bold**]{_italic_ and `code`}
Nested substitution: :sub[outer]{:sub[inner]{deep}}
Balanced brackets: :sub[f(x) = [a, b]]{expanded}Escaping
In INITIAL:
\]→ literal](doesn't close)\[→ literal[(no depth change)\\→ literal\
In REPLACEMENT:
\}→ literal}(doesn't close)\{→ literal{(no depth change)\\→ literal\
AST Node
The plugin creates sub nodes:
interface Sub extends Parent {
type: 'sub'
children: PhrasingContent[] // Parsed INITIAL
data: {
replacement: PhrasingContent[] // Parsed REPLACEMENT
}
}Boundary Rules
:sub[...] must not be preceded by a word character. Use parentheses for adjacency:
test(:sub[x]{y}) <!-- Works -->
test:sub[x]{y} <!-- Does NOT parse -->Use with remark-directive
If you use both remark-substitute and remark-directive, the plugin registered last takes precedence for :sub[...] syntax.
// Sub wins:
unified()
.use(remarkDirective)
.use(remarkSubstitute) // registered last
// Directive wins:
unified()
.use(remarkSubstitute)
.use(remarkDirective) // registered lastLicense
MIT
