remark-madoka-runes
v0.3.1
Published
remark plugin to render text in Puella Magi Madoka Magica Witch Runes (魔女文字) using the archaic, modern, musical and Latin rune fonts
Maintainers
Readme
remark-madoka-runes
A remark plugin that renders text in Puella Magi Madoka Magica Witch Runes (魔女文字).
It handles all four rune alphabets (archaic, modern, musical and Latin), and the fonts come with it.
Install
pnpm add remark-madoka-runes remark-directiveremark-directive powers the
default syntax, so put it in your pipeline first.
If you are not using it, see plain CommonMark below.
Use
:runes[HOMULILLY]
:runes[Oktavia von Seckendorff]{variant=musical}
:runes[Yaginokonoyume]{.latin}
:::runes{variant=archaic}
A block of rune text.
:::import rehypeStringify from "rehype-stringify";
import remarkDirective from "remark-directive";
import remarkMadokaRunes from "remark-madoka-runes";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import { unified } from "unified";
const file = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkMadokaRunes)
.use(remarkRehype)
.use(rehypeStringify)
.process(":runes[HOMULILLY]");:runes[HOMULILLY] becomes:
<span class="madoka-runes madoka-runes--default" data-rune-variant="default">HOMULILLY</span>Load the stylesheet once somewhere in your app. It sets up the @font-face
rules and points them at the bundled fonts:
import "remark-madoka-runes/style.css";@import "remark-madoka-runes/style.css";The HTML still holds ordinary Latin letters, so the text copies, searches and reads aloud like anything else. Only the font makes it look like runes.
The stylesheet sets the font and nothing else. Size, line height and colour are
yours to set on .madoka-runes.
Plugin order
Put this plugin after remark-directive and before anything that rewrites
directives it does not recognise. Plugins built on the Astro Starlight
admonitions pattern turn every unhandled directive back into plain text, so a
:runes[...] they reach first is gone before this plugin runs:
remarkPlugins: [remarkDirective, remarkMadokaRunes, remarkAdmonitions]Once this plugin has handled a directive it replaces the node outright, so sweepers running later leave it alone.
Variants
| Variant | Font | Case applied |
| --- | --- | --- |
| default | MadokaRunes | none |
| archaic | MadokaRunes | uppercase |
| modern | MadokaRunes | lowercase |
| musical | MadokaMusical | none |
| latin | MajoPMMM | uppercase |
MadokaRunes packs two alphabets into one font: archaic on the uppercase
letters, modern on the lowercase ones. So archaic and modern just change the
case of your text, and default leaves it alone, which means mixed-case text
comes out as a mix of the two.
Pick a variant with {variant=musical} or the shorthand {.musical}.
The alphabets have gaps. The musical font is missing J, Q, W and X, and the Latin font only covers ASCII. Anything with no rune falls back to whatever font surrounds it, and the plugin leaves a warning on the vfile when that happens.
Plain CommonMark
If you are not using remark-directive, turn on the commonmark syntax
instead. It uses a fenced code block for a run of runes and an inline code span
for a few words, both of which are ordinary CommonMark that any parser already
understands.
.use(remarkMadokaRunes, { syntax: ["commonmark"] })```runes-archaic
Kriemhild Gretchen
```
Her name is `runes:HOMULILLY`, and hers is `runes-latin:Yaginokonoyume`.The variant goes in the info string or the prefix (runes-musical), and a bare
runes uses the default. Anything else in a code block or code span is left
alone. Pass syntax: ["directive", "commonmark"] to accept both.
Directives are the default because they are the established way to add syntax to markdown, and because a code fence carries no attributes and cannot nest other markdown. Reach for CommonMark when directives are not already in your setup.
Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| syntax | Syntax[] | ["directive"] | Which syntaxes to accept: "directive", "commonmark", or both. |
| name | string | "runes" | Directive name, fence info string and inline code prefix. |
| variant | RuneVariant | "default" | Variant to use when a directive does not name one. |
| className | string | "madoka-runes" | Base class; the variant is appended as <className>--<variant>. |
| tagName | string | "span" | Element for inline runes. |
| blockTagName | string | "div" | Element for block runes. |
| inlineStyle | boolean | false | Also emit style="font-family:..." on each element. |
| inlineFonts | boolean | false | Embed the fonts as data: URI @font-face rules. Node only. |
| title | boolean | false | Add title with the original, untransformed text. |
| checkGlyphCoverage | boolean | true | Warn when text contains characters the font has no glyph for. |
Self-contained output
With inlineFonts: true the plugin puts a <style> block at the top of the
document with the fonts baked in as base64 WOFF2, and only the fonts that
document actually uses. You then need no stylesheet and no font files, but every
font adds 6 to 36 KB to each document.
.use(remarkMadokaRunes, { inlineFonts: true })Warnings
Everything below is reported as a vfile message rather than an error, so none of it breaks your build:
| ruleId | When |
| --- | --- |
| missing-glyph | The text uses a character the chosen font has no rune for. |
| unknown-variant | A variant name that does not exist was asked for. |
| missing-remark-directive | The file uses :runes[...] but remark-directive is not in the pipeline, so it would silently stay plain text. |
const file = await processor.process(":runes[JQWX]{variant=musical}");
console.log(file.messages.map(String));
// The musical rune font (MadokaMusical) has no glyph for "J", "Q", "W", "X"; ...Example
Run pnpm run build, then node example/build-demo.mjs. It writes
example/demo.html, one self-contained page showing all five variants.
Fonts
pnpm run build:fonts rebuilds the WOFF2 files and the glyph coverage tables
from the TTFs.
The Witch Runes were designed by Inu Curry for
Puella Magi Madoka Magica
(© Magica Quartet / Aniplex・Madoka Partners・MBS). The fonts are fan-made and
the MIT license does not cover them, so read
assets/fonts/NOTICE.md first, especially if this is
for commercial work. This is an unofficial fan project.
The fonts and the rune tables come from the Puella Magi Wiki.
License
MIT for the code. This project also bundles copyrighted fonts that
are not under the MIT license. Read
assets/fonts/NOTICE.md before using them.
