Typstテーマを自作した

Table of Contents

背景・動機

NixでTypstをBuildしGitHub Pagesでホスティングする でTypstから生成したPDFスライドを配信する仕組みを作った。 今後の登壇は可能な限りTypstから生成するつもりなのでテーマを自作した。

次の要件を満たす必要があった。

  • ライトテーマ/ダークテーマなどテーマを簡単に切り替えられるようにする
  • ソースコードのシンタックスハイライトサポート
  • 数式サポート

個人的にDracula Themeが好きなのでDracula Theme風に作る。

試したこと・やったこと

1. theme自作

polylux をベースに自作した。basic-polylux あたりを参考に改造した。

シンタックスハイライトは codelstDracula.tmTheme を読ませてハイライトしている。

#import "@preview/codelst:2.0.2": sourcecode
#import "@preview/polylux:0.4.0": *

#let blue = rgb("#282a36")
#let white = rgb("#f8f8f2")
#let pink = rgb("#ff79c6")
#let purple = rgb("#bd93f9")
#let orange = rgb("#ffb86c")
#let green = rgb("#50fa7b")
#let gray = rgb("#6272a4")

#let slide-title-header = toolbox.next-heading(h => {
  show: toolbox.full-width-block.with(fill: blue, inset: 1em)
  set text(fill: purple, size: 1.2em)
  strong(h)
})

#let the-footer(content) = {
  set text(size: 0.8em)
  show: pad.with(.5em)
  set align(bottom)
  context text(fill: white.lighten(40%), content)
  h(1fr)
}

#let outline = toolbox.all-sections((sections, _current) => {
  enum(tight: false, ..sections)
})

#let progress-bar = toolbox.progress-ratio(ratio => {
  set grid.cell(inset: (y: .03em))
  grid(
    columns: (ratio * 100%, 1fr),
    grid.cell(fill: pink)[],
    grid.cell(fill: purple)[],
  )
})

#let new-section(name) = slide({
  set page(header: none, footer: none)
  show: pad.with(20%)
  set text(size: 1.5em)
  name
  toolbox.register-section(name)
  progress-bar
})

#let focus(body) = context {
  set page(header: none, footer: none, fill: blue, margin: 2em)
  set text(fill: orange, size: 1.5em)
  set align(center)
  body
}

#let divider = line(length: 100%, stroke: .1em + pink)

#let setup(
  footer: none,
  text-font: "Migu",
  math-font: "Fira Math",
  code-font: "Fira Code",
  text-size: 23pt,
  body,
) = {
  set raw(theme: "./Dracula.tmTheme")

  set page(
    paper: "presentation-16-9",
    fill: blue,
    margin: (rest: 4em),
    footer: the-footer(footer),
    header: slide-title-header,
  )
  set text(
    font: text-font,
    size: text-size,
    fill: white,
  )
  set strong(delta: 100)
  show math.equation: set text(font: math-font)
  show raw: set text(font: code-font)
  set align(horizon)
  show emph: it => text(fill: pink, it.body)
  show heading.where(level: 1): _ => none

  body
}

2. flake.nixでBuildする

以下抜粋。 https://github.com/takeokunn/blog/blob/d7501711936b11d3f53c312c2775e20faab35602/typst/flake.nix

{
  pkgs.stdenv.mkDerivation {
      inherit name;
      src = ./.;
      nativeBuildInputs = with pkgs; [
        typst
        migu
        fira-math
        fira-code
        (emacsPkg.pkgs.withPackages (epkgs: with epkgs; [ org ox-typst ]))
      ];
      buildPhase = ''
        emacs --batch --load scripts/ox-typst.el --file org/${name}/article.org --funcall org-typst-slide-export-to-typst
        export TYPST_FONT_PATHS="${pkgs.migu}/share/fonts/truetype/migu:${pkgs.fira-math}/share/fonts/opentype:${pkgs.fira-code}/share/fonts/truetype/NerdFonts/FiraCode/"
        export TYPST_PACKAGE_PATH="${typstPackagesCache}/typst/packages"

        cp ./themes/dracula.typ org/${name}/dracula.typ
        cp ./themes/Dracula.tmTheme org/${name}/Dracula.tmTheme
        typst compile org/${name}/article.typ
      '';
      installPhase = ''
        mkdir -p $out
        cp org/${name}/article.pdf $out/${name}.pdf
      '';
    };
}

3. org-modeでスライドを作成

#+begin_export typst 内にtypstを記述する。

#+TYPST: #import "./dracula.typ": *
#+TYPST: #show: setup
* Title

スピーカーノートを記述。

#+begin_export typst
#slide[
  #set page(header: none, footer: none, margin: 3em)

  #text(size: 1.3em)[
    ,*こんにちは世界*
  ]

  My subtitle

  #divider

  #set text(size: .8em, weight: "light")
  The Author

  Jan 16, 2025

  Some extra info
]
#+end_export

得られた結果・所感

PDFを生成できるようになった。 https://www.takeokunn.org/pdf/example-slide.pdf

今後の展開・検討事項

今後の発表でガンガン使うことによってtypstに慣れたい。 また、ライトテーマの自作やレイアウトテンプレートの充実など自作テーマの幅を広げたい。