|
| 1 | +--- |
| 2 | +main_commit: 50db5a5b0 |
| 3 | +analyzed_date: 2026-01-22 |
| 4 | +key_files: |
| 5 | + - src/resources/formats/html/bootstrap/_bootstrap-rules.scss |
| 6 | + - src/resources/formats/revealjs/quarto.scss |
| 7 | + - src/resources/formats/html/styles-callout.html |
| 8 | + - src/resources/filters/customnodes/callout.lua |
| 9 | +--- |
| 10 | + |
| 11 | +# HTML Callout Styling Architecture |
| 12 | + |
| 13 | +This document describes the CSS architecture for Quarto callouts across HTML-based output formats. |
| 14 | + |
| 15 | +## Overview |
| 16 | + |
| 17 | +Quarto uses a **three-tier callout styling architecture** depending on the output format: |
| 18 | + |
| 19 | +| Tier | Formats | CSS Location | Features | |
| 20 | +|------|---------|--------------|----------| |
| 21 | +| Bootstrap HTML | `html` (with themes) | `formats/html/bootstrap/_bootstrap-rules.scss` | Full theming, collapsible, dark mode | |
| 22 | +| RevealJS | `revealjs` | `formats/revealjs/quarto.scss` | Presentation-specific scaling, slide-aware | |
| 23 | +| Standalone HTML | `epub`, `gfm`, plain html | `formats/html/styles-callout.html` | Inline CSS, no dependencies | |
| 24 | + |
| 25 | +All HTML callouts support three **appearance** values: |
| 26 | +- **default**: Full-featured with colored header background |
| 27 | +- **simple**: Lightweight with left border only |
| 28 | +- **minimal**: Maps to simple with `icon=false` |
| 29 | + |
| 30 | +## Format Detection (Lua Filter) |
| 31 | + |
| 32 | +The Lua filter `src/resources/filters/customnodes/callout.lua` selects the appropriate renderer: |
| 33 | + |
| 34 | +``` |
| 35 | +Renderer selection order: |
| 36 | +1. hasBootstrap() → Bootstrap HTML renderer |
| 37 | +2. isEpubOutput() || isRevealJsOutput() → Simpler HTML structure |
| 38 | +3. isGfmOutput() → GitHub markdown alerts |
| 39 | +4. Default → BlockQuote fallback |
| 40 | +``` |
| 41 | + |
| 42 | +The `hasBootstrap()` function (in `filters/common/pandoc.lua`) checks the `has-bootstrap` parameter set by TypeScript during format initialization. |
| 43 | + |
| 44 | +## HTML Structure by Format |
| 45 | + |
| 46 | +### Bootstrap HTML |
| 47 | + |
| 48 | +```html |
| 49 | +<div class="callout callout-style-default callout-note callout-titled"> |
| 50 | + <div class="callout-header d-flex align-content-center"> |
| 51 | + <div class="callout-icon-container"><i class='callout-icon'></i></div> |
| 52 | + <div class="callout-title-container flex-fill">Title</div> |
| 53 | + </div> |
| 54 | + <div class="callout-body-container"> |
| 55 | + <div class="callout-body">Content</div> |
| 56 | + </div> |
| 57 | +</div> |
| 58 | +``` |
| 59 | + |
| 60 | +### EPUB/RevealJS HTML |
| 61 | + |
| 62 | +```html |
| 63 | +<div class="callout callout-note callout-style-default"> |
| 64 | + <div class="callout-body"> |
| 65 | + <div class="callout-icon-container"><i class='callout-icon'></i></div> |
| 66 | + <div class="callout-title">Title</div> |
| 67 | + <div class="callout-content">Content</div> |
| 68 | + </div> |
| 69 | +</div> |
| 70 | +``` |
| 71 | + |
| 72 | +Note: Bootstrap uses `callout-body-container` wrapper and Bootstrap utility classes (`d-flex`, `flex-fill`). EPUB/RevealJS uses a flatter structure. |
| 73 | + |
| 74 | +## Feature Comparison |
| 75 | + |
| 76 | +| Feature | Bootstrap | RevealJS | Standalone | |
| 77 | +|---------|-----------|----------|------------| |
| 78 | +| Collapsible | Yes | No | No | |
| 79 | +| Icon type | SVG (dynamic color) | SVG (dynamic color) | PNG (base64) | |
| 80 | +| Theming | Full Bootstrap vars | Presentation vars | Fixed colors | |
| 81 | +| Dark mode | Yes | Slide background aware | No | |
| 82 | +| Font scaling | Responsive | Presentation-specific (0.7em) | Fixed (0.9rem) | |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +## Bootstrap HTML Styling |
| 87 | + |
| 88 | +File: `src/resources/formats/html/bootstrap/_bootstrap-rules.scss` |
| 89 | + |
| 90 | +### Callout States |
| 91 | + |
| 92 | +| State | CSS Class | Description | |
| 93 | +|-------|-----------|-------------| |
| 94 | +| Titled | `.callout-titled` | Has a title/header | |
| 95 | +| Untitled | `:not(.callout-titled)` | Content only, no header | |
| 96 | +| Collapsed | `.callout-header.collapsed` | Collapsible, currently closed | |
| 97 | +| Empty content | `.callout-empty-content` | No body content | |
| 98 | + |
| 99 | +### Styling Patterns |
| 100 | + |
| 101 | +**Base callout:** |
| 102 | +```scss |
| 103 | +.callout { |
| 104 | + margin-top: $callout-margin-top; |
| 105 | + margin-bottom: $callout-margin-bottom; |
| 106 | + border-radius: $border-radius; |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +**Simple vs Default appearance:** |
| 111 | +- `.callout-style-simple`: Left border only, lighter styling |
| 112 | +- `.callout-style-default`: Full border, colored header background |
| 113 | + |
| 114 | +**Body margins** vary by appearance (simple/default) and titled state (titled/untitled). The margin rules handle edge cases like collapsed callouts and empty content states. |
| 115 | + |
| 116 | +### Theming Variables |
| 117 | + |
| 118 | +Bootstrap callouts use SCSS variables (in `_bootstrap-variables.scss`): |
| 119 | + |
| 120 | +```scss |
| 121 | +$callout-border-width: 0.4rem !default; |
| 122 | +$callout-border-scale: 0% !default; |
| 123 | +$callout-icon-scale: 10% !default; |
| 124 | +$callout-margin-top: 1.25rem !default; |
| 125 | +$callout-margin-bottom: 1.25rem !default; |
| 126 | +``` |
| 127 | + |
| 128 | +Colors are defined per callout type (note, warning, important, tip, caution) using Bootstrap's color functions. |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## RevealJS Styling |
| 133 | + |
| 134 | +File: `src/resources/formats/revealjs/quarto.scss` |
| 135 | + |
| 136 | +### Presentation-Specific Adjustments |
| 137 | + |
| 138 | +```scss |
| 139 | +// Variables |
| 140 | +$callout-border-width: 0.3rem; |
| 141 | +$callout-margin-top: 1rem; |
| 142 | +$callout-margin-bottom: 1rem; |
| 143 | + |
| 144 | +// Font scaling for slide readability |
| 145 | +.reveal div.callout { |
| 146 | + font-size: 0.7em; |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +### Light/Dark Slide Awareness |
| 151 | + |
| 152 | +RevealJS callouts adjust colors based on slide background using the `shift_to_dark` mixin: |
| 153 | + |
| 154 | +```scss |
| 155 | +.has-dark-background div.callout-note { |
| 156 | + // Lighter colors for dark backgrounds |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +--- |
| 161 | + |
| 162 | +## Standalone/EPUB Styling |
| 163 | + |
| 164 | +File: `src/resources/formats/html/styles-callout.html` |
| 165 | + |
| 166 | +### Characteristics |
| 167 | + |
| 168 | +- **Inline CSS** embedded in HTML template |
| 169 | +- **PNG icons** (base64-encoded) instead of SVG |
| 170 | +- **Fixed colors**: Uses hardcoded `#acacac`, `silver` borders |
| 171 | +- **No collapsible support** |
| 172 | +- **No theming** - works without Bootstrap or any CSS framework |
| 173 | + |
| 174 | +### Key Selectors |
| 175 | + |
| 176 | +```css |
| 177 | +.callout /* Base container */ |
| 178 | +.callout.callout-style-simple /* Simple bordered style */ |
| 179 | +.callout.callout-style-default /* Default style with header */ |
| 180 | +.callout-title /* Title container */ |
| 181 | +.callout-body /* Content container */ |
| 182 | +.callout-icon::before /* Icon pseudo-element */ |
| 183 | +``` |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## CSS Class Reference |
| 188 | + |
| 189 | +Classes applied across all HTML formats: |
| 190 | + |
| 191 | +| Class | Applied When | Purpose | |
| 192 | +|-------|--------------|---------| |
| 193 | +| `.callout` | Always | Base container | |
| 194 | +| `.callout-{type}` | Always | Type: note, warning, important, tip, caution | |
| 195 | +| `.callout-style-{appearance}` | Always | Style: default, simple | |
| 196 | +| `.callout-titled` | Has title | Structural indicator | |
| 197 | +| `.no-icon` | `icon=false` | Suppress icon | |
| 198 | +| `.callout-empty-content` | No body | Empty state (Bootstrap only) | |
| 199 | + |
| 200 | +--- |
| 201 | + |
| 202 | +## Related Files |
| 203 | + |
| 204 | +### CSS/SCSS |
| 205 | + |
| 206 | +| File | Purpose | |
| 207 | +|------|---------| |
| 208 | +| `src/resources/formats/html/bootstrap/_bootstrap-rules.scss` | Bootstrap HTML callout styles | |
| 209 | +| `src/resources/formats/html/bootstrap/_bootstrap-variables.scss` | Bootstrap callout variables | |
| 210 | +| `src/resources/formats/revealjs/quarto.scss` | RevealJS callout styles | |
| 211 | +| `src/resources/formats/html/styles-callout.html` | Standalone HTML template | |
| 212 | +| `src/resources/formats/dashboard/quarto-dashboard.scss` | Dashboard margin overrides | |
| 213 | + |
| 214 | +### Lua Filters |
| 215 | + |
| 216 | +| File | Purpose | |
| 217 | +|------|---------| |
| 218 | +| `src/resources/filters/customnodes/callout.lua` | Renderer selection and AST processing | |
| 219 | +| `src/resources/filters/modules/callouts.lua` | Bootstrap renderer implementation | |
| 220 | +| `src/resources/filters/common/pandoc.lua` | `hasBootstrap()` function | |
| 221 | + |
| 222 | +### Tests |
| 223 | + |
| 224 | +| File | Purpose | |
| 225 | +|------|---------| |
| 226 | +| `tests/docs/callouts.qmd` | All callout types and appearances | |
| 227 | +| `tests/docs/playwright/html/callouts/` | Playwright test documents | |
| 228 | +| `tests/integration/playwright/tests/html-callouts.spec.ts` | Playwright CSS tests | |
0 commit comments