Shiki Plugin
The Shiki plugin provides enhanced syntax highlighting capabilities using Shiki transformers. It enables advanced code block features like callouts, which allow you to highlight and annotate specific lines in code examples.
Features
- Line Callouts - Highlight specific lines with custom styling
- Shiki Integration - Built on the powerful Shiki syntax highlighter
- Transformer Architecture - Extensible system for code transformations
- Theme Support - Works with your site’s theme configuration
Overview
The Shiki plugin exports transformers that enhance code block rendering. The primary transformer is createTransformerCallouts, which enables line-level annotations in code blocks.
Installation
The plugin is built into Doom and uses Shiki’s transformer system:
import { createTransformerCallouts } from '@alauda/doom/plugins/shiki'
The callouts transformer allows you to highlight and annotate specific lines in code blocks, perfect for drawing attention to important code or explaining changes.
Configuration
import { createTransformerCallouts } from '@alauda/doom/plugins/shiki'
import { defineConfig } from '@alauda/doom'
export default defineConfig({
markdown: {
shiki: {
transformers: [
createTransformerCallouts({
classActiveLine: 'callout',
classActivePre: 'has-callouts'
})
]
}
}
})
Options
classActiveLine
string
default:"'callout'"
CSS class applied to highlighted lines.
classActivePre
string
default:"'has-callouts'"
CSS class applied to the code block container when callouts are present.
Usage
Basic Callouts
Highlight specific lines using the [!code callout] notation:
```typescript
function greet(name: string) {
console.log(`Hello, ${name}!`) // [!code callout]
return name
}
```
The line with the callout comment will be highlighted:
function greet(name: string) {
console.log(`Hello, ${name}!`) // [!code callout]
return name
}
Multiple Callouts
Highlight multiple lines in the same block:
```typescript
class UserManager {
private users: User[] = [] // [!code callout]
addUser(user: User) {
this.users.push(user) // [!code callout]
}
}
```
Combined with Other Notations
Callouts work alongside other code block features:
```typescript title="user-service.ts"
interface User {
id: string
name: string // [!code callout]
email: string // [!code callout]
}
export class UserService {
async createUser(data: User) { // [!code callout]
// Implementation
}
}
```
Implementation
The callouts transformer uses Shiki’s transformerNotationMap under the hood:
packages/doom/src/plugins/shiki/transformers/callouts.ts:9-24
export const createTransformerCallouts = (
options: ITransformerCalloutsOptions = {},
): ShikiTransformer => {
const { classActiveLine = 'callout', classActivePre = 'has-callouts' } =
options
return transformerNotationMap(
{
classMap: {
callout: classActiveLine,
},
classActivePre,
},
'shiki-transformer:callouts',
)
}
How It Works
- Parse - Shiki parses code blocks and identifies callout notation
- Transform - The transformer adds CSS classes to marked lines
- Style - CSS rules style the highlighted lines based on the classes
CSS Classes
The transformer adds two types of classes:
- Line Class (
classActiveLine) - Applied to each highlighted line
- Container Class (
classActivePre) - Applied to the <pre> element when callouts exist
Styling Callouts
Customize callout appearance with CSS:
/* Basic callout styling */
.callout {
background-color: rgba(255, 255, 0, 0.1);
border-left: 3px solid #fbbf24;
padding-left: 0.5rem;
}
/* Dark mode adjustments */
.dark .callout {
background-color: rgba(255, 255, 0, 0.05);
}
/* Style the container */
.has-callouts {
position: relative;
}
Advanced Styling
Create more sophisticated callout styles:
.callout {
position: relative;
background: linear-gradient(
90deg,
rgba(59, 130, 246, 0.1) 0%,
transparent 100%
);
border-left: 2px solid #3b82f6;
margin-left: -1rem;
padding-left: 1rem;
}
.callout::before {
content: '→';
position: absolute;
left: 0.25rem;
color: #3b82f6;
font-weight: bold;
}
Use Cases
Highlighting Key Changes
Show what changed in a code example:
```typescript
function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => {
return sum + (item.price * item.quantity) // [!code callout]
}, 0)
}
```
Emphasizing Important Lines
Draw attention to critical code:
```typescript
async function saveData(data: Data) {
try {
await db.save(data)
} catch (error) {
logger.error('Failed to save:', error) // [!code callout]
throw error // [!code callout]
}
}
```
Tutorial Steps
Guide users through multi-step code:
First, import the required modules:
```typescript
import { createServer } from 'http' // [!code callout]
import { parse } from 'url'
```
Then create the server:
```typescript
const server = createServer((req, res) => { // [!code callout]
const { pathname } = parse(req.url || '')
// Handle request
})
```
Error Examples
Show problematic code:
```typescript
// ❌ Bad: No error handling
const data = await fetch(url) // [!code callout]
const json = await data.json() // [!code callout]
```
```typescript
// ✅ Good: Proper error handling
try {
const data = await fetch(url)
const json = await data.json()
} catch (error) { // [!code callout]
handleError(error) // [!code callout]
}
```
Extend the Shiki plugin with custom transformers:
import type { ShikiTransformer } from 'shiki'
export const createCustomTransformer = (): ShikiTransformer => {
return {
name: 'my-custom-transformer',
preprocess(code, options) {
// Modify code before highlighting
return code
},
line(node, line) {
// Transform individual lines
},
span(node, line, col) {
// Transform individual tokens
}
}
}
Then add it to your configuration:
export default defineConfig({
markdown: {
shiki: {
transformers: [
createTransformerCallouts(),
createCustomTransformer()
]
}
}
})
Best Practices
- Use Sparingly - Only highlight truly important lines
- Be Consistent - Use callouts for similar purposes across your docs
- Combine with Text - Explain why lines are highlighted in surrounding text
- Test Themes - Verify callouts work in both light and dark modes
- Accessibility - Ensure sufficient contrast for highlighted lines
Comparison with Other Features
vs. Line Highlighting
Standard line highlighting uses line numbers:
```typescript {2,5}
function example() {
const a = 1 // Line 2 highlighted
const b = 2
const c = 3
return a + b + c // Line 5 highlighted
}
```
Callouts use inline notation:
```typescript
function example() {
const a = 1 // [!code callout]
const b = 2
const c = 3
return a + b + c // [!code callout]
}
```
Regular comments:
// This is important
const value = calculate()
Callouts visually emphasize:
const value = calculate() // [!code callout]
Shiki transformers provide a powerful plugin system:
interface ShikiTransformer {
name: string
preprocess?(code: string, options: any): string
line?(node: Element, line: number): void
span?(node: Element, line: number, col: number): void
postprocess?(html: string): string
}
- Preprocess - Modify raw code before tokenization
- Tokenize - Shiki parses and highlights code
- Line Transform - Transform each line element
- Span Transform - Transform each token element
- Postprocess - Modify final HTML output
Troubleshooting
Callouts Not Working
If callouts aren’t being highlighted:
- Verify transformer is configured in
shiki.transformers
- Check notation syntax:
// [!code callout] exactly
- Ensure CSS classes are defined
- Test with different themes
Styling Issues
If callouts look wrong:
- Inspect elements to verify classes are applied
- Check CSS specificity conflicts
- Test in different color modes
- Review inherited styles from theme
If builds are slow:
- Limit number of transformers
- Avoid complex preprocessing
- Use caching where possible
- Profile build times