https://github.com/posthtml/posthtml-shiki
Highlight code blocks with PostHTML and Shiki
https://github.com/posthtml/posthtml-shiki
Last synced: 4 months ago
JSON representation
Highlight code blocks with PostHTML and Shiki
- Host: GitHub
- URL: https://github.com/posthtml/posthtml-shiki
- Owner: posthtml
- License: mit
- Created: 2024-03-04T19:02:52.000Z (almost 2 years ago)
- Default Branch: master
- Last Pushed: 2025-06-02T21:43:48.000Z (9 months ago)
- Last Synced: 2025-06-09T04:28:01.601Z (8 months ago)
- Language: HTML
- Homepage:
- Size: 998 KB
- Stars: 2
- Watchers: 5
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
Shiki Plugin
Highlight code with PostHTML and Shiki
[![Version][npm-version-shield]][npm]
[![Build][github-ci-shield]][github-ci]
[![License][license-shield]][license]
[![Downloads][npm-stats-shield]][npm-stats]
## Introduction
- [Installation](#installation)
- [Usage](#usage)
- [Attributes](#attributes)
- [Options](#options)
This is a PostHTML plugin that uses [Shiki](https://shiki.style) to highlight code blocks.
Features:
- [x] Configure [`langs`](#langs)
- [x] Configure [`themes`](#themes)
- [x] [`lang`](#lang) attribute
- [x] [`theme`](#theme) attribute
- [x] [Dual Themes](#theme-1)
- [x] [Wrap in custom tag](#wrap)
- [x] [Default color theme](#default-color)
- [x] [Decorations](#decorations)
- [x] [Transformers](#transformers)
- [x] [Custom themes](#custom-themes)
- [x] [Custom languages](#custom-languages)
Input:
```xml
Hello
```
Output:
```html
<h1 class="text-xl">Hello</h1>
```
## Installation
```
npm i posthtml posthtml-shiki
```
## Usage
Use the `` tag to highlight all code inside it:
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki()
])
.process('
Hello
')
.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.
```xml
import { codeToHtml } from 'shiki'
```
### `theme`
Use the `theme` attribute to specify the theme to use.
```xml
Hello
```
### `theme-*`
Shiki's [Dual Themes](https://shiki.style/guide/dual-themes) is supported through `theme-*` attributes:
```xml
Hello
```
> [!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:
```css
@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:
```css
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`.
```xml
Hello
```
Shiki relies on CSS specificity and changes the order of the classes on the wrapping `
` tag.
By default, the plugin does not set `default-color`.
### `wrap`
By default, the `` tag will be removed and the code block will be wrapped in a `
` tag. Use the `wrap` attribute to define a custom tag to wrap the code block in.
```xml
import { codeToHtml } from 'shiki'
```
Result:
```html
import { codeToHtml } from 'shiki'
```
> [!IMPORTANT]
> The value of the `wrap` 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.
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
tag: 'highlight'
})
])
.process('... your code')
.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.
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
langs: ['html', 'javascript']
})
])
.process(`
... some html
... some js
`)
.then(result => result.html)
```
See the list of [supported languages](https://shiki.style/languages) in Shiki.
#### Custom Languages
You may also load custom languages by passing a TextMate grammar object to the `langs` option.
```js
const customDiffLang = JSON.parse(readFileSync('./custom-diff.json', 'utf8'))
posthtml([
shiki({
langs: [customDiffLang]
})
])
.process(`
- FOO
+ BAR
`)
.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](https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-grammars) for examples.
### `themes`
Type: `Array | Array`\
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.
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: ['github-light', 'github-dark']
})
])
.process(`
[code]
[code]
`)
.then(result => result.html)
```
See the list of [available themes](https://shiki.style/themes) in Shiki.
> [!NOTE]
> If you don't specify a `theme=""` attribute, the first theme in the `themes` option will be used.
#### Custom Themes
You may also load [custom themes](https://shiki.style/guide/load-theme) by passing a TextMate theme object to the `themes` option:
```js
// Define textmate theme
const myTheme = {
name: 'my-theme',
settings: [
{
scope: ['string'],
settings: {
foreground: '#888'
}
},
]
}
posthtml([
shiki({
themes: [myTheme],
})
])
.process(`[code]`)
.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.
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
wrapTag: 'div'
})
])
.process('... your code')
.then(result => result.html)
```
Result:
```html
[highlighted code]
```
### `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.
```js
import posthtml from 'posthtml'
import shiki from 'posthtml-shiki'
posthtml([
shiki({
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: 'dark'
})
])
.process(`
[code]
`)
.then(result => result.html)
```
### `decorations`
Type: `array`\
Default: `[]`
Shiki's [Decorations](https://shiki.style/guide/decorations) are supported through the `decorations` option.
You can use this to wrap custom classes and attributes around character ranges in your code.
```js
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(`
const foo = 'bar'
`)
.then(result => result.html)
```
The word `const` will be wrapped in a `` tag.
### `transformers`
Type: `array`\
Default: `[]`
Use this option to transform the highlighted code block with Shiki's [Transformers](https://shiki.style/guide/transformers).
```js
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(`
const foo = 'bar'
let baz = 'biz' // [!code highlight]
`)
.then(result => result.html)
```
See the docs for [Shiki Transformers](https://shiki.style/guide/transformers) and a list of [common Shiki Transformers](https://shiki.style/packages/transformers).
[npm]: https://www.npmjs.com/package/posthtml-shiki
[npm-version-shield]: https://img.shields.io/npm/v/posthtml-shiki.svg
[npm-stats]: http://npm-stat.com/charts.html?package=posthtml-shiki
[npm-stats-shield]: https://img.shields.io/npm/dt/posthtml-shiki.svg
[github-ci]: https://github.com/posthtml/posthtml-shiki/actions/workflows/nodejs.yml
[github-ci-shield]: https://github.com/posthtml/posthtml-shiki/actions/workflows/nodejs.yml/badge.svg
[license]: ./LICENSE
[license-shield]: https://img.shields.io/npm/l/posthtml-shiki.svg