This is a PostHTML plugin that uses Shiki to highlight code blocks.
Features:
- Configure
langs
- Configure
themes
-
lang
attribute -
theme
attribute - Dual Themes
- Wrap in custom tag
- Default color theme
- Decorations
- Transformers
- Custom themes
- Custom languages
Input:
<shiki>
<h1 class="text-xl">Hello</h1>
</shiki>
Output:
<pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color:#81A1C1"> <h1</span><span style="color:#8FBCBB"> class</span><span style="color:#ECEFF4">=</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">text-xl</span><span style="color:#ECEFF4">"</span><span style="color:#81A1C1">></span><span style="color:#D8DEE9FF">Hello</span><span style="color:#81A1C1"></h1></span></span>
<span class="line"></span></code></pre>
npm i posthtml posthtml-shiki
Use the <shiki>
tag to highlight all code inside it:
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki()
])
.process('<shiki><h1 class="text-xl">Hello</h1></shiki>')
.then(result => result.html)
You may use certain attributes to configure which themes or language to use.
Alias: language
Use the lang
attribute to specify the language of the code block.
<shiki lang="javascript">
import { codeToHtml } from 'shiki'
</shiki>
Use the theme
attribute to specify the theme to use.
<shiki theme="github-light">
<h1 class="text-xl">Hello</h1>
</shiki>
Shiki's Dual Themes is supported through theme-*
attributes:
<shiki theme-light="github-light" theme-dark="github-dark">
<h1 class="text-xl">Hello</h1>
</shiki>
Note
If a theme
attribute is present, it will override the theme-*
attributes.
This uses CSS variables to switch between themes, so you'll need to define the CSS variables in your stylesheet.
With media queries:
@media (prefers-color-scheme: dark) {
.shiki,
.shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
}
Class-based:
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
When using multiple themes, you may specify the default color theme for Shiki to use.
The value of the attribute must be the name of one of the theme-*
attributes, so for example if you have theme-light
and theme-dark
attributes, the attribute value must be either light
or dark
.
<shiki
theme-light="github-light"
theme-dark="github-dark"
default-color="dark"
>
<h1 class="text-xl">Hello</h1>
</shiki>
Shiki relies on CSS specificity and changes the order of the classes on the wrapping <pre>
tag.
By default, the plugin does not set default-color
.
By default, the <shiki>
tag will be removed and the code block will be wrapped in a <pre>
tag. Use the wrap
attribute to define a custom tag to wrap the code block in.
<shiki lang="js" wrap="div">
import { codeToHtml } from 'shiki'
</shiki>
Result:
<div><pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"><span style="color:#81A1C1">import</span><span style="color:#ECEFF4"> {</span><span style="color:#8FBCBB"> codeToHtml</span><span style="color:#ECEFF4"> }</span><span style="color:#81A1C1"> from</span><span style="color:#ECEFF4"> '</span><span style="color:#A3BE8C">shiki</span><span style="color:#ECEFF4">'</span></span></code></pre></div>
Important
The value of the wrap
attribute must be a valid tag name, CSS selectors are not supported.
The plugin accepts an options object as the first argument, which can be used to configure things like the tag name or the options to pass to Shiki.
Type: string
Default: shiki
Use the tag
option to specify the tag name to use.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
tag: 'highlight'
})
])
.process('<highlight>... your code</highlight>')
.then(result => result.html)
Type: string[]
Default: ['html']
Use the langs
option to specify the languages for Shiki to load.
It's recommended to load only the languages that you need.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
langs: ['html', 'javascript']
})
])
.process(`
<shiki lang="html">... some html</shiki>
<shiki lang="javascript">... some js</shiki>
`)
.then(result => result.html)
See the list of supported languages in Shiki.
You may also load custom languages by passing a TextMate grammar object to the langs
option.
const customDiffLang = JSON.parse(readFileSync('./custom-diff.json', 'utf8'))
posthtml([
shiki({
langs: [customDiffLang]
})
])
.process(`
<shiki lang="custom-diff">
- FOO
+ BAR
</shiki>
`)
.then(result => result.html)
You must specify the lang
attribute with the name of the language, and the value must match the name
property of the TextMate grammar object.
See tm-grammars for examples.
Type: Array<string> | Array<object>
Default: ['nord']
Use the themes
option to specify the themes for Shiki to load.
It's recommended to load only the themes that you need.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: ['github-light', 'github-dark']
})
])
.process(`
<shiki theme="github-light">[code]</shiki>
<shiki theme="github-dark">[code]</shiki>
`)
.then(result => result.html)
See the list of available themes in Shiki.
Note
If you don't specify a theme=""
attribute, the first theme in the themes
option will be used.
You may also load custom themes by passing a TextMate theme object to the themes
option:
// Define textmate theme
const myTheme = {
name: 'my-theme',
settings: [
{
scope: ['string'],
settings: {
foreground: '#888'
}
},
]
}
posthtml([
shiki({
themes: [myTheme],
})
])
.process(`<shiki theme="my-theme">[code]</shiki>`)
.then(result => result.html)
If you're loading multiple themes, you will need to specify which theme to use with the theme=""
attribute. For custom themes, the attribute value must match the name of the theme - in the example above, that would be my-theme
.
Type: string|boolean
Default: false
Use the wrapTag
option to specify a custom tag to wrap the highlighted code block in.
By default, the plugin does not wrap the code block in any tag.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
wrapTag: 'div'
})
])
.process('<shiki>... your code</shiki>')
.then(result => result.html)
Result:
<div>
[highlighted code]
</div>
Type: string
Default: undefined
Use the defaultColor
option to specify the default color theme for Shiki to use.
The value must be the key name of one of the themes in the themes
option.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: 'dark'
})
])
.process(`
<shiki>
[code]
</shiki>
`)
.then(result => result.html)
Type: array
Default: []
Shiki's Decorations are supported through the decorations
option.
You can use this to wrap custom classes and attributes around character ranges in your code.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
decorations: [
{
// line and character are 0-indexed
start: { line: 0, character: 0 },
end: { line: 0, character: 5 },
properties: { class: 'highlighted-word' }
}
]
})
])
.process(`
<shiki>
const foo = 'bar'
</shiki>
`)
.then(result => result.html)
The word const
will be wrapped in a <span class="highlighted-word">
tag.
Type: array
Default: []
Use this option to transform the highlighted code block with Shiki's Transformers.
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
import { transformerNotationHighlight } from '@shikijs/transformers'
posthtml([
shiki({
transformers: [
transformerNotationHighlight(),
{
code(node) {
this.addClassToHast(node, 'custom-class')
},
},
]
})
])
.process(`
<shiki>
const foo = 'bar'
let baz = 'biz' // [!code highlight]
</shiki>
`)
.then(result => result.html)
See the docs for Shiki Transformers and a list of common Shiki Transformers.