Extending Markdown
Remark and rehype plugins
@domphy/press processes Markdown through remark and rehype. Add plugins in press.config.ts:
// press.config.ts
import { defineConfig } from "@domphy/press"
import remarkGfm from "remark-gfm"
import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import rehypeAutolinkHeadings from "rehype-autolink-headings"
export default defineConfig({
markdown: {
remarkPlugins: [
remarkGfm,
remarkMath,
],
rehypePlugins: [
rehypeKatex,
[rehypeAutolinkHeadings, { behavior: "wrap" }],
],
},
})Math rendering
With remark-math + rehype-katex, inline and block math works out of the box:
Inline math: $E = mc^2$
Block math:
$$
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
$$Add the KaTeX CSS to your HTML:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css">Custom code block renderer
Replace the default code block with a custom component — e.g., add a copy button:
// press.config.ts
import { defineConfig } from "@domphy/press"
import { CodeBlock } from "./src/CodeBlock"
export default defineConfig({
markdown: {
components: {
// Replace <pre><code> with custom component
pre: CodeBlock,
},
},
})// src/CodeBlock.ts
import { button, tooltip } from "@domphy/ui"
import { toState } from "@domphy/core"
export function CodeBlock({ code, lang }: { code: string; lang: string }) {
const copied = toState(false)
function copy() {
navigator.clipboard.writeText(code)
copied.set(true)
setTimeout(() => copied.set(false), 2000)
}
return {
div: [
{
pre: { code },
class: `language-${lang}`,
},
{
button: (l) => copied.get(l) ? "✓ Copied" : "Copy",
onClick: copy,
$: [button({ variant: "ghost" }), tooltip({ content: "Copy code" })],
style: { position: "absolute", top: "8px", right: "8px" },
},
],
style: { position: "relative" },
}
}Custom directive syntax
Install remark-directive to add custom block/inline directives:
// press.config.ts
import remarkDirective from "remark-directive"
import { remarkDomphyDirectives } from "./src/directives"
export default defineConfig({
markdown: {
remarkPlugins: [remarkDirective, remarkDomphyDirectives],
},
}):::tip
This is a tip callout.
:::
:::warning
Watch out for this edge case.
:::
::badge[New]{type="success"}Implement the directive handler:
// src/directives.ts
import type { Plugin } from "unified"
import { visit } from "unist-util-visit"
export const remarkDomphyDirectives: Plugin = () => (tree) => {
visit(tree, (node: any) => {
if (node.type === "containerDirective") {
const type = node.name // "tip", "warning", "danger", "info"
node.type = "html"
node.value = `<div class="callout callout-${type}">${nodeToHtml(node)}</div>`
}
})
}Frontmatter
Every Markdown file can have YAML frontmatter — access it in layouts:
---
title: "My Page"
description: "A short description."
date: 2024-01-15
tags: [guide, advanced]
---
# Content starts hereAccess in custom layout:
// press.config.ts
export default defineConfig({
theme: {
layout: ({ frontmatter, content }) => ({
article: [
{
header: [
{ h1: frontmatter.title },
{ p: frontmatter.description },
],
},
{ div: content },
frontmatter.tags?.length
? { div: frontmatter.tags.map((tag: string) => ({ span: tag, class: "tag" })) }
: null,
].filter(Boolean),
}),
},
})Syntax highlighting themes
Switch the Shiki syntax highlighting theme:
export default defineConfig({
markdown: {
highlight: {
theme: {
light: "github-light",
dark: "github-dark",
},
// Or a single theme:
// theme: "nord"
},
},
})Available themes: github-light, github-dark, monokai, one-dark-pro, dracula, solarized-light, material-theme, catppuccin-latte, etc. Full list at the Shiki docs.