This is a PostHTML plugin that uses Shiki to highlight code blocks.
Features:
- Configure
langs - Configure
themes -
langattribute -
themeattribute - 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.