doxtr-pdf-theme-core 1.0.0__tar.gz

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.
Files changed (49) hide show
  1. doxtr_pdf_theme_core-1.0.0/PKG-INFO +1196 -0
  2. doxtr_pdf_theme_core-1.0.0/README.md +1178 -0
  3. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/__init__.py +1077 -0
  4. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/assets/doxtr_icon.png +3 -0
  5. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/assets/doxtr_icon_small.png +3 -0
  6. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/__init__.py +32 -0
  7. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/codeblocks.py +34 -0
  8. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/containers.py +75 -0
  9. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/epigraphs.py +88 -0
  10. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/highlights.py +43 -0
  11. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/needs.py +113 -0
  12. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/sidebars.py +193 -0
  13. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/ast_processors/tables.py +33 -0
  14. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/colors.py +158 -0
  15. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/config.py +341 -0
  16. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/core_config.py +1132 -0
  17. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/core_fallbacks.py +211 -0
  18. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/files.py +65 -0
  19. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_escape.py +50 -0
  20. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/admonition/default.tex_t +36 -0
  21. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/admonition/note.tex_t +35 -0
  22. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/admonition/seealso.tex_t +43 -0
  23. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/code/default.tex_t +55 -0
  24. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container/default.tex_t +11 -0
  25. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container/folder.tex_t +51 -0
  26. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container/participant.tex_t +76 -0
  27. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container_title_style/classic.tex_t +2 -0
  28. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container_title_style/floating.tex_t +8 -0
  29. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/container_title_style/ribbon.tex_t +9 -0
  30. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/figure/default.tex_t +38 -0
  31. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/highlights/default.tex_t +30 -0
  32. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/need/default.tex_t +15 -0
  33. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/sidebar/default.tex_t +54 -0
  34. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/sphinxlatexstyleheadings.sty +0 -0
  35. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/sphinxlatexstylepage.sty +0 -0
  36. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/table/arrow.tex_t +179 -0
  37. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/table/default.tex_t +288 -0
  38. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/latex_styles/title_page/default.tex_t +50 -0
  39. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/preamble.tex_t +721 -0
  40. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/shell_icons.py +64 -0
  41. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/templates.py +313 -0
  42. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core/utils.py +860 -0
  43. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core.egg-info/PKG-INFO +1196 -0
  44. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core.egg-info/SOURCES.txt +47 -0
  45. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core.egg-info/dependency_links.txt +1 -0
  46. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core.egg-info/requires.txt +6 -0
  47. doxtr_pdf_theme_core-1.0.0/doxtr_pdf_theme_core.egg-info/top_level.txt +1 -0
  48. doxtr_pdf_theme_core-1.0.0/pyproject.toml +62 -0
  49. doxtr_pdf_theme_core-1.0.0/setup.cfg +4 -0
@@ -0,0 +1,1196 @@
1
+ Metadata-Version: 2.4
2
+ Name: doxtr-pdf-theme-core
3
+ Version: 1.0.0
4
+ Summary: Core PDF layout engine and AST flatteners for Doxtr themes.
5
+ Author: Jens Frey
6
+ Project-URL: Homepage, https://github.com/doxtr/doxtr-pdf-theme-core
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Framework :: Sphinx :: Extension
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: sphinx>=5.0
14
+ Requires-Dist: Jinja2>=3.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=7.0; extra == "dev"
17
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
18
+
19
+ # Doxtr PDF Theme Core
20
+
21
+ The core PDF layout engine for the Doxtr document authoring system. It provides a professional LaTeX/PDF output pipeline utilizing KOMA classes and LuaLaTeX, designed to be inherited by child themes that customize the look and feel.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install doxtr-pdf-theme-core
27
+ ```
28
+
29
+ **Note on LaTeX Engine:** This core relies on `fontspec` and KOMA classes, which require LuaLaTeX. The core will automatically set `latex_engine = 'lualatex'` if you haven't explicitly configured an engine.
30
+
31
+ ## Quick Start — Using a Child Theme
32
+
33
+ The easiest way to use the core is through a child theme (e.g., `doxtr-pdf-theme-aubergine`):
34
+
35
+ ```python
36
+ # conf.py
37
+ extensions = [
38
+ 'doxtr_pdf_theme_aubergine',
39
+ ]
40
+ ```
41
+
42
+ To use the core directly without a child theme:
43
+
44
+ ```python
45
+ # conf.py
46
+ extensions = [
47
+ 'doxtr_pdf_theme_core',
48
+ ]
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Create a Child Theme — Walkthrough
54
+
55
+ This section explains how to build your own child theme on top of the core engine.
56
+ A reference implementation is available at [doxtr-pdf-theme-aubergine](https://github.com/doxtr/doxtr-pdf-theme-aubergine).
57
+
58
+ ### Project Structure
59
+
60
+ The minimum viable theme is 3 files:
61
+
62
+ ```
63
+ my_company_theme/
64
+ ├── pyproject.toml # Package metadata
65
+ ├── README.md # Usage documentation
66
+ └── my_company_theme/
67
+ └── __init__.py # Theme logic (colors, fonts, defaults)
68
+ ```
69
+
70
+ For themes with custom LaTeX templates:
71
+
72
+ ```
73
+ my_company_theme/
74
+ ├── pyproject.toml
75
+ ├── README.md
76
+ └── my_company_theme/
77
+ ├── __init__.py
78
+ └── latex_styles/ # Optional: override visual templates
79
+ ├── admonition/
80
+ │ └── rounded.tex_t # Custom admonition rendering
81
+ ├── container/
82
+ │ └── default.tex_t # Custom container body
83
+ ├── container_title_style/
84
+ │ └── minimal.tex_t # Custom container title geometry
85
+ ├── code/
86
+ │ └── default.tex_t # Custom code block rendering
87
+ ├── figure/
88
+ │ └── default.tex_t # Custom figure captions
89
+ ├── highlights/
90
+ │ └── default.tex_t # Custom highlights rendering
91
+ ├── need/
92
+ │ └── default.tex_t # Custom sphinx-needs boxes
93
+ ├── sidebar/
94
+ │ └── default.tex_t # Custom sidebar rendering
95
+ ├── table/
96
+ │ └── default.tex_t # Custom table captions
97
+ └── title_page/
98
+ └── my_cover.tex_t # Custom title page layout
99
+ ```
100
+
101
+ ### `pyproject.toml`
102
+
103
+ ```toml
104
+ [build-system]
105
+ requires = ["setuptools>=61.0"]
106
+ build-backend = "setuptools.build_meta"
107
+
108
+ [project]
109
+ name = "my-company-theme"
110
+ version = "0.1.0"
111
+ description = "My company's PDF theme for Doxtr."
112
+ requires-python = ">=3.8"
113
+ dependencies = [
114
+ "doxtr-pdf-theme-core>=0.1.10",
115
+ ]
116
+
117
+ [tool.setuptools.packages.find]
118
+ include = ["my_company_theme*"]
119
+
120
+ [tool.setuptools]
121
+ include-package-data = true
122
+
123
+ [tool.setuptools.package-data]
124
+ my_company_theme = [
125
+ "*.tex_t",
126
+ "latex_styles/**/*.tex_t",
127
+ "assets/*.png",
128
+ ]
129
+ ```
130
+
131
+ ### The Simplest Theme (Palette + Fonts Only)
132
+
133
+ A 20-line theme that recolors the entire document:
134
+
135
+ ```python
136
+ # my_company_theme/__init__.py
137
+ from doxtr_pdf_theme_core import setup as core_setup
138
+
139
+ __version__ = "0.1.0"
140
+
141
+ def setup(app):
142
+ # 1. Initialize the core engine first — this registers all config values
143
+ core_setup(app)
144
+
145
+ # 2. Set 6 semantic colors — the entire document derives from these
146
+ app.config.doxtr_semantic_palette = {
147
+ 'primary': '#1B4F72', # Deep blue — headings, borders, table headers
148
+ 'secondary': '#F39C12', # Gold — accents, highlights, decorative lines
149
+ 'info': '#2E86C1', # Info blue — notes, specs
150
+ 'success': '#28B463', # Green — tips, hints, decisions
151
+ 'warning': '#E67E22', # Orange — warnings, caution
152
+ 'danger': '#E74C3C', # Red — errors, danger
153
+ }
154
+
155
+ # 3. Set fonts (must be installed on the build system)
156
+ app.config.doxtr_main_font = 'Noto Serif'
157
+ app.config.doxtr_sans_font = 'Noto Sans'
158
+ app.config.doxtr_mono_font = 'Noto Sans Mono'
159
+
160
+ # Return value is required by Sphinx
161
+ return {'version': __version__, 'parallel_read_safe': True}
162
+ ```
163
+
164
+ This produces a fully styled PDF with blue headings, gold accent lines, blue table headers, automatic WCAG-compliant contrast on all text, and full inheritance down the heading hierarchy.
165
+
166
+ ### Adding Element-Specific Overrides
167
+
168
+ For more control, set `doxtr_theme_defaults` — a dictionary that overrides specific elements. You only set the keys you want to change; everything else inherits from the core.
169
+
170
+ ```python
171
+ def setup(app):
172
+ core_setup(app)
173
+
174
+ app.config.doxtr_semantic_palette = { ... }
175
+ app.config.doxtr_main_font = 'Noto Serif'
176
+ app.config.doxtr_sans_font = 'Noto Sans'
177
+ app.config.doxtr_mono_font = 'Noto Sans Mono'
178
+
179
+ # 4. Set theme defaults — the "middle layer" between Core and User
180
+ app.config.doxtr_theme_defaults = {
181
+ # Dark blue title page
182
+ 'title_page': {
183
+ 'page_color': '#1B2631',
184
+ 'title_font': 'Noto Sans',
185
+ 'title_size': r'\fontsize{34pt}{40pt}\selectfont',
186
+ 'title_color': '#FFFFFF',
187
+ 'subtitle_color': '#F39C12',
188
+ },
189
+
190
+ # Alternating headings with decorative chapter line
191
+ 'headings': {
192
+ 'align': 'alternate',
193
+ 'numbers_in_margin': True,
194
+ 'chapter': {
195
+ 'font': 'Noto Sans',
196
+ 'color': '#1B4F72',
197
+ 'number_line': True,
198
+ 'line_color': '#F39C12',
199
+ },
200
+ },
201
+
202
+ # Custom admonition styling
203
+ 'admonitions': {
204
+ 'generic': {
205
+ 'title_font': 'Noto Sans',
206
+ 'title_background_color': '#1B4F72',
207
+ 'title_icon_box_background_color': '#0E3352',
208
+ 'content_background_color': '#EBF5FB',
209
+ },
210
+ 'warning': {
211
+ 'title_background_color': '#E67E22',
212
+ 'content_background_color': '#FEF5E7',
213
+ },
214
+ },
215
+
216
+ # Table styling
217
+ 'tables': {
218
+ 'generic': {
219
+ 'header_background_color': '#1B4F72',
220
+ 'header_font_color': '#FFFFFF',
221
+ 'row_color_odd': '#EBF5FB',
222
+ },
223
+ },
224
+ }
225
+
226
+ return {'version': __version__, 'parallel_read_safe': True}
227
+ ```
228
+
229
+ ### Overriding Visual Templates (Advanced)
230
+
231
+ For advanced visual changes (e.g., redesigning how admonition boxes are drawn), provide custom `.tex_t` template files and register their path.
232
+
233
+ `doxtr_theme_style_paths` is a **list** of directories searched in order. Use it for broad overrides spanning multiple style types. The per-type single-path variables (`doxtr_<type>_style_path`) take precedence over `doxtr_theme_style_paths` for their specific type.
234
+
235
+ ```python
236
+ import os
237
+ from pathlib import Path
238
+
239
+ def setup(app):
240
+ core_setup(app)
241
+
242
+ # Tell the core where to find your .tex_t overrides
243
+ pkg_dir = Path(__file__).parent.resolve()
244
+ app.config.doxtr_theme_style_paths = [
245
+ str(pkg_dir / 'latex_styles'),
246
+ ]
247
+
248
+ # Reference your custom style by name
249
+ app.config.doxtr_theme_defaults = {
250
+ 'admonitions': {
251
+ 'generic': {
252
+ 'style': 'rounded', # loads admonition/rounded.tex_t
253
+ },
254
+ },
255
+ 'containers': {
256
+ 'default': {
257
+ 'title_style': 'minimal', # loads container_title_style/minimal.tex_t
258
+ },
259
+ },
260
+ }
261
+
262
+ return {'version': __version__, 'parallel_read_safe': True}
263
+ ```
264
+
265
+ **Template resolution order:**
266
+ 1. Per-type custom path (`doxtr_<type>_style_path`)
267
+ 2. User project's `latex_styles/<type>/` folder
268
+ 3. Theme's `doxtr_theme_style_paths` list (searched in order)
269
+ 4. Core's `latex_styles/<type>/`
270
+ 5. Absolute fallback (hardcoded in `core_fallbacks.py`)
271
+
272
+ ### Font Weight Mapping
273
+
274
+ If your chosen font has non-standard weight variants (e.g., "Light" as the regular weight), use the `_options` variables to prevent LaTeX "Font shape undefined" warnings:
275
+
276
+ ```python
277
+ app.config.doxtr_main_font = 'Roboto'
278
+ app.config.doxtr_main_font_options = (
279
+ 'UprightFont={Roboto Light}, '
280
+ 'BoldFont={Roboto Medium}, '
281
+ 'ItalicFont={Roboto Light Italic}, '
282
+ 'BoldItalicFont={Roboto Medium Italic}'
283
+ )
284
+
285
+ app.config.doxtr_sans_font = 'Source Sans Pro'
286
+ app.config.doxtr_sans_font_options = 'Scale=MatchLowercase'
287
+
288
+ app.config.doxtr_mono_font = 'JetBrains Mono'
289
+ app.config.doxtr_mono_font_options = 'Scale=MatchLowercase'
290
+ ```
291
+
292
+ ### Using the Semantic Color System
293
+
294
+ The `dd:` expression system lets you derive colors dynamically from the palette. All color fields in every config section accept `dd:` expressions.
295
+
296
+ ```python
297
+ app.config.doxtr_theme_defaults = {
298
+ 'headings': {
299
+ 'chapter': {
300
+ 'color': 'dd:primary', # Palette's primary color
301
+ 'line_color': 'dd:secondary', # Palette's secondary
302
+ },
303
+ },
304
+ 'admonitions': {
305
+ 'generic': {
306
+ 'title_background_color': 'dd:primary',
307
+ 'content_background_color': 'dd:primary:lighten:85', # 85% lighter
308
+ 'content_font_color': 'dd:primary:darken:30', # 30% darker
309
+ },
310
+ },
311
+ 'tables': {
312
+ 'generic': {
313
+ 'header_background_color': 'dd:primary',
314
+ 'header_font_color': 'dd:primary:contrast:fg:primary', # Auto WCAG contrast
315
+ },
316
+ },
317
+ }
318
+ ```
319
+
320
+ **Available expressions:**
321
+
322
+ | Expression | Result |
323
+ |---|---|
324
+ | `dd:primary` | Palette color directly |
325
+ | `dd:page` | Page background color |
326
+ | `dd:primary:lighten:80` | 80% lighter |
327
+ | `dd:primary:darken:30` | 30% darker |
328
+ | `dd:primary:contrast:fg:primary` | Foreground adjusted for WCAG contrast |
329
+ | `dd:page:contrast:bg:primary` | Background adjusted for contrast |
330
+ | `dd:this:title_background_color` | Another key in the same merged section |
331
+ | `dd:theme:title_background_color` | Value from the theme's current section |
332
+ | `dd:core:title_background_color` | Value from the core's current section |
333
+ | `dd:#FFCC0025:lighten:80` | Inline hex literal with operation |
334
+ | `dd:admonitions.warning[theme]:title_background_color` | Explicit cross-section path |
335
+
336
+ **Shorthand collision:** `dd:warning:` is ambiguous — it could mean the palette key or the admonition type. Use `dd:admonitions.warning:title_background_color` for the admonition, or `dd:warning` alone for the palette.
337
+
338
+ **WCAG override suffix:**
339
+
340
+ ```python
341
+ 'color': 'dd:primary:contrast:fg:primary:aaa' # Force AAA (7:1)
342
+ 'color': 'dd:primary:contrast:fg:primary:aa' # Force AA (4.5:1)
343
+ 'color': 'dd:primary:contrast:fg:primary:7' # Explicit ratio
344
+ ```
345
+
346
+ **Resolution rules:**
347
+ - Core configs cannot reference theme configs. Theme configs cannot reference user configs.
348
+ - Two-pass resolution: values are resolved before inheritance, then re-resolved after.
349
+ - Static hex values (no `dd:` prefix) pass through unchanged.
350
+
351
+ ### Registering Custom AST Processors
352
+
353
+ Theme authors and downstream extensions can hook into the AST processing pipeline without monkey-patching, using `register_ast_processor()`. Registered processors run at priority 992, after all core processors.
354
+
355
+ ```python
356
+ from doxtr_pdf_theme_core import register_ast_processor
357
+ from docutils import nodes
358
+
359
+ def my_processor(app, doctree, docname):
360
+ """Called for every resolved doctree during a latex build."""
361
+ for node in doctree.traverse(nodes.paragraph):
362
+ # Custom processing here
363
+ pass
364
+
365
+ register_ast_processor(my_processor)
366
+ ```
367
+
368
+ The function signature must be `fn(app, doctree, docname) -> None`. Processors are called in registration order and errors are caught and logged as warnings without aborting the build.
369
+
370
+ ### Install and Test
371
+
372
+ ```bash
373
+ # Install in development mode
374
+ pip install -e /path/to/my_company_theme
375
+
376
+ # Add to your Sphinx conf.py
377
+ # extensions = ['my_company_theme']
378
+
379
+ # Build PDF
380
+ sphinx-build -b latex source/ build/latex/
381
+ cd build/latex && latexmk -pdf -lualatex *.tex
382
+ ```
383
+
384
+ ### Package and Distribute
385
+
386
+ ```bash
387
+ # Build the wheel
388
+ python -m build
389
+
390
+ # Publish to PyPI (or private registry)
391
+ twine upload dist/*
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Architecture
397
+
398
+ ### Three-Tier Merge
399
+
400
+ All configuration flows through a three-layer cascade:
401
+
402
+ ```
403
+ ┌─────────────────────────────────────────────────┐
404
+ │ User conf.py │ ← Highest priority
405
+ │ (doxtr_headings = {'chapter': {'color': ...}}) │
406
+ ├─────────────────────────────────────────────────┤
407
+ │ Theme Defaults │ ← Middle layer
408
+ │ (app.config.doxtr_theme_defaults = {...}) │
409
+ ├─────────────────────────────────────────────────┤
410
+ │ Core Defaults │ ← Lowest priority
411
+ │ (core_config.py CORE_CONFIG_MANIFEST) │
412
+ └─────────────────────────────────────────────────┘
413
+ ```
414
+
415
+ Each layer only specifies the keys it wants to override. The `deep_update()` function recursively merges nested dictionaries, so setting one key inside `headings.chapter` doesn't wipe out the other keys in that section.
416
+
417
+ ### Semantic Color Palette
418
+
419
+ Control the entire document's look by setting 6 palette colors:
420
+
421
+ ```python
422
+ doxtr_semantic_palette = {
423
+ 'primary': '#2E3959', # Structural — headings, borders
424
+ 'secondary': '#A64985', # Accents — highlights
425
+ 'info': '#9BE2F2', # Info — notes, specs
426
+ 'success': '#66D98E', # Positive — hints, tips
427
+ 'warning': '#EA9B62', # Caution — warnings
428
+ 'danger': '#F2545B', # Danger — errors
429
+ }
430
+ ```
431
+
432
+ All other colors derive from these via `dd:` expressions in the configuration.
433
+
434
+ ---
435
+
436
+ ## Features & Customization
437
+
438
+ ### Structural Layout Settings
439
+
440
+ By default, the theme pushes chapter and section numbers into the page margins and *alternates* their placement based on the page number.
441
+
442
+ ```python
443
+ doxtr_headings = {
444
+ 'align': 'alternate', # 'alternate', 'left', 'right', 'center'
445
+ 'numbers_in_margin': True, # Push numbers into the margin
446
+ 'margin_space': '1.5em', # Gap between number and title text
447
+
448
+ 'chapter': {
449
+ 'align': 'right', # Override just for chapters
450
+ 'number_margin': True,
451
+ 'number_line': True, # Decorative colored bar
452
+ 'line_height': '10cm',
453
+ 'line_color': '#FF0000',
454
+ 'margin_space': '0.75em',
455
+ },
456
+ 'section': {
457
+ 'number_margin': False,
458
+ 'number_line': False,
459
+ },
460
+ }
461
+ ```
462
+
463
+ ### Document Inheritance Hierarchy
464
+
465
+ Font, color, and size properties inherit top-down through the hierarchy (`part` → `chapter` → `section` → `subsection` → `subsubsection`):
466
+
467
+ ```python
468
+ doxtr_inherit_all = True # Global kill-switch for inheritance
469
+ doxtr_inherit_font = True # Inherit font families downward
470
+ doxtr_inherit_color = True # Inherit hex colors downward
471
+ doxtr_inherit_size = False # Let KOMA handle font scaling by default
472
+ ```
473
+
474
+ ### Core Fonts
475
+
476
+ ```python
477
+ doxtr_main_font = 'Spectral'
478
+ doxtr_main_font_options = 'BoldFont={Spectral SemiBold}, ItalicFont={Spectral Italic}, BoldItalicFont={Spectral SemiBold Italic}'
479
+ doxtr_sans_font = 'Montserrat'
480
+ doxtr_sans_font_options = '' # fontspec options for sans font
481
+ doxtr_mono_font = 'FiraCode Nerd Font'
482
+ doxtr_mono_font_options = 'Scale=MatchLowercase' # fontspec options for mono font
483
+ ```
484
+
485
+ ### Sizes & Spacing
486
+
487
+ Use Python raw strings for LaTeX commands:
488
+
489
+ ```python
490
+ doxtr_headings = {
491
+ 'chapter': {
492
+ 'size': r'\fontsize{32pt}{36pt}\selectfont',
493
+ },
494
+ }
495
+ ```
496
+
497
+ The `\fontsize{}{}\selectfont` command takes:
498
+ 1. **Font size** (e.g., `32pt`) — character height
499
+ 2. **Baselineskip** (e.g., `36pt`) — line-to-line distance
500
+
501
+ ---
502
+
503
+ ## Configuration Reference
504
+
505
+ ### Config Sections
506
+
507
+ Each section can be set via `doxtr_theme_defaults` (in a theme) or directly in `conf.py`:
508
+
509
+ | Section | `conf.py` variable | Controls |
510
+ |---|---|---|
511
+ | `title_page` | `doxtr_title_page` | Cover page colors, fonts, background image |
512
+ | `headings` | `doxtr_headings` | Chapter/section/subsection styling |
513
+ | `parts` | `doxtr_parts` | Part page styling and numbering |
514
+ | `epigraphs` | `doxtr_epigraphs` | Quote block styling |
515
+ | `draft` | `doxtr_draft` | Watermark text and styling |
516
+ | `microtype` | `doxtr_microtype` | Typographic refinement settings |
517
+ | `admonitions` | `doxtr_admonitions` | Note/warning/tip/etc. boxes |
518
+ | `tables` | `doxtr_tables` | Table header, row, and caption colors |
519
+ | `figures` | `doxtr_figures` | Figure caption styling |
520
+ | `code` | `doxtr_code` | Code block per-language styling |
521
+ | `containers` | `doxtr_containers` | Custom stylebox containers |
522
+ | `needs` | `doxtr_needs` | sphinx-needs box styling |
523
+ | `sidebar` | `doxtr_sidebar` | RST `.. sidebar::` directive styling |
524
+ | `highlights` | `doxtr_highlights` | RST `.. highlights::` directive styling |
525
+ | `toc` | `doxtr_toc` | Table of Contents entry styling |
526
+ | `bibliography` | `doxtr_bibliography` | Bibliography/citation entry styling |
527
+ | `index` | `doxtr_index` | Back-of-book index styling |
528
+ | `glossary` | `doxtr_glossary` | Glossary term/definition styling |
529
+
530
+ ### Global Variables
531
+
532
+ | Variable | Default | Purpose |
533
+ |---|---|---|
534
+ | `doxtr_main_font` | `'Spectral'` | Body text font |
535
+ | `doxtr_main_font_options` | `''` | fontspec options for main font weight mapping |
536
+ | `doxtr_sans_font` | `'Montserrat'` | Sans-serif font |
537
+ | `doxtr_sans_font_options` | `''` | fontspec options for sans font (e.g. `Scale=MatchLowercase`) |
538
+ | `doxtr_mono_font` | `'FiraCode Nerd Font'` | Monospace font |
539
+ | `doxtr_mono_font_options` | `'Scale=MatchLowercase'` | fontspec options for mono font |
540
+ | `doxtr_semantic_palette` | *(6 colors)* | Semantic color palette |
541
+ | `doxtr_page_background` | `'#FFFFFF'` | Page background used in contrast calculations |
542
+ | `doxtr_wcag_level` | `7` | Minimum contrast ratio for `contrast:` ops (4.5=AA, 7=AAA) |
543
+ | `doxtr_wcag_color_debug` | `False` | Log every WCAG contrast adjustment during build |
544
+ | `doxtr_inherit_all` | `True` | Master switch for style inheritance |
545
+ | `doxtr_inherit_font` | `True` | Inherit fonts down the heading hierarchy |
546
+ | `doxtr_inherit_color` | `True` | Inherit colors down the heading hierarchy |
547
+ | `doxtr_inherit_size` | `False` | Inherit sizes down the heading hierarchy |
548
+ | `doxtr_show_release` | `True` | Show release version on the title page |
549
+ | `doxtr_show_list_of_figures` | `True` | Print List of Figures before Index |
550
+ | `doxtr_show_list_of_tables` | `True` | Print List of Tables before Index |
551
+ | `doxtr_show_list_of_listings` | `True` | Print List of Code Blocks before Index |
552
+ | `doxtr_appendix_chapter_numbering` | `True` | Number appendix chapters as A.1, A.2, etc. |
553
+ | `doxtr_headsep` | `'8mm'` | Space between header and text body |
554
+ | `doxtr_footskip` | `'10mm'` | Space between text body and footer |
555
+ | `doxtr_headheight` | `'18pt'` | Height of the header line |
556
+ | `doxtr_footheight` | `'25pt'` | Height of the footer |
557
+ | `doxtr_footer_logo` | *(doxtr icon)* | Path to footer logo image |
558
+ | `doxtr_footer_logo_height` | `'1.5em'` | Height of the footer logo |
559
+ | `doxtr_landscape_package` | `'pdflscape'` | Package for landscape pages: `'pdflscape'`, `'lscape'`, or `''` to disable |
560
+ | `doxtr_strict_mode` | `False` | Raise an error on missing templates instead of falling back |
561
+ | `doxtr_cache_templates` | `True` | Cache compiled Jinja2 templates across pages |
562
+
563
+ ### Custom Resolution Paths (for Theme Authors)
564
+
565
+ `doxtr_theme_style_paths` is a **list** of directories searched for all style types. The per-type variables are single **strings** pointing to a specific folder and take precedence over `doxtr_theme_style_paths` for their type.
566
+
567
+ | Variable | Type | Purpose |
568
+ |---|---|---|
569
+ | `doxtr_theme_style_paths` | list | Ordered list of directories to search for any `.tex_t` file |
570
+ | `doxtr_container_title_style_path` | string | Container title `.tex_t` files |
571
+ | `doxtr_container_style_path` | string | Container body `.tex_t` files |
572
+ | `doxtr_table_style_path` | string | Table `.tex_t` files |
573
+ | `doxtr_figure_style_path` | string | Figure `.tex_t` files |
574
+ | `doxtr_code_style_path` | string | Code block `.tex_t` files |
575
+ | `doxtr_admonition_style_path` | string | Admonition `.tex_t` files |
576
+ | `doxtr_need_style_path` | string | sphinx-needs `.tex_t` files |
577
+ | `doxtr_sidebar_style_path` | string | Sidebar `.tex_t` files |
578
+ | `doxtr_title_page_template_path` | string | Title page `.tex_t` files |
579
+
580
+ ---
581
+
582
+ ### `doxtr_title_page`
583
+
584
+ ```python
585
+ doxtr_title_page = {
586
+ 'template': 'default', # Name of the .tex_t file to load for the cover
587
+ 'page_color': '#183060', # Solid background color
588
+ 'background_image': 'bg.png', # Path to background image (added to latex_additional_files)
589
+ 'background_image_mode': 'fit', # 'fit', 'stretch', or 'tile'
590
+ 'background_image_align': 'center', # 'center', 'top', 'bottom', 'left', 'right'
591
+ 'color_opacity': '0.5', # Opacity of the page_color overlay (0.0–1.0 as string)
592
+ 'top_line': False, # Render Sphinx's default top black line
593
+ 'subtitle': 'My Subtitle', # Custom subtitle text
594
+
595
+ # Font styling per element (title, subtitle, author, date, release_version):
596
+ 'title_font': 'Rye',
597
+ 'title_size': r'\fontsize{38pt}{44pt}\selectfont',
598
+ 'title_color': '#F0D890',
599
+ 'subtitle_font': 'Comfortaa',
600
+ 'subtitle_size': r'\fontsize{16pt}{20pt}\selectfont',
601
+ 'subtitle_color': '#78D8F0',
602
+ 'author_font': 'Josefin Sans',
603
+ 'author_size': r'\fontsize{14pt}{18pt}\selectfont',
604
+ 'author_color': '#90F0F0',
605
+ 'date_font': 'Montserrat',
606
+ 'date_size': r'\fontsize{11pt}{14pt}\selectfont',
607
+ 'date_color': '#F0D890',
608
+ 'release_version_font': 'Comfortaa',
609
+ 'release_version_size': r'\fontsize{11pt}{14pt}\selectfont',
610
+ 'release_version_color': '#F0C078',
611
+ }
612
+ ```
613
+
614
+ `background_image_mode` controls how the image fills the page:
615
+ - `'fit'` — scale to fit while preserving aspect ratio
616
+ - `'stretch'` — scale to fill the entire page, ignoring aspect ratio
617
+ - `'tile'` — tile the image across the page
618
+
619
+ ### `doxtr_headings`
620
+
621
+ ```python
622
+ doxtr_headings = {
623
+ # Global defaults applied to all levels unless overridden per-level:
624
+ 'align': 'alternate', # 'alternate', 'left', 'right', 'center'
625
+ 'numbers_in_margin': True, # Push numbers into the page margin
626
+ 'margin_space': '0em', # Gap between number and title text
627
+
628
+ # Per-level overrides — all keys below are accepted by every level:
629
+ 'part': {
630
+ 'font': 'Cinzel',
631
+ 'size': r'\fontsize{42pt}{48pt}\selectfont',
632
+ 'color': '#FFFFFF',
633
+ 'align': 'center',
634
+ 'number_line': False, # Decorative colored structural bar
635
+ 'line_height': '10cm', # Length of the structural line
636
+ 'line_color': '#78D8F0',
637
+ 'margin_space': '0.75em',
638
+ 'number_font': 'Kranky',
639
+ 'number_size': r'\fontsize{32pt}{38pt}\selectfont',
640
+ 'number_color': '#184878',
641
+ 'background_color': '#183060', # Part page background color
642
+ 'epigraph_color': '#D8F0F0', # Epigraph text color on part pages
643
+ 'epigraph_author_color': '#F0C078', # Epigraph attribution color on part pages
644
+ },
645
+ 'chapter': {
646
+ 'font': 'Story Script',
647
+ 'size': r'\fontsize{26pt}{32pt}\selectfont',
648
+ 'color': '#183060',
649
+ 'number_margin': True, # Push chapter number into margin
650
+ 'number_line': True,
651
+ 'line_height': '7cm',
652
+ 'line_color': '#78D8F0',
653
+ 'margin_space': '0.75em',
654
+ 'number_font': 'Kranky',
655
+ 'number_size': r'\fontsize{32pt}{38pt}\selectfont',
656
+ 'number_color': '#184878',
657
+ },
658
+ # 'section': { ... }, # Same keys as chapter, number_line defaults to False
659
+ # 'subsection': { ... },
660
+ # 'subsubsection': { ... },
661
+ }
662
+ ```
663
+
664
+ ### `doxtr_parts`
665
+
666
+ Parts are numbered pages that divide the book into major sections. Global keys set defaults for all parts; integer keys override individual parts by number.
667
+
668
+ ```python
669
+ doxtr_parts = {
670
+ # Global defaults for all part pages:
671
+ 'font': 'Cinzel',
672
+ 'size': r'\fontsize{48pt}{54pt}\selectfont',
673
+ 'color': '#FFFFFF',
674
+ 'part_number_font': 'Comfortaa', # Font for the "Part" prefix label
675
+ 'part_number_size': r'\fontsize{24pt}{28pt}\selectfont',
676
+ 'part_number_color': '#78D8F0',
677
+ 'part_number_part_font': 'Comfortaa', # Font for the word "Part"
678
+ 'part_number_part_size': r'\fontsize{18pt}{22pt}\selectfont',
679
+ 'part_number_part_color': '#78D8F0',
680
+ 'part_number_number_font': 'Cinzel', # Font for the numeral itself
681
+ 'part_number_number_size': r'\fontsize{36pt}{42pt}\selectfont',
682
+ 'part_number_number_color': '#F0D890',
683
+
684
+ # Per-part overrides (integer key = part number):
685
+ 1: {
686
+ 'appendix': True, # Switch to letter numbering from this part
687
+ 'image': 'wizard-of-docs.png', # Full-page background image
688
+ 'background_color': '#00000088', # 8-digit hex: last 2 digits = opacity
689
+ 'epigraph_color': '#FFF',
690
+ 'epigraph_author_color': '#CCC',
691
+ 'font': 'Cinzel',
692
+ 'color': '#FFFFFF',
693
+ 'size': r'\fontsize{48pt}{54pt}\selectfont',
694
+ 'number_font': 'Comfortaa',
695
+ 'number_color': '#78D8F0',
696
+ 'number_part_font': 'Comfortaa',
697
+ 'number_part_color': '#78D8F0',
698
+ 'number_number_font': 'Cinzel',
699
+ 'number_number_color': '#F0D890',
700
+ },
701
+ }
702
+ ```
703
+
704
+ ### `doxtr_epigraphs`
705
+
706
+ ```python
707
+ doxtr_epigraphs = {
708
+ 'width': r'0.55\textwidth',
709
+ 'format': '— #1', # #1 is replaced by the attribution text
710
+ 'align_box': 'right', # 'left', 'center', 'right'
711
+ 'align_text': 'left',
712
+ 'align_author': 'right',
713
+ 'font': 'Cormorant Garamond',
714
+ 'size': r'\itshape\large',
715
+ 'color': '#303048',
716
+ 'author_font': 'Merienda',
717
+ 'author_size': r'\small',
718
+ 'author_color': '#184878',
719
+
720
+ # Per-level overrides (inherit from global when not set):
721
+ # 'part': { 'width': r'0.6\textwidth', 'color': '#FFFFFF', ... },
722
+ # 'chapter': { ... },
723
+ # 'section': { ... },
724
+ # 'subsection': { ... },
725
+ # 'subsubsection': { ... },
726
+ }
727
+ ```
728
+
729
+ ### `doxtr_draft`
730
+
731
+ The watermark is **activated by setting `'text'`**. Without it, no watermark is rendered.
732
+
733
+ ```python
734
+ doxtr_draft = {
735
+ 'text': 'DRAFT - {date} - V: {project_version}', # Activates the watermark
736
+ # Placeholders: {date}, {project_version}, {ext_version}
737
+ 'date_format': '%Y-%m-%d %H:%M:%S %Z',
738
+ 'timezone': 'local', # 'local', 'UTC', or any IANA zone e.g. 'Europe/Berlin'
739
+ 'color': '#00000044', # 8-digit hex — last 2 digits control opacity
740
+ 'font_size': r'\normalsize',
741
+ 'font': 'Offside',
742
+ }
743
+ ```
744
+
745
+ Watermark is automatically disabled when `microtype` is active (draft mode implies fast iteration; microtype is for final output). Microtype is re-enabled when `'text'` is removed.
746
+
747
+ ### `doxtr_microtype`
748
+
749
+ Microtype is active by default when no draft watermark is set.
750
+
751
+ ```python
752
+ doxtr_microtype = {
753
+ 'enabled': True, # Master switch (also disabled automatically in draft mode)
754
+ 'protrusion': True, # Hanging punctuation — characters protrude slightly into margin
755
+ 'expansion': True, # Font expansion — eliminates uneven word spacing
756
+ 'kerning': False, # Fine character-pair kerning (requires microtype >= 2.6a for LuaTeX)
757
+ 'stretch': 10, # Maximum stretch percentage
758
+ 'shrink': 10, # Maximum shrink percentage
759
+ }
760
+ ```
761
+
762
+ ---
763
+
764
+ ### `doxtr_admonitions`
765
+
766
+ All admonition types inherit from `'generic'`. Override only the keys you want to change for a specific type.
767
+
768
+ **Built-in types:** `generic`, `note`, `tip`, `hint`, `important`, `warning`, `caution`, `danger`, `error`, `attention`, `seealso`
769
+
770
+ ```python
771
+ doxtr_admonitions = {
772
+ 'generic': {
773
+ 'style': 'default', # Name of the .tex_t template to use
774
+ 'title_icon': r'\faIcon{info-circle}', # LaTeX command or image path
775
+ 'title_icon_color': '#FFFFFF',
776
+ 'title_icon_size': '', # LaTeX size command (empty = inherit)
777
+ 'title_icon_padding': '3ex',
778
+ 'title_decoration_spacing': '2mm',
779
+ 'title_font': 'Montserrat',
780
+ 'title_font_size': r'\large\bfseries',
781
+ 'title_font_color': '#FFFFFF',
782
+ 'title_background_color': '#184878',
783
+ 'title_icon_box_background_color': '#183060',
784
+ 'content_font': 'Spectral',
785
+ 'content_font_size': r'\normalsize',
786
+ 'content_font_color': '#1A1A2E',
787
+ 'content_background_color': '#F0F8FF',
788
+ 'content_background_color_nested': '#FFFFFF', # Background when admonition is nested
789
+ 'before_skip': '2em plus 0.5em minus 0.5em',
790
+ 'after_skip': '1.5em plus 0.5em minus 0.5em',
791
+ },
792
+ # Per-type overrides (merge on top of generic):
793
+ 'note': {
794
+ 'title_icon': r'\faIcon{bookmark}',
795
+ 'title_background_color': '#2060A0',
796
+ 'title_icon_box_background_color': '#184878',
797
+ 'content_background_color': '#EEF5FC',
798
+ },
799
+ 'warning': {
800
+ 'title_icon': r'\faIcon{exclamation-triangle}',
801
+ 'title_background_color': '#D48030',
802
+ 'content_background_color': '#FFF8F0',
803
+ },
804
+ # 'tip', 'hint', 'important', 'caution', 'danger', 'error', 'attention', 'seealso'
805
+ # all accept the same keys as 'generic'
806
+ }
807
+ ```
808
+
809
+ If `title_icon` is a file path (not a LaTeX command starting with `\`), it is automatically included as `\includegraphics[height=1em, keepaspectratio]{file}`.
810
+
811
+ ### `doxtr_needs`
812
+
813
+ Controls [sphinx-needs](https://sphinx-needs.readthedocs.io/) box styling. Types beyond `generic` are auto-detected from `needs_types` in your `conf.py`.
814
+
815
+ **Built-in type overrides:** `generic`, `req`, `spec`, `decision`, `risk`
816
+
817
+ ```python
818
+ doxtr_needs = {
819
+ 'generic': {
820
+ 'style': 'default',
821
+ 'title_font': 'Montserrat',
822
+ 'title_font_size': r'\large\bfseries',
823
+ 'title_color': '#FFFFFF',
824
+ 'title_background_color': '#184878',
825
+ 'title_icon': r'\faIcon{clipboard-check}',
826
+ 'title_icon_color': '#FFFFFF',
827
+ 'title_icon_size': '',
828
+ 'title_icon_raise': '0pt', # Manual vertical adjustment for icon
829
+ 'title_icon_raise_offset': '0pt', # Additional offset added to raise
830
+ 'title_vertical_position': 'middle', # 'top', 'middle', 'bottom', or manual
831
+ 'segmentation_style': 'solid', # 'solid', 'dashed', 'dotted', 'dashdotted', 'none'
832
+ 'segmentation_color': '#184878',
833
+ 'metadata_background_color': '#E8F4FC',
834
+ 'metadata_font': 'Montserrat',
835
+ 'metadata_font_size': r'\small',
836
+ 'metadata_font_color': '#183060',
837
+ 'metadata_key_font': 'Montserrat',
838
+ 'metadata_key_font_size': r'\bfseries',
839
+ 'metadata_key_color': '#183060',
840
+ 'content_background_color': '#FFFFFF',
841
+ 'content_font': 'Spectral',
842
+ 'content_font_size': r'\normalsize',
843
+ 'content_font_color': '#1A1A2E',
844
+ 'before_skip': '1.5em plus 0.5em minus 0.5em',
845
+ 'after_skip': '1.5em plus 0.5em minus 0.5em',
846
+ },
847
+ # Per-type overrides:
848
+ 'req': {
849
+ 'title_background_color': 'dd:secondary',
850
+ 'segmentation_color': 'dd:secondary',
851
+ 'metadata_background_color': 'dd:secondary:lighten:85',
852
+ },
853
+ # 'spec', 'decision', 'risk' follow the same pattern
854
+ }
855
+ ```
856
+
857
+ `title_vertical_position` values:
858
+ - `'middle'` — vertically centered (uses `\dimexpr 0.5\fontcharht...`)
859
+ - `'top'` — aligned to cap height
860
+ - `'bottom'` — baseline aligned
861
+ - Any other string — treated as a raw LaTeX raise dimension
862
+
863
+ ### `doxtr_tables`
864
+
865
+ ```python
866
+ doxtr_tables = {
867
+ 'generic': {
868
+ 'style': 'default',
869
+ 'title_style': 'classic',
870
+ 'caption_position': 'side', # 'side', 'top', or 'bottom'
871
+ 'caption_top_offset': '-0.5ex',
872
+ 'title_padding': '1.5ex',
873
+ 'title_text_offset': '0pt', # Horizontal offset of caption text
874
+ 'title_fade_dots': False, # Fade dot leaders in caption
875
+ 'title_background_fade_mask_color': '#FFFFFF',
876
+ 'title_background_fade_length': '1.5ex',
877
+ 'title_background_fade_shape': 'rectangle', # 'rectangle' or 'triangle'
878
+ 'header_background_color': '#183060',
879
+ 'header_font_color': '#FFFFFF',
880
+ 'header_font': 'Montserrat',
881
+ 'header_font_size': r'\bfseries',
882
+ 'row_color_odd': '#F8FAFF',
883
+ 'row_color_even': '#FFFFFF',
884
+ 'title_background_color': '#184878',
885
+ 'title_font_color': '#FFFFFF',
886
+ 'title_font': 'Montserrat',
887
+ 'title_font_size': r'\bfseries',
888
+ }
889
+ }
890
+ ```
891
+
892
+ ### `doxtr_figures`
893
+
894
+ ```python
895
+ doxtr_figures = {
896
+ 'generic': {
897
+ 'style': 'default',
898
+ 'caption_background_color': '#F0F8FF',
899
+ 'caption_font_color': '#183060',
900
+ 'caption_font': 'Montserrat',
901
+ 'caption_font_size': r'\small\sffamily\bfseries',
902
+ 'caption_padding': '1.5ex',
903
+ 'caption_align': 'center', # 'left', 'center', 'right'
904
+ }
905
+ }
906
+ ```
907
+
908
+ ---
909
+
910
+ ### `doxtr_code`
911
+
912
+ Code blocks are styled per language. All language entries inherit from `'generic'` for any key not explicitly set.
913
+
914
+ **Built-in language overrides:** `python`, `java`, `kotlin`, `rust`, `c`, `cpp`, `csharp`, `go`, `rst`, `sh`, `bash`, `zsh`, `powershell`, `markdown`, `html`, `css`, `javascript`, `typescript`, `text`, `json`, `yaml`, `sql`, `xml`, `latex`, `dockerfile`, `toml`, `ini`, `ruby`, `php`, `lua`, `swift`, `make`
915
+
916
+ ```python
917
+ doxtr_code = {
918
+ 'generic': {
919
+ 'style': 'default',
920
+ 'border_width': '0.8pt',
921
+ 'show_mac_dots': False, # Red/yellow/green terminal dots (auto-enabled for shell languages)
922
+ 'language_label': '', # Override the auto-detected language name in the title bar
923
+ 'icon': r'\faIcon{code}', # LaTeX command or image path
924
+ 'icon_color': '#78D8F0',
925
+ 'icon_size': '', # LaTeX size command (empty = inherit)
926
+ 'icon_position': 'after_mac_dots', # 'before_mac_dots' or 'after_mac_dots'
927
+ 'title_background_color': '#183060',
928
+ 'title_font_color': '#78D8F0',
929
+ 'title_font': 'Montserrat',
930
+ 'title_font_size': r'\small\sffamily\bfseries',
931
+ 'content_background_color': '#F8FAFF',
932
+ 'content_font_color': '#1A1A2E',
933
+ 'content_font': 'FiraCode Nerd Font', # Per-language monospace font override
934
+ 'content_font_size': r'\small',
935
+ 'border_color': '#78D8F0',
936
+ },
937
+ # Per-language overrides (any key from generic is accepted):
938
+ 'python': {
939
+ 'icon': r'\faIcon{python}',
940
+ 'title_background_color': '#306998',
941
+ 'title_font_color': '#FFD43B',
942
+ 'icon_color': '#FFD43B',
943
+ 'border_color': '#306998',
944
+ },
945
+ # Add your own language override:
946
+ # 'mylang': {
947
+ # 'title_background_color': '#123456',
948
+ # 'title_font_color': '#FFFFFF',
949
+ # 'icon': r'\faIcon{file-code}',
950
+ # 'language_label': 'My Language',
951
+ # },
952
+ }
953
+ ```
954
+
955
+ Terminal/shell languages (`sh`, `bash`, `zsh`, `powershell`) have `show_mac_dots: True` by default. For all others it defaults to `False`.
956
+
957
+ If `icon` is a file path (not a LaTeX command), it is included as `\includegraphics[height=1em, keepaspectratio]{file}`.
958
+
959
+ ### `doxtr_containers`
960
+
961
+ Containers are custom styled boxes created with the `.. stylebox::` RST directive. The core ships several built-in containers that you can use directly or use as examples for your own.
962
+
963
+ **Built-in container types:** `default`, `typewriter`, `highlight-section`, `alice`, `bob`, `folder`
964
+
965
+ #### RST Usage
966
+
967
+ ```rst
968
+ .. stylebox:: my_container
969
+ :title: My Title
970
+
971
+ Content goes here.
972
+
973
+ .. stylebox:: my_container
974
+ :notitle:
975
+
976
+ No title shown (suppresses even a static title configured in the theme).
977
+ ```
978
+
979
+ Options for the `.. stylebox::` directive:
980
+
981
+ | Option | Purpose |
982
+ |---|---|
983
+ | *(first argument)* | Container type name — must match a key in `doxtr_containers` |
984
+ | `:title: Text` | Override the title for this instance |
985
+ | `:notitle:` | Suppress all title sources, including a static `title` from config |
986
+ | `:name: anchor` | RST cross-reference anchor |
987
+ | `:class: css-class` | Additional docutils class |
988
+
989
+ #### Container Configuration
990
+
991
+ ```python
992
+ doxtr_containers = {
993
+ 'my_container': {
994
+ 'style': 'default', # Body .tex_t template name
995
+ 'title_style': 'classic', # Title geometry .tex_t template name
996
+ 'title': '', # Static title shown when no :title: in RST (empty = no title)
997
+ 'title_raw': False, # Pass title as raw LaTeX without escaping
998
+ 'container_frame': True, # Draw an outer border
999
+ 'match_text_width': False, # Align box width to body text column
1000
+ 'title_icon': r'\faIcon{info}',
1001
+ 'title_font': 'Montserrat',
1002
+ 'title_font_size': r'\large\bfseries',
1003
+ 'title_color': '#1E3A8A', # Title bar background color
1004
+ 'title_font_color': '#FFFFFF',
1005
+ 'title_icon_color': '#FFFFFF',
1006
+ 'title_icon_font_size': '',
1007
+ 'content_font': 'Spectral',
1008
+ 'content_font_size': r'\normalsize',
1009
+ 'content_font_color': '#000000',
1010
+ 'content_background_color': '#F8FAFC',
1011
+ 'before_skip': '2em plus 0.5em minus 0.5em',
1012
+ 'after_skip': '1.5em plus 0.5em minus 0.5em',
1013
+ },
1014
+
1015
+ # Folder style adds shadow and tab title:
1016
+ 'folder': {
1017
+ 'style': 'folder',
1018
+ 'title': 'Background Information',
1019
+ 'title_color': '#808080', # Frame/border color
1020
+ 'title_font_color': '#000000',
1021
+ 'title_background_color': '#FFFFFF', # Tab background
1022
+ 'content_background_color': '#FFFFFF',
1023
+ 'border_width': '0.4pt',
1024
+ 'show_shadow': True,
1025
+ 'shadow_color': '#C0C0C0',
1026
+ },
1027
+
1028
+ # Participant style (alice/bob) adds a floating name badge:
1029
+ 'alice': {
1030
+ 'style': 'participant',
1031
+ 'title': 'Alice',
1032
+ 'frame_width': '0.2mm', # Border thickness
1033
+ 'frame_arc': '0mm', # Corner radius (0mm = sharp)
1034
+ 'title_position': 'left', # Badge position: 'left', 'center', 'right', or LaTeX dim
1035
+ 'title_xshift': '1cm', # Additional horizontal offset
1036
+ 'title_max_width': '-3cm', # varwidth constraint for title pill
1037
+ },
1038
+ }
1039
+ ```
1040
+
1041
+ ### `doxtr_sidebar`
1042
+
1043
+ Controls the RST `.. sidebar::` directive. Sidebars float alongside the main text using `wrapfig`.
1044
+
1045
+ ```rst
1046
+ .. sidebar:: My Sidebar Title
1047
+ :subtitle: Optional Subtitle
1048
+
1049
+ Sidebar content here.
1050
+ ```
1051
+
1052
+ ```python
1053
+ doxtr_sidebar = {
1054
+ 'style': 'default',
1055
+ 'width': r'0.4\textwidth',
1056
+ 'float_position': 'R', # 'R'=right exact, 'L'=left exact, 'O'=outer, 'I'=inner
1057
+ # Lowercase (r/l/o/i) allows LaTeX to reposition
1058
+ 'border_radius': '4pt',
1059
+ 'border_width': '0.8pt',
1060
+ 'border_color': '#184878',
1061
+ 'title_icon': r'\faIcon{columns}',
1062
+ 'title_font': 'Montserrat',
1063
+ 'title_font_size': r'\large\bfseries',
1064
+ 'title_background_color': '#184878',
1065
+ 'title_font_color': '#FFFFFF',
1066
+ 'title_icon_color': '#78D8F0',
1067
+ 'subtitle_font': 'Montserrat',
1068
+ 'subtitle_font_size': r'\small\itshape',
1069
+ 'subtitle_font_color': '#306090',
1070
+ 'content_font': 'Spectral',
1071
+ 'content_font_size': r'\small',
1072
+ 'content_font_color': '#1A1A2E',
1073
+ 'content_background_color': '#F0F8FF',
1074
+ 'before_skip': '1.5em plus 0.5em minus 0.5em',
1075
+ 'after_skip': '1.5em plus 0.5em minus 0.5em',
1076
+ }
1077
+ ```
1078
+
1079
+ ### `doxtr_highlights`
1080
+
1081
+ Controls the RST `.. highlights::` directive, rendered as an accent-bordered summary box.
1082
+
1083
+ ```rst
1084
+ .. highlights::
1085
+
1086
+ Key takeaway content here.
1087
+ ```
1088
+
1089
+ ```python
1090
+ doxtr_highlights = {
1091
+ 'style': 'default',
1092
+ 'title_text': 'Highlights', # Text shown at top of box
1093
+ 'title_icon': '', # Optional icon (e.g. r'\faIcon{star}')
1094
+ 'title_font': 'Montserrat',
1095
+ 'title_font_size': r'\large\bfseries',
1096
+ 'title_font_color': '#8B6914',
1097
+ 'border_color': '#8B6914',
1098
+ 'border_width': '3pt',
1099
+ 'content_font': '', # Empty = inherit body font
1100
+ 'content_font_size': r'\normalsize',
1101
+ 'content_font_color': '#1A1A2E',
1102
+ 'content_background_color': '#FFF8DC',
1103
+ 'before_skip': '1.5em plus 0.5em minus 0.5em',
1104
+ 'after_skip': '1.5em plus 0.5em minus 0.5em',
1105
+ }
1106
+ ```
1107
+
1108
+ ### `doxtr_toc`
1109
+
1110
+ Controls Table of Contents entry fonts, sizes, and colors.
1111
+
1112
+ ```python
1113
+ doxtr_toc = {
1114
+ 'title_font': None, # Font for the "Contents" heading (None = inherit)
1115
+ 'title_size': None,
1116
+ 'title_color': None,
1117
+
1118
+ 'chapter_font': None,
1119
+ 'chapter_size': r'\large',
1120
+ 'chapter_color': None, # dd: expressions supported
1121
+ 'chapter_bold': True,
1122
+
1123
+ 'section_font': None,
1124
+ 'section_size': r'\normalsize',
1125
+ 'section_color': None,
1126
+
1127
+ 'subsection_font': None,
1128
+ 'subsection_size': r'\small',
1129
+ 'subsection_color': None,
1130
+
1131
+ 'dot_leader_color': None, # Color of dot leaders (……)
1132
+ 'dot_leader_char': r'\normalfont.',
1133
+
1134
+ 'page_number_font': None,
1135
+ 'page_number_color': None,
1136
+ }
1137
+ ```
1138
+
1139
+ ### `doxtr_bibliography`
1140
+
1141
+ ```python
1142
+ doxtr_bibliography = {
1143
+ 'title_font': None,
1144
+ 'title_size': None,
1145
+ 'title_color': None,
1146
+ 'entry_font': None,
1147
+ 'entry_size': None,
1148
+ 'entry_color': None,
1149
+ 'label_color': None, # Color of [AuthorYear] citation labels
1150
+ 'label_font': None,
1151
+ }
1152
+ ```
1153
+
1154
+ ### `doxtr_index`
1155
+
1156
+ ```python
1157
+ doxtr_index = {
1158
+ 'title_font': None,
1159
+ 'title_size': None,
1160
+ 'title_color': None,
1161
+ 'entry_font': None,
1162
+ 'entry_size': None,
1163
+ 'subentry_font': None,
1164
+ 'subentry_size': None,
1165
+ 'letter_group_font': None, # The A, B, C group headers
1166
+ 'letter_group_color': None,
1167
+ }
1168
+ ```
1169
+
1170
+ ### `doxtr_glossary`
1171
+
1172
+ ```python
1173
+ doxtr_glossary = {
1174
+ 'term_font': None,
1175
+ 'term_size': None,
1176
+ 'term_color': None,
1177
+ 'definition_font': None,
1178
+ 'definition_size': None,
1179
+ 'definition_color': None,
1180
+ 'separator': r'\quad—\quad', # Between term and definition
1181
+ }
1182
+ ```
1183
+
1184
+ ---
1185
+
1186
+ ## Building for Release
1187
+
1188
+ ```bash
1189
+ export VERSION=v0.1.10 && git tag $VERSION && git push origin $VERSION
1190
+ ```
1191
+
1192
+ GitHub Actions will publish to PyPI automatically on release.
1193
+
1194
+ ## License
1195
+
1196
+ MIT