posthtml-shiki v1.0.0
Introduction
This is a PostHTML plugin that uses Shiki to highlight code blocks.
Features:
- Configure
langs
- Configure
themes
lang
attributetheme
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>
Installation
npm i posthtml posthtml-shiki
Usage
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)
Attributes
You may use certain attributes to configure which themes or language to use.
lang
Alias: language
Use the lang
attribute to specify the language of the code block.
<shiki lang="javascript">
import { codeToHtml } from 'shiki'
</shiki>
theme
Use the theme
attribute to specify the theme to use.
<shiki theme="github-light">
<h1 class="text-xl">Hello</h1>
</shiki>
theme-*
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 thetheme-*
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;
}
default-color
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
.
wrap
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 thewrap
attribute must be a valid tag name, CSS selectors are not supported.
Options
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.
tag
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)
langs
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.
Custom Languages
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.
themes
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 thethemes
option will be used.
Custom Themes
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
.
wrapTag
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>
defaultColor
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)
decorations
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.
transformers
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'
posthtml([
shiki({
transformers: [
{
code(node) {
this.addClassToHast(node, 'language-js')
},
}
]
})
])
.process(`
<shiki>
const foo = 'bar'
</shiki>
`)
.then(result => result.html)
The generated <code>
tag will have the language-js
class added.