feat(markdown): support GitHub-style task lists

Renders `- [ ] task` syntax via `@mdit/plugin-tasklist`, enabled by
default and configurable through the new `tasklist` markdown option
(set to `false` to disable). The default theme hides list markers for
task items and pulls checkboxes into the list gutter, GitHub-style.

close #413
close #1923
close #3648
close #5110

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5335/head
Divyansh Singh 2 weeks ago
parent 18380b0eb7
commit 97f87817ea

@ -42,6 +42,15 @@ describe('node/markdown/markdown', () => {
expect(await render(':tada:', { emoji: false })).toContain(':tada:')
})
test('tasklist', async () => {
const src = '- [ ] todo'
expect(await render(src)).toContain('<input type="checkbox"')
const disabled = await render(src, { tasklist: false })
expect(disabled).not.toContain('<input')
expect(disabled).toContain('[ ] todo')
})
test('toc', async () => {
const src = '# Title\n\n[[toc]]'
expect(await render(src)).toContain('table-of-contents')
@ -139,6 +148,26 @@ describe('node/markdown/markdown', () => {
})
})
describe('tasklist', () => {
test('renders checkboxes with their checked state', async () => {
const html = await render('- [ ] todo\n- [x] done')
expect(html).toContain('<ul class="task-list-container">')
expect(html).toContain('<li class="task-list-item">')
const inputs = html.match(/<input[^>]*>/g)!
expect(inputs).toHaveLength(2)
expect(inputs[0]).not.toContain('checked')
expect(inputs[1]).toContain('checked')
for (const input of inputs) expect(input).toContain('disabled')
})
test('forwards options to the plugin', async () => {
const html = await render('- [ ] todo', { tasklist: { label: false } })
expect(html).toContain('<input type="checkbox"')
expect(html).not.toContain('<label')
})
})
// attrs applies at a fixed position in the core chain (before linkify),
// while anchor pushes to its end, so anchor always sees user-defined ids
// no matter which plugin is registered first

@ -100,6 +100,20 @@ For more details, see [Frontmatter](../reference/frontmatter-config).
| col 2 is | centered | \$12 |
| zebra stripes | are neat | \$1 |
## Task Lists
**Input**
```md
- [ ] Write the press release
- [x] Update the website
```
**Output**
- [ ] Write the press release
- [x] Update the website
## Emoji :tada:
**Input**

@ -132,6 +132,7 @@
"@mdit/plugin-anchor": "^1.1.1",
"@mdit/plugin-attrs": "^1.0.2",
"@mdit/plugin-emoji": "^1.1.0",
"@mdit/plugin-tasklist": "^1.0.1",
"@polka/compression": "^1.0.0-next.28",
"@rollup/plugin-alias": "^6.0.0",
"@rollup/plugin-commonjs": "^29.0.3",

@ -116,6 +116,9 @@ importers:
'@mdit/plugin-emoji':
specifier: ^1.1.0
version: 1.1.0(markdown-it@14.2.0)
'@mdit/plugin-tasklist':
specifier: ^1.0.1
version: 1.0.1(markdown-it@14.2.0)
'@polka/compression':
specifier: ^1.0.0-next.28
version: 1.0.0-next.28
@ -660,6 +663,15 @@ packages:
markdown-it:
optional: true
'@mdit/plugin-tasklist@1.0.1':
resolution: {integrity: sha512-Ks/Tihw5ibbkP2qOZzltkbiTk8RfpxDRvqdn4hl1wIs3VmeKITIQ36gN4DsuotSauA9SBe/mL0QJIfuSCrXqrw==}
engines: {node: '>=22'}
peerDependencies:
markdown-it: ^14.2.0
peerDependenciesMeta:
markdown-it:
optional: true
'@napi-rs/wasm-runtime@1.1.6':
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
peerDependencies:
@ -3259,6 +3271,12 @@ snapshots:
optionalDependencies:
markdown-it: 14.2.0
'@mdit/plugin-tasklist@1.0.1(markdown-it@14.2.0)':
dependencies:
'@types/markdown-it': 14.1.2
optionalDependencies:
markdown-it: 14.2.0
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
dependencies:
'@emnapi/core': 1.11.1

@ -163,6 +163,16 @@
margin: 8px 0 0;
}
.vp-doc li.task-list-item {
list-style: none;
}
.vp-doc .task-list-item-checkbox {
margin: 0 4px 2px -1.25rem;
vertical-align: middle;
accent-color: var(--vp-c-brand-1);
}
/**
* Table
* -------------------------------------------------------------------------- */

@ -20,6 +20,10 @@ import {
type MarkdownItAttrsOptions
} from '@mdit/plugin-attrs'
import { fullEmoji as emojiPlugin } from '@mdit/plugin-emoji'
import {
tasklist as tasklistPlugin,
type MarkdownItTaskListOptions
} from '@mdit/plugin-tasklist'
import type {
CodeToHastOptions,
LanguageInput,
@ -193,6 +197,12 @@ export interface MarkdownOptions extends MarkdownItAsyncOptions {
* @see https://mdit-plugins.github.io/emoji.html
*/
emoji?: EmojiPluginOptions | false
/**
* Options for `@mdit/plugin-tasklist` (GitHub-style task lists,
* `- [ ] task`). Set to `false` to disable.
* @see https://mdit-plugins.github.io/tasklist.html
*/
tasklist?: MarkdownItTaskListOptions | false
/**
* Improves emphasis (`**bold**`) handling in Japanese, Chinese, and
* Korean text.
@ -365,6 +375,9 @@ export async function createMarkdownRenderer(
if (options.emoji !== false) {
emojiPlugin(md, options.emoji)
}
if (options.tasklist !== false) {
tasklistPlugin(md, options.tasklist)
}
if (options.cjkFriendlyEmphasis !== false) {
mditCjkFriendly(md)
}

Loading…
Cancel
Save