4

I'm trying to make my tables look nicer by default. What I'm looking to do is fill every row between a toprule and a midrow.

The next row after a toprow can be easily filled by appending \rowcolor{tableheadcolor} to the command.

The issue I have is getting this to work with multi-row table headings. I've managed to modify \@xarraycr so that until a bool is set to false it sets the next row colour to tableheadcolor. The issue I have is that this always runs for one row to many. I managed to almost get around this by overwriting the row color to the stored variable page. However that will look funny if the table is ontop of any other content, which is entirely likely to happen at some point.

Despite a fair bit of finagling, I'm unable to get this to work, so I'm turning to StackExchange for help. Unfortunately none of the similar questions I've seen have been able to help.

This is what I've currently got (colours garish for demonstration purposes)

demo image

Full MWE

\documentclass{article}

\usepackage{etoolbox}
\usepackage[table]{xcolor}
\usepackage{ctable}


\colorlet{text}{black}
\colorlet{page}{white}


\newbool{tabularTitleRow}
\colorlet{tableheadcolor}{black!5} % Table header colour = 25% gray

\AfterEndEnvironment{tabular}{%
    \global\boolfalse{tabularTitleRow}
}

\renewcommand{\toprule}{
    \arrayrulecolor{text}\specialrule{\heavyrulewidth}{\abovetopsep}{0pt}%
    \arrayrulecolor{tableheadcolor}\specialrule{\belowrulesep}{0pt}{0pt}%
    \arrayrulecolor{text}\rowcolor{tableheadcolor}%
    \global\booltrue{tabularTitleRow}%
}

\let\oldmidrule\midrule
\renewcommand{\midrule}{
    \ifbool{tabularTitleRow}{
        \arrayrulecolor{tableheadcolor}\specialrule{\aboverulesep}{0pt}{0pt}%
        \arrayrulecolor{text}\specialrule{\lightrulewidth}{0pt}{0pt}%
        \arrayrulecolor{page}\specialrule{\belowrulesep}{0pt}{0pt}%
        \arrayrulecolor{text}%
        \rowcolor{page}%
        % \global\rownum=0\relax%
        \global\boolfalse{tabularTitleRow}%
    }{%
        \oldmidrule
    }
}







\colorlet{tableheadcolor}{orange!30!page}

\catcode`@=11 % or \catcode"0040=11 or \makeatletter to change category code of @ to 11 and temporarily to access kernel macro \@xarraycr

\let\old@xarraycr\@xarraycr

\def\@xarraycr{%
{\texttt{\tiny EOL}}
\old@xarraycr \ifbool{tabularTitleRow}{\rowcolor{tableheadcolor}}{}}

\catcode`@=12 % or \catcode"0040=12 or \makeatother to restore category code of @ to 12




\begin{document}




\pagecolor{blue!10}

\begin{tabular}{lll}
    & & \ifbool{tabularTitleRow}{T}{F} \\
    \toprule
    a & b & \ifbool{tabularTitleRow}{T}{F} \\
    \midrule
    1 & 2 & \ifbool{tabularTitleRow}{T}{F} \\
    \midrule
    4 & 5 & \ifbool{tabularTitleRow}{T}{F} \\
    % \meaning\\ & & \\
    \toprule
    7 & 8 & \ifbool{tabularTitleRow}{T}{F}
\end{tabular}
\qquad
\begin{tabular}{lll}
    & & \ifbool{tabularTitleRow}{T}{F} \\
    \toprule
    a & b & \ifbool{tabularTitleRow}{T}{F} \\
    d & e & \ifbool{tabularTitleRow}{T}{F} \\
    \midrule
    1 & 2 & \ifbool{tabularTitleRow}{T}{F} \\
    4 & 5 & \ifbool{tabularTitleRow}{T}{F} \\
    \bottomrule
    7 & 8 & \ifbool{tabularTitleRow}{T}{F} \\
\end{tabular}


\end{document}

If it would also be possible to have the heading colour be transparent, I have been unable to find how to do that, but I'm fairly sure you know more than me.

Also interested in a better approach to accomplish the overall effect.

  • I don't really understand what you try to achieve. What's wrong in the images you posted? – Bernard Jun 7 at 16:12
  • So in the photos the 1,2,F row has a solid fill. I'm trying to avoid that row having a different colour or opacity to the rest of the table (rows 4,5,F etc), this whole question is because I can't find a way around this so far. – tecosaur Jun 7 at 22:54
2

The reason for your off-by-one row colouring is that when your modified \midrule sets your tabularTitleRow boolean flag to false, it is already too late: the preceding \\ command has already put the \rowcolor call that sets the row color for header rows. Moreover, your way of modifying \@xarraycr is nasty, because it prevents the \\[〈dimen〉] syntax from working inside a tabular or array. So, it's better not to redefine \@xarraycr if possible (as done below).

Based on Ulrike Fischer's idea about using \hiderowcolors, here is a way to get the space above the \midrule coloured in the proper colour for the table header (but see below for a more elaborate solution based on this technique):

\documentclass{article}
\usepackage{etoolbox}
\usepackage[table]{xcolor}
\usepackage{ctable} % or booktabs

\colorlet{text}{black}
\colorlet{page}{white}
\colorlet{tableheadcolor}{orange!30!page}

% Use of \showrowcolors and \hiderowcolors is an idea from Ulrike
% Fischer's answer here: <https://tex.stackexchange.com/a/494954/73317>
\renewcommand{\toprule}{%
    \showrowcolors
    \arrayrulecolor{text}\specialrule{\heavyrulewidth}{\abovetopsep}{0pt}%
    \arrayrulecolor{tableheadcolor}\specialrule{\belowrulesep}{0pt}{0pt}%
    \arrayrulecolor{text}%
    \rowcolor{tableheadcolor}%
}

\apptocmd\midrule{\hiderowcolors}{}{\FAILED}

\makeatletter

\let\@BTrule@ORI=\@BTrule
\let\my@BTrule=\@BTrule

% Modified version of \@BTrule that doesn't do \vskip\@aboverulesep, for use
% when the corresponding vertical space should be coloured.
\patchcmd{\my@BTrule}{%
    \ifnum\@lastruleclass=\z@\vskip\@aboverulesep\else
  }{%
    \ifnum\@lastruleclass=\z@\else
  }{}{\FAILED}

\newcommand*{\myendtablehead}{%
 \\[\aboverulesep] % this colours the additional space with the current row
                   % color
 \noalign{\global\let\@BTrule\my@BTrule}% temporarily modify \@BTrule
 \midrule
 \noalign{\global\let\@BTrule\@BTrule@ORI}% restore it
}

\makeatother

\rowcolors{1}{tableheadcolor}{tableheadcolor}

\begin{document}
\pagecolor{blue!10}

\begin{tabular}{ll}
    \toprule
    a & b \\
    d & e \myendtablehead
    1 & 2 \\
    4 & 5 \\
    7 & 8 \\
    \bottomrule
\end{tabular}

\end{document}

Screenshot

Here is a solution that allows you to write the \\ before \midrule, as usual, without needing to explicitly call \myendtablehead. Note that for \midrule to be recognized by the modified \@arraycr, it must immediately follow the \\ command (well, there may be space tokens between them, but nothing else).

Since the \rowcolors command respects grouping, we define a coloredtableheaders environment inside which alignments are colored according to this scheme. Outside this environment, \toprule, \midrule and \@arraycr have their usual meaning. Any \rowcolors command used outside the coloredtableheaders environment is independent of what we do inside, and should behave as usual.

\documentclass{article}
\usepackage{etoolbox}
\usepackage[table]{xcolor}
\usepackage{booktabs} % or ctable

\colorlet{text}{black}
\colorlet{page}{white}
\colorlet{tableheadcolor}{orange!30!page}

\makeatletter

% The use of \showrowcolors and \hiderowcolors is an idea from Ulrike
% Fischer's answer here: <https://tex.stackexchange.com/a/494954/73317>
\newcommand*{\my@coloredtoprule}{%
  \showrowcolors
  \arrayrulecolor{text}\specialrule{\heavyrulewidth}{\abovetopsep}{0pt}%
  \arrayrulecolor{tableheadcolor}\specialrule{\belowrulesep}{0pt}{0pt}%
  \arrayrulecolor{text}%
  \rowcolor{tableheadcolor}%
}

\let\my@coloredmidrule\midrule
\apptocmd\my@coloredmidrule{\hiderowcolors}{}{\FAILED}

\let\@BTrule@ORI=\@BTrule
\let\my@BTrule=\@BTrule

% Modified version of \@BTrule that doesn't do \vskip\@aboverulesep, for use
% when the corresponding vertical space should be coloured.
\patchcmd{\my@BTrule}{%
    \ifnum\@lastruleclass=\z@\vskip\@aboverulesep\else
  }{%
    \ifnum\@lastruleclass=\z@\else
  }{}{\FAILED}

\let\@arraycrORI=\@arraycr

% The “master counter” hackery is explained in the TeXbook appendix D (Dirty
% Tricks), pp. 385-386. It is also mentioned in the array.sty implementation
% notes concerning \@arraycr.
\newcommand*{\my@colored@arraycr}{%
  % Increase the master counter. This is needed to prevent TeX from
  % prematurely finishing the alignment entry in case \\ was followed by '&'
  % (when the \futurelet from \@ifnextchar causes TeX to read a '&', this
  % finishes the entry unless the master counter has a different value than it
  % had when the entry was started).
  \relax\iffalse{\fi\ifnum 0=`}\fi
  % Each of the two branches takes care of decreasing the master counter.
  \@ifnextchar\midrule
    {\@firstoftwo{\my@endtablehead}}% gobble the following \midrule
    {\my@closebrace@and@arraycrORI}%
}

\newcommand*{\my@endtablehead}{%
  \ifnum 0=`{}\fi % the second brace decreases the master counter
  \@arraycrORI[\aboverulesep]% this colours the additional space with the
                             % current row color
  \noalign{\global\let\@BTrule\my@BTrule}% temporarily modify \@BTrule
  \midrule
  \noalign{\global\let\@BTrule\@BTrule@ORI}% restore it
}

\newcommand*{\my@closebrace@and@arraycrORI}{%
  \ifnum 0=`{}\fi % the second brace decreases the master counter
  \@arraycrORI
}

\newenvironment{coloredtableheaders}{%
  \let\toprule\my@coloredtoprule
  \let\midrule\my@coloredmidrule
  \let\@arraycr\my@colored@arraycr
  \rowcolors{1}{tableheadcolor}{tableheadcolor}%
  \ignorespaces
}{%
  \unskip\ignorespacesafterend
}

\makeatother

\begin{document}
\pagecolor{blue!10}

No colored header in the following \verb|tabular|:\quad
\begin{tabular}{l}
   a \\
   b \\
   c
\end{tabular}

\bigskip
\begin{coloredtableheaders}
  \begin{tabular}{ll}
    \toprule
    a & b \\
    d & e \\
    \midrule
    1 & 2 \\
    4 & 5 \\
    7 & 8 \\
    \bottomrule
  \end{tabular}

  \vspace{4ex}
  \begin{tabular}{>{\hspace{3pt}\normalsize}l>{\hspace{5pt}}*{3}{p{7.9em}}}
    \toprule
    Category & \multicolumn{3}{l}{\normalsize Packages} \\
    \midrule
    General  & etb & xpt & sil \\
             & tts & ttc & frm \\
    \bottomrule
  \end{tabular}
\end{coloredtableheaders}

\vspace{4ex}
No colored header in the following \verb|tabular|:\quad
\begin{tabular}{lr}
  Foo     & 1\\
  Bar     & 2\\
  And baz & 3
\end{tabular}
\end{document}

Screenshot

Using \cline in the colored header

As documented in the colortbl manual:

Lines produced by \cline are coloured if you use \arrayrulecolor but you may not notice as they are covered up by any colour pannels in the following row. This is a ‘feature’ of \cline. If using this package you would probably [be] better using the - rule type in a \hhline argument, rather than \cline.

Example using the above code and the hhline package:

  \begin{tabular}{lll}
    \toprule
    a & \multicolumn{2}{c}{b} \\
    % \cline{2-3} % problem: covered by the next colored row
    % \hhline provides a viable alternative:
    \hhline{>{\arrayrulecolor{tableheadcolor}}->{\arrayrulecolor{black}}--}
      & c & d\\ \midrule
    e & f & g \\ \bottomrule
  \end{tabular}

Screenshot

  • Nice! It's almost perfect except for the requirement that you drop a `\\\` before using it. If that requirement could be removed, I think it would be a more-or-less perfect solution. – tecosaur Jun 9 at 13:39
  • Please see my edit, it should do what you want. – frougon Jun 9 at 14:23
  • That works perfectly. Thank you very much! – tecosaur Jun 9 at 16:34
  • Ooops. Looks like I spoke too soon. I tried that as a drop in replacement, and suddenly I'm getting "misplaced \cr" errors. See pastebin.com/rEJB5eGr for a MWE. – tecosaur Jun 9 at 17:07
  • This problem is fixed now. I've added your problematic example with the last two columns removed to the sample code at the end of my answer, it works fine now (I removed two columns only to avoid an overfull \hbox due to the default \linewidth, but don't worry, it also works with them). – frougon Jun 9 at 20:31
4

You can clear colors with \hiderowcolors:

\documentclass{article}

\usepackage{etoolbox}
\usepackage[table]{xcolor}
\usepackage{booktabs}


\colorlet{text}{black}
\colorlet{page}{white}


\renewcommand{\toprule}{%
    \showrowcolors\arrayrulecolor{text}\specialrule{\heavyrulewidth}{\abovetopsep}{0pt}%
    \arrayrulecolor{tableheadcolor}\specialrule{\belowrulesep}{0pt}{0pt}%
    \arrayrulecolor{text}}%

\colorlet{tableheadcolor}{orange!30!page}
\apptocmd\midrule{\hiderowcolors}{}{\fail}
\rowcolors{1}{tableheadcolor}{tableheadcolor}
\begin{document}

\pagecolor{blue!10}

\begin{tabular}{lll}
    & & \\
    \toprule
    a & b & \\
    \midrule
    1 & 2 & \\
    \midrule
    4 & 5 & \\
    \midrule
    7 & 8 & 
\end{tabular}
\quad
\begin{tabular}{lll}
    \toprule
    a & b & \\
    \midrule
    1 & 2 & \\
    \midrule
    4 & 5 & \\
    % \meaning\\ & & \\
    \midrule
    7 & 8 & 
\end{tabular}

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.