2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【鉄テキに匹敵】凝った tcolorbox の作り方【後編】(LaTeX確認シリーズ#6)

2
Posted at

【鉄テキに匹敵】凝った tcolorbox の作り方【後編】(LaTeX確認シリーズ#6)

前編 では、枠そのものを描き替える技法を4つ紹介しました。角を 45 度に落とす、内側にえぐる、地紋を敷く、タイトル帯を旗の形にする。どれも「輪郭を1本、自分で引き直す」話です。

後編は方向が変わります。テーマは装飾を配ることです。

1個だけ描いて、あとは座標変換で配る

リングを等間隔に3つ。階段を任意の段数。渦巻きを四隅に4つ。これらを手で4回書いたら負けです。1個描いて、比率と鏡像で展開します。そうしておくと、あとから形を変えるときに直すのが1箇所で済みます。

前編と同様、コードは全て TeX Live 2026 + LuaLaTeX + ltjsarticle で実際にコンパイルし、PNG に書き出して目視確認済みです。

前編のおさらい(これだけ覚えていれば読めます)

  finish / overlay / 本文 / underlay / interior / frame / shadow
  + タイトル箱は独立したレイヤースタックを持つ
  + upper と lower の間に segmentation
  • 箱の定義には必ず enhanced を書く。これがないと underlay 系は全部無視される
  • underlay の中身は素の TikZ。frame.north westinterior.south east などのノードが使える
  • 射影座標 (frame.north west -| title.east) は「上辺の高さで、タイトルの右端」
  • タイトルは underlay より先に描かれるので、閉じたパスを引くと題字を突き抜ける

配色トークンも前編と同じものを使います。

\definecolor{qdeep}{HTML}{075985}   % sky-800
\definecolor{qmid}{HTML}{0284C7}    % sky-600
\definecolor{qcyan}{HTML}{38BDF8}   % sky-400
\definecolor{qlight}{HTML}{E0F2FE}  % sky-100

\newlength{\BoxIndent}\setlength{\BoxIndent}{1\zw}
\NewDocumentCommand{\BoxStrut}{}{\vphantom{}\vphantom{j}}

必要なパッケージは \usepackage[most]{tcolorbox} / \usepackage{varwidth} / \usetikzlibrary{calc} の3つです。


技法5:辺ごとに太さを変える+中間点座標(リング綴じ)

![リング綴じ風]](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4131267/4cbf5f30-194e-4e2a-98d8-6808f0c16784.png)

枠線の太さは辺ごとに指定できます

leftrule=6mm, rightrule=1mm, toprule=1mm, bottomrule=1mm,

左だけ極端に太くすると、そこが装飾を置く「帯」になります。リング綴じのノート風はこれで作れます。

% #1 = 座標の「名前」だけを渡す(括弧は付けない)
\NewDocumentCommand{\RingAt}{ m }{%
  \fill[black!35!gray] ([xshift=-4pt]#1) -- ++(15pt,-1.2pt) circle (3pt);
  \fill[qdeep]         ([xshift=-4pt]#1) -- ++(16pt,-1.2pt) circle (3pt);
  \fill[left color=qdeep!70, right color=white]
    ([xshift=-4pt]#1) arc [start angle=90, end angle=180, radius=1pt]
    -- ++(0pt,-1pt) arc [start angle=180, end angle=270, radius=1pt]
    -- ++(8pt,0pt) -- ++(0pt,2.5pt) -- cycle;}

\DeclareTColorBox{RingBox}{ m O{} }{%
  enhanced, breakable=false,
  colback=white, colframe=qdeep, arc=2pt,
  leftrule=6mm, rightrule=1mm, toprule=1mm, bottomrule=1mm,
  left=5mm, right=3mm, top=6mm, bottom=3mm,
  before upper={\setlength{\parindent}{\BoxIndent}},
  attach boxed title to top left={xshift=8mm, yshift=-\tcboxedtitleheight/2},
  boxed title style={size=small, colback=white, colframe=white},
  coltitle=qdeep, fonttitle=\gtfamily\bfseries,
  underlay={%
    \coordinate (RA) at ([xshift=3mm]frame.north west);
    \coordinate (RC) at ([xshift=3mm]frame.south west);
    \coordinate (R1) at ($(RA)!.25!(RC)$);
    \coordinate (R2) at ($(RA)!.50!(RC)$);
    \coordinate (R3) at ($(RA)!.75!(RC)$);
    \RingAt{R1}\RingAt{R2}\RingAt{R3}},
  title={#1}, #2}

主役は中間点座標 $(A)!.5!(B)$ です。

これは「点 A と点 B を結んだ線分の、50% の位置」を意味します。!.25! なら 25%。\usetikzlibrary{calc} が必要です。

これを使うと、箱の高さが変わってもリングが自動的に等間隔で再配置されますyshift=-20pt のような絶対座標で3つ並べると、箱が伸びた瞬間に下半分がガラ空きになります。

「比率で置く」は装飾を反復配置するときの基本原則だと思ってください。後編の残り2つも、結局この原則の変奏です。


技法6:\foreach で装飾を反復する

階段の目盛り

装飾を規則的に並べるなら \foreach です。ここでは、タイトルの右から箱の右端まで、上罫そのものを階段に置き換えます

% 装飾に使える幅は「\linewidth からタイトル幅と左オフセットを引いた残り」。
% ここは \NewDocumentCommand ではなく \newcommand。座標の中の式は
% 展開しながら読まれるので、robust な命令を置くと数値として読めない。
\newcommand{\StepSpan}{(\linewidth-\tcboxedtitlewidth-12mm)}

\DeclareTColorBox{StepBox}{ m O{5} O{} }{%
  % 下段だけ色を変える colbacklower は enhanced では無視される。
  % 上下で塗りを分ける skin=bicolor(enhanced 系の一員)を使う。
  enhanced, skin=bicolor, breakable=false,
  colback=white, colframe=qdeep, boxrule=.8pt, arc=0mm,
  colbacklower=qlight,
  left=6mm, right=6mm, top=8mm, bottom=4mm,
  before upper={\setlength{\parindent}{\BoxIndent}},
  attach boxed title to top left={xshift=6mm, yshift=-\tcboxedtitleheight/2},
  boxed title style={size=small, sharp corners,
    colback=white, boxrule=.7pt, colframe=qdeep},
  coltitle=qdeep, fonttitle=\gtfamily\bfseries,
  varwidth boxed title=.6\linewidth,
  segmentation style={draw=qdeep, line width=.9pt, solid},
  underlay={%
    % 残り幅を #2 等分し、右へ行くほど深い段を積む
    \foreach \n [evaluate=\n as \lv using {int(18+62*\n/#2)}] in {1,...,#2}{%
      \fill[qmid!\lv!white]
        ([xshift=-\StepSpan*(#2-\n+1)/#2, yshift=-5.5mm*\n/#2]frame.north east)
        rectangle
        ([xshift=-\StepSpan*(#2-\n)/#2, yshift=0mm]frame.north east);}
    % 階段の輪郭を一筆でなぞる(\foreach はパスの途中でも使える)
    \draw[qdeep, line width=.7pt, line join=miter]
      ([xshift=-\StepSpan]frame.north east)
      \foreach \n in {1,...,#2}{%
        -- ([xshift=-\StepSpan*(#2-\n+1)/#2, yshift=-5.5mm*\n/#2]frame.north east)
        -- ([xshift=-\StepSpan*(#2-\n)/#2, yshift=-5.5mm*\n/#2]frame.north east)};},
  title={\BoxStrut #1}, #3}

\tcboxedtitlewidth で「残り幅」を測る

\newcommand{\StepSpan}{(\linewidth-\tcboxedtitlewidth-12mm)}

タイトルは上罫の上に載っているので、その幅の分だけ装飾を置ける場所が減ります。 版面幅からタイトル幅と左オフセットを引いた残りが、階段に使える幅です。図版の2つ目の箱がこれで、タイトルを倍以上に伸ばしても階段が自動的に縮んで収まっています。

\tcboxedtitleheight / \tcboxedtitlewidth の2つは、凝った箱で最も使う寸法です。この2つを基準に組み立てると、フォントサイズにも文字数にも追随する箱になります。

反復するときは \n ではなく \n/#2 で書く

深さも色の濃さも、\n そのものではなく \n/#2 を使っています。

yshift=-5.5mm*\n/#2                     % 総高は常に 5.5mm
evaluate=\n as \lv using {int(18+62*\n/#2)}  % 濃さは常に 18〜80

こう書くと、段数を変えても階段の総高と色の幅が変わりません。 \n を直接使うと段数に比例して深くなり、段を増やしたときに階段が本文へめり込みます。色に至っては qmid!111!white のような混合率が生まれて、

! Package color Error: Argument `-0.10129' not in range [0,1].

で落ちます。引数で個数を変えられるようにした装飾は、必ず「個数を増やした場合」を1回試してください。

\foreach はパスの途中でも使える

階段の輪郭は、\drawパスの中\foreach を回して一筆で書いています。

\draw[...] (開始点)
  \foreach \n in {1,...,#2}{ -- (点A) -- (点B) };

図形を1個ずつ \draw するのに比べて、線の継ぎ目が出ません。角の処理(line join)もパス全体で一貫します。

仕切りと下段の塗り分け

segmentation style\tcblower の仕切り線です。ここは実線の細罫にして、枠と同じ重さに揃えました。

そして下段の背景色を変える colbacklower は、enhanced では効きません。 上下で塗りを分ける仕組みを持っているのは bicolor 系のスキンなので、skin=bicolor を足す必要があります。enhanced の機能(underlay やノード)はそのまま使えます。エラーも警告も出ず、ただ色が変わらないだけなので気付きにくい部類です。


技法7:四隅に飾りを置く(隅飾りフレーム)

隅飾りフレーム

ここまでの集大成です。四隅に渦巻き(ヴォリュート)を置いた、いわゆるカルトゥーシュ風の枠を作ります。

装飾を4つ手で書くのは論外なので、1本だけ描いて、あとは鏡像で展開します

% 渦は対角線から少しずらす。対角線上に置くと鏡像が自分自身に重なる。
\NewDocumentCommand{\Volute}{}{%
  \draw[qmid, line width=.5pt]
    (14mm,-1.2mm)
      .. controls (11.0mm,-1.4mm) and (9.2mm,-2.4mm) .. (8.8mm,-4.0mm)
      .. controls (8.4mm,-5.5mm) and (9.6mm,-6.7mm) .. (10.9mm,-6.2mm)
      .. controls (11.9mm,-5.8mm) and (12.0mm,-4.5mm) .. (11.1mm,-4.2mm)
      .. controls (10.4mm,-4.0mm) and (9.9mm,-4.6mm) .. (10.2mm,-5.1mm);}

\NewDocumentCommand{\CornerOrn}{ m }{%
  \begin{scope}[#1]
    % 主線:辺から来て小さな四分円で折れる
    \draw[qdeep, line width=.7pt]
      (14mm,0) -- (4mm,0)
        .. controls (1.8mm,0) and (0,-1.8mm) .. (0,-4mm) -- (0,-14mm);
    % 渦を2本、対角線について鏡像に置く((x,y) -> (-y,-x))
    \Volute
    \begin{scope}[cm={0,-1,-1,0,(0,0)}] \Volute \end{scope}
    % 対角線上の小さな結び
    \fill[qcyan] (6.4mm,-6.4mm) circle (.6pt);
    \draw[qcyan, line width=.4pt]
      (4.6mm,-4.6mm) .. controls (6.4mm,-5.4mm) and (5.4mm,-6.4mm)
                      .. (4.6mm,-8.2mm);
  \end{scope}}

そして4隅へは scope のオプションを差し替えるだけで展開します。

  underlay={%
    \draw[qdeep, line width=.7pt]
      ([xshift=14mm]frame.north west) -- (frame.north west -| title.west)
      (frame.north west -| title.east) -- ([xshift=-14mm]frame.north east)
      ([xshift=14mm]frame.south west) -- ([xshift=-14mm]frame.south east)
      ([yshift=-14mm]frame.north west) -- ([yshift=14mm]frame.south west)
      ([yshift=-14mm]frame.north east) -- ([yshift=14mm]frame.south east);
    \CornerOrn{shift={(frame.north west)}}
    \CornerOrn{shift={(frame.north east)}, xscale=-1}
    \CornerOrn{shift={(frame.south west)}, yscale=-1}
    \CornerOrn{shift={(frame.south east)}, xscale=-1, yscale=-1}},

局所座標で描いて、shift で運ぶ。 装飾の中身は「原点=角、x は右、y は下」という前提だけで書きます。実際にどの角に置くかは scope が決めるので、装飾コード側は箱のことを何も知らなくて済みます。

残り3つは xscale=-1 / yscale=-1 の鏡像。 左右反転・上下反転・両方、で 4 隅が揃います。あとから渦の形を変えたくなっても、直すのは 1 箇所だけです。

対角線での鏡像には cm を使います。 cm={0,-1,-1,0,(0,0)}(x,y) → (-y,-x)、つまり対角線に対する反転です。これで「1本の渦」から左右対称な一対が作れます。

ハマりどころ:渦の中心を対角線ちょうどに置くと、鏡像が元の渦に完全に重なって1本にしか見えません。少しずらしてください。最初これで「鏡像が効いていない」と 10 分悩みました。


後編で実際に踏んだエラー

反復と座標変換をやると、前編とは違う種類のエラーが出ます。**3つとも原因は「座標の中で何が展開されるか」**です。

Package pgf Error: No shape named '(RB' is known.

座標を引数に取るマクロで踏みました。

\NewDocumentCommand{\RingAt}{ m }{ \fill ([xshift=-4pt]#1) ... }
\RingAt{(RB)}     % → ([xshift=-4pt](RB)) となって落ちる

([xshift=...]座標) の形で使うマクロには、括弧を付けない名前だけを渡します。

\RingAt{RB}       % → ([xshift=-4pt]RB) で正しい

複雑な座標式を渡したい場合は、先に \coordinate で名前を付けてから渡すのが安全です。技法5でそうしているのはこの理由です。

! Missing number, treated as zero.

技法6の \StepSpan を、最初 \NewDocumentCommand で定義して踏みました。

\NewDocumentCommand{\StepSpan}{}{(\linewidth-\tcboxedtitlewidth-12mm)}  % 落ちる
\newcommand{\StepSpan}{(\linewidth-\tcboxedtitlewidth-12mm)}            % 正しい

\NewDocumentCommand が作るのは robust な(=展開されない)命令です。ところが ([xshift=...]...) の中身は pgfmath が展開しながら数式として読みます。robust な命令はそこで展開されないので、数値が来るべき位置に読めないトークンが残り、Missing number になります。

座標式の中に埋めるマクロは \newcommand で定義する。 逆に、パス全体やスコープを返すマクロ(\ChamferPath\CornerOrn)は \NewDocumentCommand で構いません。**「数値として読まれるか、コードとして実行されるか」**が分かれ目です。

! Package color Error: Argument '-0.10129' not in range [0,1].

技法6で書いたとおり、\n をそのまま濃さに使って混合率が 100 を超えたやつです。ちなみに最初はこれを \foreach の変数名のせいだと誤診しました\t は LaTeX のアクセント命令なので変数名に使えません。これはこれで本当です)。名前を変えても直らず、そこでようやく数値の範囲を疑いました。

エラーメッセージの数値をそのまま読むのが結局は近道です。 -0.10129 は「混合率が負」であって、変数名の話ではありませんでした。


後編のまとめ

技法

# 技法 鍵となるキー
1 辺ごとに太さを変える leftrule / rightrule / toprule / bottomrule
2 装飾を等間隔に置く 中間点座標 $(A)!.5!(B)$calc が必要)
3 装飾を反復する \foreach\tcboxedtitlewidth。比は \n/#2 で書く
4 \foreach を1本のパスに埋める 継ぎ目が出ず line join も一貫する
5 四隅に飾りを置く 局所座標+shiftxscale=-1 / cm={0,-1,-1,0}

落とし穴

# 要点
6 反復装飾は「個数を増やした場合」を必ず1回試す(混合率が 100 を超える)
7 座標式に埋めるマクロは \newcommand\NewDocumentCommand は展開されない
8 ([xshift=..]#1) に渡すのは括弧なしの名前だけ
9 colbacklowerenhanced では無視される。skin=bicolor が要る
10 鏡映の対称軸の上に装飾を置かない。像が自分自身に重なる
11 反復装飾・隅飾りは breakable=false にする

前編と後編で伝えたかったことは、実は同じ1つです。

凝った箱は「キーを探す作業」ではなく「どの層に何をどう配るかの設計」

レイヤーの地図を持っていれば、あとは TikZ で好きに描けます。そして装飾を1個に集約して座標変換で配る形にしておけば、意匠の変更コストが1箇所分に収まります。この2つが揃って初めて、凝った箱は「作れる」から「増やせる」に変わります。

同じ沼にいる人の役に立てば。

続き:この7つを毎回手で書くのは現実的ではありません。同じものを AI に作らせて量産する方法凝った tcolorbox を AI に量産させる(LaTeX確認シリーズ#7) にまとめました。凝った箱ほど AI は失敗しますが、それには理由と対策があります。


付録:後編の完全なソース

プリアンブルは前編と共通です。

\documentclass[a4paper,11pt]{ltjsarticle}
\usepackage[margin=22mm]{geometry}
\usepackage[most]{tcolorbox}   % most で enhanced/breakable/skins をまとめてロード
\usepackage{varwidth}          % varwidth boxed title に必須(most では入らない)
\usetikzlibrary{calc}          % ($(A)+(0,-1mm)$) と $(A)!.5!(B)$ に必要

%==================== 配色(Tailwind sky / slate 系) ====================
\definecolor{qdeep}{HTML}{075985}   % sky-800
\definecolor{qmid}{HTML}{0284C7}    % sky-600
\definecolor{qcyan}{HTML}{38BDF8}   % sky-400
\definecolor{qlight}{HTML}{E0F2FE}  % sky-100

%==================== 寸法トークン ====================
\newlength{\BoxIndent}\setlength{\BoxIndent}{1\zw}
\NewDocumentCommand{\BoxStrut}{}{\vphantom{}\vphantom{j}}

以降の箱の定義は本文中のコードをそのまま並べれば動きます。順序は 技法5 → 技法7 で、マクロ(\RingAt / \StepSpan / \Volute / \CornerOrn)を対応する \DeclareTColorBox より前に置いてください。全文は qiita-ornate-tcolorbox-part2.tex を参照してください。

前編の4つと後編の3つは、同じプリアンブルの下に7つとも並べられます。


宣伝

  • Evolton: 問題を貼るとAIが自動でシケプリにしてくれるアプリ。
    Evolton

  • Mathhover_1: 数式を打つIME。
    Mathhover
    Mathhover_2


参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?