Кастомные темы подсветки

Библиотеки Remark и Rehype предоставляют мощный механизм для работы с Markdown и HTML на уровне AST (Abstract Syntax Tree), что позволяет гибко настраивать обработку контента. Подсветка синтаксиса в кодовых блоках является одной из ключевых задач при визуализации документации, блогов и статей. Кастомные темы подсветки дают возможность создавать уникальный стиль отображения кода, согласованный с дизайном сайта или приложения.

Интеграция синтаксиса с Rehype

Для подсветки кода часто используется связка Remark → Rehype → rehype-prism или rehype-highlight. Принцип работы основан на трансформации AST Markdown в AST HTML с последующей обработкой узлов <code> и <pre>.

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeHighlight from 'rehype-highlight';
import fs from 'fs';

const markdown = fs.readFileSync('example.md', 'utf-8');

const result = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeHighlight, { subset: ['javascript', 'css'] })
  .use(rehypeStringify)
  .process(markdown);

console.log(result.toString());

В этом примере rehype-highlight автоматически применяет встроенные темы подсветки. Для кастомных стилей требуется подключить CSS с определёнными классами для токенов.

Структура CSS для кастомной темы

Токены синтаксиса обычно получают классы вида .token.keyword, .token.string, .token.comment. Основная задача кастомной темы — задать визуальные свойства этих классов:

pre code {
  display: block;
  padding: 1em;
  background-color: #1e1e2f;
  color: #dcdcdc;
  border-radius: 6px;
  overflow-x: auto;
}

.token.keyword { color: #ff5370; font-weight: bold; }
.token.string { color: #c3e88d; }
.token.comment { color: #6272a4; font-style: italic; }
.token.function { color: #82aaff; }
.token.variable { color: #f78c6c; }

CSS можно расширять, добавляя эффекты для идентификаторов, операторов и литералов, что позволяет создавать полное соответствие фирменному стилю.

Использование rehype-prism с кастомными темами

rehype-prism-plus предоставляет более детализированные возможности, включая поддержку плагинов для подсветки: линии, номера строк и расширенные темы.

import rehypePrism from 'rehype-prism-plus';
import 'prismjs/themes/prism-okaidia.css'; // Базовая тема

const result = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypePrism, { plugins: ['line-numbers'] })
  .use(rehypeStringify)
  .process(markdown);

Чтобы кастомизировать тему, можно заменить стандартный CSS-файл на собственный, сохранив имена классов токенов или расширив их через CSS-переменные.

Создание динамических тем

Для более сложных проектов создают динамические темы с помощью CSS-in-JS или препроцессоров:

import { css } from '@emotion/css';

const codeTheme = css`
  pre code {
    background-color: ${darkMode ? '#282a36' : '#f5f5f5'};
    color: ${darkMode ? '#f8f8f2' : '#333'};
  }
  .token.keyword { color: ${darkMode ? '#ff79c6' : '#d73a49'}; }
  .token.string { color: ${darkMode ? '#f1fa8c' : '#032f62'}; }
`;

Использование таких подходов позволяет менять тему подсветки на лету, например, при переключении пользовательского интерфейса между светлым и тёмным режимом.

Настройка пользовательских токенов

Remark и Rehype позволяют обрабатывать узлы AST до передачи их на подсветку. Можно создать плагин, который добавляет новые типы токенов или модифицирует существующие:

function rehypeCustomTokens() {
  return (tree) => {
    visit(tree, 'element', (node) => {
      if (node.tagName === 'code' && node.properties.className?.includes('language-js')) {
        node.children.forEach((child) => {
          if (child.type === 'text' && child.value.includes('TODO')) {
            child.type = 'element';
            child.tagName = 'span';
            child.properties = { className: ['token', 'todo'] };
          }
        });
      }
    });
  };
}

После этого можно задать стиль для .token.todo, выделяя определённые комментарии или ключевые слова по своему вкусу.

Лайфхаки для крупных проектов

  • Использовать subset языков для ускорения сборки, если подсветка нужна только для определённых языков.
  • Комбинировать rehype-highlight и кастомный CSS, если необходимо быстро внедрить визуальные изменения.
  • Разделять темы на базовую и дополнительные слои: базовая для всех блоков, дополнительные для спецэффектов (TODO, FIXME, warnings).
  • Применять CSS-переменные для динамических цветов и шрифтов, что упрощает масштабирование темы на весь проект.

Примеры применения

  • Документация библиотек с различными языками, где каждая секция кода получает уникальную визуализацию.
  • Блоги с возможностью переключения между тёмной и светлой темой без перезагрузки страницы.
  • Образовательные платформы, где ключевые элементы кода (например, ошибки или подсказки) подсвечиваются отдельным стилем.

Кастомные темы в Remark и Rehype позволяют создавать гибкую и выразительную подсветку, полностью контролируемую через CSS и плагины AST, что делает библиотеку мощным инструментом для любой веб-документации и платформ, работающих с Markdown и HTML.