[Tex/LaTex] Automatically add \vfill for breakable tcolorbox

boxestcolorbox

I'm using tcolorbox with nested tcolorboxes, the upper level ones being breakable. Here is an example.

\documentclass{article}
\usepackage{lipsum}
\usepackage{tcolorbox}
\tcbuselibrary{skins,breakable}
\tcbset{enhanced jigsaw}
\begin{document}
\begin{tcolorbox}[breakable,title=My breakable box]
  \begin{tcolorbox}[title = box1]
    \lipsum[1]
  \end{tcolorbox}
  \begin{tcolorbox}[title = box2]
    \lipsum[1]
  \end{tcolorbox}
  \begin{tcolorbox}[title = box3]
    \lipsum[1]
  \end{tcolorbox}
\end{tcolorbox}
\end{document}

enter image description here

Note the huge blank space at the bottom of the first page.

Now, I'd like something like that (I got it adding an ugly \vspace*{3cm} between first and second box):

enter image description here

A bit better, in my opinion.

So my question is: would it be possible to make tcolorbox add automatically some kind of \vfill between inner boxes, once the breaking algorithm has decided where to break.

Best Answer

From an aesthetical point of view, all solutions are somewhat ugly ... the choice of what is least ugly should be finally up to you ;-)

Let's call your problem description a first 'solution', i.e. the available space is put after the first box part.

The second solution would be to put the available space inside but at the end of the first box part. tcolorbox supports this using the height fixed for option:

\documentclass{article}
\usepackage{lipsum}
\usepackage{tcolorbox}
\tcbuselibrary{skins,breakable}
\tcbset{enhanced jigsaw}
\begin{document}
\begin{tcolorbox}[breakable,title=My breakable box,
  height fixed for=first and middle]
  \begin{tcolorbox}[title = box1]
    \lipsum[1]
  \end{tcolorbox}
  \vfill
  \begin{tcolorbox}[title = box2]
    \lipsum[1]
  \end{tcolorbox}
  \begin{tcolorbox}[title = box3]
    \lipsum[1]
  \end{tcolorbox}
\end{tcolorbox}
\end{document}

enter image description here

The \vfill was added for fun to show that it has no effect here.

The third solution tries to stretch the content of the first box part to cope the available space. For this, I use a patch for the height fixed for option. Note that this will not work properly, if the outer box has a lower part. Now, the \vfill comes to action:

\documentclass{article}
\usepackage{lipsum}
\usepackage{tcolorbox}
\tcbuselibrary{skins,breakable}
\tcbset{enhanced jigsaw}

\makeatletter

\def\tcb@break@ch@fixed{%
  \ifdim\tcb@natheight<\tcb@h@page\relax%
    \tcbdimto\tcb@h@upper{\tcb@h@upper+\tcb@h@page-\tcb@natheight}%
    \setbox\tcb@upperbox=\vbox to\tcb@h@upper\relax{\unvbox\tcb@upperbox}%
    \tcbdimto\kvtcb@height@fixed{\tcb@h@page}%
    \tcb@ch@fixed%
  \else%
    \tcb@ch@natural%
  \fi%
}

\makeatother

\begin{document}
\begin{tcolorbox}[breakable,title=My breakable box,
  height fixed for=first and middle]
  \begin{tcolorbox}[title = box1]
    \lipsum[1]
  \end{tcolorbox}
  \vfill
  \begin{tcolorbox}[title = box2]
    \lipsum[1]
  \end{tcolorbox}
  \begin{tcolorbox}[title = box3]
    \lipsum[1]
  \end{tcolorbox}
\end{tcolorbox}
\end{document}

enter image description here

Of course, you could use after={\par\vfill} as an option for your inner boxes to get rid of manually adding the \vfill commands.