markdown-it-underline-cjk-friendly
v1.0.3
Published
Renders _underline_ to <u>underline</u>
Downloads
30
Maintainers
Readme
markdown-it-underline
Renders
_underline_to<u>underline</u>.
— CJK-friendly, language-agnostic.
Why this plugin?
Standard Markdown offers two ways to produce italic: *emphasis* and _emphasis_. Having two syntaxes for the same output is confusing. This plugin reclaims the underscore syntax to produce underline instead, giving each marker a distinct purpose:
| Input | Output |
|---|---|
| _underline_ | <u>underline</u> |
| *emphasis* | <em>emphasis</em> |
| **strong** | <strong>strong</strong> |
| __text__ | Not modified by this plugin, Default — <strong>text</strong> |
The
__double underscore__syntax is not modified by this plugin. By default it remains strong emphasis (<strong>), but you are free to install another plugin to customize its behavior.
Why not CommonMark?
This plugin deliberately deviates from CommonMark because CommonMark's flanking rules are unfriendly to CJK (Chinese, Japanese, Korean) and other scripts that do not use spaces between words.
In standard CommonMark, _你好_世界! does not produce underline because the closing _ is considered "intraword." To make it work you would need to write _你好_ 世界! (with an ugly space) or insert <wbr> tags — both of which clutter your source text and harm readability.
This plugin treats underscores the same way across all writing systems:
| Input | Output |
|---|---|
| 你_好_世界! | 你<u>好</u>世界! |
| Hello_w_orld | Hello<u>w</u>orld |
| Γεια _σ_ας κόσμος | Γεια <u>σ</u>ας κόσμος |
Installation
# npm
npm install markdown-it-underline-cjk-friendly
# yarn
yarn add markdown-it-underline-cjk-friendly
# pnpm
pnpm add markdown-it-underline-cjk-friendlyUsage
import markdownIt from "markdown-it";
import markdownItUnderline from "markdown-it-underline-cjk-friendly";
const md = markdownIt().use(markdownItUnderline);
md.render("_Hello_ World!");
// <p><u>Hello</u> World!</p>Features
Basic underline and coexistence with emphasis
_underline_ becomes <u> while *emphasis* and **strong** remain unchanged:
_underline_ *emphasis* **strong**<p><u>underline</u> <em>emphasis</em> <strong>strong</strong></p>Nesting
Underline nests correctly with emphasis, strong, and strikethrough:
_*underline emphasis*_
*_underline emphasis_*
**bold *italic _underline ~~strikethrough~~ underline_ italic* bold**<p><u><em>underline emphasis</em></u>
<em><u>underline emphasis</u></em></p>
<p><strong>bold <em>italic <u>underline <s>strikethrough</s> underline</u> italic</em> bold</strong></p>Lone underscores are ignored
A single _ with no matching pair is treated as literal text — my_var, _hello, and hello_ all remain unchanged.
Language-agnostic
Works identically across all scripts. A single character between underscores is underlined regardless of language:
| Language | Markdown | HTML |
|--|--|--|
| English | un_d_erline | un<u>d</u>erline
| Greek | υ_π_ογράμμιση | υ<u>π</u>ογράμμιση |
| Cyrillic | по_д_черкнуть | по<u>д</u>черкнуть |
| Armenian | ը_ն_դգծել | ը<u>ն</u>դգծել |
| Georgian | ხა_ზ_გასწორება | ხა<u>ზ</u>გასწორება |
| Arabic | ا_ل_تأكيد | ا<u>ل</u>تأكيد |
| Devanagari | अं_ड_रलाइन | अं<u>ड</u>रलाइन |
| Thai | บรร_ย_าย | บรร<u>ย</u>าย |
| Bengali | আন্ডা_র_লাইন | আন্ডা<u>র</u>লাইন |
| Burmese | အော_က်_ခြေ | အော<u>က်</u>ခြေ |
| Chinese | 下_划_线 | 下<u>划</u>线 |
| Japanese | アン_ダ_ーライン | アン<u>ダ</u>ーライン |
| Korean | 하_드_라인 | 하<u>드</u>라인 |
CJK text
Underscores work naturally with CJK characters in any position:
_你好_世界!
你_好_世界!
你好_世界_!
你好_世界!_<p><u>你好</u>世界!</p>
<p>你<u>好</u>世界!</p>
<p>你好<u>世界</u>!</p>
<p>你好<u>世界!</u></p>Escaping
CAUTION:
_file_name_ will become <u>file</u>name_, which may not you want.
In that case, you need escaping.
Use backslashes to escape underscores:
Useful to keep snake_case variable names.
_file_name_ → <u>file</u>name_
_file\_name_ → <u>file_name</u>
\_file_name_ → _file<u>name</u>
\_file\_name\_ → _file_name_Mixed marker priority
Whichever marker opens first takes priority:
*foo_bar*baz_ → <em>foo_bar</em>baz_ (* opened first)
_foo*bar_baz* → <u>foo*bar</u>baz* (_ opened first)
**foo_bar**baz_ → <strong>foo_bar</strong>baz_
_foo**bar_baz** → <u>foo**bar</u>baz**
~~foo_bar~~baz_ → <s>foo_bar</s>baz_
_foo~~bar_baz~~ → <u>foo~~bar</u>baz~~Code spans and code blocks
Underscores inside inline code and fenced code blocks are never treated as underline:
`get_local_documents`
_foo`bar_baz`<p><code>get_local_documents</code></p>
<p>_foo<code>bar_baz</code></p>Links and images
Link text supports underline; link URLs, image URLs, and image alt text are left untouched:
[hello_world_again](http://example.com/hello_world_again)
<p><a href="http://example.com/hello_world_again">hello<u>world</u>again</a></p>
<p><img src="http://example.com/hello_world_again.jpg" alt="hello_world_again"></p>Reference links work the same way — underline in link text, not in reference tags, and no crossing between adjacent reference links.
Tables
Underscores do not cross table cell boundaries. Inside a cell, paired underscores produce underline:
| f_o_o | b_a_r |
|-------|-------|
| b_a_z | q_u_x |<table>
<thead>
<tr><th>f<u>o</u>o</th><th>b<u>a</u>r</th></tr>
</thead>
<tbody>
<tr><td>b<u>a</u>z</td><td>q<u>u</u>x</td></tr>
</tbody>
</table>Double and triple underscores
__text__ is not handled by this plugin — it remains as <strong>text</strong> (default markdown-it behavior), or can be customized by another plugin.
___text___ is also not handled by this plugin, but by the default behavior, it could be <u><strong>text</strong></u>.
On semantics
Is <u> semantically correct? Maybe not — but who cares?
<u>is the simplest way to create underlined text without CSS.<ins>looks identical but implies document insertion, which is semantically worse for general underlining.- Repurposing
<em>with CSS:
would break theem { font-style: normal; text-decoration: underline; }*italic*syntax.
Sometimes practicality beats purity.
References
- arve0/markdown-it-underline — A CommonMark-compliant underline plugin. Does not work well with CJK text.
- tats-u/markdown-cjk-friendly — A companion plugin that makes built-in Markdown syntax (
*emphasis*,**strong**) more CJK-friendly.
