mdgriffith/elm-markup
is an interesting package that allows you to take marked up text,
and transform it into the output format of your choosing. In this post, I’d like to give
you a short introduction to it, and also show you how it can be used in conjunction
with my static site generator, Elmstatic.
Here is some (slightly modified) markup from the package README:
| Title
My fancy cat blog article
Welcome! Have you heard about /cats/? They're great.
| Image
src = http://placekitten/200/500
description = Here's a great picture of my cat, pookie.
How much do I like cats? *A bunch*.
Let me begin my {Highlight| 87 part argument } in favor of ~dogs~ cats.
There are three types of markup:
|Title
block at the start{Highlight| 87 part argument }
on the last line/cats/
, bold *A bunch*
, and also crossed out ~dogs~
.There are two key ideas behind elm-markup
:
elm-ui
-based renderer).This makes it very flexible and suitable for many different purposes.
Elmstatic is my Elm-based static site generator. It allows you to define page layout in Elm, and write content for posts and pages in Markdown.
I’ve been asked whether Elmstatic could support elm-markup
in addition to Markdown. Well,
it turns out that it already does!
With Elmstatic, you have a lot of freedom for generating HTML and CSS from Elm. Effectively,
all it does is compile your Elm code and then set it loose on an empty HTML document.
It places a few constraints on the directory structure and the like, but you can use any way
you like to produce Html msg
values.
Since rendering of elm-markup
documents is up to us, we can turn them into HTML – more
specifically, into Html msg
values, which means we can plug in rendering results into Elmstatic.
But where are the source strings going to come from? Well, the content of .md files following
the frontmatter block is passed into the Elm code as a string, so we can simply write
elm-markup
instead of Markdown in .md files. It’s a bit weird, but fine for a proof of concept.
Side note: in a regular Elm application, it’s possible to use markup blocks to create dynamic or interactive regions. However, Elmstatic generates static content so that’s not something we can do here.
With elm-markup
, we need to define a renderer, which is a value of type Mark.Document result
.
For HTML, this becomes Mark.Document (Html msg)
. Then, we use it like this:
main : Elmstatic.Layout
main =
Elmstatic.layout Elmstatic.decodePage <|
\content ->
let
htmlContent =
case Mark.parse document content.markdown of
Ok html ->
html
Err _ ->
Html.text "Error parsing elm-markup"
in
layout content.title [ htmlContent ]
Note the Mark.parse
call in the snippet above. This main
definition comes straight
from an Elmstatic scaffold, except instead of passing [ Page.markdown content.markdown ]
to layout
on the last line, we instead use [ htmlContent ]
which is the result
of calling Mark.parse
.
This renders the page layout and the content:
We passed document
to Mark.parse
, so let’s see how it’s defined:
document : Mark.Document (Html msg)
document =
Mark.document
(Html.article [])
<|
Mark.manyOf
[ Mark.block "Title" (Html.h3 []) docText
, Mark.record2 "Image"
(\src description ->
Html.img [ Attr.src src, alt description ] []
)
(Mark.field "src" Mark.string)
(Mark.field "description" Mark.string)
-- top level Text
, Mark.map (Html.p []) docText
]
This describes how the custom blocks (Title
and Image
) should be converted into
Html msg
values. It also refers to docText
in a couple of places. docText
defines how to deal with inline elements in the text:
docText =
let
toStyles style =
case style of
Mark.Bold ->
Attr.style "font-weight" "bold"
Mark.Italic ->
Attr.style "font-style" "italic"
Mark.Strike ->
Attr.style "text-decoration" "line-through"
textToHtml (Mark.Text styles str) =
Html.span (List.map toStyles styles) [ Html.text str ]
in
Mark.text
{ view = textToHtml
-- custom inline blocks
, inlines =
[ Mark.inline "Highlight"
(\texts ->
Html.span [ Attr.class "highlight" ]
(List.map textToHtml texts)
)
|> Mark.inlineText
]
-- pre-parsing string replacements
, replacements = [ Mark.replacement "..." "…" ]
}
This deals with both the custom inline block (Highlight
) and the built-in syntax
elements for italics, bold and strikethrough, which we’re also free to style any way we like.
The advantage of Markdown is that it’s easy to render with elm-explorations/markdown
(and it’s built into the default Elmstatic scaffold). Markdown also allows you to insert
bits of HTML when you need more complex page elements.
That’s very convenient when your pages are mostly text aside from headers, footers and other layout elements.
However, if you have complex page content where text is interspersed with HTML, and maybe where you are repeating blocks of HTML (such as forms) multiple times within the text, it becomes somewhat inconvenient to manage in Elmstatic. You have to create a custom page layout, break up the Markdown into pieces in Elm, and then combine those pieces with the other content.
In these situations, you might want to consider elm-markup
.
Conveniently, there’s nothing stopping you from using both Markdown and elm-markup
because Elmstatic allows you to define any number of page layouts. You can define
one page layout that handles Markdown, and another one that renders elm-markup
.