decktile 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- decktile/__init__.py +5 -0
- decktile/__main__.py +3 -0
- decktile/assets/lucide/VENDORED.md +21 -0
- decktile/assets/lucide/lucide.css +2193 -0
- decktile/assets/lucide/lucide.ttf +3 -0
- decktile/cli.py +269 -0
- decktile/parser/__init__.py +0 -0
- decktile/parser/ast.py +191 -0
- decktile/parser/markdown.py +289 -0
- decktile/plugins/cards.py +25 -0
- decktile/plugins/directives.py +56 -0
- decktile/plugins/fenced.py +228 -0
- decktile/plugins/layouts.py +69 -0
- decktile/plugins/lucide.py +65 -0
- decktile/plugins/lucide_font.py +26 -0
- decktile/render/__init__.py +0 -0
- decktile/render/html.py +287 -0
- decktile/render/ir.py +536 -0
- decktile/render/odf/__init__.py +4 -0
- decktile/render/odf/geometry.py +510 -0
- decktile/render/odf/shapes.py +719 -0
- decktile/render/odf/styles.py +828 -0
- decktile/render/odp.py +380 -0
- decktile/render/pdf.py +17 -0
- decktile/render/pdg.py +72 -0
- decktile/render/seaborn.py +155 -0
- decktile/render/templates/base.css +741 -0
- decktile/render/templates/decktile.js +371 -0
- decktile/render/templates/shell.html +106 -0
- decktile/seaborn_styles/__init__.py +5 -0
- decktile/seaborn_styles/ossummit_europe.py +30 -0
- decktile/theme/__init__.py +0 -0
- decktile/theme/colors.py +53 -0
- decktile/theme/loader.py +22 -0
- decktile/theme/parser.py +264 -0
- decktile/themes/default.css +146 -0
- decktile/themes/kcd_vietnam.css +159 -0
- decktile/themes/kubecon_china.css +213 -0
- decktile/themes/kubecon_japan.css +156 -0
- decktile/themes/ossummit_europe.css +196 -0
- decktile/themes/ossummit_korea.css +196 -0
- decktile-0.2.0.dist-info/METADATA +625 -0
- decktile-0.2.0.dist-info/RECORD +46 -0
- decktile-0.2.0.dist-info/WHEEL +4 -0
- decktile-0.2.0.dist-info/entry_points.txt +6 -0
- decktile-0.2.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
/* decktile base layout -- structural CSS only, no colors */
|
|
2
|
+
|
|
3
|
+
/* ---- reset ---- */
|
|
4
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5
|
+
|
|
6
|
+
/* ---- variables ---- */
|
|
7
|
+
:root {
|
|
8
|
+
--radius: 0.18em;
|
|
9
|
+
--img-radius: 0.45em;
|
|
10
|
+
--gap: 1em;
|
|
11
|
+
--font-mono: "Noto Sans Mono", Consolas, "SFMono-Regular", Menlo, monospace;
|
|
12
|
+
--section-padding-v: 2.8em;
|
|
13
|
+
--section-padding-h: 3.6em;
|
|
14
|
+
--card-border: 1px solid var(--color-border);
|
|
15
|
+
--color-accent-primary: var(--color-accent);
|
|
16
|
+
--color-accent-secondary: var(--color-accent);
|
|
17
|
+
--color-accent-contrast: var(--color-accent);
|
|
18
|
+
--bg-image: none;
|
|
19
|
+
--bg-overlay: transparent;
|
|
20
|
+
--watermark: none;
|
|
21
|
+
--logo: none;
|
|
22
|
+
--logo-dark: var(--logo);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/* ---- logo (top-right, override via logo: frontmatter) ---- */
|
|
27
|
+
section::before {
|
|
28
|
+
content: "";
|
|
29
|
+
position: absolute;
|
|
30
|
+
top: 4%;
|
|
31
|
+
right: 5%;
|
|
32
|
+
width: 14%;
|
|
33
|
+
height: 0;
|
|
34
|
+
padding-bottom: 6%;
|
|
35
|
+
background: var(--logo) center / contain no-repeat;
|
|
36
|
+
opacity: 0.92;
|
|
37
|
+
z-index: 1;
|
|
38
|
+
}
|
|
39
|
+
[data-theme="dark"] section::before,
|
|
40
|
+
section[data-theme="dark"]::before {
|
|
41
|
+
/* Repeats the full shorthand, not just background-image: weasyprint
|
|
42
|
+
fails to resolve a var()-based background-image longhand override
|
|
43
|
+
applied after an earlier background shorthand -- it resets to
|
|
44
|
+
`none` instead of swapping just the image (confirmed via isolated
|
|
45
|
+
repro, unrelated to CSS custom-property chaining itself, which
|
|
46
|
+
weasyprint handles fine on its own). Real browsers resolve this
|
|
47
|
+
correctly either way; this avoids relying on a longhand override
|
|
48
|
+
weasyprint (both the --pdf output and the ODP renderer's geometry
|
|
49
|
+
capture) gets wrong. */
|
|
50
|
+
background: var(--logo-dark) center / contain no-repeat;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/* ---- watermark (conference logo, brand mark) ---- */
|
|
55
|
+
section::after {
|
|
56
|
+
display: block;
|
|
57
|
+
content: "";
|
|
58
|
+
position: absolute;
|
|
59
|
+
bottom: 4%;
|
|
60
|
+
right: 5%;
|
|
61
|
+
width: 10%;
|
|
62
|
+
height: 10%;
|
|
63
|
+
background: var(--watermark) right bottom / contain no-repeat;
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
z-index: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.accent-primary { color: var(--color-accent-primary); stroke: var(--color-accent-primary); }
|
|
69
|
+
.accent-secondary { color: var(--color-accent-secondary); stroke: var(--color-accent-secondary); }
|
|
70
|
+
.accent-contrast { color: var(--color-accent-contrast); stroke: var(--color-accent-contrast); }
|
|
71
|
+
svg.accent-primary, svg.accent-secondary, svg.accent-contrast { fill: none; }
|
|
72
|
+
|
|
73
|
+
/* ---- layout defaults ---- */
|
|
74
|
+
section {
|
|
75
|
+
font-size: 18pt;
|
|
76
|
+
padding: var(--section-padding-v) var(--section-padding-h);
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
}
|
|
80
|
+
.slide-body {
|
|
81
|
+
flex: 1;
|
|
82
|
+
min-height: 0;
|
|
83
|
+
padding-bottom: 1.8em;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
h1, h2, h3, h4, h5, h6 { margin: 0; }
|
|
87
|
+
h2 { padding-top: 0; margin-bottom: 0.15em; }
|
|
88
|
+
h4 { margin: 0.4em 0 0.15em; }
|
|
89
|
+
h3::after {
|
|
90
|
+
display: block;
|
|
91
|
+
content: "";
|
|
92
|
+
width: 100%;
|
|
93
|
+
height: 0;
|
|
94
|
+
/* same 1px weight as the card border (--card-border); 2px accent read
|
|
95
|
+
as heavier than the light grey frame around it */
|
|
96
|
+
border-bottom: 1px solid var(--color-accent-primary);
|
|
97
|
+
margin-top: 0.3em;
|
|
98
|
+
opacity: 0.3;
|
|
99
|
+
}
|
|
100
|
+
.card.side-image {
|
|
101
|
+
align-self: center;
|
|
102
|
+
justify-self: center;
|
|
103
|
+
padding: 0.3em;
|
|
104
|
+
background: none;
|
|
105
|
+
border: none;
|
|
106
|
+
}
|
|
107
|
+
.card.side-image img { max-width: 8em; }
|
|
108
|
+
.subtitle { margin-bottom: 0.6em; }
|
|
109
|
+
h2 + .subtitle { font-size: 0.85em; }
|
|
110
|
+
p { margin-bottom: 0.5em; }
|
|
111
|
+
ul { margin: 0 14pt; }
|
|
112
|
+
li { margin: 0.4em 0; line-height: 1.5; overflow-wrap: break-word; }
|
|
113
|
+
ul { list-style: var(--list-style); }
|
|
114
|
+
ul li::marker { color: var(--color-accent); font-weight: bold; }
|
|
115
|
+
|
|
116
|
+
img { max-width: 100%; height: auto; border-radius: var(--img-radius); }
|
|
117
|
+
section img { max-height: IMG_MAX_Hpx; object-fit: contain; display: block; margin-left: auto; margin-right: auto; }
|
|
118
|
+
figure.video { margin: 0; display: flex; align-items: center; justify-content: center; height: 100%; min-height: 0; }
|
|
119
|
+
figure.video video { width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--img-radius); }
|
|
120
|
+
img.video-print { display: none; }
|
|
121
|
+
[data-theme="dark"] section img,
|
|
122
|
+
section[data-theme="dark"] img { max-height: 16em; }
|
|
123
|
+
a { text-decoration: none; }
|
|
124
|
+
|
|
125
|
+
blockquote {
|
|
126
|
+
border-left: 4px solid;
|
|
127
|
+
padding-left: 0.9em;
|
|
128
|
+
margin: 0.5em 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
code {
|
|
132
|
+
font-family: var(--font-mono);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.note {
|
|
136
|
+
border-left: 0.25em solid var(--color-accent);
|
|
137
|
+
|
|
138
|
+
padding: 0.5em 0.8em;
|
|
139
|
+
margin: 0.5em 0;
|
|
140
|
+
font-size: 0.8em;
|
|
141
|
+
border-radius: 0 var(--radius) var(--radius) 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
pre {
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
border-radius: var(--radius);
|
|
147
|
+
padding: 0.55em 0.8em;
|
|
148
|
+
margin: 0.5em 0;
|
|
149
|
+
font-size: 0.75em;
|
|
150
|
+
}
|
|
151
|
+
pre code {
|
|
152
|
+
padding: 0;
|
|
153
|
+
white-space: pre-wrap;
|
|
154
|
+
word-break: break-word;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
table { border-collapse: collapse; width: 100%; margin: 0.5em 0; font-size: 0.75em; }
|
|
158
|
+
th, td { padding: 0.4em 0.6em; text-align: left; vertical-align: top; overflow-wrap: break-word; }
|
|
159
|
+
th { font-weight: 700; }
|
|
160
|
+
|
|
161
|
+
.grid { display: grid; gap: var(--gap); margin: 0 0 var(--gap) 0; }
|
|
162
|
+
.grid > * { min-width: 0; }
|
|
163
|
+
|
|
164
|
+
.card {
|
|
165
|
+
border-radius: var(--radius);
|
|
166
|
+
padding: 1em;
|
|
167
|
+
overflow-wrap: break-word;
|
|
168
|
+
min-width: 0;
|
|
169
|
+
}
|
|
170
|
+
.card h3 { margin-top: 0; margin-bottom: 0.5em; }
|
|
171
|
+
.card p { margin-bottom: 0.5em; }
|
|
172
|
+
.card p:last-child { margin-bottom: 0; }
|
|
173
|
+
|
|
174
|
+
/* ---- metric cards (::: card {metric}) ---- */
|
|
175
|
+
.card.metric {
|
|
176
|
+
text-align: center;
|
|
177
|
+
padding: 0.5em 0.2em;
|
|
178
|
+
}
|
|
179
|
+
.card.metric h3 {
|
|
180
|
+
font-size: 2.4em;
|
|
181
|
+
font-weight: 800;
|
|
182
|
+
line-height: 1.15;
|
|
183
|
+
margin: 0 0 0.15em;
|
|
184
|
+
color: var(--color-accent-primary);
|
|
185
|
+
}
|
|
186
|
+
.card.metric h3::after { display: none; }
|
|
187
|
+
.card.metric p {
|
|
188
|
+
color: var(--color-dimmed);
|
|
189
|
+
line-height: 1.3;
|
|
190
|
+
margin: 0;
|
|
191
|
+
}
|
|
192
|
+
/* metrics-N layout: center grid vertically in slide body */
|
|
193
|
+
[class*="layout-metrics"] .slide-body {
|
|
194
|
+
display: flex;
|
|
195
|
+
flex-direction: column;
|
|
196
|
+
align-items: center;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
}
|
|
199
|
+
[class*="layout-metrics"] .slide-body .grid { width: 100%; margin: 0; }
|
|
200
|
+
[class*="layout-metrics"] .slide-body .row { width: 100%; }
|
|
201
|
+
|
|
202
|
+
.quote {
|
|
203
|
+
border-left: 0.25em solid;
|
|
204
|
+
padding-left: 1em;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.kicker { font-family: var(--font-mono); font-size: 0.75em; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
|
|
208
|
+
.speaker { font-size: 1.05em; font-weight: 700; margin-top: 1.8em; }
|
|
209
|
+
.speaker + .speaker { margin-top: 0.3em; }
|
|
210
|
+
.contributor { font-size: 0.85em; }
|
|
211
|
+
.contributor-label { font-family: var(--font-mono); font-size: 0.7em; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.75; }
|
|
212
|
+
.speaker-links { font-weight: 400; font-size: 0.8em; }
|
|
213
|
+
.speaker-links a { margin-right: 0.6em; }
|
|
214
|
+
.speaker-links svg { height: 0.9em; width: auto; vertical-align: middle; }
|
|
215
|
+
|
|
216
|
+
footer {
|
|
217
|
+
position: absolute;
|
|
218
|
+
bottom: 1em;
|
|
219
|
+
left: var(--section-padding-h);
|
|
220
|
+
font-size: 0.6em;
|
|
221
|
+
text-align: left;
|
|
222
|
+
background: var(--footer-bg);
|
|
223
|
+
color: var(--footer-color);
|
|
224
|
+
}
|
|
225
|
+
.slide-num {
|
|
226
|
+
position: absolute;
|
|
227
|
+
bottom: 1em;
|
|
228
|
+
right: var(--section-padding-h);
|
|
229
|
+
font-size: 0.6em;
|
|
230
|
+
text-align: right;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* ---- theme ---- */
|
|
234
|
+
THEME_CSS
|
|
235
|
+
|
|
236
|
+
LOGO_CSS
|
|
237
|
+
|
|
238
|
+
.mermaid, .seaborn-plot, .pdg-plot {
|
|
239
|
+
max-width: 100%;
|
|
240
|
+
max-height: 100%;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
text-align: center;
|
|
243
|
+
}
|
|
244
|
+
.mermaid svg, .seaborn-plot svg, .pdg-plot svg {
|
|
245
|
+
max-width: 100%;
|
|
246
|
+
height: auto;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* ---- ::: card {blueprint}: engineering-drawing panel for diagrams ----
|
|
250
|
+
Theme-independent on purpose (same blue on light and dark slides); a
|
|
251
|
+
```pdg fence inside it renders with the matching blueprint palette
|
|
252
|
+
(render/dot.py). Grid = minor 0.6em + major 3em lines over the blue. */
|
|
253
|
+
.card.blueprint {
|
|
254
|
+
background:
|
|
255
|
+
linear-gradient(rgba(143,211,255,0.16) 1px, transparent 1px) 0 0 / 3em 3em,
|
|
256
|
+
linear-gradient(90deg, rgba(143,211,255,0.16) 1px, transparent 1px) 0 0 / 3em 3em,
|
|
257
|
+
linear-gradient(rgba(143,211,255,0.07) 1px, transparent 1px) 0 0 / 0.6em 0.6em,
|
|
258
|
+
linear-gradient(90deg, rgba(143,211,255,0.07) 1px, transparent 1px) 0 0 / 0.6em 0.6em,
|
|
259
|
+
linear-gradient(160deg, #123f78 0%, #0b2f5c 100%);
|
|
260
|
+
border: 1px solid #8fd3ff;
|
|
261
|
+
color: #eaf4ff;
|
|
262
|
+
padding: 0.6em 0.8em;
|
|
263
|
+
}
|
|
264
|
+
.card.blueprint h3 { color: #8fd3ff; }
|
|
265
|
+
.card.blueprint h3::after { border-bottom-color: #8fd3ff; }
|
|
266
|
+
.card.blueprint p { color: #eaf4ff; }
|
|
267
|
+
|
|
268
|
+
/* ---- predefined slide layouts ---- */
|
|
269
|
+
.layout-cols {
|
|
270
|
+
display: grid;
|
|
271
|
+
grid-template-columns: 1fr 1fr;
|
|
272
|
+
gap: var(--gap);
|
|
273
|
+
align-items: start;
|
|
274
|
+
height: 100%;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.layout-image-right .col-right,
|
|
278
|
+
.layout-image-left .col-left {
|
|
279
|
+
height: 100%;
|
|
280
|
+
justify-content: center;
|
|
281
|
+
overflow: hidden;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.col-right img, .col-left img {
|
|
285
|
+
width: 100%;
|
|
286
|
+
height: 100%;
|
|
287
|
+
object-fit: contain;
|
|
288
|
+
object-position: center;
|
|
289
|
+
}
|
|
290
|
+
.layout-cols img { max-height: none; }
|
|
291
|
+
|
|
292
|
+
.result-full {
|
|
293
|
+
margin-top: 1em;
|
|
294
|
+
width: 100%;
|
|
295
|
+
}
|
|
296
|
+
.result-full img {
|
|
297
|
+
max-height: IMG_MAX_Hpx;
|
|
298
|
+
display: block;
|
|
299
|
+
margin-left: auto;
|
|
300
|
+
margin-right: auto;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.col-left, .col-right {
|
|
304
|
+
height: 100%;
|
|
305
|
+
display: flex;
|
|
306
|
+
flex-direction: column;
|
|
307
|
+
justify-content: center;
|
|
308
|
+
min-width: 0;
|
|
309
|
+
overflow-wrap: break-word;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.compare {
|
|
313
|
+
grid-template-columns: 1fr auto 1fr !important;
|
|
314
|
+
align-items: center;
|
|
315
|
+
height: 80%;
|
|
316
|
+
margin-bottom: 0;
|
|
317
|
+
}
|
|
318
|
+
.compare .col-left,
|
|
319
|
+
.compare .col-right {
|
|
320
|
+
justify-content: center;
|
|
321
|
+
}
|
|
322
|
+
.card-arrow {
|
|
323
|
+
font-size: 2em;
|
|
324
|
+
padding: 0 0.3em;
|
|
325
|
+
}
|
|
326
|
+
.notes {
|
|
327
|
+
border-radius: var(--radius);
|
|
328
|
+
padding: 1em;
|
|
329
|
+
margin-top: 0.5em;
|
|
330
|
+
font-style: italic;
|
|
331
|
+
}
|
|
332
|
+
.notes.tag-green { background: var(--tag-green-bg); border-left: 0.25em solid var(--tag-green-border); }
|
|
333
|
+
.notes.tag-yellow { background: var(--tag-yellow-bg); border-left: 0.25em solid var(--tag-yellow-border); }
|
|
334
|
+
.notes.tag-red { background: var(--tag-red-bg); border-left: 0.25em solid var(--tag-red-border); }
|
|
335
|
+
|
|
336
|
+
.note-green { border-left-color: var(--tag-green-border); background: var(--tag-green-bg); }
|
|
337
|
+
.note-yellow { border-left-color: var(--tag-yellow-border); background: var(--tag-yellow-bg); }
|
|
338
|
+
.note-red { border-left-color: var(--tag-red-border); background: var(--tag-red-bg); }
|
|
339
|
+
|
|
340
|
+
.tag-compare {
|
|
341
|
+
flex: 1;
|
|
342
|
+
border: 1px solid var(--color-border);
|
|
343
|
+
margin: 0.1em;
|
|
344
|
+
height: 100%;
|
|
345
|
+
min-height: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* ---- ecosystem layout ---- */
|
|
349
|
+
.layout-ecosystem h3 { font-size: 0.7em; margin-bottom: 0.1em; }
|
|
350
|
+
.layout-ecosystem h3::after { display: none; }
|
|
351
|
+
.layout-ecosystem .card { padding: 0.3em 0.5em; margin-bottom: 0.4em; }
|
|
352
|
+
.layout-ecosystem .card p { margin: 0; line-height: 1.2; display: inline; }
|
|
353
|
+
.layout-ecosystem .card.metric p { display: block; }
|
|
354
|
+
.layout-ecosystem .card.metric {
|
|
355
|
+
padding: 0.2em 0.15em;
|
|
356
|
+
display: flex;
|
|
357
|
+
flex-direction: column;
|
|
358
|
+
justify-content: center;
|
|
359
|
+
}
|
|
360
|
+
.layout-ecosystem .card.metric h3 { font-size: 1.3em; margin-bottom: 0.05em; }
|
|
361
|
+
.layout-ecosystem .card.metric p { font-size: 0.6em; }
|
|
362
|
+
.layout-ecosystem .card img {
|
|
363
|
+
height: 0.9em;
|
|
364
|
+
width: 2.5em;
|
|
365
|
+
object-fit: contain;
|
|
366
|
+
display: inline-block;
|
|
367
|
+
margin: 0.08em 0.2em;
|
|
368
|
+
vertical-align: middle;
|
|
369
|
+
}
|
|
370
|
+
.layout-ecosystem .grid { margin: 0; gap: 0.4em; }
|
|
371
|
+
.layout-ecosystem .card.metric h3 {
|
|
372
|
+
font-size: 1.5em;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* ---- timeline layouts ---- */
|
|
376
|
+
/* ::: grid {cols=1} of ::: card blocks, flex (not CSS grid) so any item
|
|
377
|
+
count distributes naturally. The spine spans a fixed length (full
|
|
378
|
+
available height/width); justify-content: space-evenly auto-spaces
|
|
379
|
+
however many milestones there are along it -- no manually-tuned
|
|
380
|
+
gap/margin per item count. Items alternate sides of the spine -- the
|
|
381
|
+
connector stub's direction (left card <- stub -> right card,
|
|
382
|
+
vertical; above <- stub -> below, horizontal) flips every item, the
|
|
383
|
+
literal "line going up and down". {focus} on a card scales it up.
|
|
384
|
+
{label="Q1"} on a card prints a short name directly on the spine,
|
|
385
|
+
next to that card's own milestone dot. */
|
|
386
|
+
.layout-timeline-vertical .grid,
|
|
387
|
+
.layout-timeline-horizontal .grid {
|
|
388
|
+
display: flex;
|
|
389
|
+
position: relative;
|
|
390
|
+
}
|
|
391
|
+
/* grid fills the remaining body height via flex:1, not height:100% --
|
|
392
|
+
weasyprint resolves the percentage against more than the leftover
|
|
393
|
+
space below the heading, pushing the whole grid onto a new page
|
|
394
|
+
(broke --pdf/--odp: cards landed on an extra page). */
|
|
395
|
+
.layout-timeline-vertical .slide-body {
|
|
396
|
+
display: flex;
|
|
397
|
+
flex-direction: column;
|
|
398
|
+
padding-bottom: 0; /* the spine runs to the section padding, not 1.8em
|
|
399
|
+
short of it (sat too high, empty band below) */
|
|
400
|
+
}
|
|
401
|
+
.layout-timeline-vertical .grid {
|
|
402
|
+
flex-direction: column;
|
|
403
|
+
justify-content: space-evenly;
|
|
404
|
+
flex: 1;
|
|
405
|
+
min-height: 0;
|
|
406
|
+
margin: 0.8em 0 0; /* clear the heading: the first card touched it */
|
|
407
|
+
padding: 0 2.5em;
|
|
408
|
+
}
|
|
409
|
+
.layout-timeline-vertical .grid::before {
|
|
410
|
+
content: "";
|
|
411
|
+
position: absolute;
|
|
412
|
+
left: 50%;
|
|
413
|
+
top: 0;
|
|
414
|
+
bottom: 0;
|
|
415
|
+
width: 0.12em;
|
|
416
|
+
background: var(--color-accent-primary);
|
|
417
|
+
opacity: 0.4;
|
|
418
|
+
transform: translateX(-50%);
|
|
419
|
+
}
|
|
420
|
+
.layout-timeline-horizontal .grid {
|
|
421
|
+
flex-direction: row;
|
|
422
|
+
justify-content: space-evenly;
|
|
423
|
+
align-items: flex-start;
|
|
424
|
+
margin-top: 8em; /* reserves room for odd cards shifted above the spine */
|
|
425
|
+
}
|
|
426
|
+
.layout-timeline-horizontal .grid::before {
|
|
427
|
+
content: "";
|
|
428
|
+
position: absolute;
|
|
429
|
+
top: 0;
|
|
430
|
+
left: 0;
|
|
431
|
+
right: 0;
|
|
432
|
+
height: 0.12em;
|
|
433
|
+
background: var(--color-accent-primary);
|
|
434
|
+
opacity: 0.4;
|
|
435
|
+
}
|
|
436
|
+
.layout-timeline-vertical .card {
|
|
437
|
+
position: relative;
|
|
438
|
+
width: calc(50% - 2.5em); /* padding (2.5em) on both sides makes the
|
|
439
|
+
card-to-spine gap exactly 2.5em either way */
|
|
440
|
+
padding: 0.6em 1em;
|
|
441
|
+
}
|
|
442
|
+
.layout-timeline-vertical .card:nth-child(odd) { margin-right: auto; }
|
|
443
|
+
.layout-timeline-vertical .card:nth-child(even) { margin-left: auto; }
|
|
444
|
+
/* consecutive cards sit on opposite sides of the spine, so they may
|
|
445
|
+
overlap vertically: 4 cards stacked end to end overflowed the slide.
|
|
446
|
+
Card i and i+2 (same side) still clear each other while the card
|
|
447
|
+
between them is taller than 3em. */
|
|
448
|
+
.layout-timeline-vertical .card + .card { margin-top: -1.5em; }
|
|
449
|
+
.layout-timeline-horizontal .card {
|
|
450
|
+
position: relative;
|
|
451
|
+
flex: 0 1 11em; /* basis 11em, may shrink: few milestones leave free
|
|
452
|
+
space for space-evenly; 5-6 shrink to fit the
|
|
453
|
+
width instead of overflowing the slide */
|
|
454
|
+
min-width: 0;
|
|
455
|
+
height: 6em; /* fixed so the stub below reliably reaches the spine --
|
|
456
|
+
keep horizontal-timeline card text brief, it won't
|
|
457
|
+
clip (no overflow:hidden -- that would also clip the
|
|
458
|
+
card's own connector dot/stub below) */
|
|
459
|
+
margin-top: 2em; /* stub gap below the spine */
|
|
460
|
+
}
|
|
461
|
+
.layout-timeline-horizontal .card:nth-child(odd) {
|
|
462
|
+
margin-top: -8em; /* 6em card + 2em stub, shifted above the spine */
|
|
463
|
+
}
|
|
464
|
+
/* connector dot on the spine */
|
|
465
|
+
.layout-timeline-vertical .card::before,
|
|
466
|
+
.layout-timeline-horizontal .card::before {
|
|
467
|
+
content: "";
|
|
468
|
+
position: absolute;
|
|
469
|
+
width: 0.6em;
|
|
470
|
+
height: 0.6em;
|
|
471
|
+
border-radius: 50%;
|
|
472
|
+
background: var(--color-accent-primary);
|
|
473
|
+
border: 0.15em solid var(--color-background);
|
|
474
|
+
z-index: 1;
|
|
475
|
+
}
|
|
476
|
+
.layout-timeline-vertical .card:nth-child(odd)::before { right: -2.8em; top: 1em; }
|
|
477
|
+
.layout-timeline-vertical .card:nth-child(even)::before { left: -2.8em; top: 1em; }
|
|
478
|
+
.layout-timeline-horizontal .card::before { left: 50%; transform: translateX(-50%); }
|
|
479
|
+
.layout-timeline-horizontal .card:nth-child(even)::before { top: -2.3em; }
|
|
480
|
+
.layout-timeline-horizontal .card:nth-child(odd)::before { bottom: -2.3em; }
|
|
481
|
+
/* connector stub -- the alternating line itself, not just the dot */
|
|
482
|
+
.layout-timeline-vertical .card::after {
|
|
483
|
+
content: "";
|
|
484
|
+
position: absolute;
|
|
485
|
+
top: 1.3em;
|
|
486
|
+
height: 0.08em;
|
|
487
|
+
width: 2.5em;
|
|
488
|
+
background: var(--color-accent-primary);
|
|
489
|
+
opacity: 0.5;
|
|
490
|
+
}
|
|
491
|
+
.layout-timeline-vertical .card:nth-child(odd)::after { right: -2.5em; }
|
|
492
|
+
.layout-timeline-vertical .card:nth-child(even)::after { left: -2.5em; }
|
|
493
|
+
.layout-timeline-horizontal .card::after {
|
|
494
|
+
content: "";
|
|
495
|
+
position: absolute;
|
|
496
|
+
left: 50%;
|
|
497
|
+
width: 0.08em;
|
|
498
|
+
height: 2em;
|
|
499
|
+
background: var(--color-accent-primary);
|
|
500
|
+
opacity: 0.5;
|
|
501
|
+
transform: translateX(-50%);
|
|
502
|
+
}
|
|
503
|
+
.layout-timeline-horizontal .card:nth-child(even)::after { top: -2em; }
|
|
504
|
+
.layout-timeline-horizontal .card:nth-child(odd)::after { bottom: -2em; }
|
|
505
|
+
/* {label="..."}: short name printed directly on the spine, next to
|
|
506
|
+
this card's own dot -- distinct from the card's fuller ### header */
|
|
507
|
+
.timeline-label {
|
|
508
|
+
position: absolute;
|
|
509
|
+
font-size: 0.6em;
|
|
510
|
+
font-weight: 700;
|
|
511
|
+
color: var(--color-accent-primary);
|
|
512
|
+
white-space: nowrap;
|
|
513
|
+
z-index: 2;
|
|
514
|
+
}
|
|
515
|
+
/* beside the dot, past the spine, vertically centred on it. Offsets are
|
|
516
|
+
in the label's own 0.6em font: 5.5em = 3.3 card-em = dot edge + gap;
|
|
517
|
+
top 1.8em centres it on the dot (top 1em, 0.9em tall). Was above the
|
|
518
|
+
dot, touching the spine. */
|
|
519
|
+
.layout-timeline-vertical .card:nth-child(odd) .timeline-label {
|
|
520
|
+
left: 100%;
|
|
521
|
+
margin-left: 5.5em;
|
|
522
|
+
top: 1.8em;
|
|
523
|
+
}
|
|
524
|
+
.layout-timeline-vertical .card:nth-child(even) .timeline-label {
|
|
525
|
+
right: 100%;
|
|
526
|
+
margin-right: 5.5em;
|
|
527
|
+
top: 1.8em;
|
|
528
|
+
}
|
|
529
|
+
/* beside the dot: centre on it, then shift right. margin-left, not
|
|
530
|
+
translate(calc(-50% + Xem)) -- weasyprint crashes on calc() inside
|
|
531
|
+
transform (AttributeError in its cascade), breaking --pdf/--odp. */
|
|
532
|
+
.layout-timeline-horizontal .card:nth-child(even) .timeline-label {
|
|
533
|
+
left: 50%;
|
|
534
|
+
top: -2.3em;
|
|
535
|
+
margin-left: 1.4em;
|
|
536
|
+
transform: translateX(-50%);
|
|
537
|
+
}
|
|
538
|
+
.layout-timeline-horizontal .card:nth-child(odd) .timeline-label {
|
|
539
|
+
left: 50%;
|
|
540
|
+
bottom: -2.3em;
|
|
541
|
+
margin-left: 1.4em;
|
|
542
|
+
transform: translateX(-50%);
|
|
543
|
+
}
|
|
544
|
+
/* {focus}: bigger, highlighted with the theme's own card colours (accent
|
|
545
|
+
border + accent background) -- not box-shadow, which weasyprint
|
|
546
|
+
ignores, so the highlight vanished in PDF and ODP. Size via
|
|
547
|
+
font-size/padding, not transform: scale(), which would drag the card's
|
|
548
|
+
own ::before/::after connector (positioned in em relative to the card)
|
|
549
|
+
off the spine with it. Vertical gets the font-size bump too;
|
|
550
|
+
horizontal's height/margin are all in em relative to its own
|
|
551
|
+
font-size, so bumping it there would throw off the exact
|
|
552
|
+
spine-alignment math -- padding only. */
|
|
553
|
+
.layout-timeline-vertical .card.focus,
|
|
554
|
+
.layout-timeline-horizontal .card.focus {
|
|
555
|
+
border: 2px solid var(--color-accent-primary);
|
|
556
|
+
background: var(--color-accent-bg);
|
|
557
|
+
z-index: 2;
|
|
558
|
+
}
|
|
559
|
+
.layout-timeline-horizontal .card.focus {
|
|
560
|
+
padding: 1.3em;
|
|
561
|
+
}
|
|
562
|
+
.layout-timeline-vertical .card.focus {
|
|
563
|
+
padding: 0.8em 1.2em;
|
|
564
|
+
font-size: 1.1em;
|
|
565
|
+
}
|
|
566
|
+
.layout-timeline-horizontal .slide-body {
|
|
567
|
+
display: flex;
|
|
568
|
+
flex-direction: column;
|
|
569
|
+
justify-content: center;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* ---- slide transitions ---- */
|
|
573
|
+
section[data-transition]:not([data-transition="none"]).active > .slide-body {
|
|
574
|
+
animation-duration: .4s;
|
|
575
|
+
animation-fill-mode: both;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
section[data-transition="fade"].active > .slide-body { animation-name: sldr-fade; }
|
|
579
|
+
section[data-transition="slide"].active > .slide-body { animation-name: sldr-slide; }
|
|
580
|
+
section[data-transition="zoom"].active > .slide-body { animation-name: sldr-zoom; }
|
|
581
|
+
section[data-transition="wipe"].active > .slide-body { animation-name: sldr-wipe; }
|
|
582
|
+
|
|
583
|
+
@keyframes sldr-fade {
|
|
584
|
+
from { opacity: 0; }
|
|
585
|
+
}
|
|
586
|
+
@keyframes sldr-slide {
|
|
587
|
+
from { opacity: 0; translate: 3em 0; }
|
|
588
|
+
}
|
|
589
|
+
@keyframes sldr-zoom {
|
|
590
|
+
from { opacity: 0; scale: .85; }
|
|
591
|
+
}
|
|
592
|
+
@keyframes sldr-wipe {
|
|
593
|
+
from { clip-path: inset(0 100% 0 0); }
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* ---- outgoing slide crossfade ---- */
|
|
597
|
+
section[data-transition]:not([data-transition="none"]).outgoing {
|
|
598
|
+
display: flex !important;
|
|
599
|
+
z-index: -1;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/* ---- screen mode ---- */
|
|
603
|
+
@media screen {
|
|
604
|
+
html, body {
|
|
605
|
+
width: 100vw;
|
|
606
|
+
height: 100vh;
|
|
607
|
+
overflow: hidden;
|
|
608
|
+
background: #111;
|
|
609
|
+
}
|
|
610
|
+
body > section {
|
|
611
|
+
display: none !important;
|
|
612
|
+
position: absolute;
|
|
613
|
+
top: 50%;
|
|
614
|
+
left: 50%;
|
|
615
|
+
width: SLIDE_Wpx;
|
|
616
|
+
height: SLIDE_Hpx;
|
|
617
|
+
transform-origin: center center;
|
|
618
|
+
transform: translate(-50%, -50%) scale(var(--s));
|
|
619
|
+
overflow: hidden !important;
|
|
620
|
+
}
|
|
621
|
+
body > section.active {
|
|
622
|
+
display: flex !important;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.layout-title {
|
|
627
|
+
display: flex;
|
|
628
|
+
flex-direction: column;
|
|
629
|
+
justify-content: center;
|
|
630
|
+
padding-top: 3.2em;
|
|
631
|
+
}
|
|
632
|
+
.layout-title h1 {
|
|
633
|
+
font-size: var(--title-h1-size, 3.5em);
|
|
634
|
+
max-width: 62%;
|
|
635
|
+
}
|
|
636
|
+
.layout-title .subtitle {
|
|
637
|
+
margin-bottom: 0.3em;
|
|
638
|
+
font-size: var(--title-subtitle-size, 1.8em);
|
|
639
|
+
max-width: 62%;
|
|
640
|
+
}
|
|
641
|
+
.layout-title .side-image {
|
|
642
|
+
position: absolute;
|
|
643
|
+
right: 8%;
|
|
644
|
+
bottom: 22%;
|
|
645
|
+
max-width: 26%;
|
|
646
|
+
z-index: 1;
|
|
647
|
+
}
|
|
648
|
+
.layout-title .side-image img {
|
|
649
|
+
max-width: 100%;
|
|
650
|
+
max-height: 14em;
|
|
651
|
+
object-fit: contain;
|
|
652
|
+
display: block;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/* ---- print mode ---- */
|
|
656
|
+
@media print {
|
|
657
|
+
body > section {
|
|
658
|
+
position: relative !important;
|
|
659
|
+
transform: none !important;
|
|
660
|
+
width: SLIDE_Wpx;
|
|
661
|
+
height: SLIDE_Hpx;
|
|
662
|
+
overflow: hidden;
|
|
663
|
+
page-break-before: always;
|
|
664
|
+
break-before: page;
|
|
665
|
+
}
|
|
666
|
+
body > section:first-child { page-break-before: avoid; }
|
|
667
|
+
body > section:last-child { page-break-after: avoid; }
|
|
668
|
+
#decktile-nav, #presenter-panel { display: none !important; }
|
|
669
|
+
.slide-body {
|
|
670
|
+
flex: 1;
|
|
671
|
+
min-height: auto;
|
|
672
|
+
padding-bottom: 1.8em;
|
|
673
|
+
overflow: hidden;
|
|
674
|
+
}
|
|
675
|
+
pre {
|
|
676
|
+
max-height: none;
|
|
677
|
+
overflow: visible;
|
|
678
|
+
}
|
|
679
|
+
/* video slides: single big screenshot centered. The figure is
|
|
680
|
+
absolutely positioned: weasyprint collapses height:100% through the
|
|
681
|
+
.slide-body flex layout, and a flowed figure would spill to a second
|
|
682
|
+
page. Absolute pinning makes the img size resolvable and keeps it
|
|
683
|
+
off the page flow. */
|
|
684
|
+
figure.video {
|
|
685
|
+
display: block;
|
|
686
|
+
position: absolute;
|
|
687
|
+
top: 0;
|
|
688
|
+
bottom: 0;
|
|
689
|
+
left: 0;
|
|
690
|
+
right: 0;
|
|
691
|
+
}
|
|
692
|
+
figure.video video { display: none; }
|
|
693
|
+
figure.video img.video-print {
|
|
694
|
+
display: block;
|
|
695
|
+
max-height: none;
|
|
696
|
+
height: 100%;
|
|
697
|
+
width: 100%;
|
|
698
|
+
object-fit: contain;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
@page {
|
|
703
|
+
size: SLIDE_Wpx SLIDE_Hpx;
|
|
704
|
+
margin: 0;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* ---- nav bar (screen only) ---- */
|
|
708
|
+
#decktile-nav { display: none; }
|
|
709
|
+
@media screen {
|
|
710
|
+
#decktile-nav {
|
|
711
|
+
display: flex;
|
|
712
|
+
position: fixed;
|
|
713
|
+
bottom: 0;
|
|
714
|
+
left: 0;
|
|
715
|
+
right: 0;
|
|
716
|
+
height: 27pt;
|
|
717
|
+
align-items: center;
|
|
718
|
+
justify-content: center;
|
|
719
|
+
gap: 12pt;
|
|
720
|
+
background: rgba(0,0,0,0.7);
|
|
721
|
+
color: #aaa;
|
|
722
|
+
font-family: system-ui, sans-serif;
|
|
723
|
+
font-size: 10pt;
|
|
724
|
+
z-index: 1000;
|
|
725
|
+
user-select: none;
|
|
726
|
+
}
|
|
727
|
+
#decktile-nav button {
|
|
728
|
+
background: none;
|
|
729
|
+
border: 1px solid #555;
|
|
730
|
+
color: #ccc;
|
|
731
|
+
padding: 2pt 9pt;
|
|
732
|
+
border-radius: 3pt;
|
|
733
|
+
cursor: pointer;
|
|
734
|
+
font-size: 10pt;
|
|
735
|
+
}
|
|
736
|
+
#decktile-nav button:hover { background: rgba(255,255,255,0.1); color: #fff; }
|
|
737
|
+
#decktile-nav button:disabled { opacity: 0.3; cursor: default; }
|
|
738
|
+
#decktile-nav span { min-width: 45pt; text-align: center; }
|
|
739
|
+
#decktile-nav.hidden { opacity: 0; pointer-events: none; }
|
|
740
|
+
#decktile-nav { transition: opacity 0.4s; }
|
|
741
|
+
}
|