blob: c55b95f4812ec16989631ed02a2b5f1e13273b2c [file] [log] [blame]
Marc Kupietza737b1b2023-10-07 09:32:20 +02001/**
2 * IDS theme for reveal.js
3 *
4 * By Marc Kupietz
5 */
6
7
8// Default mixins and settings -----------------
9@import "../template/mixins";
10@import "../template/settings";
11// ---------------------------------------------
12
13
14// Include theme-specific fonts
Marc Kupietza0f8f682026-08-08 14:13:11 +020015// NB: https://korap.ids-mannheim.de/font/{fira-condensed,libertinus}.css both
16// return 404, so the condensed and bold faces never loaded and headings fell
17// back to a regular-width, regular-weight face. Google Fonts serves the real
18// Fira Sans Condensed incl. weights 700/800 (this is where the IDS reference
19// decks get it from too); the others are kept for locally installed copies.
20@import url('https://fonts.googleapis.com/css2?family=Fira+Sans+Condensed:wght@300;400;700;800&family=Fira+Sans:wght@200;300;400;700&family=Fira+Sans+Extra+Condensed:wght@300;700&display=swap');
Marc Kupietza737b1b2023-10-07 09:32:20 +020021@import url('https://code.cdn.mozilla.net/fonts/fira.css');
Marc Kupietzc22be152026-08-18 15:54:55 +020022// Fira Code: the monospaced Fira Sans variant for highlighted/code text (as
23// in the IDS reference decks; falls back to Fira Mono from the Mozilla CSS
24// above, then DejaVu Sans Mono)
25@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&display=swap');
Marc Kupietza737b1b2023-10-07 09:32:20 +020026@import url('https://korap.ids-mannheim.de/font/libertinus.css');
27
28$idsOrange: #f6a800;
29
30// Override theme settings (see ../template/settings.scss)
31$backgroundColor: #fff;
32
33$mainColor: #222;
34$headingColor: $idsOrange;
35
36$mainFontSize: 42px;
37$mainFont: 'Libertinus Serif', Helvetica, sans-serif;
Marc Kupietza0f8f682026-08-08 14:13:11 +020038$headingFont: 'Fira Sans Extra Condensed', 'Fira Sans Condensed', 'Fira Sans', Helvetica, sans-serif;
Marc Kupietza737b1b2023-10-07 09:32:20 +020039$headingTextShadow: none;
40$headingLetterSpacing: 0.05ex;
41$headingTextTransform: uppercase;
Marc Kupietza0f8f682026-08-08 14:13:11 +020042$headingFontWeight: 700;
Marc Kupietza737b1b2023-10-07 09:32:20 +020043$linkColor: #2a76dd;
44$linkColorHover: lighten( $linkColor, 15% );
45$selectionBackgroundColor: lighten( $linkColor, 25% );
46
47$heading1Size: 2em;
48$heading2Size: 1.35em;
Marc Kupietz3fdee732023-10-07 18:52:20 +020049$heading3Size: 0.9em;
50$heading4Size: 0.7em;
Marc Kupietza737b1b2023-10-07 09:32:20 +020051
52$listBulletColor: $idsOrange;
53
Marc Kupietzc22be152026-08-18 15:54:55 +020054// --r-code-font (template emits #{$codeFont} as-is)
55$codeFont: "Fira Code", "Fira Mono", "DejaVu Sans Mono", "Source Code Pro", monospace;
56
Marc Kupietza737b1b2023-10-07 09:32:20 +020057// Change text colors against dark slide backgrounds
58@include dark-bg-text-color(#fff);
59
60
61// Theme template ------------------------------
62@import "../template/theme";
63// ---------------------------------------------
64
Marc Kupietza0f8f682026-08-08 14:13:11 +020065// Bar/logo sizes, expressed as fractions of the slide's own height (reveal.js
66// exposes the configured slide size as --slide-width / --slide-height custom
67// properties). Sizing relative to the slide rather than the raw viewport (vh
68// units) keeps things pinned to the true slide edges instead of drifting
69// into the letter-/pillarboxed margins when the window's aspect ratio
70// doesn't exactly match the configured one.
71$idsBarWidthRatio: 0.05;
72$idsLogoWidthRatio: 0.25;
Marc Kupietz59e95a62026-08-29 10:19:05 +030073// Partner (second institution) logo: shown next to the IDS logo on every
74// slide when the document sets the `partner_logo` yaml entry (the template
75// then publishes the URL as the --partner-logo custom property).
76$idsPartnerGap: 16px;
77$idsPartnerHeightRatio: 0.085;
Marc Kupietza0f8f682026-08-08 14:13:11 +020078$idsBarWidth: calc(var(--slide-height) * #{$idsBarWidthRatio});
79$idsHeadingGap: 24px;
80
81// Vertical gap between the title and the subtitle. The subtitle's bar segment
82// reaches up by this much to bridge it, so the two segments read as one bar.
83$idsHeadingSpacing: 8px;
84
85// Rendered height of one subtitle line. Slides without a subtitle reserve
86// exactly this much, so the bar and the start of the body content stay put
87// from slide to slide instead of jumping. Must stay in sync with
88// $heading3Size (0.9em) and $headingLineHeight (1.2, from template/settings).
89$idsSubtitleHeight: 0.9 * $mainFontSize * 1.2;
90
91// The two bar segments abut at a fractional pixel, which browsers round into a
92// visible hairline. Overlapping them slightly makes the join seamless.
93$idsBarOverlap: 2px;
94
95// reveal.js centres the scaled slide in the viewport, so whenever the window's
96// aspect ratio does not match the deck's, an empty band is left beside (or
97// above) the slide and the bar would not reach the window's physical edge.
98// Working out how wide that band is needs viewport / scale, and CSS cannot
99// divide a length by a length -- which is why the hand-written IDS decks solve
100// this in JavaScript. revealjs_presentation()'s template therefore publishes
101// the band widths, in slide coordinates, as --ids-band-left / --ids-band-top,
102// and the bar simply shifts out by that much. Falling back to 0px keeps this
103// correct whenever the aspect ratios do match, and if the script never runs.
104$idsBandLeft: var(--ids-band-left, 0px);
105
106// revealjs_presentation() sets reveal.js' own `margin` option to 0, so that the
107// slide box is the whole viewport and the orange bar can sit flush in its top
108// left corner. That would otherwise leave the body content running edge to
109// edge, so the breathing room reveal.js' margin used to provide is added back
110// here as slide padding; the bar and the headings reach back out to the edge
111// with matching negative margins below. The inset is the bar plus its gap, so
112// body content lines up with the heading text rather than with the bar.
113$idsContentInset: calc(#{$idsBarWidth} + #{$idsHeadingGap});
114
Marc Kupietz61c8ec72026-08-20 20:47:45 +0200115// The html prefix is solely about specificity: reveal's print styles
116// (html.reveal-print .reveal .slides section { padding: 0 !important }) would
117// otherwise beat this rule and flatten the inset on ?print-pdf pages. An html
118// prefix ties that specificity, and since the theme is loaded after
119// reveal.css, this rule wins both on screen and in print.
120html .reveal .slides section:not(.stack) {
Marc Kupietza0f8f682026-08-08 14:13:11 +0200121 box-sizing: border-box !important;
122 padding-left: #{$idsContentInset} !important;
123 padding-right: #{$idsContentInset} !important;
124}
125
126// Body content is left aligned with the headings; the title frame keeps its
127// own centered layout.
128.reveal .slides section:not(.stack):not(.title-frame) {
129 text-align: left !important;
130}
131
132// reveal.js centers lists by making them inline-block; make them ordinary
Marc Kupietze0826862026-08-11 16:34:54 +0200133// blocks so they line up with the headings.
134//
135// The markers are NOT native ::marker glyphs: browsers anchor outside-position
136// markers at slightly different offsets per engine (and per scale), and even
137// ::before font glyphs carry font-specific side bearings, so a padding(text)-
138// based alignment of the bullet column with the heading text can never be
139// pixel-exact everywhere. Instead we DRAW the markers as CSS boxes in
140// absolutely positioned li::before rules. Geometry (all values in the item's
141// own font size, so the hierarchy scales with the 0.8em per-level sizing from
142// the NESTED LISTS section below):
143//
144// - item text starts at (list left edge) + 0.65em
145// - the marker box starts exactly at the list's left edge; on content
146// slides that edge coincides with the heading text's left edge, i.e. the
147// bullet column is flush with the slide title, deterministically in every
148// browser and at every window size
149//
150// For nested lists the ::before offset has to be divided by the 0.8 font-size
151// factor, because the list's padding is computed in the PARENT item's font
152// size while the ::before offset is computed in the (smaller) child size.
153$idsListIndent: 0.65em;
154$idsListShrink: 0.8;
155$idsMarkerSize: 0.22em;
156$idsMarkerTop: calc((1.3em - #{$idsMarkerSize}) / 2);
157
Marc Kupietza0f8f682026-08-08 14:13:11 +0200158.reveal .slides section:not(.title-frame) ul,
159.reveal .slides section:not(.title-frame) ol {
160 display: block !important;
161 text-align: left !important;
162 margin-left: 0 !important;
Marc Kupietze0826862026-08-11 16:34:54 +0200163 padding-left: $idsListIndent !important;
164 list-style: none !important;
165}
166
167.reveal .slides section:not(.title-frame) li {
168 position: relative !important;
169}
170
171.reveal .slides section:not(.title-frame) li::before {
172 position: absolute !important;
173 left: calc(-1 * #{$idsListIndent}) !important;
174 color: var(--r-list-bullet-color) !important;
175}
176
177.reveal .slides section:not(.title-frame) li li::before {
178 left: calc(-1 * #{$idsListIndent} / #{$idsListShrink}) !important;
179}
180
181// Unordered-list markers: drawn boxes (round dot, square, ring for deeper
182// levels), vertically centered on the item's first line.
183.reveal .slides section:not(.title-frame) ul > li::before {
184 content: "" !important;
185 top: $idsMarkerTop !important;
186 width: $idsMarkerSize !important;
187 height: $idsMarkerSize !important;
188 box-sizing: border-box !important;
189 border-radius: 50% !important;
190 background: var(--r-list-bullet-color) !important;
191}
192
193.reveal .slides section:not(.title-frame) ul ul > li::before {
194 border-radius: 0 !important; // square
195}
196
197.reveal .slides section:not(.title-frame) ul ul ul > li::before {
198 background: transparent !important; // ring
199 border: 0.055em solid var(--r-list-bullet-color) !important;
200 border-radius: 50% !important;
201}
202
203// Ordered lists: the CSS list-item counter honours the HTML start/value
204// attributes, so pandoc's `start`/`value` lists keep their numbers. Numbers
205// are right-aligned into the indent, ending just before the item text.
206.reveal .slides section:not(.title-frame) ol > li::before {
207 content: counter(list-item, decimal) "." !important;
208 width: calc(#{$idsListIndent} - 0.12em) !important;
209 text-align: right !important;
210}
211
212// Checkbox (pandoc task) lists keep their checkboxes, no markers on top.
213.reveal .slides section:not(.title-frame) ul.task-list li::before {
214 content: none !important;
Marc Kupietza0f8f682026-08-08 14:13:11 +0200215}
216
217// reveal.js centres code blocks (width: 90%; margin: auto); make them fill the
218// slide's text column so they start on the same left edge as the headings.
219.reveal .slides section:not(.title-frame) pre {
220 width: auto !important;
221 margin-left: 0 !important;
222 margin-right: 0 !important;
223}
224
225// The IDS logo stays a slide-wide watermark (top right of every slide,
Marc Kupietz28822cd2026-08-20 14:10:18 +0200226// including the title page). The SVG carries no internal padding of its own,
227// so it needs an explicit inset from the slide's right edge; the top offset is
228// chosen so the logo's cap line meets the top of the heading text.
229$idsLogoTop: 6px;
Marc Kupietza0f8f682026-08-08 14:13:11 +0200230$idsLogoRight: 8px;
231
232.reveal .slides {
Marc Kupietz59e95a62026-08-29 10:19:05 +0300233 // Second layer: the optional partner institution logo (--partner-logo is
234 // supplied by the template from the document's `partner_logo` yaml entry);
235 // paints as "none" without it, leaving a harmless empty layer.
236 background-image: url('https://corpora.ids-mannheim.de/slides/reveal.js.ids/images/IDS-Logo-2019.svg'),
237 var(--partner-logo, none);
Marc Kupietza0f8f682026-08-08 14:13:11 +0200238 background-repeat: no-repeat;
Marc Kupietz59e95a62026-08-29 10:19:05 +0300239 background-position: top #{$idsLogoTop} right #{$idsLogoRight},
240 top #{$idsLogoTop} right calc(#{$idsLogoRight} + var(--slide-height) * #{$idsLogoWidthRatio} + #{$idsPartnerGap});
241 background-size: calc(var(--slide-height) * #{$idsLogoWidthRatio}) auto,
242 auto calc(var(--slide-height) * #{$idsPartnerHeightRatio});
Marc Kupietza0f8f682026-08-08 14:13:11 +0200243}
244
245// The orange bar is drawn as a ::before on the headings rather than on the
246// slide, in two segments: one behind the title and one behind the subtitle.
247// Because each segment is sized as a percentage of its own heading's box, the
248// bar always ends exactly at the bottom of the last heading -- it follows a
249// subtitle when there is one, stops at the title when there isn't, and grows
250// by itself when a heading wraps onto a second line. No fixed heights, and no
251// JavaScript. Headings carry no vertical padding, so they sit as high on the
252// slide as the bar does. Every rule is !important because this theme has to
253// win against plugin- and document-level CSS loaded after it.
254.reveal .slides section:not(.title-frame) > h1:first-child,
255.reveal .slides section:not(.title-frame) > h2:first-child {
256 position: relative !important;
257 display: block !important;
258 text-align: left !important;
259 margin: 0 calc(-1 * #{$idsContentInset}) #{$idsHeadingSpacing} calc(-1 * #{$idsContentInset}) !important;
260 padding: 0 0 0 #{$idsContentInset} !important;
261 font-weight: var(--r-heading-font-weight) !important;
262}
263
264.reveal .slides section:not(.title-frame) > h1:first-child::before,
265.reveal .slides section:not(.title-frame) > h2:first-child::before,
266.reveal .slides section:not(.title-frame) > h2:first-child + h3::before {
267 content: "" !important;
268 position: absolute !important;
269 width: #{$idsBarWidth} !important;
270 // A flat fill rather than orange_bar.png: that image is two-toned (a lighter
271 // band across its top), so stretching it over each segment separately left a
272 // pale seam where the two met. The IDS reference decks draw a flat #f6a800.
273 background: #{$idsOrange} !important;
274}
275
276// Title segment: pulled out to the window's physical left edge.
277.reveal .slides section:not(.title-frame) > h1:first-child::before,
278.reveal .slides section:not(.title-frame) > h2:first-child::before {
279 top: 0 !important;
280 left: calc(-1 * #{$idsBandLeft}) !important;
281 height: 100% !important;
282}
283
284// A slide without a subtitle reserves the room one would have taken, so the
285// bar keeps the same height and the body content starts at the same place on
286// every slide. :has() is what lets us ask "is a subtitle coming?" in CSS; where
287// it is unsupported such slides simply keep a title-height bar.
Marc Kupietz6acbd662026-08-20 14:41:00 +0200288//
289// The lone title is vertically CENTRED in its reserved bar: half of the
290// reservation goes above the text as top padding (the bar segment, anchored
291// to the heading box, moves up with it), and the bottom half stays in the bar
292// extension plus margin. The space consumed below the bar is reduced by the
293// same half, so body content starts where it always did.
Marc Kupietza0f8f682026-08-08 14:13:11 +0200294.reveal .slides section:not(.title-frame) > h1:first-child:not(:has(+ h3)),
295.reveal .slides section:not(.title-frame) > h2:first-child:not(:has(+ h3)) {
Marc Kupietz6acbd662026-08-20 14:41:00 +0200296 padding-top: calc((#{$idsHeadingSpacing} + #{$idsSubtitleHeight}) / 2) !important;
297 margin-bottom: calc((#{$idsHeadingSpacing} + #{$idsSubtitleHeight}) / 2 + #{$blockMargin}) !important;
Marc Kupietza0f8f682026-08-08 14:13:11 +0200298}
299
300.reveal .slides section:not(.title-frame) > h1:first-child:not(:has(+ h3))::before,
301.reveal .slides section:not(.title-frame) > h2:first-child:not(:has(+ h3))::before {
Marc Kupietz6acbd662026-08-20 14:41:00 +0200302 height: calc(100% + (#{$idsHeadingSpacing} + #{$idsSubtitleHeight}) / 2) !important;
Marc Kupietza0f8f682026-08-08 14:13:11 +0200303}
304
Marc Kupietzd023f072026-08-20 15:27:37 +0200305// Vertical balance when a subtitle follows: the orange strip between the
306// title's baseline and the subtitle's cap height used to be title descent (16)
307// + segment spacing (8) + subtitle leading (3) ≈ 27px, against only ≈ 11px of
308// orange above the title's cap height. Moving the title 8px DOWN inside its
309// box while REMOVING the 8px segment spacing makes the two margins equal
310// (19px each) AND keeps the bar's total height and the body content's start
311// position pixel-identical: box grows by 8, spacing drops by 8. The subtitle
312// segment still rises (#{$idsHeadingSpacing} + #{$idsBarOverlap}) above its
313// own box, so the bar stays seamless.
314.reveal .slides section:not(.title-frame) > h1:first-child:has(+ h3),
315.reveal .slides section:not(.title-frame) > h2:first-child:has(+ h3) {
316 padding-top: #{$idsHeadingSpacing} !important;
317 margin-bottom: 0 !important;
318}
319
Marc Kupietza0f8f682026-08-08 14:13:11 +0200320// The subtitle sits in the slide's normal text column, so its segment has to
321// reach back out to the slide edge, and up over the gap to the title.
322.reveal .slides section:not(.title-frame) > h2:first-child + h3 {
323 position: relative !important;
324 text-align: left !important;
325 margin-top: 0 !important;
326 margin-left: 0 !important;
327 margin-bottom: $blockMargin !important;
328}
329
330.reveal .slides section:not(.title-frame) > h2:first-child + h3::before {
331 top: calc(-1 * (#{$idsHeadingSpacing} + #{$idsBarOverlap})) !important;
332 left: calc(-1 * (#{$idsContentInset} + #{$idsBandLeft})) !important;
333 height: calc(100% + #{$idsHeadingSpacing} + #{$idsBarOverlap}) !important;
Marc Kupietza737b1b2023-10-07 09:32:20 +0200334}
335
Marc Kupietz55c9cef2026-08-08 16:03:06 +0200336// Section-divider slides: a level 1 heading alone on a slide (pandoc marks
337// these .level1). The IDS reference decks centre that title on the slide while
338// the bar stays in its usual top left corner, so here the bar cannot hang off
339// the heading -- it is painted on the slide itself, at the height it has on
340// every other slide, and the heading gets none of the top-left treatment.
341$idsTitleHeight: 1.35 * $mainFontSize * 1.2;
342$idsBarBlockHeight: $idsTitleHeight + $idsHeadingSpacing + $idsSubtitleHeight;
343
344.reveal .slides section.level1:not(.title-frame) {
345 height: 100% !important;
346 display: flex !important;
347 flex-direction: column !important;
348 justify-content: center !important;
349}
350
351.reveal .slides section.level1:not(.title-frame)::before {
352 content: "" !important;
353 position: absolute !important;
354 top: 0 !important;
355 left: calc(-1 * #{$idsBandLeft}) !important;
356 width: #{$idsBarWidth} !important;
357 height: #{$idsBarBlockHeight} !important;
358 background: #{$idsOrange} !important;
359}
360
361.reveal .slides section.level1:not(.title-frame) > h1:first-child {
362 text-align: center !important;
363 margin: 0 !important;
364 padding: 0 !important;
365}
366
367.reveal .slides section.level1:not(.title-frame) > h1:first-child::before {
368 content: none !important;
369}
370
Marc Kupietza737b1b2023-10-07 09:32:20 +0200371// Define additional color effects based on Dracula spec
372// https://spec.draculatheme.com/
373:root {
374 --r-bold-color: #{$idsOrange};
375 --r-list-bullet-color: #{$listBulletColor};
376}
377
378.reveal {
Marc Kupietz9b795bd2026-08-19 06:58:44 +0200379 // Bold ("highlighted") text gets the orange colour AND the condensed Fira
380 // Sans face, as in the IDS reference decks -- the lighter weight and 90%
381 // size keep it from dominating the serif body text.
Marc Kupietza737b1b2023-10-07 09:32:20 +0200382 strong, b {
Marc Kupietz9b795bd2026-08-19 06:58:44 +0200383 font-family: "Fira Sans Condensed", sans-serif;
384 font-weight: 600;
385 font-size: 90%;
Marc Kupietza737b1b2023-10-07 09:32:20 +0200386 color: var(--r-bold-color);
387 }
388
389 // Dracula colored list bullets and numbers
390 ul, ol {
391 li::marker {
392 color: var(--r-list-bullet-color) !important;
393 }
394 }
395}
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200396
Marc Kupietz281ba9c2026-08-11 15:37:48 +0200397// Nested list items are set progressively smaller, both to emphasise the
398// hierarchy and to keep deeply nested lists from visually competing with the
399// first level. The em size compounds down the nesting levels, so each deeper
400// level is 80% of its parent (level 2: 80%, level 3: 64%, level 4: 51%).
401// Markers and the hanging indentation (padding-left: 0.7em above) scale with
402// the text automatically.
403.reveal li li {
404 font-size: 0.8em;
405}
406
Marc Kupietzf4945eb2026-08-19 17:48:41 +0200407// Description lists: dt in orange Fira Sans Condensed, dd indented.
408.reveal dl {
409 font-size: 0.62em;
410 line-height: 1.25;
411}
412.reveal dt {
413 font-weight: 700;
414 margin-top: 0.35em;
415 font-family: "Fira Sans Condensed", sans-serif;
416 color: $idsOrange;
417}
418.reveal dd {
419 margin: 0 0 0 1.2em;
420 color: #444;
421}
422
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200423
424/*********************************************
425 * FOOTER
426 *********************************************/
Marc Kupietzc0b272d2023-10-08 14:24:18 +0200427 #ids-footer:not(title-slide) {
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200428 opacity: 1;
429 background: white;
430 color: #444444;
431 border-top: dotted orange 2px;
432 transition: opacity 800ms ease-in-out;
433 position: fixed;
434 height: 6%;
435 line-height: 6%;
436 z-index: -20;
437 width: 100%;
438 letter-spacing: 0em;
439 text-align: center;
440 display: table;
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200441}
442
443#ids-footer span {
444 height: 100%;
445 line-height: 100%;
446 z-index: -20;
Marc Kupietz61ad0262024-05-14 22:07:50 +0200447 font-size: 1.8vh;
Marc Kupietzc0b272d2023-10-08 14:24:18 +0200448 font-family: "Fira Sans Condensed", "Fira Sans", "Roboto Condensed", "League Gothic", Impact, sans-serif;
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200449 display: inline-block;
450 display: table-cell;
451 vertical-align: middle;
452}
453
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200454/* Bottom position for the IDS-Footer footer when both progress bar and TOC-Progress are visible */
Marc Kupietzc0b272d2023-10-08 14:24:18 +0200455div.progress[style="display: block;"] ~ #ids-footer {
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200456 bottom: calc(3px + 0vh);
457}
458
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200459/* Bottom position for the IDS-Footer footer when TOC-Progress is visible */
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200460#ids-footer {
461 bottom: 3px;
462}
463
464#ids-footer a {
465 color: #555555;
466}
467
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200468/* Bottom position for the IDS-Footer footer when progress bar is visible */
Marc Kupietzc0b272d2023-10-08 14:24:18 +0200469div.progress[style="display: block;"] ~ footer:last-of-type#ids-footer {
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200470 bottom: 3px;
471}
472
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200473/* Bottom position for the IDS-Footer footer when neither progress bar nor TOC-Progress are visible */
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200474footer:last-of-type#ids-footer {
475 bottom: 0px;
476}
477
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200478/* Make IDS-Footer invisible if explicitly indicated */
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200479.no-ids-footer #ids-footer {
480 opacity: 0;
481 transition: opacity 800ms ease-in-out;
482}
483
484.title-frame #ids-footer {
485 opacity: 0;
486 transition: opacity 800ms ease-in-out;
487}
488
489.no-toc-progress #ids-footer {
490 opacity: 0;
491 transition: opacity 800ms ease-in-out;
492}
493
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200494/* Make IDS-Footer invisible in overview mode */
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200495.overview #ids-footer {
496 opacity: 0;
497 transition: opacity 800ms ease-in-out;
498}
499
Marc Kupietz61c8ec72026-08-20 20:47:45 +0200500/* In print, the footer's negative stacking drops it behind the .pdf-page
501 * backgrounds (z-index 1); lift it above them. position: fixed repeats it at
502 * the bottom of every printed page. */
503html.reveal-print #ids-footer {
504 z-index: 100 !important;
505}
506
507// reveal's print CSS gives every .pdf-page z-index:1, which forms a separate
508// stacking context per page. The slide-number-pdf lives INSIDE such a page, so
509// its own z-index only competes within the page: the footer (z-index 100 at
510// root level) always paints over it, and raising the number's z-index alone
511// cannot help. Remove the per-page stacking context in print (the pages are
512// separated by page-break-after:always anyway, so nothing needs stacking), and
513// the number's z-index 200 wins against the footer globally. Position stays
514// the theme's screen rule (right:29px / bottom:22px), which puts the number
515// inside the footer band with its white background cutting the dotted line --
516// the same look as on screen.
517html.reveal-print .reveal .slides .pdf-page {
518 z-index: auto !important;
519}
520
521// On screen the number is flanked by the arrow buttons, so its slightly raised
522// baseline reads as deliberate; in print it shares the footer band with the
523// author/title/date line, whose vertically centered text has its baseline
524// ~19px above the page bottom. The number's screen offset (bottom:22px, 14px
525// font from reveal's print rule) puts its baseline ~25px up. 15px aligns the
526// two baselines. Print-only: the screen rule keeps its 22px.
527html.reveal-print div.slide-number-pdf {
528 z-index: 200 !important;
529 bottom: 15px !important;
530}
531
532// On screen the bar segments shift left by --ids-band-left so the bar reaches
533// the window's physical edge when the slide is pillarboxed. In print there are
534// no bands (.pdf-page == slide box), and if the band values are ever stale
535// (e.g. an outdated template script), a nonzero band-left would push the
536// segments outside the pdf-page, whose overflow:hidden then clips them.
537// Pin the geometry explicitly for print instead of trusting the custom
538// property: title segments start at the heading's (== pdf-page's) left edge,
539// the subtitle segment (which lives in the text column, inset by
540// $idsContentInset) reaches back out by exactly the inset, and the level1
541// divider bar starts at the section's left edge. Widths stay the normal bar
542// width, so the visible result is pixel-identical to the on-screen bar.
543html.reveal-print .reveal .slides section:not(.title-frame) > h1:first-child::before,
544html.reveal-print .reveal .slides section:not(.title-frame) > h2:first-child::before {
545 left: 0 !important;
546}
547
548html.reveal-print .reveal .slides section:not(.title-frame) > h2:first-child + h3::before {
549 left: calc(-1 * #{$idsContentInset}) !important;
550}
551
552html.reveal-print .reveal .slides section.level1:not(.title-frame)::before {
553 left: 0 !important;
554}
555
556// The WGL membership logo sits at bottom:10px of the title frame -- inside
557// the footer's ~46px band on print pages (the footer is force-shown on every
558// page in print view and cannot be hidden per page, position:fixed). Lift the
559// logo above the footer in print only; the screen layout stays put.
560html.reveal-print .reveal .slides section.title-frame {
561 background-position: right 8px bottom 55px;
562}
563
Marc Kupietza0f8f682026-08-08 14:13:11 +0200564// Like the orange bar/IDS logo above, this is painted on the slide itself
565// (rather than on .slide-background-content, which spans the raw,
566// unscaled viewport) so it stays pinned to the title slide's true
Marc Kupietz67715592026-08-15 14:55:18 +0200567// bottom-right corner instead of the browser window's. Its margin mirrors
Marc Kupietz28822cd2026-08-20 14:10:18 +0200568// the IDS logo's (top 6px / right 8px of the slide), so the two sit on
Marc Kupietz67715592026-08-15 14:55:18 +0200569// one shared inset.
Marc Kupietza0f8f682026-08-08 14:13:11 +0200570.reveal .slides section.title-frame {
571 background-image: url("https://corpora.ids-mannheim.de/slides/reveal.js.ids/images/Mitglied_WGL_transparent.svg");
Marc Kupietzf0ce0322023-10-07 15:33:47 +0200572 background-repeat: no-repeat;
Marc Kupietz67715592026-08-15 14:55:18 +0200573 background-position: right 8px bottom 10px;
574 background-size: calc(var(--slide-height) * 0.1) auto;
575 /* full slide height, so "bottom" really is the slide's bottom edge */
576 height: 100%;
Marc Kupietze9ace292024-05-14 22:11:15 +0200577}
578
Marc Kupietz59e95a62026-08-29 10:19:05 +0300579// Website QR code and funder logo on the title slide (document options
580// `website` and `funder_logo`): top-left and bottom-left corners. All
581// logos on the title slide keep the same distance to their respective
582// margins: top 6px / left 8px (QR), top 6px / right 8px (IDS [+ partner]
583// logo), bottom 10px / left 8px (funder), bottom 10px / right 8px (WGL
584// membership logo), so the four corners share one inset vocabulary. The QR
585// is sized generously (a quarter of the slide height) so it stays scannable
586// in all circumstances. Both are DOM elements injected into the title frame
587// by the pandoc template (the QR itself is generated in R by qrlink());
588// absolute positioning sticks to the slide, because reveal.js positions the
589// sections themselves.
590.reveal .slides section.title-frame .title-qr {
591 position: absolute;
592 top: #{$idsLogoTop};
593 left: #{$idsLogoRight};
594 // The generated QR matrix has no quiet zone of its own (dark modules reach
595 // the svg edges), which makes it unscannable; the white plate's padding of
596 // ~4 modules supplies it. The plate's edge is also the element's visual
597 // boundary for the uniform corner insets, hence an outer size of 28% of
598 // the slide height minus the padding on two sides (~23% of pattern).
599 box-sizing: border-box;
600 width: calc(var(--slide-height) * 0.28);
601 padding: calc(var(--slide-height) * 0.025);
602 background: #fff;
603}
604
605.reveal .slides section.title-frame .title-qr a.qrcode,
606.reveal .slides section.title-frame .title-qr svg {
607 display: block;
608 width: 100%;
609 height: auto;
610 margin: 0;
611}
612
613.reveal .slides section.title-frame img.funder-logo {
614 position: absolute;
615 left: #{$idsLogoRight};
616 bottom: 10px;
617 height: calc(var(--slide-height) * 0.07);
618 width: auto;
619}
620
621// Lift the funder logo above the print footer band, like the WGL logo above
622html.reveal-print .reveal .slides section.title-frame img.funder-logo {
623 bottom: 55px;
624}
625
626// The navigation chrome is hidden on the title slide (the template toggles
627// the ids-title-plain class when the title frame is the current slide). Use
628// opacity/visibility rather than display:none so the transition out of the
629// title slide fades the chrome in instead of popping it up, and so reveal.js'
630// own controls bookkeeping keeps working underneath.
631html.ids-title-plain .reveal .controls,
632html.ids-title-plain .reveal .slide-number,
633html.ids-title-plain .reveal .slide-menu-button {
634 opacity: 0 !important;
635 visibility: hidden !important;
636 pointer-events: none !important;
637}
638
639.reveal .controls,
640.reveal .slide-number,
641.reveal .slide-menu-button {
642 transition: opacity 0.4s;
643}
644
Marc Kupietze9ace292024-05-14 22:11:15 +0200645
Marc Kupietz903104a2026-08-20 10:10:41 +0200646/*********************************************
647 * SLIDES MENU
648 *********************************************/
649
650// The menu plugin copies .reveal's computed font-family onto .slide-menu as an
651// inline style (and its own CSS is loaded after the theme), so without
652// !important here the menu would come out in the Libertinus serif body face.
653// Fira Sans Condensed is the IDS sans for navigation/UI text, and its narrow
654// glyphs keep long (German) slide titles on one line.
655.slide-menu-wrapper .slide-menu {
656 font-family: "Fira Sans Condensed", sans-serif !important;
657}
658
659// Widen the menu a little over the plugin's 300px default -- but only the
660// default: a width the user picked via the menu-width option maps to one of
661// the plugin's width classes (or an inline style), which must keep winning.
662.slide-menu-wrapper .slide-menu:not(.slide-menu--wide):not(.slide-menu--half):not(.slide-menu--third):not(.slide-menu--full):not(.slide-menu--custom) {
Marc Kupietz7427cca2026-08-20 10:53:03 +0200663 width: 372px !important;
Marc Kupietz903104a2026-08-20 10:10:41 +0200664}
665
666// Tighter, less wasteful rows than the plugin's 10px 18px padding with a 12px
667// icon gap: the icon column and generous side padding eat exactly the width
668// the titles need.
669.slide-menu-wrapper .slide-menu-item,
670.slide-menu-wrapper .slide-menu-item-vertical {
671 padding: 6px 12px !important;
672 line-height: 1.3 !important;
673}
674
675.slide-menu-wrapper .slide-menu-item-vertical {
676 padding-left: 26px !important;
677}
678
679// Wrapped titles: make the row a flex line so the second line aligns with the
680// first instead of sliding under the bullet/circle icon. The icon sits on the
681// first line's text baseline; the title column takes the remaining width and
682// wraps within itself.
683.slide-menu-wrapper .slide-menu-item,
684.slide-menu-wrapper .slide-menu-item-vertical {
685 display: flex !important;
686 align-items: baseline !important;
687}
688
689.slide-menu-wrapper .slide-menu-item-title {
690 flex: 1 !important;
691 min-width: 0 !important;
692}
693
694.slide-menu-wrapper .slide-menu-item i.far,
695.slide-menu-wrapper .slide-menu-item i.fas,
696.slide-menu-wrapper .slide-menu-item-vertical i.far,
697.slide-menu-wrapper .slide-menu-item-vertical i.fas,
698.slide-menu-wrapper .slide-menu-item svg.svg-inline--fa,
699.slide-menu-wrapper .slide-menu-item-vertical svg.svg-inline--fa {
700 padding-right: 8px !important;
701}
702
Marc Kupietz7427cca2026-08-20 10:53:03 +0200703// Top-level (section) entries in bold, marking the hierarchy as the indented
704// sub-slides alone do now. Note the Google Fonts import only ships Fira Sans
705// Condensed in 300/400/700/800 -- there is no 600.
706.slide-menu-wrapper .slide-menu-item {
707 font-weight: 700 !important;
708}
709
Marc Kupietz903104a2026-08-20 10:10:41 +0200710// Current slide in the IDS accent colour, matching the slide-number and the
711// orange bar.
712.slide-menu-wrapper .active-menu-panel li.active {
713 color: #{$idsOrange} !important;
Marc Kupietzf0ce0322023-10-07 15:33:47 +0200714}
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200715
716/*
717 * Menu controls
718 */
Marc Kupietzc0b272d2023-10-08 14:24:18 +0200719body .reveal .slide-menu-button {
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200720 color: #aaaaaa !important;
721 position: fixed;
722 left: 30px;
723 bottom: 2%;
724 vertical-align: middle;
725 z-index: 30;
726 font-size: 2vh;
727}
728
729#customcontrols > ul {
730 color: #aaaaaa !important;
731 position: fixed;
Marc Kupietzf51e9152023-10-07 15:32:33 +0200732 left: 50px;
Marc Kupietz7c8f7de2023-10-07 11:42:29 +0200733 bottom: 2% !important;
734 vertical-align: middle;
735 z-index: 30;
736 font-size: 2vh;
737}
738
739@media only screen and (min-width: 500px) {
740 #customcontrols > ul {
741 bottom: 2% !important;
742 }
743}
Marc Kupietz3fdee732023-10-07 18:52:20 +0200744
Marc Kupietzc7249f92023-10-10 07:16:50 +0200745.reveal .controls .controls-arrow {
746 position: relative;
747 width: 3.6em;
748 height: 3.6em;
749 bottom: -12px;
750 right: -4px;
751}
752
Marc Kupietz3fdee732023-10-07 18:52:20 +0200753.reveal h3, h4, h5, h6 {
754 text-transform: none !important;
755}
756
757.reveal h3, h4, h5, h6 {
758 text-transform: none !important;
759}
760
761.reveal h2 + h3 {
762 margin-top: -0.5em !important;
763}
Marc Kupietzdf0eda92023-10-07 20:50:00 +0200764
765.title-frame h1,
766.title-frame h2 {
767 margin: 0 0 0 0;
768 background: rgb(246, 168, 0);
769 color: white;
770 font-family: "Fira Sans Condensed", "Roboto Condensed", Impact, sans-serif;
771 font-weight: 600;
772 line-height: 1.2;
773 letter-spacing: 0.05ex;
774 text-transform: uppercase;
775 width: 100%;
776 margin-top: .5em;
777 padding-top: .5em;
778 font-size: 1.75em;
779 margin-left: 0;
780 text-align: center;
781 text-shadow: none;
782 word-wrap: break-word;
783}
784
785
786.title-frame .author {
787 font-family: "Fira Sans Condensed", sans-serif;
788 font-weight: 400;
789 color: #657b83;
790 margin-top: 10vh;
791}
792
793.title-frame .place {
794 font-family: "Fira Sans Condensed", sans-serif;
795 margin-top: 40px;
796 font-weight: 400;
797 color: #657b83;
798}
799
800.title-frame h2 {
801 margin-top: -3px;
802 margin-bottom: 1em;
803 padding-bottom: .5em;
804 font-size: 1em;
805}
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200806
807/*********************************************
808 * TABLES
809 *********************************************/
810
811 th {
812 color: rgb(246, 168, 0);
813}
814
815table {
816 font-family: 'Fira Sans Condensed', sans-serif;
817 font-weight: 400;
818 font-size: 16px;
Marc Kupietz25570ef2026-08-28 14:03:21 +0300819 // Fira Sans Condensed ships proportional figures by default, so the digits
820 // in neighbouring rows do not line up; tabular numbers (tnum) fix the digit
821 // widths and align numeric table columns vertically.
822 font-variant-numeric: tabular-nums;
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200823}
824
825.reveal table.dataTable {
826 border: #aaaaaa 1px solid;
827 border-collapse: collapse;
828}
829
830.reveal table.dataTable td {
831 border: #aaaaaa 1px solid;
832 border-collapse: collapse;
833}
834
835.dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_filter, .dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_processing, .dataTables_wrapper .dataTables_paginate, table.dataTable thead > tr > th.sorting::before, table.dataTable thead > tr > th.sorting::after, table.dataTable thead > tr > th.sorting_asc::before, table.dataTable thead > tr > th.sorting_asc::after, table.dataTable thead > tr > th.sorting_desc::before, table.dataTable thead > tr > th.sorting_desc::after, table.dataTable thead > tr > th.sorting_asc_disabled::before, table.dataTable thead > tr > th.sorting_asc_disabled::after, table.dataTable thead > tr > th.sorting_desc_disabled::before, table.dataTable thead > tr > th.sorting_desc_disabled::after, table.dataTable thead > tr > td.sorting::before, table.dataTable thead > tr > td.sorting::after, table.dataTable thead > tr > td.sorting_asc::before, table.dataTable thead > tr > td.sorting_asc::after, table.dataTable thead > tr > td.sorting_desc::before, table.dataTable thead > tr > td.sorting_desc::after, table.dataTable thead > tr > td.sorting_asc_disabled::before, table.dataTable thead > tr > td.sorting_asc_disabled::after, table.dataTable thead > tr > td.sorting_desc_disabled::before, table.dataTable thead > tr > td.sorting_desc_disabled::after {
836 display: none;
837}
838
839.caption {
840 font-family: 'Fira Sans Condensed', sans-serif;
841 font-weight: 400;
842 font-size: 16px;
843 text-align: center;
844}
845
846/*
847table.display td { white-space: nowrap; }
848*/
849
850.dt-buttons, .dataTables_filter {
851 margin-top: 10pt;
852}
853
Marc Kupietz069f1e42023-10-11 10:26:16 +0200854/* Sort Arrows in DataTables */
855table.dataTable thead>tr>th.sorting:before, table.dataTable thead>tr>th.sorting:after, table.dataTable thead>tr>th.sorting_asc:before, table.dataTable thead>tr>th.sorting_asc:after, table.dataTable thead>tr>th.sorting_desc:before, table.dataTable thead>tr>th.sorting_desc:after, table.dataTable thead>tr>th.sorting_asc_disabled:before, table.dataTable thead>tr>th.sorting_asc_disabled:after, table.dataTable thead>tr>th.sorting_desc_disabled:before, table.dataTable thead>tr>th.sorting_desc_disabled:after, table.dataTable thead>tr>td.sorting:before, table.dataTable thead>tr>td.sorting:after, table.dataTable thead>tr>td.sorting_asc:before, table.dataTable thead>tr>td.sorting_asc:after, table.dataTable thead>tr>td.sorting_desc:before, table.dataTable thead>tr>td.sorting_desc:after, table.dataTable thead>tr>td.sorting_asc_disabled:before, table.dataTable thead>tr>td.sorting_asc_disabled:after, table.dataTable thead>tr>td.sorting_desc_disabled:before, table.dataTable thead>tr>td.sorting_desc_disabled:after {
856 right: 0 !important;
857 line-height: 80% !important;
858}
859
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200860/*********************************************
861 * BIBLIOGRAPHY
862 *********************************************/
863
864.reveal .references {
865 font-family: "Fira Sans Condensed", sans-serif;
866 font-size: 16px;
867 text-align: left;
Marc Kupietzbfab8a12026-08-19 14:42:42 +0200868 margin-top: 10px;
869 max-height: 510px;
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200870 font-weight: 400;
871 color: #253b43;
872 overflow-y: auto;
873}
874
875.hanging-indent > .csl-entry {
876 padding-left: 22px ;
877 text-indent: -22px ;
878}
879
Marc Kupietz21f526c2026-08-14 12:32:56 +0200880// .references scopes these above pandoc's CSL defaults (div.csl-entry), which
881// otherwise win on specificity and pin margin-bottom to 0em.
882.references .csl-entry {
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200883 color: #888888;
Marc Kupietz21f526c2026-08-14 12:32:56 +0200884 margin-bottom: 0.5em;
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200885}
886
Marc Kupietz21f526c2026-08-14 12:32:56 +0200887// "Authors (Year):" as a bold dark first line; the theme template's JS wraps
888// the leading text of each entry in .csl-authors-year (see default.html) --
889// which part of an entry citeproc wraps in spans depends on the entry type, so
890// a CSS-only break cannot place it reliably.
891.csl-entry .csl-authors-year {
892 display: block;
893 font-weight: bold;
894 color: #4d616b;
Marc Kupietz0a3c2e52023-10-08 20:20:43 +0200895}
Marc Kupietz11797162023-10-09 23:17:36 +0200896
897.reveal code.sourceCode:last-child + pre {
898 height: 800px !important;
Marc Kupietzc7249f92023-10-10 07:16:50 +0200899}
900
Marc Kupietz589d1b82023-10-10 12:31:33 +0200901
902/*********************************************
Marc Kupietz54d9ce02026-08-21 11:38:45 +0200903 * FULLSCREEN TOGGLE
Marc Kupietz589d1b82023-10-10 12:31:33 +0200904 *********************************************/
905
Marc Kupietz54d9ce02026-08-21 11:38:45 +0200906// Small fixed toggle for touch devices (script injected by default.html):
907// phones have no F11 and the browser chrome eats slide pixels. Hidden on
908// pointer devices (F11 exists there) and whenever the Fullscreen API is
909// unavailable -- the script also removes it then. Same muted look and column
910// as the slide menu button, one slot above it.
911#ids-fullscreen {
912 display: none;
913 position: fixed;
914 left: 30px;
915 bottom: 6%;
916 z-index: 30;
917 width: 28px;
918 height: 28px;
919 border: 0;
920 padding: 0;
921 cursor: pointer;
922 opacity: 0.55;
923 // Feather-style corner brackets; gray idle, IDS orange while fullscreen
924 // (the two data URIs differ only in the stroke colour).
925 background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23aaaaaa' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H5a2 2 0 0 0-2 2v3'/%3E%3Cpath d='M16 3h3a2 2 0 0 1 2 2v3'/%3E%3Cpath d='M8 21H5a2 2 0 0 1-2-2v-3'/%3E%3Cpath d='M16 21h3a2 2 0 0 0 2-2v-3'/%3E%3C/svg%3E") center / contain no-repeat;
926
927 &:active,
928 &:focus-visible {
929 opacity: 0.9;
930 }
931
932 &.is-fullscreen {
933 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f6a800' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H5a2 2 0 0 0-2 2v3'/%3E%3Cpath d='M16 3h3a2 2 0 0 1 2 2v3'/%3E%3Cpath d='M8 21H5a2 2 0 0 1-2-2v-3'/%3E%3Cpath d='M16 21h3a2 2 0 0 0 2-2v-3'/%3E%3C/svg%3E");
934 }
935}
936
937// Only touch devices get the button; desktops keep their F11.
938@media (hover: none) and (pointer: coarse) {
939 #ids-fullscreen {
940 display: block;
941 }
942}
943
944// position:fixed elements repeat on every printed page -- never print it.
945html.reveal-print #ids-fullscreen {
946 display: none !important;
947}
948
949
950
Marc Kupietz589d1b82023-10-10 12:31:33 +0200951 .reveal .controls .controls-arrow:after, .reveal .controls .controls-arrow:before {
952 width: 13px !important; /* size */
953 height: 4px; /* line width */
954}
955
956.reveal .controls {
957 display: none;
958 position: absolute;
959 top: auto;
960 bottom: -8px;
961 right: 16px;
962 left: auto;
963 z-index: 11;
964 /* color: #000;*/
965 pointer-events: none;
966 font-size: 8px;
967}
968
969.reveal .controls .controls-arrow {
970 position: relative;
971 height: 3.6em;
972 width: 1.2em; /* distance betwee arrows */
973 bottom: 0;
974 right: 0;
975}
976
977.reveal div.slide-number {
978 text-align: center;
979 width: 2em;
980 position: absolute;
981 display: block;
982 right: 29px;
983 bottom: 22px;
984 z-index: 31;
985 font-family: "Fira Sans Condensed", sans-serif;
986 font-size: 18px;
987 line-height: 1;
988 background-color: white;
989 color: #666;
990 padding: 0;
991}
992