remark-dgmo
v0.3.5
Published
Remark plugin to render DGMO diagrams from fenced code blocks at build time. Framework-agnostic core shared by astro-dgmo, docusaurus-plugin-dgmo, and any unified pipeline.
Downloads
2,594
Maintainers
Readme
remark-dgmo
Framework-agnostic remark plugin that renders DGMO diagrams from ```dgmo fenced code blocks at build time. Powered by @diagrammo/dgmo. Zero client JavaScript by default.
sequence
Client -POST /login-> API
API -validate-> Auth
Auth -JWT-> API
API -200 OK-> ClientDrop a fenced block with the language dgmo into any markdown or MDX file processed by a unified-style pipeline — Astro, Docusaurus, Starlight, Vitepress, eleventy-with-remark, or your own custom toolchain — and it becomes an inline <svg> at build time.
By default, every diagram is rendered twice (once with the palette's light mode, once with its dark mode) and wrapped in <div class="dgmo-light"> / <div class="dgmo-dark">. A tiny shipped stylesheet hides the wrong one based on [data-theme="dark"] (the convention used by Docusaurus, Starlight, and most other docs frameworks). The result: your diagrams follow the host page's color-mode toggle without any client-side rendering.
Install
pnpm add remark-dgmo @diagrammo/dgmo
# or
npm install remark-dgmo @diagrammo/dgmo@diagrammo/dgmo is a peer dependency.
ESM-only. Your config file must be .mjs, .ts, or .mts — or your package.json must have "type": "module".
Use — three integration patterns
Pattern 1: Astro
Use astro-dgmo — it wraps this plugin and handles the integration plumbing.
pnpm add astro-dgmo @diagrammo/dgmo// astro.config.mjs
import { defineConfig } from 'astro/config';
import dgmo from 'astro-dgmo';
export default defineConfig({
integrations: [dgmo()],
});You'll also need to import the color-mode stylesheet in your global layout:
---
// src/layouts/Base.astro
import 'remark-dgmo/client.css';
---Pattern 2: Docusaurus
Use docusaurus-plugin-dgmo — it handles getClientModules() registration for the CSS + client script.
pnpm add docusaurus-plugin-dgmo @diagrammo/dgmo// docusaurus.config.ts
import type { Config } from '@docusaurus/types';
const config: Config = {
// …
plugins: ['docusaurus-plugin-dgmo'],
presets: [
[
'classic',
{
docs: {
remarkPlugins: [
(await import('docusaurus-plugin-dgmo/remark')).default,
],
},
blog: {
remarkPlugins: [
(await import('docusaurus-plugin-dgmo/remark')).default,
],
},
pages: {
remarkPlugins: [
(await import('docusaurus-plugin-dgmo/remark')).default,
],
},
},
],
],
};
export default config;The plugin registers client.css + client.js via getClientModules(). You still wire remarkPlugins into each preset slot manually — Docusaurus's plugin API has no hook to auto-inject into a sibling preset.
Pattern 3: Fumadocs (Next.js app router)
Use fumadocs-dgmo — it wraps mdxOptions for fumadocs-mdx, ships a .dark-rewritten stylesheet (Fumadocs UI's next-themes default), and provides a Client Component that re-binds on every soft navigation.
pnpm add fumadocs-dgmo @diagrammo/dgmo// source.config.ts
import { defineConfig } from 'fumadocs-mdx/config';
import { withDgmo } from 'fumadocs-dgmo/config';
export default defineConfig({
mdxOptions: withDgmo(),
});/* app/global.css */
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-dgmo/client.css';// app/layout.tsx — add <DgmoClient /> inside <RootProvider>
import { DgmoClient } from 'fumadocs-dgmo/client';
// …
<RootProvider>
{children}
<DgmoClient />
</RootProvider>;Pattern 4: Vanilla unified pipeline
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import remarkDgmo from 'remark-dgmo';
const out = await unified()
.use(remarkParse)
.use(remarkDgmo, { mode: 'showcase', palette: 'dracula' })
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeStringify, { allowDangerousHtml: true })
.process(source);In your output HTML's <head>, add the shipped stylesheet (or inline its three rules):
<link
rel="stylesheet"
href="/path/to/node_modules/remark-dgmo/dist/client.css"
/>
<script
type="module"
src="/path/to/node_modules/remark-dgmo/dist/client.js"
></script>The client script is optional — it tightens each diagram's viewBox to its content bounds and wires up showcase-mode copy buttons. Without it, diagrams still render but may have extra whitespace and copy buttons won't function.
Options
remarkDgmo({
// Output mode for `dgmo` blocks. 'diagram' (default) = SVG only.
// 'showcase' = syntax-highlighted source + diagram + copy + open-in-editor.
mode: 'diagram',
// Default palette name (any registered @diagrammo/dgmo palette).
palette: 'nord',
// Color-mode strategy. 'auto' (default) renders both light and dark and
// toggles via CSS. 'light' or 'dark' single-renders with the matching theme.
colorMode: 'auto',
// Default theme when colorMode is 'light' or 'dark' (single-render). Ignored under 'auto'.
theme: 'dark',
// Showcase chrome — enabled automatically in showcase mode.
showSource: undefined, // boolean; default = (mode === 'showcase')
showCopy: undefined, // boolean; default = (mode === 'showcase')
showOpenInEditor: undefined, // boolean; default = (mode === 'showcase')
// Where the "Open in editor" link points.
editorBaseUrl: 'https://online.diagrammo.app',
// Outer wrapper element + class hook.
wrapper: 'figure',
className: 'dgmo',
// Append additional class names to every emitted wrapper. Used by
// astro-dgmo v0.3.0 to keep the legacy `astro-dgmo*` class names for one
// minor cycle of backward compat.
legacyClassNames: [],
// Emit MDX-compatible output. Default: false (raw `html` mdast node).
// Set to true when the host pipeline routes files through @mdx-js/mdx —
// Docusaurus with `markdown.format: 'mdx'`, Astro `.mdx`, Fumadocs, etc.
// The plugin then emits an `mdxJsxFlowElement` instead of an `html` node,
// so MDX accepts the output without "Cannot handle unknown node `raw`".
mdx: false,
});Per-block overrides
Append options to the fence info string. Tokens are space-separated; values may be quoted.
```dgmo showcase title="Login flow" palette=catppuccin theme=light
sequence
A -> B
```| Token | Effect |
| ------------------------------------------------------- | ----------------------------------- |
| diagram / showcase | Set mode for this block |
| palette=<name> | Override palette |
| theme=light / theme=dark / theme=transparent | Override theme (single-render only) |
| colorMode=auto / colorMode=light / colorMode=dark | Override color-mode strategy |
| title="…" | Add a caption (<figcaption>) |
| source / noSource | Force source listing on/off |
| copy / noCopy | Force copy button on/off |
| openInEditor / noOpenInEditor | Force editor link on/off |
Working reference site
For an end-to-end example of remark-dgmo running inside a real
framework, see docusaurus-plugin-dgmo's tests/fixture/
— a minimal Docusaurus 3 site that wires this plugin into every preset
slot and exercises plain, tagged, showcase, and per-block-override
blocks. The astro-dgmo repo has an equivalent Astro 6 fixture at
tests/fixture/;
the fumadocs-dgmo repo has a Next.js app-router fixture at
tests/fixture/.
All three fixtures pin to link:../.. against the wrapper plugin's
source, so they're the canonical reference for the smallest correct
config — including the non-obvious gotchas (Docusaurus's async-function
default export + markdown: { format: 'md' }, Astro's manual import
'remark-dgmo/client.css', Fumadocs's mdx: true requirement and
html.dark selector mapping).
Custom color-mode selector
The shipped client.css keys on [data-theme="dark"] — the convention used by Docusaurus and Starlight. For Tailwind-style sites that signal dark mode via a .dark class on <html> (which is also what Fumadocs UI's next-themes default produces), don't import client.css directly — fumadocs-dgmo/client.css ships a build-time rewrite for that case. For any other custom selector, inline these three rules in your own CSS instead, swapping the selector:
.dgmo-dark {
display: none;
}
html.dark .dgmo-light {
display: none;
}
html.dark .dgmo-dark {
display: block;
}For data-color-scheme="dark", :root[data-mode="dark"], etc. — same three rules, swap the selector to match what your toggle sets.
How it works
- The remark transformer walks the mdast, finding
codenodes withlang === 'dgmo'. - For each block,
renderDgmoBlock()callsrender()from@diagrammo/dgmo— twice ifcolorMode: 'auto'(one light, one dark), once otherwise. - Each SVG is normalized: width/height stripped,
viewBoxadded, inline background removed. - The original
codenode is replaced with anhtmlnode carrying the rendered wrapper(s). - The optional client script (
dist/client.js) tightens viewBoxes and binds showcase-mode copy buttons.
Rendering happens at build time. The browser sees only the inline SVG and the small color-mode CSS.
License
MIT
