itemize(箇条書き)

LaTeX の itemize が行頭に打つ黒丸は、かつて斜体の文章のなかに入ると一緒に傾いてしまうことがありました。記号が周囲のフォント指定をそのまま受け継いでいたからです。2020-02-02 のリリースで \labelitemfont という命令が入り、この癖はようやく止まりました。つまり箇条書きの「点」は飾りではなく、一つひとつが組版された文字です。このページでは itemize を、行頭記号がどこで定義されているか、\item[...] で一つだけ差し替えると何が起きるか、そして項目のあいだの空きを決めている二つの長さ、という三つの角度から見ていきます。

黒丸はどこで定義されているのか——\labelitemi から \labelitemiv

行頭記号は itemize 環境ではなく、ドキュメントクラスに書かれています。article.cls を開くと \labelitemi から \labelitemiv まで四つの命令が並んでいて、順に \textbullet(•)、太字の \textendash(–)、\textasteriskcentered(*)、\textperiodcentered(·)です。itemize 側の仕事は、いま何段目にいるかを数えて \labelitem + ローマ数字という名前を組み立て、その命令を呼ぶことだけ。だから記号を変えたければ環境に手を入れる必要はなく、命令を差し替えれば足ります。四つ目の後ろが無いのも、単に五つ目の名前が定義されていないからです。

命令階層記号article.cls での中身
\labelitemi1\labelitemfont \textbullet
\labelitemii2\labelitemfont \bfseries \textendash(太字)
\labelitemiii3*\labelitemfont \textasteriskcentered
\labelitemiv4·\labelitemfont \textperiodcentered
\labelitemfont\normalfont。周囲の書体から記号を切り離す

表の最後にある \labelitemfont が、冒頭で触れた 2020 年の修正です。四つの記号のすべてがこれを頭に付けて呼ばれるので、いまは斜体や太字のブロックのなかに箇条書きを置いても、記号だけは正立のまま保たれます。逆にこれを利用して、記号だけ別の書体で組むこともできます——\renewcommand{\labelitemfont}{\normalfont\sffamily} と書けば、本文はセリフのまま行頭記号だけがサンセリフになります。階層全体の記号を差し替えたいときは、対応する \labelitem…\renewcommand で上書きします。プリアンブルに置けば文書全体に、環境やグループの中に置けばそこだけに効きます。

latex
% swap the first-level marker for the whole document
\renewcommand{\labelitemi}{$\diamond$}

\begin{itemize}
  \item this level now uses a diamond
  \item so does this one
\end{itemize}

\item[...] で一項目だけ記号を差し替える

\item に角括弧で引数を渡すと、その項目だけ行頭記号が引数の中身に置き換わります。\item[$\star$] なら星、\item[--] ならダッシュ、\item[Note] なら「Note」という語がそのまま記号の位置に組まれます。ここでよくある誤解を一つ正しておくと、itemize の角括弧ラベルは太字になりません。太字になるのは description 環境で、あちらは \descriptionlabel\bfseries を含んでいます。itemize のラベル整形はただの \hss\llap{…}——右寄せするだけで、書体には触れません。

latex
\begin{itemize}
  \item the default marker
  \item[$\star$] a star for this one entry
  \item[Note] a whole word in the marker slot
\end{itemize}

この \llap が落とし穴も生みます。ラベルは \labelwidth の幅に右寄せで置かれますが、\llapはみ出しても警告を出しません\item[Note] くらいなら収まりますが、\item[Prerequisite] のように長い語を入れると、ラベルは左へずるずる伸びて本文の左端を越え、ひどいときは版面の外まで出ます。行送りも崩れませんし、Overfull \hbox の警告も出ないので、PDF を見るまで気づけません。長い語をラベルにしたいなら、それはもう itemize ではなく description の仕事です。enumitem を使うなら labelwidth=leftmargin= で幅を確保できます。

項目のあいだが空きすぎるとき——\itemsep\parsep

項目と項目のあいだの空きは、一つの長さではなく二つの合計です。\item は次の項目を始める前に \itemsep を挿入しますが、その直後に新しい段落が始まるので \parskip の分もさらに入ります。そしてリストの中では \parskip\parsep に設定されている。つまり実際の間隔は \itemsep + \parsep です。10pt の article なら第 1 階層でどちらも 4pt、合わせて 8pt——行送りが 12pt ですから、項目のあいだに 3 分の 2 行ぶんの空きが入っている計算になります。「なぜか間延びして見える」の正体はたいていこれで、\itemsep だけをゼロにしても半分しか詰まらないのもこのためです。

長さ第 1 階層の既定値(10pt の article)どこに効くか
\itemsep4pt plus 2pt minus 1pt\item が次の項目の前に入れる空き
\parsep4pt plus 2pt minus 1pt項目内の段落間。リスト内では \parskip にもなるので項目間にも効く
\topsep8pt plus 2pt minus 4ptリストの上下に入る空き
\partopsep2pt plus 1pt minus 1pt直前に空行があるときだけ \topsep に加算される

表の最後の行は、知らないと必ず一度は悩む挙動です。\begin{itemize} の直前に 空行を入れるかどうかで、リストの上の空きが 8pt と 10pt に変わります。空行があるとリストが垂直モードで始まるため \partopsep が加算されるからで、原稿を整形しただけのつもりが版面が動く、という事故はこれが原因のことが多くあります。値そのものにも仕掛けがあって、plusminus の付いたゴム長なので、ページを組むときに 6pt まで縮み 12pt まで伸びます。数値がそのまま出力に現れるわけではありません。なお階層が深くなると既定値は自動的に小さくなり、第 2 階層では \itemsep\parsep とも 2pt、第 3 階層では \parsep が 0pt になります。入れ子のリストが締まって見えるのは、そういう設計だからです。

latex
% tighten one list by hand: inside the body it is \parskip,
% not \parsep, that still carries the second half of the gap
\begin{itemize}
  \setlength{\itemsep}{0pt}
  \setlength{\parskip}{0pt}
  \item first
  \item second
  \item third
\end{itemize}

ここでゼロにしているのが \parsep ではなく \parskip である点に注意してください。\parsep から \parskip へのコピーは \begin{itemize} の時点で済んでいるので、本文に入ってから \parsep を変えても後半の 4pt は残ります。実測すると、既定で 19.93pt あった行送りは \parsep を 0 にしただけでは 15.94pt までしか縮まらず、\parskip を 0 にして初めて 11.96pt——ちょうど 1 行分——になります。同じことを毎回書くくらいなら enumitem を読み込んで [noitemsep]\itemsep\parsep をゼロに)や [nosep](さらに \topsep\partopsep もゼロに)を渡すほうが速く、\setlist で文書全体の既定にもできます。もう一つ、リストが思わぬところでページをまたぐという相談もよくあります。これは事故ではなく設計で、article クラスは \@itempenalty−51 に設定しています。負のペナルティは「ここで改ページしてよい」ではなく「ここで改ページしたい」の意味なので、LaTeX は項目の切れ目を積極的に改ページ候補として扱います。どうしても割りたくないリストは、\begin{minipage} に入れるか \samepage を使って明示的に押さえます。

\item にまつわる二つのエラー

itemize は空にできません。\item を一つも書かずに \begin{itemize}\end{itemize} を閉じると ! LaTeX Error: Something's wrong--perhaps a missing \item. で止まります。実際には「\item を書き忘れた」より、リストの先頭に本文や画像を直接書いたときに出ることのほうが多いエラーです。環境を開いた直後に置けるのは \item か、空きや長さの設定だけだと考えてください。逆向きの失敗もあります。リストの外で \item を使うと ! LaTeX Error: Lonely \item--perhaps a missing list environment. が出ます。これは \end{itemize} を書きすぎた、あるいは \item を含むブロックをコピーしたときに \begin を取りこぼした、という場面でよく現れます。どちらもメッセージがそのまま原因を言い当てているので、行番号だけ見て前後の \begin\end の対応を確かめれば片が付きます。

箇条書きにすべきとき、してはいけないとき

itemize は「短くした文章」ではなく、項目どうしを横に比べさせるための道具です。番号が無いということは、読者に対して「この四つに順序も優劣もない」と宣言しているのと同じ。だから粒度も文体も長さもばらばらの項目を並べると、読者は比較の軸を探して迷子になります。逆に、順序や優先度が意味を持った瞬間に enumerate へ移るべきで、そこを曖昧にしたまま黒丸を並べるのがいちばん読みにくい。以下は、原稿を読み返すときのチェックリストです。

  • 文法をそろえる。 一つ目が名詞句なら全部を名詞句に、命令形なら全部を命令形にします。
  • 説明が長くなったら段落に戻す。 各項目が三文を超えるなら、それは箇条書きではなく小見出し付きの本文です。
  • 入れ子は 2 階層まで。 LaTeX 自体は 4 階層まで許しますが、読者が構造を追えるのはたいてい 2 階層までです。
  • 順序に意味が出たら enumerate へ。 「まず」「次に」と書きたくなったら、それは番号付きリストの合図です。
  • 記号を変えるのは意味があるときだけ。 \item[$\star$] は一項目を目立たせる道具で、装飾ではありません。