Skip to content
On This Page

Customization

Doctrine has two extension points: a stylesheet for appearance and a React component map for MDX. Both are project-level customization. See MDX components for the built-in component reference.

Add a stylesheet

Set styles to an existing file resolved from the project root:

TypeScript
import { defineConfig } from '@amamo/doctrine'

export default defineConfig({
  styles: './docs/theme.css',
})

Doctrine imports this file after its precompiled runtime CSS. Override only what the site needs:

CSS
:root {
  --doctrine-accent: oklch(0.9 0.05 155);
  --doctrine-accent-foreground: oklch(0.25 0.04 155);
  --doctrine-separator: oklch(0.88 0.025 155);
  --doctrine-sidebar: oklch(0.97 0.01 155);
  --doctrine-ring: oklch(0.48 0.12 155);
  --doctrine-radius: 0.9rem;
  --doctrine-font-sans: Inter, sans-serif;
  --doctrine-font-mono: 'JetBrains Mono', monospace;
  --doctrine-content-width: 52rem;
}

[data-theme='dark'] {
  --doctrine-background: oklch(0.14 0.02 250);
  --doctrine-muted: oklch(0.21 0.02 250);
}

Theme variables

VariableControls
--doctrine-background, --doctrine-foregroundPage background and primary text
--doctrine-card, --doctrine-card-foregroundRaised card surfaces
--doctrine-popover, --doctrine-popover-foregroundMenus, selects, and dialogs
--doctrine-primary, --doctrine-primary-foregroundPrimary actions and links
--doctrine-secondary, --doctrine-secondary-foregroundSecondary surfaces
--doctrine-muted, --doctrine-muted-foregroundMuted surfaces and secondary text
--doctrine-accent, --doctrine-accent-foregroundHovered and selected controls
--doctrine-border, --doctrine-separatorStrong borders and subtle separators
--doctrine-input, --doctrine-ringInput boundaries and keyboard focus
--doctrine-sidebar, --doctrine-sidebar-foregroundNavigation surface and text
--doctrine-sidebar-accent, --doctrine-sidebar-accent-foregroundNavigation hover and selection
--doctrine-code, --doctrine-code-foregroundCode block surface and text
--doctrine-radiusShared surface radius
--doctrine-font-sans, --doctrine-font-displayUI, body, and heading font stacks
--doctrine-font-monoCode font stack
--doctrine-content-widthMaximum article width
--doctrine-sidebar-width, --doctrine-toc-widthDesktop navigation and TOC widths
--doctrine-header-heightSticky header height

Put light values in :root and dark overrides in [data-theme='dark'].

Target stable layout slots

Use data-slot instead of depending on generated utility classes:

CSS
[data-slot='header'] {
  box-shadow: 0 1px 0 color-mix(in oklab, var(--doctrine-ring) 25%, transparent);
}

[data-slot='brand'] {
  color: var(--doctrine-accent);
}

[data-slot='content'] {
  letter-spacing: 0.005em;
}

The shell exposes header, header-inner, brand, navigation, sidebar, main, content, page-actions-row, page-actions, page-actions-copy, page-actions-menu-trigger, page-actions-menu, page-actions-markdown, page-actions-source, page-navigation, and footer. Built-ins expose badge, callout, card, card-grid, code-block, code-block-header, code-block-filename, code-block-language, code-block-copy, file-tree, file-tree-list, file-tree-folder, file-tree-file, live-preview, live-preview-title, live-preview-canvas, live-preview-code, live-preview-code-toggle, live-preview-source, step, steps, table-scroll, tabs, tab-list, tab, and tab-panel.

Use Tailwind CSS

Doctrine's own interface is already compiled, so consumers do not need Tailwind. To use utilities inside MDX or custom components, declare Tailwind in the documentation project's root manifest:

Shell
pnpm add -D tailwindcss

Then opt the configured stylesheet into Tailwind and state what it should scan:

CSS
@import 'tailwindcss' source(none);
@source './';

Doctrine checks the root package.json dependencies, dev dependencies, and optional dependencies. When one declares tailwindcss, it enables @tailwindcss/vite in both commands. Without that declaration, styles remains ordinary CSS.

Register or override components

Set components to a module with a default component map. Custom entries merge after the built-ins, so equal names replace them:

TSX
import type { IDoctrineComponents } from '@amamo/doctrine'
import type { ComponentProps } from 'react'
import { Callout } from '@amamo/doctrine/components'

function BrandedCallout(props: ComponentProps<typeof Callout>) {
  return <Callout className="branded-callout" {...props} />
}

export default { Callout: BrandedCallout } satisfies IDoctrineComponents
TypeScript
export default defineConfig({
  components: './docs/components.tsx',
})

Define .branded-callout in styles, or use utilities after enabling Tailwind. Registering a replaces Doctrine's base-aware link component, so the replacement must preserve any desired subpath and external-link behavior.

Custom modules are executable in both Node.js SSR and the browser. Keep module initialization and rendering free of browser-only globals; use effects or event handlers for browser access.

copyright © 2026 白熱。