• Stars
    star
    283
  • Rank 146,066 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 3 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A custom element for rendering stylable (light DOM) Markdown

Motivation

There are many web components these days to render Markdown to HTML. Here are a few:

However, all render the resulting Markdown in Shadow DOM, making it painful to style like a regular part of the page, which my use cases required. <zero-md> supports opt-in light DOM rendering, but it's tedious to add an extra attribute per element.

I also wanted a few more things existing web components didn't have. Plus, making stuff is fun. ๐Ÿ˜…

So I made my own. Feel free to use it. Or don't. ๐Ÿคท๐Ÿฝโ€โ™€๏ธ I primarily wrote it to scratch my own itch anyway! ๐Ÿ˜Š

Features

  • Zero dependencies (except marked, obvs, which is only loaded if a <md-block> or <md-span> element is actually used on the page)
  • Styleable with regular selectors, just like the rest of the page
  • Load external Markdown files or render inline content
  • Customize start heading level (e.g. so that # Foo becomes a <h3> and not an <h1>)
  • Also comes with <md-span>, for lightweight inline markdown
  • Prism is automatically used for syntax highlighting, if included (but can be included dynamically too)

View demos

Usage

Via HTML:

<script type="module" src="https://md-block.verou.me/md-block.js"></script>

In JS:

import {MarkdownBlock, MarkdownSpan, MarkdownElement} from "https://md-block.verou.me/md-block.js";

Of course you can also use npm if that's your jam:

npm install md-block
import {MarkdownBlock, MarkdownSpan, MarkdownElement} from "md-block";

Importing the module in any of these ways also registers two custom elements: <md-block> for block level content and <md-span> for inline content. If you additionally want to use other tag names, you can.

API

Both <md-block> and <md-span>

Attribute Property Type Description
- mdContent String Actual Markdown code initially read from the HTML or fetched from src. Can also be set to render new Markdown code
rendered rendered (Read-only) String Added to the element after Markdown has been rendered. Thus, you can use md-block:not([rendered]) in your CSS to style the element differently before rendering and minimize FOUC
untrusted untrusted (Read-only) Boolean Sanitize contents. Read more

<md-block>

Attribute Property Type Description
src src String or URL External Markdown file to load. If specified, original element content will be rendered and displayed while the file is loading (or if it fails to load).
hmin hmin Number Minimum heading level
hlinks hlinks String Whether to linkify headings. If present with no value, the entire heading text becomes the link, otherwise the symbol provided becomes the link. Note that this is only about displaying links, headings will get ids anyway

<md-span>

(No attributes or properties at the moment)

Recipes

Updating the Markdown

While you can provide initial Markdown inline, after the element is rendered, changing its contents will not cause it to re-render, since its contents are now the parsed HTML (this is a disadvantage of this approach, compared to the Shadow DOM ones).

If you need to update its contents dynamically, use element.mdContent. You can also read that property to get access to the Markdown code that was last rendered, whether it came from the element's contents, or fetched from a URL.

Note that setting mdContent will override any remote URL provided via src.

Minimizing FOUC

md-block adds a rendered attribute to elements whose Markdown has been rendered. This allows you to style unrendered content however you please, by using a md-block:not([rendered]) CSS selector.

  • You could hide it entirely via md-block:not([rendered]) { display: none }
  • You could apply white-space: pre-line to it so that at least paragraphs are not all smushed together
  • โ€ฆor you could do something fancier.

I'd recommend you consider how it fails before deciding what to do. It's the Internet, ๐Ÿ’ฉ happens. Do you want your content to not be visible if a script doesn't load?

When loading remote content, there are two renders: First, any fallback content renders, then the remote content. Because we often want to style the element differently until the remote content renders, the rendered attribute has keyword values, depending on what happened:

  • fallback when only fallback content has been rendered
  • remote if remote content has been rendered
  • content if element content has been rendered and there is no src attribute present
  • property if content has been rendered by setting this.mdContent directly

Using different tag names

By default, md-block registers two custom elements: <md-block> for block-level content and <md-span> for inline content. You can use different names, but since each class can only be associated with one tag name, you need to create your own subclass:

import {MarkdownBlock, MarkdownSpan, MarkdownElement} from "https://md-block.verou.me/md-block.js"

customElements.define("md-content", class MarkdownContent extends MarkdownBlock {});

Handling untrusted content

By default md-block does not santize the Markdown you provide, since in most use cases the content is trusted.

If you need to render untrusted content use the untrusted attribute, which will dynamically load DOMPurify and use it. This is not dynamic, you need to add it in your actual markup (or before the element is connected, if dynamically generated). The reason is that it's unsafe to add it later: if the content has been already rendered once and treated as safe, it's pointless to sanitize it afterwards and re-render.

Important: Do not rely on the untrusted attribute for inline Markdown! This is mainly useful for content linked via the src attribute. If there is potentially malicious code in the inline Markdown you are using, it will be picked up by the browser before md-block has the chance to do anything about it. Instead, use a regular <md-block> element, and MarkdownElement.sanitize() for the untrusted content.

Using different URLs for marked and DOMPurify

By default, md-block dynamically loads marked and DOMPurify from a CDN. If you want to use different versions, there is a number of ways:

Probably the easiest is if you use the versions of these libraries that create a global, md-block will use that instead of loading them.

The URLs md-block uses to fetch these libraries reside on a separate URLs export. So theoretically you could do something like this:

import {URLs as MdBlockURLS, MarkdownBlock, MarkdownSpan, MarkdownElement} from "./md-block.js";

MdBlockURLS.marked = "./marked.js";
MdBlockURLS.DOMPurify = "./purify.es.js";

But it's uncertain whether the new URLs will be picked up before the default ones load. In my tests that seems to work for DOMPurify but not marked. These libraries are loaded when the element is connected, so you could add the <md-block> elements dynamically to the document after you set the URLs, but that's a bit of a hassle.

You can also use the URLs export to import these modules yourself, in case you want to add plugins or whatnot.

Loading Prism dynamically

By default md-block will use Prism if it's available, but wonโ€™t load it dynamically if it isn't. You could tell it to load Prism dynamically, only if there are actual code elements, by providing a Prism URL:

import {URLs as MdBlockURLS, MarkdownBlock, MarkdownSpan, MarkdownElement} from "./md-block.js";

MdBlockURLS.Prism = "./prism.js";
// You can optionally also provide a Prism CSS URL:
MdBlockURLS.PrismCSS = "./prism.css";

<md-block> inception

Did you know you can actualy use <md-block> inside your Markdown and it works correctly?

For a cool example of this, check out the Stretchy docs

How to set different Markdown options/flavor?

Right now, this element uses GFM as a Markdown flavor and doesnโ€™t expose a whole lot of options (besides hmin and hlinks). Thatโ€™s because I originally wrote it for my own needs, and thatโ€™s what I needed. Iโ€™m not opposed to adding more customizability, if there are actual use cases that require it. If you have such use cases, please open an issue.

More Repositories

1

awesomplete

Ultra lightweight, usable, beautiful autocomplete with zero dependencies.
JavaScript
6,964
star
2

prefixfree

Break free from CSS prefix hell!
JavaScript
3,841
star
3

animatable

One property, two values, endless possiblities
HTML
2,580
star
4

bliss

Blissful JavaScript
JavaScript
2,390
star
5

inspire.js

Lean, hackable, extensible slide deck framework. Previously known as CSSS.
JavaScript
1,717
star
6

color.js

Color conversion & manipulation library by the editors of the CSS Color specifications
JavaScript
1,581
star
7

css3patterns

The popular CSS3 patterns gallery, now on Github :)
HTML
1,415
star
8

stretchy

Form element autosizing, the way it should be
JavaScript
1,275
star
9

dabblet

An interactive CSS playground
JavaScript
817
star
10

dpi

dpi love - Easily find the DPI/PPI of any screen
JavaScript
748
star
11

multirange

A tiny polyfill for HTML5 multi-handle sliders
CSS
605
star
12

conic-gradient

Polyfill for conic-gradient() and repeating-conic-gradient()
HTML
486
star
13

rety

Record typing on one or more editors and replay it at will, to simulate live coding
JavaScript
383
star
14

parsel

A tiny, permissive CSS selector parser
TypeScript
361
star
15

regexplained

JavaScript
273
star
16

chainvas

Make APIs suck less
HTML
262
star
17

css3test

How does your browser score for its CSS3 support?
JavaScript
214
star
18

css.land

Hands on CSS demos
HTML
213
star
19

nudeui

Lea's kitchen sink of form components. WIP. Try at your own risk or come back later.
JavaScript
204
star
20

HTML5-Progress-polyfill

Polyfill for the HTML5 <progress> element
JavaScript
178
star
21

rgba.php

Script for automatic generation of one pixel alpha-transparent images for non-RGBA browsers to easily emulate RGBA colors in backgrounds
PHP
176
star
22

cubic-bezier

Playground for CSS bezier-based timing functions
JavaScript
169
star
23

markapp

Building apps by authoring HTML
CSS
134
star
24

duoload

Simple, client-side comparison of website loading behavior
CSS
130
star
25

play.csssecrets.io

CSS Secrets Book live demos
CSS
119
star
26

talks

All my talks (move still in progress)
HTML
92
star
27

incrementable

Increment length values in textfields
JavaScript
83
star
28

corner-shape

Play with corner-shape before itโ€™s implemented!
JavaScript
75
star
29

StronglyTyped

A library for strongly typed properties & global variables in JavaScript
JavaScript
70
star
30

css-colors

Share & convert CSS colors
JavaScript
62
star
31

bubbly

[Unfinished] CSS speech bubbles made easy!
JavaScript
50
star
32

html-syntax-guidelines

48
star
33

whathecolor

CSS
47
star
34

css-almanac

Repo for planning & voting on which stats to study
SCSS
34
star
35

forkgasm

Lea & Chrisโ€™ culinary adventures
HTML
33
star
36

talks-list

Automatic talks list, generated from JSON. Unmaintained, use Mavo instead.
JavaScript
29
star
37

chroma-zone

Slides for my talk โ€œThe Chroma Zone: Engineering Color on the Webโ€
CSS
21
star
38

htest

Declarative, boilerplate-free unit testing
JavaScript
19
star
39

lea.verou.me

Towards a serverless lea.verou.me! WIP, either help out or move along.
JavaScript
17
star
40

mavoice

Prioritize features for open source projects
CSS
15
star
41

issue-closing

View issue closing stats for any repo!
HTML
14
star
42

missing-slice

The Missing Slice talk slides
HTML
12
star
43

hci.mit.edu

WIP. Preview:
HTML
12
star
44

tweeplus

Longer tweets, no strings attached.
JavaScript
11
star
45

brep

Write batch find & replace scripts that transform files with a simple human-readable syntax
JavaScript
10
star
46

leaverou.github.io

Just a data repo, I don't intend to put a website here for now
10
star
47

mygraine

A migraine tracker, built with Mavo. Work in progress, come back later.
HTML
9
star
48

bytesizematters

JavaScript
8
star
49

refresh-it

Refresh page resources without a page reload. An alternative to the heavyweight processes that take over your local server.
JavaScript
8
star
50

uist2017

Website for ACM UIST 2017
HTML
6
star
51

homesearch

A sample Mavo app for people looking for housing to store info about and compare the homes they are trying to decide between
HTML
6
star
52

feedback

Easily save & share lists of tweets and own your data. Made with Mavo.
HTML
5
star
53

eleventy-plugin-citations

JavaScript
5
star
54

my-lifesheets

HTML
4
star
55

expenses

App to generate summary of expenses, made with Mavo
CSS
4
star
56

rework-utils

Utilities to explore ASTs generated by the Rework CSS parser. Originally written for the Web Almanac.
JavaScript
3
star
57

mv-data

3
star
58

testlib

Just a test, move along
JavaScript
2
star
59

blissful-hooks

Deep extensibility for all!
JavaScript
2
star
60

labelizer

Manage repo labels (WIP)
JavaScript
1
star
61

contacts

1
star
62

zoe-eats

Baby food log, made with Mavo
1
star
63

zoelearns.com

JavaScript
1
star
64

website

1
star
65

pathed

Get/set/subset deep objects via readable, extensible paths. WIP.
JavaScript
1
star