A remark plugin for inline substitution syntax using :sub[INITIAL]{REPLACEMENT}.
Inline substitution allows progressive disclosure of content — show a brief version initially, then reveal the full content on activation.
npm install remark-substituteimport { 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.'):sub[INITIAL]{REPLACEMENT}- INITIAL: Text shown initially
- REPLACEMENT: Text shown after activation (one-way toggle)
Both sections support inline Markdown:
:sub[**bold**]{_italic_ and `code`}Nested :sub is allowed in REPLACEMENT:
:sub[outer]{:sub[inner]{deep}}In INITIAL:
\]→ literal](doesn't close)\[→ literal[(no depth change)\\→ literal\
In REPLACEMENT:
\}→ literal}(doesn't close)\{→ literal{(no depth change)\\→ literal\
Unescaped brackets/braces are balanced:
:sub[f(x) = [a, b]]{expanded} <!-- INITIAL = "f(x) = [a, b]" -->
:sub[x]{{a, b}} <!-- REPLACEMENT = "{a, b}" -->:sub[must NOT be preceded by a word character (a-z, A-Z, 0-9, _)- Cannot span multiple lines
foo:sub[x]{y} <!-- Does NOT parse -->
test(:sub[x]{y}) <!-- Parses -->
test :sub[x]{y} <!-- Parses -->This monorepo contains:
| Package | Purpose |
|---|---|
micromark-extension-substitute |
Tokenizer |
mdast-util-substitute |
AST utilities |
remark-substitute |
Remark plugin |
For direct micromark or mdast integration, see the individual package READMEs.
interface Sub extends Parent {
type: 'sub'
children: PhrasingContent[] // Parsed INITIAL
data: {
replacement: PhrasingContent[] // Parsed REPLACEMENT
}
}If using both plugins, the one registered last wins for :sub[...] syntax:
// Sub wins:
unified()
.use(remarkDirective)
.use(remarkSubstitute) // registered last
// Directive wins:
unified()
.use(remarkSubstitute)
.use(remarkDirective) // registered lastMIT