合同会社Serverspec Operations代表社員
フリーランスのソフトウェアエンジニア
mizzy.org / mizzy / gosukenator
北海道出身・神奈川県在住
沖縄は約4年ぶりです
コンテンツはMarkdown、レイアウトはHTML、見た目はCSS
ビルドすると静的なファイル一式になる
他にあまりない特徴は2種類の検査
静的検査(型検査): レイアウトに書いたスロット定義がそのままスキーマになる
動的検査(lint): 実際に描画して、はみ出しや小さすぎる文字を報せる
1997年 〜 2014年
PowerPoint / Keynote
2014年 〜 2025年
Deckset
2025年
k1LoW/deck
Markdownで内容を書き、デザインはプリセットのテーマから選択
色やフォントはカスタマイズできるが、レイアウトは変えられない
オリジナルテーマの作成はできない
デザインのことを考えずに中身に集中できるのは利点
レイアウト調整ができない、特にコードを見やすくするための調整ができないのが難点
Markdownで書いた内容をGoogle Slidesに流し込む
デザインはGoogle Slidesのレイアウトとして自分でつくれる
HTML/CSSを書かなくてもGoogle SlidesのUIでデザインを調整できるのは自分にはうれしい
Songmu/laminateと組み合わせることでコードも見やすくできる
シンタックスハイライト+画像化が簡単にできる
AIの進歩がすさまじい
自分が苦手なデザインをAIに任せたいと思うようになった
Google SlidesはAIにデザインを任せるには不便
AIにデザインを任せやすいプレゼンツールが欲しくなった
ただしコンテンツの主導権は自分が握りたい
だから自分でつくることにした
「Claude Designにスライドをデザインしてもらうなら、フォーマットは何がベスト?」
答えはHTML/CSS。Claude Designが生成するのがHTML/CSSだから
当初Speaker Deckへの入稿(PDF)も考慮
Speaker Deckにはこだわらないことにした
コンテンツとデザインの分離
書いている間、内容に集中できる
バージョン管理したい
既存ツールはどれもMarkdown
Deckset・k1LoW/deck・Marp・Slidev...etc
最初に考えたのは「Markdownを書く → Claude Designが完成したスライドのHTMLにする → Peithoで発表する」
だがAIが完成品を吐くならPeithoの役割は発表だけになる
それならreveal.jsで十分ではないか、という話になる
そもそもAIの生成は同じ入力から同じ出力になるとは限らない
Markdown修正 → 生成 → 確認のループを回す時間も気になる
そこでHTMLはPeithoが決まったルールで生成し、AIを通さない
Markdown
内容と構造を記述する
Peitho
MarkdownをレイアウトHTMLに流し込み、発表まで担う
Claude Design
スライドの生成には挟まず、テーマとレイアウト体系をつくる
それをClaude CodeがレイアウトHTMLとCSSに書き起こす
HTMLネイティブでMarkdownから組み立てるツールは既にある
ではPeithoの差別化をどこに置くか
レイアウトに型を持たせて内容を検査する
Carinaで独自DSLと型システムをやってるmizzyさんなら、スライド記法そのものに型や構造の保証を持ち込む(例: レイアウトの制約を型で縛る、参照切れをコンパイル時に検出する)あたりが、既存ツールにないPeithoらしさになりうる
k1LoW/deckが使うGoogle Slidesのレイアウトは、名前付きプレースホルダ(TITLE / SUBTITLE / BODY)を持つテンプレート
Markdownは決まったルールでプレースホルダに流し込まれる
Peithoはこのプレースホルダに型を持たせることにした
前ページのレイアウトに流し込むMarkdown
<!-- {"layout": "title-subtitle-image"} -->
# スライドのタイトル
## サブタイトル

プレースホルダが足りない場合、残りのコンテンツは描画されない
— k1LoW/deckのREADME
k1LoW/deckではプレースホルダに対する検査がない
Peithoではプレースホルダをスロットと呼び、受け取る型と個数を宣言する
合わなければ流し込む前にビルドエラーにする
スロットが受け取る型と個数をレイアウトHTMLの中で宣言する。これが契約になる
<section>
<h1><slot name="title"
accepts="inline" arity="1"></slot></h1>
<slot name="code"
accepts="code" arity="0..1"></slot>
</section>
本文を書くとこうなる
error: slide 1 ('slide-1'), line 7:
no slot in layout 'simple' accepts this paragraph
help: add a 'body' slot to the layout or remove the paragraph
コードブロックを2つ書くとこうなる
error: slide 1 ('slide-1'), line 7:
slot 'code' got 2 item(s), but layout 'simple' allows 0..1
help: use a layout with more code capacity
or remove one code block
レイアウト名の指定は省略できる
Peithoがレイアウトを決める手順
明示指定{"layout":"…"}があればそれを使う。未知の名前はビルドエラー
明示指定がなければスライドの内容を各レイアウトの契約に当てる
適合がちょうど1つならそれに決まる
複数でも0個でもビルドエラー。黙ってどれかを選ぶことはしない
合うレイアウトが存在しないとこうなる
error: slide 1 ('slide-1'), line 5:
no layout matches this slide
shot: no slot in layout 'shot' accepts this code block
simple: no slot in layout 'simple' accepts this image
help: adjust the slide content,
or add a layout that accepts it
契約に合うレイアウトが3つある場合、こうなる
error: slide 1 ('slide-1'), line 5:
slide matches multiple layouts:
chapter, cover, statement
help: pick one explicitly
with <!-- {"layout":"…"} -->
レイアウト名をcodeではなくkodeと書くとこうなる
error: slide 1 ('slide-1'), line 5:
unknown layout 'kode'
help: use one of: books, chapter, code-stack,
code, compare, cover, photos, portrait,
profile, shot, statement, topic
キーを付けたスライドを消してCSSだけ残すとこうなる
error: line 7: overrides.css:
unknown slide key 'whoami'
in override selector
help: add "key":"whoami" to the page
settings comment of the slide it
styles (<!-- {"key":"whoami"} --> if
it has none), or delete this rule
from overrides.css
型検査は最初の設計段階からあった
はみ出しの検査を2週間後に追加
入れてみたらPeithoのサンプルスライドで2枚はみ出していた
さらに2週間後、文字サイズの検査を追加
はみ出しを文字を縮めてごまかせないようにする
ビルドした全スライドをheadless Chromeで描画する
はみ出し
スライドの枠: 全要素のgetBoundingClientRect()と比べる
要素の枠: 中身を切る要素(overflow: hiddenなど)のscrollHeight - clientHeight
文字サイズ: getComputedStyle(要素).fontSizeの最小値を見る
中身がスライドの枠を超えるとこうなる
warning: slide 1 content overflows the slide box vertically by 257px (content 977px, box 720px) help: shrink or split the slide content, or adjust the layout CSS checked 1 slide(s): 1 warning(s)
コードがパネルに収まらないとこうなる
warning: slide 1 content overflows the `code` slot vertically by 95px help: shrink or split the slide content, or adjust the layout CSS checked 1 slide(s): 1 warning(s)
文字が24ptより小さいとこうなる
warning: slide 1 has text at 12pt, below the recommended 24pt: "行1 ダミー…" help: raise the font size in the layout CSS, or move content to another slide instead of shrinking it
CSSを静的に読んでも実際の大きさは分からない
カスケード、継承、emや%のような相対単位がある
何行になるかは描いてみないと分からない
折り返す位置はフォントの字幅によって変わる
描画してもすぐには大きさが確定しない
画像とWebフォントは読み込みが終わると再描画される
動的検査は読み込みを待ってから測る
静的検査(型検査): 内容がレイアウトの型に合うかを確かめる
合わなければ黙って捨てずにビルドエラーにする
動的検査(lint): 型では見つからない崩れを補う
はみ出しや小さすぎる文字を描画して測る
コンテンツとデザインを分離する
k1LoW/deckから
Markdownをレイアウトのプレースホルダに流し込む
k1LoW/deckから
型で契約を縛り、合わなければエラーにする
Carinaから
github.com/mizzy/peitho
peitho.gosu.ke(ドキュメントとデモ)
brew install mizzy/tap/peitho