Syntax Extensions#
Non-standard Markdown syntax is opt-in, so ordinary documents render the same everywhere until a site explicitly enables an extension.
| Option | Type | Default |
|---|---|---|
emojiShortcodes |
boolean / EmojiShortcodeOptions |
false |
wikiLinks |
boolean / WikiLinkOptions |
false |
attrs |
boolean / AttrsOptions |
false |
cjkEmphasis |
boolean |
false |
Emoji Shortcodes#
Expand GitHub-style :shortcode: aliases to Unicode emoji:
import { oxContent } from "@ox-content/vite-plugin";
export default {
plugins: [
oxContent({
emojiShortcodes: true,
}),
],
};
The built-in table covers hundreds of common aliases. Expansion happens outside fenced and inline code, and unknown shortcodes are left unchanged:
Ship it :rocket: :tada:
Status: :white_check_mark: passed, :warning: flaky, :x: failed
Unknown aliases like :no-such-emoji: stay untouched, and so does
inline code: `:rocket:`.
Rendered:
Ship it 🚀 🎉
Status: ✅ passed, ⚠️ flaky, ❌ failed
Unknown aliases like :no-such-emoji: stay untouched, and so does
inline code: :rocket:.
Custom shortcodes#
Custom values are merged into the built-in table and override it on conflict. Keys are written without colons:
oxContent({
emojiShortcodes: {
custom: {
shipit: "🚢",
oxc: "🦀",
},
},
});
Wiki Links#
Resolve Obsidian-style [[target]] links into normal site links:
oxContent({
wikiLinks: {
// Defaults to the top-level `base` option.
baseUrl: "/docs/",
},
});
The expansion runs before Markdown parsing, and fenced code blocks and inline code spans are protected. Given this source:
See [[getting-started|Getting started]] and [[api/transform#options]].
the transform emits:
<p>
See <a href="/docs/getting-started">Getting started</a> and
<a href="/docs/api/transform#options">api/transform#options</a>.
</p>
[[target]] uses the target as the label, [[target|label]] overrides it, and
#fragment parts are slugified. Site-relative targets are prefixed with
baseUrl.
Wiki links also run before raw HTML is parsed, so [[...]] inside literal
<code> tags in embedded HTML is expanded too — keep literal examples inside
Markdown code spans or fences instead.
Attribute Syntax#
Add IDs, classes, and attributes with markdown-it-attrs syntax:
oxContent({
attrs: true,
});
Supported tokens are #id, .class, and key=value. A trailing {...} block
attaches to the element rendered from that line:
A lead paragraph. {.lead}
## Install {.section data-section=install}
produces:
<p class="lead">A lead paragraph.</p>
<h2 id="install" class="section" data-section="install">Install</h2>
The transform runs as a post-render HTML pass over the full document — raw
HTML embedded in Markdown is affected as well, so literal {...} examples
belong in code spans or fences.
CJK Emphasis#
Emphasis adjacent to CJK characters needs no configuration — CommonMark's delimiter rules already allow it, and no ASCII spaces are required:
これは**重要**です。次の文でも*強調*できます。
Rendered:
これは重要です。次の文でも強調できます。
What plain CommonMark rejects is a delimiter run sitting directly against
punctuation on its outer side. Its flanking rules read Unicode punctuation as a
whole, so East Asian punctuation blocks a run just like ASCII punctuation does,
and A**強調。**B stays literal text. Latin prose rarely hits this because a
space usually separates the two; CJK sets punctuation against the preceding
word, so it comes up constantly.
cjkEmphasis classifies East Asian punctuation as an ordinary character for
that decision only:
oxContent({
cjkEmphasis: true,
});
A**強調。**B
renders as A<strong>強調。</strong>B with the option on, and as literal text
with it off. Halfwidth ASCII punctuation is deliberately untouched, so a Latin
document parses identically either way.
This is a deliberate deviation from the specification, which is why it is opt-in. See CJK Emphasis for the exact boundary and the reclassified character ranges.
Related#
- Markdown Baseline — the default syntax these extensions build on.
- Code Blocks — annotation and import syntax for fences.