Skip to main content

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'

Callouts Transformer

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

  1. Parse - Shiki parses code blocks and identifies callout notation
  2. Transform - The transformer adds CSS classes to marked lines
  3. 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]
}
```

Creating Custom Transformers

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

  1. Use Sparingly - Only highlight truly important lines
  2. Be Consistent - Use callouts for similar purposes across your docs
  3. Combine with Text - Explain why lines are highlighted in surrounding text
  4. Test Themes - Verify callouts work in both light and dark modes
  5. 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]
}
```

vs. Comments

Regular comments:
// This is important
const value = calculate()
Callouts visually emphasize:
const value = calculate() // [!code callout]

Transformer Architecture

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
}

Transform Lifecycle

  1. Preprocess - Modify raw code before tokenization
  2. Tokenize - Shiki parses and highlights code
  3. Line Transform - Transform each line element
  4. Span Transform - Transform each token element
  5. Postprocess - Modify final HTML output

Troubleshooting

Callouts Not Working

If callouts aren’t being highlighted:
  1. Verify transformer is configured in shiki.transformers
  2. Check notation syntax: // [!code callout] exactly
  3. Ensure CSS classes are defined
  4. Test with different themes

Styling Issues

If callouts look wrong:
  1. Inspect elements to verify classes are applied
  2. Check CSS specificity conflicts
  3. Test in different color modes
  4. Review inherited styles from theme

Performance Concerns

If builds are slow:
  1. Limit number of transformers
  2. Avoid complex preprocessing
  3. Use caching where possible
  4. Profile build times

Build docs developers (and LLMs) love