Closed3

MarpでUnoCSSを使う

eyemono.moeeyemono.moe

https://zenn.dev/eyemono_moe/articles/5d5156661b5aa2

MarpではMarkdownから簡単にスライドを作れる一方で、たびたびスタイリングに困る。「2列左右に配置したい」「長くて入りきらないのでちょっとだけ字を小さくしたい」...

HTMLで<style>を書くこともできるし、theme css内にutility classっぽいものを置いてそれを使うこともできるが、いちいちCSSを書くのはちょっと面倒。UnoCSSみたいに自動でcssを生成してくれればいいのに...と思ってMarpの仕様を眺めてたら行けそうだったのでメモ

eyemono.moeeyemono.moe

今回はMarp CLIでリポジトリに置いたMarkdownからスライドを自動生成することを目指す。

Marp CLIではMarkdown→スライドの変換エンジンにデフォルトでMarp Coreが使用される。

https://github.com/marp-team/marp-cli?tab=readme-ov-file#engineで紹介されているように、このエンジン部分は設定ファイルを作成することで自由に指定することができる。このファイル内で、変換処理後にUnoCSSを噛ませてあげれば良さそう。

結論としては以下の通り。

engine.mjs
import { Marp } from "@marp-team/marp-core";
import { createGenerator } from "@unocss/core";
import markdownItAttrs from "markdown-it-attrs";
import markdownItBracketedSpans from "markdown-it-bracketed-spans";
import markdownItContainer from "markdown-it-container";
import unoConfig from "./uno.config.mjs";

const unoGenerator = await createGenerator(unoConfig);

class UnoMarp extends Marp {
  async render(markdown, env) {
    const res = super.render(markdown, env);
    const { css: unoCss } = await unoGenerator.generate(res.html, {
      minify: true,
    });
    return {
      ...res,
      css: unoCss + res.css,
    };
  }
}

/**
 * @type {import('@marp-team/marp-cli').Config<typeof import('@marp-team/marpit').Marpit>["engine"]}
 */
export default async ({ marp: _marp, ...config }) => {
  const uno = new UnoMarp(config);

  return uno
    .use(markdownItBracketedSpans)
    .use(markdownItAttrs)
    .use(markdownItContainer, "", {
      // allow empty name
      validate: () => true,
      render: (tokens, idx) => {
        const _class = tokens[idx].info.trim();
        if (tokens[idx].nesting === 1) {
          // opening tag
          return `<div class="${_class}">\n`;
        }
        // closing tag
        return "</div>\n";
      },
    });
};

engine.mjsMarpitクラスまたはMarpitクラスを返す関数をexportする。
(@marp-team/marp-coreMarpitクラスがMarpitクラスを継承している)

Markdownからhtml, cssへの変換はMarpit.render()によって行われているため、これをうまくラップすればよい。
UnoCSSによるcssの生成は@unocss/coreで行っている。
render内でMarpit側で生成されたhtmlからcssを生成し、Marpit側のcssと単に結合しているだけ。

Marp用にUnoCSS側のconfigも最適化する必要がありそうだけど(extractorとか)これで動いているので一旦保留。

あとはMarkdown内で簡単にクラスを指定する必要がある。見せてあげよう、Marp の真髄をで紹介されているように、とりあえずmarkdown-it-attrsを使うのが楽か。

https://zenn.dev/ykicchan/articles/c30efd827337c3

これで{.bg-red}などと書けば動作するようになった🎉

eyemono.moeeyemono.moe

TODO

  • VSCodeでannotation表示/autocompleteできるようにする
    • .hogeのようにクラス直前に.があるとannotation表示されない
    • HTMLタグ内でないとautocompleteできないhttps://github.com/unocss/unocss/blob/8cfcf603ae54fa92098ed32e1dfe3148eac1d796/packages/vscode/src/utils.ts#L206-L225
  • {.hoge .puni .poyo}の書き方が冗長
    • {hoge puni poyo}で書けるようにmarkdown-itのpluginを書く?
このスクラップは2025/01/10にクローズされました