@stnd/press
Markdown → Beautiful HTML with classical typography.
The rendering engine for Standard Garden. Built on markdown-it, enhanced with a typography pipeline inspired by 500 years of printing tradition.
Designed for Cloudflare Workers and edge environments.
Install
pnpm add @stnd/press
Quick Start
import { Press } from “@stnd/press”;
const press = new Press(“# Hello\n\nThis is **beautiful** text.”);
await press.parse();
console.log(press.html); // Rendered HTML with proper typography
With Astro Content Collections
When using Astro’s content collections, frontmatter is already parsed. Pass it directly to skip redundant parsing:
const { service } = Astro.props;
const press = new Press(service.body, { frontmatter: service.data });
await press.parse();
What it does
- Pre-processing —
::syntaxpatterns, Obsidian comment stripping - Module plugins — Custom
::patternsfrom yourindex.module.js(passed explicitly) - Markdown rendering — CommonMark via
markdown-itwith callouts, footnotes, heading anchors, and Prism syntax highlighting - Post-processing — Smart quotes, em dashes, ellipses, fractions, orphan prevention, locale-aware spacing
- Sanitization — DOMPurify (opt-in, off by default)
Options
const press = new Press(content, {
locale: “fr”, // Typography locale (en, fr, de, es, it)
sanitize: false, // DOMPurify sanitization (default: false)
renderHtmlAsRaw: false, // Render ```html blocks as raw HTML
frontmatter: entry.data, // Pre-parsed frontmatter (skip re-parsing)
plugins: [], // Module press plugins from index.module.js
checkSyntax: true, // Warn about unprocessed ::patterns
html: true, // Allow HTML in markdown
breaks: true, // Convert \n to <br>
linkify: true, // Auto-link URLs
});
API
Press class
const press = new Press(content, options);
await press.parse();
press.html; // Rendered HTML (headings include id attributes)
press.css; // Injected CSS from plugins
press.head; // Injected <head> content from plugins
press.frontmatter; // Parsed (or pre-provided) frontmatter
press.title; // Extracted title
press.readingTime; // “3 mins”
press.wordCount; // 450
press.preview; // Plain-text excerpt
press.isProcessed; // true after parse()
Lightweight Helpers
Import from @stnd/press/helpers to avoid pulling in the rendering engine:
import {
parseFrontmatter, // Parse YAML frontmatter from markdown
extractTitleFromContent, // Extract title from headings or first line
createPreview, // Generate plain-text excerpt
getReadingTime, // Estimate reading time
applyFrontmatterEnrichment, // Add computed metadata to frontmatter
extractCssVariables, // Convert stnd_* keys to CSS custom properties
generateOgImageUrl, // Build OG image URL from metadata
shouldShowIndex, // Check if content needs a table of contents
} from “@stnd/press/helpers”;
Typography (direct access)
import {
runPreProcessing,
runPostProcessing,
getRulesForLocale,
} from “@stnd/press”;
// Or access configuration directly
import {
typographyRules,
orphanWords,
fractionMap,
} from “@stnd/press/typography/config.js”;
Heading Anchors
All headings in rendered HTML automatically receive id attributes via markdown-it-anchor.
Fragment links like #my-heading work out of the box — no post-processing needed.
<!— Input —>
## My Heading
<!— Output —>
<h2 id="my-heading">My Heading</h2>
Typography
Press applies locale-aware typographic rules automatically:
| Rule | Example | Result |
|---|---|---|
| Em dash | — |
— |
| Ellipsis | … |
… |
| Smart quotes | “hello” |
“hello” |
| Fractions | ½ |
½ |
| Arrows | → |
→ |
| Orphan prevention | a cat |
a + non-breaking space + cat |
| French spacing | Bonjour ! |
Thin space before ! |
Supported Locales
- English (
en) — Curly quotes"“‘’, comma thousands1,000 - French (
fr) — Guillemets«», thin-space thousands1 000 - German (
de) — Low quotes„”, dot thousands1.000 - Spanish (
es) — Guillemets«», dot thousands1.000 - Italian (
it) — Guillemets«», dot thousands1.000
::syntax Patterns
Press includes a built-in pattern system for layout directives in markdown:
<div class=“hero”>
This text becomes a hero block.
</div>
<div class=“callout” data-callout=“tip”>
<div class=“callout-title”>
<span class=“callout-icon”>💡</span>
<span class=“callout-title-inner”>Tip</span>
</div>
<div class=“callout-content”>
This is a tip callout with an icon.
</div>
</div>
<div class=“columns-2”>
<div class=“column”>
Left column content.
</div>
<div class=“column”>
Right column content.
</div>
</div>
<aside class=“note”>This is an inline note.</aside>
See typography/SYNTAX_GUIDE.md for the full list of built-in patterns.
Syntax Highlighting
Prism.js with these languages out of the box:
bash, yaml, json, markdown, javascript, typescript, css
Need more? Import them before creating a Press instance:
import “prismjs/components/prism-python.js”;
import “prismjs/components/prism-ruby.js”;
import { Press } from “@stnd/press”;
Sanitization
Sanitization is off by default. Astro and most frameworks handle this already.
Enable it for user-generated content (e.g., Standard Garden’s paste feature):
const press = new Press(untrustedContent, { sanitize: true });
When enabled, DOMPurify strips dangerous tags/attributes while preserving semantic HTML, SVGs, <video>, <details>, and style attributes.
Relationship to other packages
Raw file (DOCX, PDF, etc.)
↓
@stnd/ingest → “Here's clean Markdown”
↓
@stnd/press → “Here's beautiful HTML”
@stnd/ingest handles messy input. @stnd/press handles beautiful output.
Changelog
0.7.0
- Breaking: Renamed
foliosoption →plugins(aligns with module architecture rename) - Breaking: Renamed all internal typography “folio” terminology → “plugin”
- Renamed
initializeTypographyFolios→initializeTypographyPlugins - Renamed
registerTypographyFolio→registerTypographyPlugin - Renamed
getMarkdownItFolios→getMarkdownItPlugins - Renamed
getAllTypographyFolios→getAllTypographyPlugins - Renamed
clearTypographyFolios→clearTypographyPlugins - Renamed
markdownFolioexport →markdownPlugin(in tags.js, wikilinks.js) - Updated all references from
index.folio.js→index.module.js
0.6.0
- Breaking: Removed
document-converter.js→ moved to@stnd/ingest - Breaking: Removed
rehype-markdown/plugins (use markdown-it pipeline instead) - Breaking: Sanitization now opt-in (
sanitize: falseby default) - Added
frontmatteroption to skip re-parsing - Added
localeoption (replaceslang) - Lazy-initialize typography plugins (no side effects on import)
- Removed dead dependencies:
cheerio,mammoth,pdfjs-dist,html-to-md - Removed
@stnd/utilsdependency - Added
<video>,<section>,<aside>,<nav>,styleto sanitization allowlist - Cleaner, more focused API