# Plugin Migration Guide from v5 to v6

Source: https://developers.inkdrop.app/appendix/plugin-migration-from-v5-to-v6

## New `ipm` CLI tool

In v6, there is a new CLI tool named `ipm` (Inkdrop Plugin Manager) for publishing plugins.

```sh
npm install -g @inkdropapp/ipm-cli

ipm configure
```

Check out [the repository](https://github.com/inkdropapp/ipm-cli) for more details.

## New TypeScript definitions

TypeScript definitions for Inkdrop v6 are now available at [@inkdropapp/types](https://github.com/inkdropapp/types). You can install them to get type-checking and autocompletion for the Inkdrop API in your plugin:

```sh
npm install --save-dev @inkdropapp/types
```

## `@electron/remote` is deprecated

### `remote.dialog` → `inkdrop.dialog`

Before:

```js
const remote = require('@electron/remote')
const { dialog } = remote

return dialog.showOpenDialog(
  inkdrop.window,
  {
  ...
})
```

After:

```js
return inkdrop.dialog.showOpenDialog({
  ...
})
```

> **Note:**
>
> The&#x20;
>
> `inkdrop.window`
>
> &#x20;argument is no longer required.

### `inkdrop.window.on()`

Before:

```js
inkdrop.window.on('focus', this.handleAppFocus)
```

After:

```js
const sub = inkdrop.window.onFocus(this.handleAppFocus)

// Unsubscribe
sub.dispose()
```

## `activate()` receives the `inkdrop` environment

In v6, the plugin's `activate()` function is called with the `inkdrop` environment instance as the first argument, followed by the persisted package state.

Before:

```js
module.exports = {
  activate() {
    inkdrop.components.registerClass(MyComponent)
  }
}
```

After:

```ts
import type { Environment } from '@inkdropapp/types'

module.exports = {
  activate(env: Environment) {
    env.components.registerClass(MyComponent)
  }
}
```

> **Note:**
>
> A future release will deprecate the global `inkdrop` reference for sandboxing,
> so please update your plugin to use the argument form. The global `inkdrop`
> reference still works for backward compatibility at the moment.

Since the `inkdrop` environment is now passed as an argument rather than read from the global scope, you need a way to share it across the other modules of your plugin. A common pattern is to capture the instance in a dedicated module that exposes a getter and setter:

```ts
import type { Environment } from '@inkdropapp/types'

/**
 * Captures the `Environment` instance handed to `activate()` so the plugin's
 * other modules can reach it without touching the (discouraged) global
 * `inkdrop` variable.
 */
let captured: Environment | undefined

export function setEnv(env: Environment | undefined): void {
  captured = env
}

export function getEnv(): Environment {
  if (!captured) {
    throw new Error('env accessed before activate()')
  }
  return captured
}
```

Then call `setEnv` from `activate()` so the rest of your plugin can retrieve the environment via `getEnv()`, and clear it in `deactivate()` to avoid holding a stale reference:

```ts
import type { Environment, IInkdropPlugin } from '@inkdropapp/types'
import { setEnv } from './env'

class YourPlugin implements IInkdropPlugin {
  activate(env: Environment) {
    setEnv(env)
    // initialize your plugin
  }

  deactivate(env: Environment) {
    // cleanup
    setEnv(undefined)
  }
}

export default new YourPlugin()
```

## `onEditorLoad` → `ensureEditorLoaded`

In v6, you can use `ensureEditorLoaded` instead of `onEditorLoad` to run code with the active editor.
`onEditorLoad` only fires when the editor component loads, so if the editor was already loaded by the time your plugin subscribed, you had to check for an active editor yourself and then also subscribe for future loads.
`ensureEditorLoaded` handles both cases: it invokes the callback immediately if an editor is already loaded, and otherwise waits for the next one to load.

Before:

```js
function extendEditor() {
  // ...
}

const editor = inkdrop.getActiveEditor()
if (editor) extendEditor()
inkdrop.onEditorLoad(() => {
  extendEditor()
})
```

After:

```js
inkdrop.ensureEditorLoaded((editor: EditorView) => {
  // extend the editor
})
```

You no longer have to do the same dance of checking for an already-active editor before subscribing.

## `inkdrop.main.dataStore.getLocalDB()` → `inkdrop.localDB`

`inkdrop.main` was a `remote` reference to the main process, and with it `inkdrop.main.dataStore` is gone. The local database is now an IPC proxy on the environment, with the same `notes` / `books` / `tags` / `files` / `utils` sub-objects and change events — see [Accessing the local database](https://developers.inkdrop.app/guides/access-the-local-database).

Before:

```js
const db = inkdrop.main.dataStore.getLocalDB()
const note = await db.notes.get('note:F8xUp-23G')
```

After:

```js
const db = inkdrop.localDB
const note = await db.notes.get('note:F8xUp-23G')
```

> **Note:**
>
> Returned documents are plain objects now, not Remote Objects, so
> `structuredClone()` is no longer needed. Direct access to the PouchDB instance
> (`inkdrop.main.dataStore.localPouch`) is not available to plugins.

### Changes in v6.2.0

Since v6.2.0, the local database uses a new SQLite-based search index, and `inkdrop.localDB` has changed with it. The types are updated in `@inkdropapp/types` 0.1.0.

- Query and search results are returned as `rows[].doc`, instead of `docs`. Search highlights are in `rows[].extra.fts`.
- Removed `utils.countNotesWithTag`. Use `notes.count({ tagId })` instead, which counts the notes listed for the tag, excluding those in the trash and those whose status is `'completed'` or `'dropped'`.
- Removed `notes.searchWithQuery`. Use `notes.findAllIdsInBook` and `notes.findAllIdsWithTag` instead of `utils.findAllNoteIdsInBook` and `utils.findAllNoteIdsWithTag`.
- `onFullTextIndexBuildStart/End/Error` are renamed to `onIndexBuildStart/Progress/End/Error`.
- `notes.get` also accepts an array of note IDs.
- Custom note list item views get search highlights from a new `highlights` prop, instead of `note._fts`.
  See [inkdrop-simple-note-list](https://github.com/inkdropapp/inkdrop-simple-note-list/commit/c3b8c5ec341334af8e278c259169933ee84af73d) for an example migration.
- Local HTTP server: `GET /notes` returns up to 50 notes by default when `limit` is omitted (it was 20).

## CSS selectors for keymaps and commands

- `.mde-preview` → `.mde-preview-container`

## LESS is deprecated

The necessity of LESS has been less and less over the years, since CSS supports nested selectors and variables.
In v6, the app has dropped support for LESS.

- Rename `.less` to `.css`

Before:

```less
@primary-color: #ff0000;
.my-plugin {
  .component {
    color: @primary-color;
  }
}
```

After:

```css
:root {
  --primary-color: #ff0000;
}
.my-plugin {
  .component {
    color: var(--primary-color);
  }
}
```

## Electron's `clipboard` is deprecated

Accessing the system clipboard through Electron's `clipboard` module is no longer supported. Use `env.clipboard` instead, which proxies the system clipboard over IPC.

Before:

```js
const { clipboard } = require('electron')

const text = clipboard.readText()
clipboard.writeText('Hello, Inkdrop!')
```

After:

```js
const text = env.clipboard.readText()
env.clipboard.writeText('Hello, Inkdrop!')
```

> **Note:**
>
> `env.clipboard` also provides `readHTML()`, `readImageAsDataURL()`, `writeImage(dataUrl)`, `availableFormats()`, `write({ text, html })`, and `saveAsImageAttachment()`. See the `clipboard` property on the [Environment](https://github.com/inkdropapp/types/blob/main/src/environment.d.ts) for details.

## Themes are now a single, unified type

In Inkdrop 5, a visual theme was up to **three separate packages** — a UI theme, a syntax theme, and a preview theme — each declaring `"theme": "ui" | "syntax" | "preview"` in its `package.json` and selected independently from three drop-downs.

Inkdrop 6 merges them into **one theme package**:

- `package.json` sets `"theme": true` — a **boolean**, not a string.
- It ships up to three stylesheets — `ui.css`, `syntax.css`, `preview.css` — one per area (app chrome, editor, preview), each wrapped in its own cascade layer.
- A single `themeAppearance: "light" | "dark"` describes the whole theme.
- Preferences → Themes shows **one Theme drop-down** instead of three.

Before (three packages):

```json
{
  "theme": "ui",
  "themeAppearance": "light",
  "styleSheets": ["theme.css"]
}
```

After (one package):

```json
{
  "theme": true,
  "themeAppearance": "light",
  "styleSheets": ["ui.css", "syntax.css", "preview.css"]
}
```

> **Note:**
>
> The old string values are **no longer recognized**. If a package still
> declares `"theme": "ui"` (or `"syntax"` / `"preview"`), Inkdrop logs a warning
> — *“\<name> does not support v6. Please update the theme.”* — and
> does **not** load or activate it. Convert the package to `"theme": true` to
> make it loadable again.

### Wrap each stylesheet in its cascade layer

The app no longer wraps your theme CSS in an `@layer` for you — your stylesheets are now injected **verbatim**. Wrap each one in the matching layer (`theme.ui`, `theme.syntax`, or `theme.preview`) so its overrides land in the right place in the cascade:

```css
@layer theme.ui {
  :root {
    --page-background: var(--color-neutral-50);
    --sidebar-background: var(--color-neutral-100);
  }
}
```

The app's built-in defaults live in the `theme.ui.base` / `theme.ui.components`, `theme.syntax.base`, and `theme.preview.base` **sub-layers**, and a layer's own rules outrank its sub-layers. So wrapping your overrides in `@layer theme.ui` (etc.) lets them win over the defaults reliably, without resorting to high-specificity selectors.

### Merging three v5 themes into one

If you maintained separate `-ui`, `-syntax`, and `-preview` packages, combine them into a single repository:

1. Keep **one** `package.json` with `"theme": true`, a single `themeAppearance`, and a `styleSheets` array.
2. Move each old package's CSS into `ui.css`, `syntax.css`, and `preview.css` respectively, each wrapped in its `@layer`.
3. If the three packages shared a color palette, keep those raw tokens in **one** shared file (e.g. `palette.css`, wrapped in `@layer theme`) listed first in `styleSheets`, and reference them from the three layer files via `var()`. Don't copy the color definitions into all three — define them once. See [Sharing a palette across the three stylesheets](https://developers.inkdrop.app/guides/create-a-theme#sharing-a-palette-across-the-three-stylesheets).
4. List the files in `styleSheets` (tokens first, then the layered stylesheets).

The official [Solarized](https://github.com/inkdropapp/solarized-light/) and [Kanagawa](https://github.com/inkdropapp/kanagawa-lotus/) themes were migrated exactly this way and make good references.

### Update the manifest and tooling

Bump the engine, the dev-helpers (they provide the v6 `dev-server` and `generate-palette` binaries), and add a `prepublishOnly` script so `palette.json` stays current:

Before:

```json
{
  "theme": "ui",
  "styleSheets": ["theme.css"],
  "devDependencies": {
    "@inkdropapp/theme-dev-helpers": "^0.3.6"
  },
  "engines": {
    "inkdrop": "^5.9.0"
  }
}
```

After:

```json
{
  "theme": true,
  "styleSheets": ["ui.css", "syntax.css", "preview.css"],
  "scripts": {
    "prepublishOnly": "generate-palette"
  },
  "devDependencies": {
    "@inkdropapp/theme-dev-helpers": "^0.6.0"
  },
  "engines": {
    "inkdrop": "^6.0.0"
  }
}
```

Like every other plugin, themes also [drop LESS for plain CSS](#less-is-deprecated).

### Use the v6 design tokens

The biggest thing to be aware of is that the shared color palette was overhauled. v6's [`@inkdropapp/css`](https://github.com/inkdropapp/css) (loaded before your theme) provides a set of **Tailwind-inspired design tokens** to build on instead of hard-coding colors:

- A neutral grayscale ramp, `--hsl-neutral-50` … `--hsl-neutral-950` (`50` = lightest, `950` = darkest)
- Accent families at the same scale — `blue`, `cyan`, `green`, `yellow`, `orange`, `red`, `violet`, `pink` — e.g. `--hsl-blue-500`, `--hsl-red-600`
- `--color-<family>-<step>` wrappers (e.g. `--color-neutral-50`) that resolve the matching `--hsl-*` triplet to an `hsl()` color

```css
:root {
  --page-background: var(--color-neutral-50);
  --text-color: var(--color-neutral-600);
  --primary-color: hsl(var(--hsl-blue-500) / 90%);
}
```

If your v5 theme referenced palette tokens that no longer exist, repoint them at the tokens above. (For example, Solarized Light dropped its old base16-style grayscale and semantic aliases in favor of the `neutral` ramp, and switched its `--hsl-magenta-*` references to `--hsl-pink-*`.) The full set of color tokens is published in the [`@inkdropapp/css`](https://github.com/inkdropapp/css) repository.

### Generate `palette.json`

All v6 themes ship a `palette.json`: a JSON dump of the theme's variables, used for theme previews and (in the future) the mobile app. The `prepublishOnly` script above runs the `generate-palette` binary from `@inkdropapp/theme-dev-helpers` on every publish, so you don't need to generate or commit the file by hand. To inspect it beforehand, run it yourself:

```sh
npx generate-palette
```

See [Creating a theme](https://developers.inkdrop.app/guides/create-a-theme#creating-your-first-theme) for the full dev loop (`ipm init --type theme`, `npx dev-server`, hot reloading, and the component preview).

## The UI stylesheet (`ui.css`)

This stylesheet styles the app chrome — sidebar, note list, toolbar, drop-downs, modals — by overriding **CSS variables** in `:root`. That model is unchanged in v6: you still set the same component variables (`--sidebar-background`, `--note-list-bar-background`, `--editor-background`, …). What changed is the [primitive color tokens](#use-the-v6-design-tokens) those variables build on.

The stock [Solarized Light](https://github.com/inkdropapp/solarized-light/) and [Solarized Dark](https://github.com/inkdropapp/solarized-dark/) themes have been updated for v6 and make good references.

### Override the built-in variables

The app defines named CSS variables for nearly every UI surface, and your theme overrides just the ones you want to change. For the full list, refer to the source CSS files in [`@inkdropapp/css`](https://github.com/inkdropapp/css), which the app loads before your theme:

- [`tokens.css`](https://github.com/inkdropapp/css/blob/main/tokens.css) — design tokens
- [`ui.css`](https://github.com/inkdropapp/css/blob/main/ui.css) — the main file that defines CSS variables for UI components (sidebar, note list, editor chrome, modals, …)
- [`status.css`](https://github.com/inkdropapp/css/blob/main/status.css) — note status colors (`--note-status-*`)
- [`tags.css`](https://github.com/inkdropapp/css/blob/main/tags.css) — tag colors (`--tag-*`)
- [`task-progress.css`](https://github.com/inkdropapp/css/blob/main/task-progress.css) — task progress view colors (`--task-progress-view-*`)

### Optional: recolor by overriding the ramps

If your theme's palette is shaped like Inkdrop's own design tokens — Tailwind-style color ramps with numeric shades — you can recolor the whole UI from one place by **redefining the primitive `--hsl-*` / `--color-*` ramps** in a token file wrapped in `@layer theme`, rather than repointing every component variable by hand. The theme authoring guide covers this in detail — see [recolor by overriding the ramps](https://developers.inkdrop.app/guides/create-a-theme#optional-recolor-by-overriding-the-ramps).

### Move preview styling out

If your v5 UI theme styled preview content directly (e.g. `.mde-preview h1`, `.mde-preview em`), move those rules into your theme's [preview stylesheet](#the-preview-stylesheet-preview-css) instead.
For any UI-level rule you keep, note that the container class was renamed `.mde-preview` → `.mde-preview-container` ([see above](#css-selectors-for-keymaps-and-commands)).

## The editor stylesheet (`syntax.css`)

This stylesheet styled **CodeMirror 5** in v5.
Inkdrop 6 runs on [CodeMirror 6](https://codemirror.net/), and the model shifts in two ways: the theme now (1) sets **CSS variables** instead of styling CodeMirror's DOM, and (2) sets **`--syntax-*` color variables** instead of writing per-token rules. The rules that paint the tokens — the CodeMirror 6 / [Lezer](https://lezer.codemirror.net/) `.tok-*` classes — now live in the shared [`@inkdropapp/css`](https://github.com/inkdropapp/css) package and read your variables. So a v6 editor stylesheet is a single `:root { … }` block (wrapped in `@layer theme.syntax`) with no selectors of its own.

The base syntax theme now lives in [`@inkdropapp/css`'s `syntax.css`](https://github.com/inkdropapp/css/blob/main/syntax.css): it defines every `--editor-*`, `--syntax-*`, and `--md-*` variable with a default value (the built-in light and dark themes merged via `light-dark()`). The app loads it before your theme, so you set only the variables you want to change.

### Editor chrome: DOM selectors → CSS variables

You no longer style CodeMirror's DOM. Replace the old `.CodeMirror-*` rules with the corresponding `--editor-*` variable, set in `:root`:

| v5 (CodeMirror 5)                          | v6 (CSS variable)                                                                 |
| ------------------------------------------ | --------------------------------------------------------------------------------- |
| `.CodeMirror { background }`               | `--editor-background-color`                                                       |
| `.CodeMirror { color }`                    | `--editor-foreground-color`                                                       |
| `.CodeMirror-cursor` border / fat cursor   | `--editor-caret-color`                                                            |
| `.CodeMirror-selected`                     | `--editor-selection-background`                                                   |
| `.CodeMirror-focused .CodeMirror-selected` | `--editor-focused-selection-background`                                           |
| `.CodeMirror-gutters`                      | `--editor-gutter-background-color`, `--editor-gutter-border-right`                |
| `.CodeMirror-linenumber`                   | `--editor-gutter-color`                                                           |
| `.CodeMirror-activeline-background`        | `--editor-active-line-background-color`                                           |
| `.CodeMirror-matchingbracket`              | `--editor-matching-bracket-outline`, `--editor-matching-bracket-background-color` |

See the full list of `--editor-*` and `--md-*` variables — with their default values — in [`@inkdropapp/css`'s `syntax.css`](https://github.com/inkdropapp/css/blob/main/syntax.css).

### Syntax tokens: `.cm-*` → `--syntax-*` variables

CodeMirror 5 mode tokens map to v6 highlight tokens — but instead of writing rules for them, you set one `--syntax-*` color variable per token. `@inkdropapp/css` applies each variable to the matching `.tok-*` class it emits under `.cm-editor` and `.mde-preview .codeblock` (the latter for rendered preview code blocks):

| v5 (CodeMirror 5) | v6 (`--syntax-*` variable)                                                |
| ----------------- | ------------------------------------------------------------------------- |
| `.cm-keyword`     | `--syntax-keyword-color`                                                  |
| `.cm-string`      | `--syntax-string-color`                                                   |
| `.cm-comment`     | `--syntax-comment-color`                                                  |
| `.cm-number`      | `--syntax-number-color`                                                   |
| `.cm-variable`    | `--syntax-variable-name-color`                                            |
| `.cm-def`         | `--syntax-name-definition-color`, `--syntax-variable-name-function-color` |
| `.cm-type`        | `--syntax-class-name-color`                                               |
| `.cm-operator`    | `--syntax-operator-color`                                                 |
| `.cm-tag`         | `--syntax-tag-name-color`                                                 |
| `.cm-attribute`   | `--syntax-attribute-name-color`                                           |
| `.cm-atom`        | `--syntax-atom-color`, `--syntax-bool-color`                              |
| `.cm-meta`        | `--syntax-meta-color`                                                     |

Before:

```less
.cm-s-default {
  .cm-keyword {
    color: #c678dd;
  }
  .cm-string {
    color: #98c379;
  }
}
```

After:

```css
@layer theme.syntax {
  :root {
    --syntax-keyword-color: #c678dd;
    --syntax-string-color: #98c379;
  }
}
```

A few conventions: each token also exposes `-font-style` / `-font-weight` / `-text-decoration` variants where relevant (`--syntax-comment-font-style`, `--syntax-heading-font-weight`, …); modifier variants default to their base token (`--syntax-name-function-color` falls back to `--syntax-name-color`); and any token you don't color falls back to `--editor-foreground-color`. [`@inkdropapp/css`'s `syntax.css`](https://github.com/inkdropapp/css/blob/main/syntax.css) defines a value for every token — copy it as a starting point.

> **Note:**
>
> The token catalog is finer-grained than CodeMirror 5's modes (separate
> variables for line vs. block comments, the six heading levels, each operator
> kind, and so on). If you need to style something the variables don't cover,
> you can still write an ordinary `.tok-*` rule scoped under
> `.cm-editor, .mde-preview .codeblock`.

## The preview stylesheet (`preview.css`)

This stylesheet styles the **rendered Markdown preview** — headings, body text, links, blockquotes, tables, code blocks, task lists, GFM alerts, and so on. This is the area that changed the most between v5 and v6.

In v5 a preview theme **shipped the entire preview stylesheet**: a fork of GitHub's `github-markdown.css` (usually with a separate `github-markdown-dark.css` for the dark UI), plus extra files such as `gfm-alerts.css` and `metadata-section.css`, often authored in LESS. Every rule hard-coded its own colors, so recoloring the preview meant editing hundreds of lines of CSS — and the theme had to be kept in sync with each change Inkdrop made to the preview markup.

In v6 those base styles are **embedded in the app**. Inkdrop ships them in [`@inkdropapp/css`'s `markdown.css`](https://github.com/inkdropapp/css/blob/main/markdown.css), loaded inside an `@layer theme.preview.base`, so your preview stylesheet no longer carries the full stylesheet — it only **overrides CSS variables**.

The base stylesheet drives every element through a family of `--mde-preview-*` variables, most of which default to the **active theme's** tokens (`--text-color`, `--border-color`, `--link-color`, …). So in v6 the preview matches the rest of your theme out of the box, and you set only the handful of `--mde-preview-*` variables you want to diverge on.

### Ship full stylesheets → override variables

This is the breaking change. Delete your forked GitHub-markdown rules and replace them with variable overrides on `:root`, inside `@layer theme.preview`. Anything you don't override falls back to the app's base styles (which track the rest of your theme).

Before (v5 — styling elements directly):

```css
.mde-preview {
  color: #333;
}
.mde-preview a {
  color: #4078c0;
}
.mde-preview code {
  background-color: rgba(27, 31, 35, 0.05);
}
.mde-preview blockquote {
  color: #777;
  border-left: 0.25em solid #ddd;
}
```

After (v6 — overriding variables):

```css
@layer theme.preview {
  :root {
    --mde-preview-link-color: hsl(var(--hsl-blue-500));
    --mde-preview-heading-color: hsl(var(--hsl-slate-900));
    --mde-preview-strong-color: hsl(var(--hsl-slate-900));
    --mde-preview-em-color: hsl(var(--hsl-slate-700));
    --mde-preview-inline-code-background-color: hsl(var(--hsl-slate-100));
    --mde-preview-blockquote-text-color: hsl(var(--hsl-slate-500));
    --mde-preview-blockquote-border-color: hsl(var(--hsl-slate-200));
  }
}
```

Reach for the shared [`@inkdropapp/css`](https://github.com/inkdropapp/css) palette (`--hsl-<family>-<scale>` / `--color-<family>-<scale>`) for values, the same way the UI and editor stylesheets do.

For non-color tweaks that no variable covers — font weight, letter-spacing, and the like — you can still write ordinary rules under `.mde-preview`:

```css
@layer theme.preview {
  .mde-preview {
    strong {
      font-weight: 800;
    }
    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
      letter-spacing: -0.02em;
    }
  }
}
```

The old `gfm-alerts.css` and `metadata-section.css` are now part of the embedded base, so you can drop them.

### The variables

The full list lives in the `:root` block of [`markdown.css`](https://github.com/inkdropapp/css/blob/main/markdown.css). The most commonly themed ones:

| Area                   | Variable(s)                                                                                                                                                |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Links                  | `--mde-preview-link-color`                                                                                                                                 |
| Headings               | `--mde-preview-heading-color`, `--mde-preview-heading-border-color`, `--mde-preview-heading-muted-text-color`                                              |
| Strong / emphasis      | `--mde-preview-strong-color`, `--mde-preview-em-color`                                                                                                     |
| Blockquotes            | `--mde-preview-blockquote-text-color`, `--mde-preview-blockquote-border-color`                                                                             |
| Inline code            | `--mde-preview-inline-code-text-color`, `--mde-preview-inline-code-background-color`, `--mde-preview-inline-code-border-color` / `-border-width`           |
| Code blocks            | `--mde-preview-codeblock-background-color`, `--mde-preview-codeblock-border-color`, `--mde-preview-codeblock-text-color`, `--mde-preview-codeblock-meta-*` |
| Tables                 | `--mde-preview-table-head-*`, `--mde-preview-table-border-color`, `--mde-preview-table-row-*`                                                              |
| Highlight (`==mark==`) | `--mde-preview-inline-mark-text-color`, `--mde-preview-inline-mark-background-color`, `--mde-preview-inline-mark-underline-color`                          |
| Task lists             | `--mde-preview-task-list-checked-color`                                                                                                                    |
| Footnotes              | `--mde-preview-footnote-*`                                                                                                                                 |
| Keyboard keys          | `--mde-preview-kbd-*`                                                                                                                                      |
| GFM alerts             | `--gfm-alert-note`, `--gfm-alert-tip`, `--gfm-alert-important`, `--gfm-alert-warning`, `--gfm-alert-caution`                                               |

### Code blocks: frame vs. tokens

The preview stylesheet styles the code block **frame** — background, border, and the meta/title bar — through the `--mde-preview-codeblock-*` variables above. The **syntax highlighting inside** the block (the `.tok-*` classes under `.mde-preview .codeblock`) is driven by your theme's [editor stylesheet](#the-editor-stylesheet-syntax-css), not the preview stylesheet.

> **Note:**
>
> Override the `--mde-preview-*` variables on `:root` by
> default, the same as the UI and editor stylesheets. The
> `.mde-preview` content root is only for the extra rules that the
> variables don't cover — don't confuse it with
> `.mde-preview-container`, the outer wrapper renamed for [keymap and
> command scoping](#css-selectors-for-keymaps-and-commands).

### Mermaid diagram colors

In v5, diagrams rendered by the [mermaid](https://github.com/inkdropapp/inkdrop-mermaid) plugin had their own `theme` / `themeCSS` / `themeVariables` plugin settings, separate from your theme's stylesheet — matching a diagram's colors to your theme meant configuring the plugin on top of it, if at all.

That plugin setting is gone in v6. Diagrams now always render with a built-in theme driven by CSS variables: `@inkdropapp/css`'s [`mermaid.css`](https://github.com/inkdropapp/css/blob/main/mermaid.css) defines a `--mermaid-*` token for every color Mermaid can draw, layered the same way as the rest of the preview — you override the ones you care about in `preview.css`, and everything else falls back to defaults that already track `--text-color`, `--border-color`, and the rest of your palette. See [Diagram colors (Mermaid)](https://developers.inkdrop.app/guides/create-a-theme#diagram-colors-mermaid) in the theme guide for the token list and an example.

If your v5 theme shipped custom Mermaid CSS or documented the plugin's theme option for users, drop it — it no longer applies, and the `--mermaid-*` variables replace it.

See [Creating a theme](https://developers.inkdrop.app/guides/create-a-theme#creating-your-first-theme) for the full dev loop (`ipm init --type theme`, `ipm link --dev`, and hot reloading).
