20
0
Files
cdsc_tex/slides_template/trantor.sty
Benjamin Mako Hill 01534406dc Update the vendored theme from the trantor fork
Brings across the work now sitting in pull requests upstream: a palette of
named colours in place of hardcoded black and white, the frame title in a
dark band, section and subsection dividers set metropolis-style on the
light background over a progress bar, \appendix so backup slides stay out
of the frame count, and a fix for the divide by zero the progress bar hit
before the aux file existed.

The separate MIT licence file goes away. The notice it carried now sits in
the header of trantor.sty itself, which is what the licence asks for and
leaves one licence file in the repository.

Fork: https://github.com/makoshark/trantor
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 07:26:23 -07:00

345 lines
12 KiB
TeX

% trantor.sty -- a Metropolis-style theme for the ltx-talk class.
% LuaLaTeX only.
%
% Copyright (c) 2026 Marco Pompili, https://github.com/mrc-pop/trantor
% Received under the MIT license and redistributed here, with modifications,
% under the GNU GPL version 3 or later. See COPYING.
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{trantor}[2026-08-01 v1.0 Metropolis-like theme for ltx-talk]
% LuaTeX only (XeTeX is unsupported and the fonts need a Unicode engine).
\RequirePackage{iftex}
\RequireLuaTeX
\RequirePackage{tikz}
\RequirePackage{pifont}
% -- Fonts. Upstream sets Libertinus here and loads lum-euler for maths.
% Both are left to the document instead: this template ships OpenSans and
% RobotoMono, and lum-euler is not packaged in Debian's TeX Live. The document
% is expected to \setsansfont, \setmonofont and load a Unicode maths font
% before using anything below. \headerfont is what the theme uses for titles
% and other display text; redefine it if you want titles in a second face.
\renewcommand{\familydefault}{\sfdefault}
\linespread{1.18}
\providecommand\headerfont{\sffamily}
\makeatletter
% Tighten the space around display maths -- the default 11pt reads as a big gap
% on a slide, especially inside a list. Re-applied on every \normalsize so the
% size files don't reset it.
\g@addto@macro\normalsize{%
\setlength\abovedisplayskip{5pt plus 2pt minus 1pt}%
\setlength\belowdisplayskip{5pt plus 2pt minus 1pt}%
\setlength\abovedisplayshortskip{5pt plus 2pt minus 1pt}%
\setlength\belowdisplayshortskip{5pt plus 2pt minus 1pt}%
}
% -- Palette. Four colours drive everything the theme draws. Override any of
% them in the preamble, before or after loading trantor:
%
% \definecolor{accent}{HTML}{BF1238}
% \colorlet{TrantorDark}{makopurple1}
%
% accent progress bars and emphasis
% TrantorDark header band, section dividers, standout panels, frame numbers
% TrantorLight text on dark, and anything that has to read against it
% TrantorMuted affiliations, footlines, arxiv tags. This is the lightest grey
% that still meets WCAG AA contrast (4.5:1) on white (~4.54:1).
\providecolor{accent}{HTML}{2A7AE2}
\providecolor{TrantorDark}{HTML}{000000}
\providecolor{TrantorLight}{HTML}{FFFFFF}
\providecolor{TrantorMuted}{HTML}{767676}
\AddToHook{begindocument}{\colorlet{TrantorAccent}{accent}}
% itemize/enumerate labels in the body colour rather than the class default
\AddToHook{begindocument}{\colorlet{structure}{TrantorDark}}
% -- Header bar carrying the frame title (hidden on plain pages).
% The band is a rule of height (header height - headsep) with depth headsep, so
% headsep is the only thing that puts space under the title. geometry owns it.
\geometry{headsep=10pt}
\AddToHook{begindocument}{%
\EditInstance{header}{std}{
background-color = TrantorDark,
color = TrantorLight,
print-frame-title = true,
height = 40pt,
font = \headerfont\bfseries,
left-hspace = \dimexpr\Gm@lmargin - 0.6cm\relax,
}%
\EditInstance{footer}{std}{element-order = {}}%
\EditInstance{frametitle}{header}{after-vspace = 6mm minus 3mm}%
}
% Like \frametitle, but forces a gap below the title on packed slides where the
% header's own spacing collapses. The \par gets us into vertical mode first.
\NewDocumentCommand\stitle{m}{\frametitle{#1}\par\addvspace{4mm}}
% \standoutline{...}: a centred line of text in the title's sans/size/colour,
% for emphasis within a normal (light) frame, e.g. closing a slide.
\NewDocumentCommand\standoutline{m}{%
\par
\begingroup
\centering\headerfont\Large\bfseries\color{TrantorDark}#1\par
\endgroup
}
% Tick and cross, for marking things right or wrong.
\NewDocumentCommand\yes{}{\ding{51}}
\NewDocumentCommand\no{}{\ding{55}}
% \footline{...}: a small reference or citation pinned to the bottom-left of
% the slide. Overlaid, so it can go anywhere inside the frame.
\NewDocumentCommand\footline{m}{%
\begin{tikzpicture}[remember picture, overlay]
\node[anchor=south west, xshift=1ex, yshift=0.7ex, font=\scriptsize, text=TrantorMuted]
at (current page.south west) {#1};
\end{tikzpicture}%
}
% \arxiv{1306.5001}: a small, grey "arXiv:<id>" tag linked to the abstract.
% Meant to sit inline next to a statement, e.g. a theorem.
\NewDocumentCommand\arxiv{m}{%
\href{https://arxiv.org/abs/#1}{{\headerfont\scriptsize\color{TrantorMuted}arXiv:#1}}%
}
% -- \appendix, which ltx-talk does not provide. Frames after it keep counting
% but are left out of the total, so the denominator is the last frame of the
% talk proper. This is what appendixnumberbeamer does for beamer. The count is
% carried through the aux file, so it settles on the second run.
\newif\iftrantorAppendix
\trantorAppendixfalse
\newcommand\trantorMainFrames{\@totalframes}
% Appendix frames are numbered among themselves, as appendixnumberbeamer does,
% but by subtracting an offset rather than resetting the counter. The class
% records a property per frame keyed on that counter, so resetting it collides
% with ltx-talk's own frame.<n> labels and breaks \ref to a frame. Properly
% this belongs in the class, which could renumber and relabel together.
\NewDocumentCommand \appendix { } {%
\global\trantorAppendixtrue
\protected@write\@auxout{}{%
\string\gdef\string\trantorMainFrames{\the\value{frame}}}%
}
% Numerator and denominator for the frame counter and the progress bars. These
% are count registers rather than macros because pgfmath expands its argument
% while parsing, and a macro carrying a conditional sends it into a loop.
\newcount\trantor@n
\newcount\trantor@d
\newcommand\trantor@setcounts{%
\iftrantorAppendix
\trantor@n=\numexpr\value{frame}-\trantorMainFrames\relax
\trantor@d=\numexpr\@totalframes-\trantorMainFrames\relax
\else
\trantor@n=\value{frame}\relax
\trantor@d=\numexpr\trantorMainFrames\relax
\fi
}
\newcommand\trantorFrameNumber{\trantor@setcounts\the\trantor@n}
\newcommand\trantorTotalFrames{\trantor@setcounts\the\trantor@d}
\newcommand\trantor@setratio{%
\ifnum\trantor@d>0
\pgfmathsetmacro{\trantorratio}{min(\the\trantor@n / \the\trantor@d, 1)}%
\else
\def\trantorratio{0}%
\fi
}
% -- Bottom progress bar + frame counter (hidden on plain pages).
\newif\iftrantorPlainPage
\trantorPlainPagefalse
\AddToHook{shipout/foreground}{%
\iftrantorPlainPage\else
\trantorArtifactBegin
\begin{tikzpicture}[remember picture, overlay]
\trantor@setcounts
\trantor@setratio
\fill[TrantorAccent]
(current page.south west)
rectangle
([xshift=\trantorratio\paperwidth, yshift=1pt] current page.south west);
\node[anchor=south east, inner sep=5pt, font=\scriptsize, text=TrantorDark]
at (current page.south east)
{\the\trantor@n\,/\,\the\trantor@d};
\end{tikzpicture}%
\trantorArtifactEnd
\fi
\global\trantorPlainPagefalse
}
% -- Remember the current section title, and dim the others in the mini-TOC.
\ExplSyntaxOn
\tl_new:N \g__trantor_section_tl
\AddToHook{section/begin}{
\cs_if_exist:NT \l__talk_section_tl {
\tl_gset:NV \g__trantor_section_tl \l__talk_section_tl
}
}
\cs_new_protected:Npn \__trantor_print_section: { \tl_use:N \g__trantor_section_tl }
\cs_set_eq:NN \trantorSectionTitle \__trantor_print_section:
% Likewise the subsection, for \subsectionpage. Cleared at each new section so
% that a stale subsection title cannot leak into the next one.
\tl_new:N \g__trantor_subsection_tl
\AddToHook{section/begin}{ \tl_gclear:N \g__trantor_subsection_tl }
\AddToHook{subsection/begin}{
\cs_if_exist:NT \l__talk_subsection_tl {
\tl_gset:NV \g__trantor_subsection_tl \l__talk_subsection_tl
}
}
\cs_new_protected:Npn \__trantor_print_subsection: { \tl_use:N \g__trantor_subsection_tl }
\cs_set_eq:NN \trantorSubsectionTitle \__trantor_print_subsection:
% Suspend tagging around the theme's decorative / pagination graphics when
% tagging is on (a no-op otherwise), so the progress bar and frame counter stay
% out of the structure tree instead of being flagged as loose content.
\cs_new_protected:Npn \trantorArtifactBegin
{ \cs_if_exist:NT \tag_suspend:n { \tag_suspend:n { trantor } } }
\cs_new_protected:Npn \trantorArtifactEnd
{ \cs_if_exist:NT \tag_resume:n { \tag_resume:n { trantor } } }
% Soften the dimming of inactive TOC entries on the section page (0.2 -> 0.7).
% ltx-talk exposes no key for this yet, so we override the class-private
% \__talk_toc_dest:w. Revisit if a supported interface appears or the internal
% changes.
\cs_set_protected:Npn \__talk_toc_dest:w #1 . #2 . #3 . #4 . #5 \q_stop #6
{
\bool_lazy_or:nnTF
{
\bool_lazy_and_p:nn
{ \int_compare_p:nNn { \value { section } } = {#2} }
{ \int_compare_p:nNn { \value { subsection } } = 0 }
}
{
\bool_lazy_and_p:nn
{ \int_compare_p:nNn { \value { section } } = {#2} }
{ \int_compare_p:nNn { \value { subsection } } = {#3} }
}
{#6}
{ \opacity_begin:n { 0.7 } #6 \opacity_end: }
}
\ExplSyntaxOff
% -- Title page. Drop a logo.png next to your slides, or redefine \titlelogo.
\newcommand{\titlelogo}{%
\IfFileExists{logo.png}
{\includegraphics[height=1.4cm, alt={Organisation logo}]{logo.png}}{}%
}
\renewcommand{\maketitle}{%
\global\trantorPlainPagetrue
\begin{frame}{}%
\thispagestyle{plain}%
\vfill
\hspace*{0.04\paperwidth}%
\begin{minipage}{0.82\paperwidth}
\raggedright
{\color{TrantorDark}\headerfont\Large\bfseries \@title\par}%
\vspace{0.5em}%
{\color{TrantorAccent}\rule{\linewidth}{0.8pt}\par}%
\vspace{1.3em}%
% Metadata on the left, logo on the right; bottom-aligned so the logo sits
% level with the date and grows upward rather than below it.
\begin{minipage}[b]{0.66\linewidth}
\raggedright
{\normalsize \@author\par}%
\ifx\@institute\empty\else
\vspace{0.2em}%
{\small\color{TrantorMuted} \@institute\par}%
\fi
\vspace{0.2em}%
{\small \@date\par}%
\end{minipage}%
\hfill
\begin{minipage}[b]{0.30\linewidth}
\raggedleft
\titlelogo
\end{minipage}%
\end{minipage}%
\hspace*{0.04\paperwidth}%
\vspace{0.12\paperheight}%
\end{frame}%
}
% -- Divider pages, following metropolis: on the ordinary light background, a
% block set in the middle of the slide holding the section title over a
% progress bar, and for a subsection the subsection title beneath that. The
% bar is the same progress the footer bar shows, drawn at half width. The
% footer bar and frame number stay, so these are not plain pages in the sense
% the shipout hook means.
\newlength\trantor@barwidth
\newlength\trantor@barfill
% \trantorProgressBar{<width>}: filled to the current position, grey after.
\NewDocumentCommand \trantorProgressBar { m } {%
\setlength\trantor@barwidth{#1}%
\trantor@setcounts
\trantor@setratio
\setlength\trantor@barfill{\trantorratio\trantor@barwidth}%
\textcolor{TrantorDark}{\rule{\trantor@barfill}{0.6pt}}%
\textcolor{TrantorDark!20}{%
\rule{\dimexpr\trantor@barwidth-\trantor@barfill\relax}{0.6pt}}%
}
\NewDocumentCommand \trantor@divider { m } {%
\begin{frame}{}%
\thispagestyle{plain}%
\vfill
\centering
\begin{minipage}{0.52\linewidth}%
\raggedright
{\headerfont\Large\bfseries\trantorSectionTitle\par}%
\vspace{0.4em}%
\trantorProgressBar{\linewidth}%
\IfBlankF{#1}{%
\par\vspace{0.8em}%
{\headerfont\large\bfseries #1\par}%
}%
\end{minipage}%
\par
\vfill
\end{frame}%
}
\NewDocumentCommand \sectionpage { } { \trantor@divider {} }
\NewDocumentCommand \subsectionpage { } { \trantor@divider {\trantorSubsectionTitle} }
% \standout{...}: a full frame with the same dark panel as \sectionpage,
% and the given text centred on it.
\NewDocumentCommand \standout { +m } {%
\global\trantorPlainPagetrue
\begin{frame}{}%
\thispagestyle{plain}%
\noindent
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (\linewidth,-\textheight);
\fill[TrantorDark]
(-\Gm@lmargin, \paperheight)
rectangle
(\dimexpr\paperwidth-\Gm@lmargin\relax, -\paperheight);
\node[text=TrantorLight, anchor=center, inner sep=0pt]
at (0.5\linewidth, -0.5\textheight) {%
\begin{minipage}{0.8\linewidth}
\centering
\headerfont\LARGE\bfseries
#1
\end{minipage}%
};
\end{tikzpicture}%
\end{frame}%
}
\makeatother