🔬

Quarto + Typstでアカデミックなスライドを作る

2024/11/13に公開

Beamerひしめく経済学の世界の片隅で

2年前, Quarto + Reveal.jsによるアカデミックなスライドの作り方を解説しました.

https://zenn.dev/nicetak/articles/quarto-tips-2022

以来, デザイン等をアップデートしながら, Quarto + Reveal.jsでスライドを作成してきました. Beamerが98%, PowerPointが2%という経済学の世界では異端ながらも, Quarto + Reveal.jsは私にとってはデザインとカスタマイズ性から最適な選択肢でした.

しかし, PDFスライドがデファクトスタンダードな世界でReveal.jsを用いたHTMLスライドを使うことには限界があると感じました. Reveal.jsはPDFに変換することができますが, いくつかの問題がありました.

  1. HTML版だと1枚に収まっていたスライドがPDF版だと下部分がはみ出てしまうことがある
  2. 数式のレンダリングがうまくいかないことがある
  3. 日本語のフォントのサイズが重すぎてembed-resource: trueで埋め込めない
  4. PDF版ではHyperlinkが機能しない. つまりボタンのリンクができない.

特に最後の点が致命的で, Robustness CheckやDetailsへのリンクをBeamerのボタンで表す文化のある経済学の世界ではかなり不便で不評でした.

とはいえ, Beamerはコンパイルが遅く, デザインのカスタマイズが難しいという問題もあります.[1] そこで, Quarto + Typstという高速にコンパイルでき, 文法が平易かつグラフの調整しやすい方法に着目しました. また, Quarto + Typstによるスライドを作るテンプレートは存在しなかったため, 自分で作成しました.

https://github.com/kazuyanagimoto/quarto-clean-typst

これはGrant McDermottさんのClean themeというReveal.jsテーマをTypstに移植したものです. このテンプレートを使うことで, Quarto + TypstでBeamerのようなPDFスライドを作成することができます.

このデモに用いたコードはここにあります.

Touying

このテンプレートはフレームワークとしてTouying[2]を採用しています. Typstでスライドを作成するフレームワークとしてはPolyluxが有名ですが, h2タグをスライドの区切りかつタイトルとして使うことのできるTouyingはQuartoとの親和性が高いと感じました. Touyingは簡潔な文法ながら, PolyluxやBeamerに負けないパワフルな機能を持っています.

なお以下では特段解説しませんが, Quartoでは{=typst}ブロックを使うことでTypstのコードを記述することができ, Touyingの全ての機能を利用することができます. 特に数式中のアニメーションなどは, Typstの数式記法に依存しているため, Typstネイティブのコードでしか実現できません. また, Quarto + Typstにおける基本的な機能はこちらを参照してください.

数式中のアニメーション

Touyingでは以下のようなpausemeanwhileコマンドが数式中でも使うことが可能です.

slides.qmd
```{=typst}
#slide[
  Touying equation with pause:

  $
    f(x) &= pause x^2 + 2x + 1  \
         &= pause (x + 1)^2  \
  $

  #meanwhile

  Touying equation is very simple.
]
```

さらに, Typst独自の数式記法ではなく, \LaTeXの記法からTypst記法に変換できるMiTeXをサポートしているため

slides.qmd
```{=typst}
#touying-mitex(mitex, `
  f(x) &= \pause x^2 + 2x + 1  \\
      &= \pause (x + 1)^2  \\
`)
```

のように\LaTeX記法を用いることもできます.

quarto-clean-typstの使い方

スライドの作成

基本的には, Quartoのスライド記法 (特にReveal.jsの) を使います. h2レベルのヘッダーがスライドの区切りかつスライドタイトルであり, h1がセクションタイトル, h3がスライドのサブタイトルとなります.

slides.qmd
# Section Slide as Header Level 1

## Slide Title as Header Level 2

### Subtitle as Header Level 3

You can put any content here, including text, images, tables, code blocks, etc.

- first unorder list item
    - A sub item

1. first ordered list item
    1. A sub item

Next, we'll brief review some theme-specific components.

- Note that _all_ of the standard Quarto + Typst
[features](https://quarto.org/docs/output-formats/typst.html) 
can be used with this theme
- Also, all the [Touying](https://touying-typ.github.io) features can be used by **Typst native code**.

YAMLによるカスタマイズ

スライドの基本的な要素のテーマはYAMLヘッダーでカスタマイズすることができます.

slides.qmd
format:
  clean-typst:
    font-size: 20pt
    font-heading: Josefin Sans
    font-body: Montserrat
    font-weight-heading: bold
    font-weight-body: normal
    font-size-title: 2.5em
    font-size-subtitle: 1.5em
    color-jet: "#272822"
    color-accent: "009F8C"
    color-accent2: "B75C9D"
  • font-size: デフォルトのフォントサイズ
  • font-heading: スライドタイトルのフォント
  • font-body: 本文のフォント
  • font-weight-heading: スライドタイトルのフォントウェイト
  • font-weight-body: 本文のフォントウェイト
  • font-size-title: タイトルスライドのタイトルのフォントサイズ
  • font-size-subtitle: タイトルスライドのサブタイトルのフォントサイズ
  • color-jet: テキストのカラー. 基本的に#000000はコントラストが強すぎます. デフォルトは#131516です.
  • color-accent: サブタイトル, ボタン, リンクなどの色です
  • color-accent2: .alertの色です.

また, タイトルスライドは以下のように記述します.

slides.qmd
title: Quarto Clean Theme
subtitle: A Minimalistic Theme for Quarto + Typst + Touying
date: today
date-format: long
author:
  - name: Kazuharu Yanagimoto
    orcid: 0009-0007-1967-8304
    email: kazuharu.yanagimoto@cemfi.edu.es
    affiliations: CEMFI

独自機能

TouyingやQuartoのTypstの機能に加えて, このテンプレートにはいくつかの独自の機能があります.

slides.qmd
## Components

### Alerts & Cross-refs {#sec-crossref}

Special classes for emphasis

- `.alert` class for default emphasis, e.g. [the second accent color]{.alert}.
- `.fg` class for custom color, e.g. [with `options='fill: rgb("#5D639E")'`]{.fg options='fill: rgb("#5D639E")'}.
- `.bg` class for custom background, e.g. [with the default color]{.bg}.

To cross-reference, you have several options, for example:

- Beamer-like .button class provided by this theme, e.g. [[Appendix]{.button}](#sec-appendix)
- Sections are not numbered in Touying, you cannot use `@sec-` cross-references

  • .alert: デフォルトの強調. color-accent2の色が使われます.
  • .fg: カスタムカラーの強調. options='fill: rgb("#5D639E")'で指定します. デフォルトは#"e64173"です.
  • .bg: カスタム背景の強調. options='fill: rgb("#5D639E")'のように色を指定できます. デフォルトは#"e64173"です.
  • .button: Beamerのボタンに相当するクラス. マークダウン記法のリンクでリンク先のセクション名を指定することで, ハイパーリンクを貼ることができます.

局所的なカスタマイズ

スライドを作成する際, 一部のスライドだけスタイルを変更したり, 行間を調整したいことがよくあります. QuartoのTypst CSSを用いたり, 独自機能の{{ v 1em}}を使うことで局所的なカスタマイズが可能です.

slides.qmd
## Ad-hoc Styling

### Typst CSS

- Quarto supports [Typst CSS](https://quarto.org/docs/advanced/typst/typst-css.html) for simple styling
- You can change [colors]{style="color: #009F8C"}, [backgrounds]{style="background-color: #F0F0F0"}, and [opacity]{style="opacity: 0.5"} for `span` elements

::: {style="font-size: 30pt; font-family: 'Times New Roman'"}

You can also change the font size and family for `div` elements.

:::

{{< v 1em >}}

### Vertical Spacing

- A helper shortcode `{{{< v DIST >}}}` is provided to add vertical spacing
- This is converted to a Typst code `#v(DIST)` internally.

{{< v 2em >}}

This is a `2em` vertical spaced from above.

カスタマイズ関数の登録

Quartoの拡張機能であるlatex-environmentのように, Typstの関数をdivspan要素として登録することができます. 以下の例では, smallsmall-citeという関数を登録しています. このようにすることで, 自作の関数をQuartoの中で[hogehoge]{.small}のように使うことができます.

slides.qmd
format:
  clean-typst:
    include-in-header: "custom.typ"
    commands: [small, small-cite]
custom.typ
#let small(it) = text(size: 0.8em, it)

#let _small-cite(self: none, it) = text(
  size: 0.7em,
  fill: self.colors.neutral-darkest.lighten(30%),
  it
)

#let small-cite(it) = touying-fn-wrapper(_small-cite.with(it))

small-citeは, スライドの中で内部的に用いられているパラメータ(self.colors.neutral-darkest, YAML上のcolor-jetに相当)を使っているため, touying-fn-wrapperを使う必要があります. 実用上はcolor-accentなどの色を使いたい時などに使うことを想定しています.

commandsに登録した関数は, span要素として, environmentsに登録した関数はdiv要素として使うことができます. もちろん自作関数ではなく, Typstのネイティブ関数を登録することも可能です. 以下の例はTypstのalign()関数をQuarto内で使う例を示しています.

https://github.com/kazuyanagimoto/quarto-slides-typst/issues/2

アニメーション

Touyingには, 上から順にアニメーションを適用するsimple animationsと, 複雑なアニメーションを実装するcomplex animationsがあります. このテンプレートでは, どちらも利用可能です. なお, handout: trueをYAMLヘッダーに追加することで, アニメーションを無効にしたPDFを作成することができます.

シンプルなアニメーション

{{ pause }}{{ meanwhile }}を使うことで, アニメーションを簡単に実装することができます.

slides.qmd
## Simple Animations {#sec-simple-animation}

Touying's [simple animations](https://touying-typ.github.io/docs/dynamic/simple) is available as `{{{< pause >}}}` and `{{{< meanwhile >}}}`

{{< pause >}}

**Animations in Lists**

Simple animations can be used in lists

- First {{< pause >}}
- Second

複雑なアニメーション

{.complex-anim repeat=}クラスと{.uncover options=''}{.only options=''}クラスを使うことで, 複雑なアニメーションを実装することができます. シンプルなアニメーションと異なり, アニメーションのコマ数(repeat)を指定し, 全体を{.complex-anim}で囲む必要があります. なお, #alternatives関数はTypstのネイティブコードでしか使えません.

slides.qmd
## Complex Animations {#sec-complex-animation}

:::: {.complex-anim repeat=4}

Touying's [complex animations](https://touying-typ.github.io/docs/dynamic/complex) is available as `{.complex-anim repeat=4}` environment.


At subslide `#self.subslide`{=typst}, we can

use [`{.uncover}` environment]{.uncover options='"2-"'} for reserving space,

use [`{.only}` environment]{.only options='"2-"'} for not reserving space,

```{=typst}
#alternatives[call `#only` multiple times \u{2717}][use `#alternatives` function #sym.checkmark] for choosing one of the alternatives. But only works in a native Typst code. \
```

::: {.only options='4'}

### Other Features

- All the animation functions can be used in Typst Math code [[Appendix]{.button}](#sec-math-animations)
- `handout: true` in YAML header is available for handout mode (without animations)

:::

::::

グラフ

通常のQuartoのようなRコードチャンクを使ってグラフを作成することができます. 以下はggplot2を使った例です. スライドの場合は二つのグラフを横に並べることが多いので, ggplot2facet_wrap()を用いた例と, Quartoのチャンクオプションlayout-ncol: 2を使った例を示します.

slides.qmd
## Figures

```{r}
#| fig-width: 10
#| fig-height: 4
#| fig-align: center

penguins |>
  filter(!is.na(sex)) |>
  mutate(lbl_facet = recode_factor(sex, `male` = "Male", `female` = "Female")) |>
  ggplot(aes(x = flipper_length_mm, y = bill_length_mm,
             color = species, shape = species)) +
  geom_point(size = 3) +
  geom_smooth(method = "lm", se = FALSE) +
  scale_color_manual(values = c(color_accent, color_accent2, color_accent3)) +
  facet_wrap(~lbl_facet) +
  labs(x = "Flipper Length (mm)", y = "Bill Length (mm)") +
  theme_quarto() +
  theme(legend.position = c(0.9, 0.1))
```

This is a `facet_wrap` example with `penguins` dataset.

slides.qmd
## Figures

```{r}
#| layout-ncol: 2
#| fig-width: 5
#| fig-height: 3
#| fig-align: center

penguins |>
  ggplot(aes(x = flipper_length_mm, y = body_mass_g,
             color = species, shape = species)) +
  geom_point(size = 3) +
  scale_color_manual(values = c(color_accent, color_accent2, color_accent3)) +
  labs(x = "Flipper Length (mm)", y = "Body Mass (g)") +
  theme_quarto() +
  theme(legend.position = c(0.9, 0.1))

penguins |>
  ggplot(aes(x = flipper_length_mm, color = species, shape = species)) +
  geom_density() +
  scale_color_manual(values = c(color_accent, color_accent2, color_accent3)) +
  labs(x = "Flipper Length (mm)", y = "Density") +
  theme_quarto() +
  theme(legend.position = c(0.9, 0.1))
```

This is an example of `layout-ncol: 2` for two figures.

他には, patchworkパッケージを使ってグラフを組み合わせることもできます.

テーブル

論文中の表はtinytableパッケージで作成することができます. このパッケージはTypstに対応しているため, HTMLや\LaTeXの表を作るのと同様の手順で表を作成することができます.
tinytableの使い方は以前の記事も参照してください.

https://zenn.dev/nicetak/articles/r-tips-tinytable-2024

slides.qmd
```{r}
tt_sum <- penguins |>
  filter(!is.na(sex)) |>
  summarize(across(bill_length_mm:body_mass_g, ~mean(.x, na.rm = TRUE)),
            .by = c(species, sex)) |>
  tidyr::pivot_wider(names_from = sex,
                     values_from = c(bill_length_mm, bill_depth_mm,
                                     flipper_length_mm, body_mass_g)) |>
  select(species, ends_with("_male"), ends_with("_female")) |>
  `colnames<-`(c("", rep(c("Bill Length (mm)", "Bill Depth (mm)",
                                  "Flipper Length (mm)", "Body Mass (g)"),
                                times = 4))) |>
  tt() |>
  group_tt(j = list("Male" = 2:5, "Female" = 6:9)) |>
  format_tt(j = c(2:9), digits = 4)
```

なお, tinytable::style_tt().complex-animを組み合わせることで, 表をアニメーションでハイライトすることも可能です.

slides.qmd
:::: {.complex-anim repeat=3}
::: {.only options='1'}
```{r}
tt_sum
```
:::

::: {.only options='2'}
```{r}
tt_sum |>
  style_tt(i = 2, background = color_accent,
           color = "white", bold = TRUE)
```
:::

ABBREVIATED

::::

また, 回帰表を作成できるmodelsummaryパッケージのデフォルトバックエンドはversion2.0.0以降tinytableに変更されたので, modelsummaryを普段通り使うことが可能です.

slides.qmd
```{r}
cm <- c(
  "speciesChinstrap" = "Chinstrap",
  "speciesGentoo" = "Gentoo",
  "sexmale" = "Male",
  "year" = "Year"
)

gm <- tibble(
  raw = c("nobs", "r2"),
  clean = c("Observations", "$R^2$"),
  fmt = c(0, 3)
)

list("(1)" = lm(bill_length_mm ~ species, data = penguins),
     "(2)" = lm(bill_length_mm ~ species + sex, data = penguins),
     "(3)" = lm(bill_length_mm ~ species + sex + year, data = penguins),
     "(4)" = lm(body_mass_g ~ species, data = penguins),
     "(5)" = lm(body_mass_g ~ species + sex, data = penguins),
     "(6)" = lm(body_mass_g ~ species + sex + year, data = penguins)) |>
  modelsummary(stars = c("+" = .1, "*" = .05, "**" = .01),
               coef_map = cm,
               gof_map = gm) |>
  group_tt(j = list("Bill Length (mm)" = 2:4, "Body Mass (g)" = 5:7))
```

Typst 数式問題

Typstの数式記法は\LaTeXとは異なります. Quarto上で記述する場合, \LaTeX記法がPandoc側で自動でTypst記法に変更されるため問題ありません. しかし, 表中の数式はそのままではTypst記法に変換されません (tinytableはあくまでデータフレームをTypstのコードに変換するだけです).

ありがたいことにTypstにはMiTeXという\LaTeX記法をTypst記法に変換するパッケージがあります. 表中の\LaTeX記法の数式は#mitex()をかませることでTypst記法に変換することができます. 以下のような関数を作成し, tinytableに適応することで表中の数式をMiTeXでレンダリングすることができます.

theme_mitex <- function(x, ...) {
    fn <- function(table) {
        if (isTRUE(table@output == "typst")) {
          table@table_string <- gsub("\\$(.*?)\\$", "#mitex(`\\1`)", table@table_string)
        }
        return(table)
    }
    x <- style_tt(x, finalize = fn)
    x <- theme_tt(x, theme = "default")
    return(x)
}

詳細はtinytableのGitHub Issue #345を参考にしてください.
https://github.com/vincentarelbundock/tinytable/issues/345

おわりに

以上がquarto-clean-typstの基本的な使い方になります. 私自身も実際の研究のプレゼンテーションに使用しており, その出来には満足しております. Beamerのコンパイルの遅さに辟易している方にはぜひ使ってもらいたいです.
Happy Quarto + Typst Life! 🥂

脚注
  1. 十分な\LaTeXの知識があればRevealjsとほとんど同様のカスタマイズ性があるはずですが, 私はその仕様の複雑さに挫折しました. Typstのフォントやレイアウト等の関数はHTMLに近く, その意味で「直感的」であると感じました. ↩︎

  2. 名前の由来は中国語の"投影" (tóuyǐng) から来ています. これは\LaTeXにおける"Beamer"がビデオプロジェクターを意味するドイツ語から来ていることに対応をしています. ちなみにPolyluxはそのドイツで有名なプロジェクターの一つのブランド名です. ↩︎

GitHubで編集を提案

Discussion