markdown-viewer

Render markdown with progressive enhancement. Supports external files, inline content, pluggable parsers, and VB theme integration.

Overview

A platform-native markdown viewer custom element. Renders markdown into light DOM so VB's CSS cascade, themes, and component styles apply automatically. Progressive enhancement via the <pre> slot ensures content is readable without JavaScript.

Content Slots

The component resolves content using this priority chain:

<pre>

Raw markdown is visible as readable text if JS is disabled. Hidden after render.

<markdown-viewer> <pre> # Hello world This is **inline** markdown with a graceful fallback. The raw text is visible if JS is disabled. </pre> </markdown-viewer>

<script>

If a <script> has an unknown MIME type the browser does not execute or render it. Here we use text/markdown to create a markdown block to render. Like the next approach using <template>, there is no fallback without JavaScript support.

<markdown-viewer> <script type="text/markdown"> # Hello world This content is **invisible** without JavaScript. </script> </markdown-viewer>

<template>

Inert DOM node can be consulted as long as you put the data-md attribute on it, framework-friendly. No fallback without JS.

<markdown-viewer> <template data-md> # Hello world | Name | Role | |-------|----------| | Alice | Engineer | | Bob | Designer | </template> </markdown-viewer>

src

Fetch external markdown using the JS fetch() API. CORS rules apply.

<markdown-viewer src="/path/to/document.md"></markdown-viewer>

Theme Propagation

Set data-theme on the viewer or let it inherit from the nearest ancestor with [data-theme]. The theme propagates to the .md-content container.

<!-- Explicit theme --> <markdown-viewer data-theme="brutalist"> <pre># Themed content</pre> </markdown-viewer> <!-- Inherited from ancestor --> <section data-theme="editorial"> <markdown-viewer> <pre># Inherits editorial theme</pre> </markdown-viewer> </section>

Custom Parser

The default parser is marked with GFM enabled. Override it with any function that accepts a markdown string and returns an HTML string:

const viewer = document.querySelector('markdown-viewer'); viewer.parser = (md) => myCustomRenderer(md);

Syntax Highlighting

Add the highlight attribute to fire markdown-viewer:highlight events for each code block. Bring your own highlighter:

<markdown-viewer highlight> <pre> # Code Blocks ```javascript console.log('hello'); ``` </pre> </markdown-viewer> <script type="module"> document.addEventListener('markdown-viewer:highlight', ({ detail }) => { // detail.node = the code element // detail.language = e.g. "javascript" hljs.highlightElement(detail.node); }); </script>

Mermaid Diagrams

Listen for markdown-viewer:rendered and lazy-load Mermaid only when needed:

document.addEventListener('markdown-viewer:rendered', ({ detail }) => { const diagrams = detail.node.querySelectorAll('code.language-mermaid'); if (diagrams.length) { import('mermaid').then(m => m.default.run({ nodes: diagrams })); } });

Callout Extension

Register the optional callout extension with marked for :::type blocks (warning, info, tip, danger, note):

import { marked } from 'marked'; import { calloutExtension } from './markdown-viewer/vb-extensions.js'; marked.use({ extensions: [calloutExtension] });

Attributes

AttributeValuesDefaultDescription
src string URL of external markdown file
loading "eager", "lazy" eager Defer fetch until element enters viewport (Phase 3)
highlight boolean Fire per-block markdown-viewer:highlight events after render
ping string URL to ping with render metadata via sendBeacon
data-theme string Theme name propagated to .md-content container

Events

EventDetailDescription
markdown-viewer:fetch { src } Fired when external fetch begins
markdown-viewer:rendered { src, node } Fired after parse and render complete
markdown-viewer:highlight { node, language } Fired per code block when highlight attribute is set
markdown-viewer:error { error } Fired on fetch or parse failure

JavaScript API

Property / MethodDescription
.parserGet/set custom parser function (md → html string). Overrides the default marked parser.
.render()Force a re-render from the current content source. Returns a Promise.
.reload()Re-fetch the src URL and re-render. Returns a Promise.

Progressive Enhancement

The <pre> slot provides the best progressive enhancement:

SlotJS disabledCrawlableRecommended for
srcNothing visibleNoExternal files
<script>Nothing visibleNoBuild-time content
<template>Nothing visibleNoFramework content
<pre>Raw text visibleYesProgressive enhancement