Typstで共通テスト数学のテンプレートを作成してみた
はじめに
共通テスト数学IIBCの問題形式を再現するためのTypstテンプレートを作成しました。Typstは新しい組版システムで、LaTeXよりもシンプルな記法で美しい文書を作成できます。
この記事では、Claude Codeを使ってテンプレートを作成した経緯と、完成したテンプレートの機能を紹介します。
作成の背景
共通テストの問題冊子には、独特のレイアウトやフォーマットがあります:
- 解答欄のボックス(ア、イ、ウなど)
- 丸囲み数字(①、②など)
- 選択肢番号(⓪、①、②など)
- 会話形式のボックス
- 注意事項のボックス
- 問題番号と配点表示
これらの要素をTypstで再現することで、問題作成の効率化を図ることができます。
完成したテンプレートの出力例
実際にテンプレートを使って作成したPDFを見てみましょう。
表紙・注意事項ページ

共通テストの表紙を再現。注意事項や出題科目、選択方法などを記載
選択方法の説明ページ

第1問~第7問の選択方法を表で示す
第1問(三角関数)

解答欄ボックス、丸囲み数字、会話ボックス、選択肢などの要素が含まれる

増減表や解答群の表示例
第2問(図形と計量)

余弦定理や三角形の面積を扱う問題
第3問(複素数平面)

複素数平面上の三角形に関する問題
テンプレートの主な機能
1. ページ設定
A4サイズ、適切な余白設定、ページ番号表示を自動で行います。
#set page(
paper: "a4",
margin: (top: 25mm, bottom: 25mm, left: 20mm, right: 20mm),
numbering: "— 1 —",
number-align: center,
footer: context [
#align(right)[
#text(size: 9pt)[2604—#counter(page).display()]
]
]
)
2. フォント設定
日本語に適したヒラギノフォントを使用し、読みやすい文字サイズを設定しています。
#set text(
font: ("Hiragino Mincho ProN", "Hiragino Sans"),
size: 10.5pt,
lang: "ja"
)
3. 便利な関数群
解答欄ボックス(answer-box)
解答を記入するためのボックスを作成します。
#let answer-box(content) = {
h(0.15em, weak: true)
box(
stroke: 1pt + black,
inset: 3pt,
baseline: 20%,
width: 3em,
height: 1.4em,
align(center + horizon, $display(#content)$)
)
h(0.15em, weak: true)
}
使用例:
#answer-box[ア] $pi$
選択肢番号(choice)
選択肢の丸囲み番号を作成します。
#let choice(body) = {
box(
stroke: 0.8pt + black,
inset: (x: 2pt, y: 4pt),
radius: 100%,
baseline: 25%,
text(size: 9pt, body)
)
}
使用例:
#choice[0] 点 P と点 Q の $x$ 座標が等しい
#choice[1] 点 P と点 Q の $y$ 座標が等しい
丸囲み数字(circle)
問題文中の丸囲み数字(①、②など)を表示します。
#let circle(num) = {
let circled = (
"①", "②", "③", "④", "⑤",
"⑥", "⑦", "⑧", "⑨", "⑩",
"⑪", "⑫", "⑬", "⑭", "⑮",
"⑯", "⑰", "⑱", "⑲", "⑳"
)
if type(num) == int and num >= 1 and num <= 20 {
circled.at(num - 1)
} else {
num
}
}
使用例:
$ sin(theta + pi/6) = sin 2theta #h(2em) dots.h.c #circle(1) $
問題番号(question)
問題番号、必答/選択の区別、配点を表示します。
#let question(num, points: none, required: false) = {
let req-text = if required {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(必答問題)]
} else {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(選択問題)]
}
let point-text = if points != none {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(配点 #points)]
} else { "" }
[
= 第 #num 問 #req-text #point-text
]
}
使用例:
#question(1, points: 15, required: true)
#question(4, points: 20, required: false)
注意事項ボックス(notice-box)
表紙の注意事項を囲むボックスです。
#let notice-box(content) = {
rect(
width: 100%,
inset: 10pt,
stroke: 1.5pt + black,
[#content]
)
}
使用例:
#notice-box[
試験開始の指示があるまで、この問題冊子の中を見てはいけません。
]
会話ボックス(dialogue-box)
問題内の会話形式の部分を表現します。
#let dialogue-box(content) = {
rect(
width: 100%,
inset: 10pt,
stroke: (
top: none,
bottom: none,
left: none,
right: none,
rest: 1pt + black
),
[#content]
)
}
使用例:
#dialogue-box[
太郎:角が等しくなくても、サインの値が等しくなることがあるね。
花子:サインの値が等しくなるのはどんなときか、単位円を用いて考えてみようか。
]
分数の解答欄(frac-answer)
分数形式の解答欄を作成します。
#let frac-answer(num, den) = {
$frac(#answer-box(num), #answer-box(den))$
}
使用例:
$ theta = frac(#answer-box[カ], #answer-box[キ]) pi $
実際の使用例
三角関数の問題(第1問)
#question(1, points: 15, required: true)
(1) $0 <= theta < pi$ のとき、方程式
$ sin(theta + pi/6) = sin 2theta #h(2em) dots.h.c #circle(1) $
の解を求めよう。以下では、$alpha = theta + pi/6$,$beta = 2theta$ とおく。
(i) $alpha = beta$ を満たす $theta$ は #answer-box[ア] $pi$ であり、
これは #circle(1) の解の一つである。
図形と計量の問題(第2問)
#question(2, points: 15, required: true)
三角形 $upright(A) upright(B) upright(C)$ において、
$upright(A) upright(B) = 5$,$upright(B) upright(C) = 7$,
$angle upright(A) upright(B) upright(C) = 60 degree$ とする。
(1) 余弦定理により、$upright(A) upright(C) = $ #answer-box[ア]
$sqrt(#answer-box[イ])$ である。
複素数平面の問題(第3問)
#question(3, points: 20, required: true)
複素数平面上の3点 $upright(A)(alpha)$,$upright(B)(beta)$,
$upright(C)(gamma)$ を頂点とする三角形について考える。
(1) $alpha = 2 + i$,$beta = -1 + 2i$,$gamma = 1 - i$ のとき、
三角形はどのような三角形か。#answer-box[ア] が成り立つ。
#answer-box[ア] の解答群
#choice[0] 正三角形である
#choice[1] 直角二等辺三角形である
#choice[2] 直角三角形であるが二等辺三角形ではない
Typstのメリット
1. シンプルな記法
LaTeXと比較して、より直感的で読みやすい記法を採用しています。
- 関数定義が簡潔(
#letで定義) - 条件分岐が自然な書き方
- 組版コマンドの記法が分かりやすい
2. 高速なコンパイル
LaTeXよりも圧倒的に高速にPDFを生成できます。リアルタイムプレビューも快適です。
3. モダンな機能
- 関数定義が柔軟
- 変数スコープが明確
- エラーメッセージが分かりやすい
Claude Codeとの協働作成プロセス
このテンプレート作成では、Claude Codeと以下のようなプロセスで進めました:
- 要件定義: 共通テストの問題形式を分析し、必要な要素をリストアップ
- 関数設計: 各要素に対応する関数を設計
- 実装とテスト: 実際にTypstコードを書き、PDFで出力を確認
- 微調整: レイアウトやスタイルを細かく調整
特に有用だったのは、Claude Codeが以下の点でサポートしてくれたことです:
- Typstの記法についての即座のフィードバック
- より簡潔な実装方法の提案
- エラー修正の迅速な対応
- 実際の共通テストの形式に近づけるための細かな調整
テンプレートの改善ポイント
実際に作成してみて、以下の改善点が見つかりました:
実装済みの機能
- ✅ 解答欄ボックス(
answer-box) - ✅ 選択肢番号(
choice) - ✅ 丸囲み数字(
circle) - ✅ 問題番号と配点表示(
question) - ✅ 注意事項ボックス(
notice-box) - ✅ 会話ボックス(
dialogue-box) - ✅ 解答表組み(
answer-table) - ✅ 分数の解答欄(
frac-answer)
今後の改善予定
- 図形描画機能: TypstのCeTZ(図形描画パッケージ)を使った図形の追加
- より精密なレイアウト: 実際の共通テストとの差分を詰める
- 数式表現の拡充: より複雑な数式への対応
- カスタマイズ性の向上: 科目や年度に応じた柔軟な設定
- 解答用紙テンプレート: マークシート形式の解答用紙の作成
まとめ
Claude Codeを活用して、Typstで共通テスト数学のテンプレートを作成しました。これらの関数を組み合わせることで、共通テストの問題形式を効率的に作成できます。
Typstは新しい組版システムですが、教育現場での資料作成にも十分活用できると感じました。特に数学の問題作成においては、LaTeXの代替として有力な選択肢になると思います。
AI(Claude Code)との協働により、短時間で実用的なテンプレートを作成できたことも大きな成果でした。実際の問題形式を見ながら、必要な機能を一つずつ実装していくプロセスは、とても効率的でした。
参考
付録:完全なTypstコード
実際に使用した.typファイルの全コードを以下に掲載します。
#set page(
paper: "a4",
margin: (top: 25mm, bottom: 25mm, left: 20mm, right: 20mm),
numbering: "— 1 —",
number-align: center,
footer: context [
#align(right)[
#text(size: 9pt)[2604—#counter(page).display()]
]
]
)
#set text(
font: ("Hiragino Mincho ProN", "Hiragino Sans"),
size: 10.5pt,
lang: "ja"
)
#set heading(numbering: "1.")
#set par(
leading: 0.65em,
justify: true,
first-line-indent: 1em
)
#show heading: it => {
set text(font: "Hiragino Sans")
it
}
#let answer-box(content) = {
h(0.15em, weak: true)
box(
stroke: 1pt + black,
inset: 3pt,
baseline: 20%,
width: 3em,
height: 1.4em,
align(center + horizon, $display(#content)$)
)
h(0.15em, weak: true)
}
#let choice(body) = {
box(
stroke: 0.8pt + black,
inset: (x: 2pt, y: 4pt),
radius: 100%,
baseline: 25%,
text(size: 9pt, body)
)
}
#let circle(num) = {
let circled = (
"①", "②", "③", "④", "⑤",
"⑥", "⑦", "⑧", "⑨", "⑩",
"⑪", "⑫", "⑬", "⑭", "⑮",
"⑯", "⑰", "⑱", "⑲", "⑳"
)
if type(num) == int and num >= 1 and num <= 20 {
circled.at(num - 1)
} else {
num
}
}
#let question(num, points: none, required: false) = {
let req-text = if required {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(必答問題)]
} else {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(選択問題)]
}
let point-text = if points != none {
text(fill: black, font: "Hiragino Sans", size: 11pt)[(配点 #points)]
} else { "" }
[
= 第 #num 問 #req-text #point-text
]
}
#let notice-box(content) = {
rect(
width: 100%,
inset: 10pt,
stroke: 1.5pt + black,
[#content]
)
}
#let dialogue-box(content) = {
rect(
width: 100%,
inset: 10pt,
stroke: (
top: none,
bottom: none,
left: none,
right: none,
rest: 1pt + black
),
[#content]
)
}
#let answer-table(..args) = {
table(
columns: args.pos().len(),
stroke: 1pt + black,
..args.pos()
)
}
#let frac-answer(num, den) = {
$frac(#answer-box(num), #answer-box(den))$
}
#align(center)[
#v(5em)
#text(size: 12pt, font: "Hiragino Sans")[
*令和7年度大学入学共通テスト*
]
#v(2em)
#text(size: 16pt, weight: "bold", font: "Hiragino Sans")[
*数学II・B・C*
]
#v(3em)
#notice-box[
#set par(first-line-indent: 0em)
#align(left)[
#v(0.5em)
#text(size: 11pt)[
*注意事項*
]
#v(1em)
1. 試験開始の指示があるまで、この問題冊子の中を見てはいけません。
2. この問題冊子は42ページあります。
3. 解答用紙は別に配付されます。
4. 問題は、第1問から第7問まであります。このうち、第1問、第2問、第3問は必答問題で、第4問から第7問までは選択問題です。選択問題については、次ページの指示に従って解答してください。
5. 問題冊子の余白等は適宜利用してもかまいませんが、どのページも切り離してはいけません。
#v(0.5em)
]
]
#v(3em)
#text(size: 11pt, font: "Hiragino Sans")[
(下書用紙は、ページの後にあります。)
]
]
#pagebreak()
#align(center)[
#v(3em)
#text(size: 13pt, font: "Hiragino Sans")[
*選択方法*
]
#v(2em)
]
#set par(first-line-indent: 0em)
第4問から第7問までのうちから3問を選択し、解答してください。
4問以上解答した場合は、はじめの3問を採点します。
#v(2em)
#align(center)[
#table(
columns: (auto, auto, auto, auto),
stroke: 1pt + black,
align: center + horizon,
inset: 12pt,
table.header(
[*問題番号*],
[*出題分野*],
[*配点*],
[*備考*]
),
[第1問], [三角関数], [15], [必答],
[第2問], [図形と計量], [15], [必答],
[第3問], [複素数平面], [20], [必答],
[第4問], [確率分布], [20], [選択],
[第5問], [数列], [20], [選択],
[第6問], [ベクトル], [20], [選択],
[第7問], [微分・積分], [20], [選択]
)
]
#pagebreak()
#question(1, points: 15, required: true)
(1) $0 <= theta < pi$ のとき、方程式
$ sin(theta + pi/6) = sin 2theta #h(2em) dots.h.c #circle(1) $
の解を求めよう。以下では、$alpha = theta + pi/6$,$beta = 2theta$ とおく。
(i) $alpha = beta$ を満たす $theta$ は #answer-box[ア] $pi$ であり、これは #circle(1) の解の一つである。
(ii) $alpha + beta = pi$ を満たす $theta$ は #answer-box[イ] $pi$ であり、これは #circle(1) の解の一つである。
(i)、(ii)より、#circle(1) の解は #answer-box[ア] $pi$,#answer-box[イ] $pi$ である。
#v(1em)
#dialogue-box[
太郎:角が等しくなくても、サインの値が等しくなることがあるね。
花子:サインの値が等しくなるのはどんなときか、単位円を用いて考えてみようか。
太郎:$alpha = beta$ となるときと、$alpha + beta = pi$ となるときがあるね。
花子:なるほど。#circle(1) の両辺の角がどのような関係にあるか、一般的に考えてみよう。
]
#v(1em)
(2) $0 <= theta < 2pi$ のとき、不等式
$ sin(theta + pi/6) > sin 2theta #h(2em) dots.h.c #circle(2) $
を解こう。
$f(theta) = sin(theta + pi/6) - sin 2theta$ とおく。$0 <= theta < 2pi$ における $f(theta)$ の増減を調べると、次の表のようになる。
#v(1em)
#align(center)[
#table(
columns: (1fr, 0.4fr, 0.4fr, 0.4fr, 0.4fr, 0.4fr, 0.4fr, 0.4fr, 0.4fr),
stroke: 1pt + black,
align: center + horizon,
inset: 8pt,
[$theta$], [0], [], [$#answer-box[ア] pi$], [], [$#answer-box[イ] pi$], [], [$#answer-box[ウ] pi$], [$2pi$],
[$f'(theta)$], [], [+], [0], [-], [0], [+], [0], [-],
[$f(theta)$], [0], [↗], [], [↘], [0], [↗], [], [↘], [0]
)
]
#v(1em)
したがって、不等式 #circle(2) の解は
#h(2em) $#answer-box[エ] < theta < #answer-box[オ] pi, #h(1em) #answer-box[カ] pi < theta < #answer-box[キ] pi$
である。ただし、#answer-box[エ] には 0 を入れよ。
#v(2em)
(3) $0 <= theta < 2pi$ において、$sin theta = cos 2theta$ を満たす $theta$ は全部で #answer-box[ク] 個ある。
#v(1em)
#answer-box[ク] の解答群
#choice[0] 2 #h(2em) #choice[1] 3 #h(2em) #choice[2] 4 #h(2em) #choice[3] 5 #h(2em) #choice[4] 6
#pagebreak()
#question(2, points: 15, required: true)
三角形 $upright(A) upright(B) upright(C)$ において、$upright(A) upright(B) = 5$,$upright(B) upright(C) = 7$,$angle upright(A) upright(B) upright(C) = 60 degree$ とする。
(1) 余弦定理により、$upright(A) upright(C) = $ #answer-box[ア] $sqrt(#answer-box[イ])$ である。
また、正弦定理により、三角形 $upright(A) upright(B) upright(C)$ の外接円の半径は $#answer-box[ウ] sqrt(#answer-box[エ]) / #answer-box[オ]$ である。
#v(1em)
(2) 三角形 $upright(A) upright(B) upright(C)$ の面積は $#answer-box[カ] sqrt(#answer-box[キ]) / #answer-box[ク]$ である。
#v(1em)
(3) 辺 $upright(B) upright(C)$ を $2:1$ に内分する点を $upright(D)$ とする。このとき、$upright(A) upright(D) = $ #answer-box[ケ] $sqrt(#answer-box[コ])$ である。
また、$cos angle upright(A) upright(D) upright(B) = #answer-box[サ] / #answer-box[シ] sqrt(#answer-box[ス])$ である。
#v(1em)
(4) 辺 $upright(A) upright(B)$ 上に点 $upright(P)$ をとり、$upright(C) upright(P) perp upright(A) upright(D)$ となるようにする。このとき、$upright(A) upright(P)$ の長さは #answer-box[セ] が成り立つ。
#v(1em)
#answer-box[セ] の解答群
#choice[0] $upright(A) upright(P) < 2$
#choice[1] $upright(A) upright(P) = 2$
#choice[2] $2 < upright(A) upright(P) < 3$
#choice[3] $upright(A) upright(P) = 3$
#choice[4] $3 < upright(A) upright(P) < 4$
#choice[5] $upright(A) upright(P) = 4$
#choice[6] $4 < upright(A) upright(P)$
#pagebreak()
#question(3, points: 20, required: true)
複素数平面上の3点 $upright(A)(alpha)$,$upright(B)(beta)$,$upright(C)(gamma)$ を頂点とする三角形について考える。
(1) $alpha = 2 + i$,$beta = -1 + 2i$,$gamma = 1 - i$ のとき、三角形 $upright(A) upright(B) upright(C)$ はどのような三角形か。#answer-box[ア] が成り立つ。
#v(1em)
#answer-box[ア] の解答群
#choice[0] 点 $upright(A)$ と点 $upright(B)$ の $x$ 座標が等しい
#choice[1] 点 $upright(A)$ と点 $upright(B)$ の $y$ 座標が等しい
#choice[2] 点 $upright(B)$ と点 $upright(C)$ の $x$ 座標が等しい
#choice[3] 点 $upright(B)$ と点 $upright(C)$ の $y$ 座標が等しい
#choice[4] 点 $upright(C)$ と点 $upright(A)$ の $x$ 座標が等しい
#choice[5] 点 $upright(C)$ と点 $upright(A)$ の $y$ 座標が等しい
#v(1em)
(2) 一般に、三角形 $upright(A) upright(B) upright(C)$ が正三角形であるための条件は、
#h(2em) $(gamma - alpha)^2 + (alpha - beta)^2 + (beta - gamma)^2 = #answer-box[イ]$
である。
#v(1em)
#answer-box[イ] の解答群
#choice[0] $0$ #h(2em) #choice[1] $1$ #h(2em) #choice[2] $-1$ #h(2em) #choice[3] $i$ #h(2em) #choice[4] $-i$
#v(1em)
(3) 複素数 $omega = (-1 + sqrt(3)i) / 2$ について考える。
(i) $omega^3 = $ #answer-box[ウ] であり、$1 + omega + omega^2 = $ #answer-box[エ] である。
#v(1em)
#answer-box[ウ]、#answer-box[エ] の解答群(同じものを繰り返し選んでもよい。)
#choice[0] $-1$ #h(2em) #choice[1] $0$ #h(2em) #choice[2] $1$ #h(2em) #choice[3] $i$ #h(2em) #choice[4] $omega$
#v(1em)
(ii) $alpha$,$beta$,$gamma$ が
#h(2em) $gamma = omega alpha + (1 - omega)beta$
を満たすとき、三角形 $upright(A) upright(B) upright(C)$ は #answer-box[オ] である。
#v(1em)
#answer-box[オ] の解答群
#choice[0] 正三角形である
#choice[1] 直角二等辺三角形である
#choice[2] 直角三角形であるが二等辺三角形ではない
#choice[3] $angle upright(B) upright(A) upright(C) = 120 degree$ の二等辺三角形である
#choice[4] $angle upright(A) upright(B) upright(C) = 120 degree$ の二等辺三角形である
#choice[5] $angle upright(B) upright(C) upright(A) = 120 degree$ の二等辺三角形である
#choice[6] 上記のいずれでもない
Discussion