From 42c591b7d189586aabbb93cb0eb37ff4037b5233 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 22 Jan 2026 13:23:57 +0100 Subject: [PATCH 1/4] Fix unequal spacing in simple untitled callouts Simple callouts have a -0.4em bottom margin on .callout-body for collapsed/contentless callouts. For untitled simple callouts, this creates asymmetric spacing since only 0.2rem compensation exists. Add scoped rule for simple untitled callouts to set 0.5em bottom margin on last-child, matching the top margin compensation. --- news/changelog-1.9.md | 1 + src/resources/formats/html/bootstrap/_bootstrap-rules.scss | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/news/changelog-1.9.md b/news/changelog-1.9.md index 570473d90d7..9ddaece8757 100644 --- a/news/changelog-1.9.md +++ b/news/changelog-1.9.md @@ -32,6 +32,7 @@ All changes included in 1.9: - ([#11929](https://github.com/quarto-dev/quarto-cli/issues/11929)): Import all `brand.typography.fonts` in CSS, whether or not fonts are referenced by typography elements. - ([#13413](https://github.com/quarto-dev/quarto-cli/issues/13413)): Fix uncentered play button in `video` shortcodes from cross-reference divs. (author: @bruvellu) - ([#13508](https://github.com/quarto-dev/quarto-cli/issues/13508)): Add `aria-label` support to `video` shortcode for improved accessibility. +- ([#13883](https://github.com/quarto-dev/quarto-cli/issues/13883)): Fix unequal top/bottom spacing in simple untitled callouts. ### `typst` diff --git a/src/resources/formats/html/bootstrap/_bootstrap-rules.scss b/src/resources/formats/html/bootstrap/_bootstrap-rules.scss index 8aba5b288bc..f2dfa7e0829 100644 --- a/src/resources/formats/html/bootstrap/_bootstrap-rules.scss +++ b/src/resources/formats/html/bootstrap/_bootstrap-rules.scss @@ -1498,6 +1498,11 @@ kbd, margin-bottom: 0.2rem; } +.callout.callout-style-simple:not(.callout-titled) .callout-body > :last-child, +.callout.callout-style-simple:not(.callout-titled) .callout-body > div > :last-child { + margin-bottom: 0.5em; +} + $code-block-border-left-color: $table-border-color !default; .callout.callout-style-simple .callout-icon::before, From 9b2f6a81e018ac8bb36ae026f04e61712102319a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 22 Jan 2026 13:24:31 +0100 Subject: [PATCH 2/4] docs: Add HTML callout styling architecture guide Document the three-tier callout styling system (Bootstrap, RevealJS, Standalone/EPUB) for LLM reference when working on callout-related issues. Add a CLAUDE.md to help claude code write these docs Co-Authored-By: Claude Opus 4.5 --- llm-docs/CLAUDE.md | 50 +++++++ llm-docs/callout-styling-html.md | 228 +++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 llm-docs/CLAUDE.md create mode 100644 llm-docs/callout-styling-html.md diff --git a/llm-docs/CLAUDE.md b/llm-docs/CLAUDE.md new file mode 100644 index 00000000000..bc556a0511b --- /dev/null +++ b/llm-docs/CLAUDE.md @@ -0,0 +1,50 @@ +# LLM Documentation + +This directory contains documentation written for LLM assistants working on the Quarto codebase. + +## Staleness Check + +Each document has YAML frontmatter with analysis metadata: + +```yaml +--- +main_commit: abc1234 # merge-base with main (stable reference) +analyzed_date: 2025-01-22 +key_files: + - path/to/file1.ts + - path/to/file2.lua +--- +``` + +**Why merge-base?** Branch commits can be rebased or disappear. The merge-base with main is stable and represents the baseline from main that was analyzed. + +**Before relying on a document**, check if key files have changed: + +```bash +git log --oneline ..main -- +``` + +If there are significant changes, re-explore the codebase and update the document. + +## Updating Documents + +After re-analyzing, update the frontmatter: + +```bash +# Get merge-base with main (use upstream/main if that's the main remote) +git merge-base HEAD main | cut -c1-9 +``` + +Then update `main_commit`, `analyzed_date`, and verify `key_files` list is complete. + +## Document Purpose + +These docs capture architectural understanding that would otherwise require extensive codebase exploration. They are NOT: +- User documentation (that's at quarto.org) +- Code comments (those live in source files) +- Issue-specific notes (those go in PR descriptions) + +They ARE: +- Architectural overviews for AI assistants +- File location maps for common tasks +- Pattern documentation for consistency diff --git a/llm-docs/callout-styling-html.md b/llm-docs/callout-styling-html.md new file mode 100644 index 00000000000..decec589a44 --- /dev/null +++ b/llm-docs/callout-styling-html.md @@ -0,0 +1,228 @@ +--- +main_commit: 50db5a5b0 +analyzed_date: 2025-01-22 +key_files: + - src/resources/formats/html/bootstrap/_bootstrap-rules.scss + - src/resources/formats/revealjs/quarto.scss + - src/resources/formats/html/styles-callout.html + - src/resources/filters/customnodes/callout.lua +--- + +# HTML Callout Styling Architecture + +This document describes the CSS architecture for Quarto callouts across HTML-based output formats. + +## Overview + +Quarto uses a **three-tier callout styling architecture** depending on the output format: + +| Tier | Formats | CSS Location | Features | +|------|---------|--------------|----------| +| Bootstrap HTML | `html` (with themes) | `formats/html/bootstrap/_bootstrap-rules.scss` | Full theming, collapsible, dark mode | +| RevealJS | `revealjs` | `formats/revealjs/quarto.scss` | Presentation-specific scaling, slide-aware | +| Standalone HTML | `epub`, `gfm`, plain html | `formats/html/styles-callout.html` | Inline CSS, no dependencies | + +All HTML callouts support three **appearance** values: +- **default**: Full-featured with colored header background +- **simple**: Lightweight with left border only +- **minimal**: Maps to simple with `icon=false` + +## Format Detection (Lua Filter) + +The Lua filter `src/resources/filters/customnodes/callout.lua` selects the appropriate renderer: + +``` +Renderer selection order: +1. hasBootstrap() → Bootstrap HTML renderer +2. isEpubOutput() || isRevealJsOutput() → Simpler HTML structure +3. isGfmOutput() → GitHub markdown alerts +4. Default → BlockQuote fallback +``` + +The `hasBootstrap()` function (in `filters/common/pandoc.lua`) checks the `has-bootstrap` parameter set by TypeScript during format initialization. + +## HTML Structure by Format + +### Bootstrap HTML + +```html +
+
+
+
Title
+
+
+
Content
+
+
+``` + +### EPUB/RevealJS HTML + +```html +
+
+
+
Title
+
Content
+
+
+``` + +Note: Bootstrap uses `callout-body-container` wrapper and Bootstrap utility classes (`d-flex`, `flex-fill`). EPUB/RevealJS uses a flatter structure. + +## Feature Comparison + +| Feature | Bootstrap | RevealJS | Standalone | +|---------|-----------|----------|------------| +| Collapsible | Yes | No | No | +| Icon type | SVG (dynamic color) | SVG (dynamic color) | PNG (base64) | +| Theming | Full Bootstrap vars | Presentation vars | Fixed colors | +| Dark mode | Yes | Slide background aware | No | +| Font scaling | Responsive | Presentation-specific (0.7em) | Fixed (0.9rem) | + +--- + +## Bootstrap HTML Styling + +File: `src/resources/formats/html/bootstrap/_bootstrap-rules.scss` + +### Callout States + +| State | CSS Class | Description | +|-------|-----------|-------------| +| Titled | `.callout-titled` | Has a title/header | +| Untitled | `:not(.callout-titled)` | Content only, no header | +| Collapsed | `.callout-header.collapsed` | Collapsible, currently closed | +| Empty content | `.callout-empty-content` | No body content | + +### Styling Patterns + +**Base callout:** +```scss +.callout { + margin-top: $callout-margin-top; + margin-bottom: $callout-margin-bottom; + border-radius: $border-radius; +} +``` + +**Simple vs Default appearance:** +- `.callout-style-simple`: Left border only, lighter styling +- `.callout-style-default`: Full border, colored header background + +**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. + +### Theming Variables + +Bootstrap callouts use SCSS variables (in `_bootstrap-variables.scss`): + +```scss +$callout-border-width: 0.4rem !default; +$callout-border-scale: 0% !default; +$callout-icon-scale: 10% !default; +$callout-margin-top: 1.25rem !default; +$callout-margin-bottom: 1.25rem !default; +``` + +Colors are defined per callout type (note, warning, important, tip, caution) using Bootstrap's color functions. + +--- + +## RevealJS Styling + +File: `src/resources/formats/revealjs/quarto.scss` + +### Presentation-Specific Adjustments + +```scss +// Variables +$callout-border-width: 0.3rem; +$callout-margin-top: 1rem; +$callout-margin-bottom: 1rem; + +// Font scaling for slide readability +.reveal div.callout { + font-size: 0.7em; +} +``` + +### Light/Dark Slide Awareness + +RevealJS callouts adjust colors based on slide background using the `shift_to_dark` mixin: + +```scss +.has-dark-background div.callout-note { + // Lighter colors for dark backgrounds +} +``` + +--- + +## Standalone/EPUB Styling + +File: `src/resources/formats/html/styles-callout.html` + +### Characteristics + +- **Inline CSS** embedded in HTML template +- **PNG icons** (base64-encoded) instead of SVG +- **Fixed colors**: Uses hardcoded `#acacac`, `silver` borders +- **No collapsible support** +- **No theming** - works without Bootstrap or any CSS framework + +### Key Selectors + +```css +.callout /* Base container */ +.callout.callout-style-simple /* Simple bordered style */ +.callout.callout-style-default /* Default style with header */ +.callout-title /* Title container */ +.callout-body /* Content container */ +.callout-icon::before /* Icon pseudo-element */ +``` + +--- + +## CSS Class Reference + +Classes applied across all HTML formats: + +| Class | Applied When | Purpose | +|-------|--------------|---------| +| `.callout` | Always | Base container | +| `.callout-{type}` | Always | Type: note, warning, important, tip, caution | +| `.callout-style-{appearance}` | Always | Style: default, simple | +| `.callout-titled` | Has title | Structural indicator | +| `.no-icon` | `icon=false` | Suppress icon | +| `.callout-empty-content` | No body | Empty state (Bootstrap only) | + +--- + +## Related Files + +### CSS/SCSS + +| File | Purpose | +|------|---------| +| `src/resources/formats/html/bootstrap/_bootstrap-rules.scss` | Bootstrap HTML callout styles | +| `src/resources/formats/html/bootstrap/_bootstrap-variables.scss` | Bootstrap callout variables | +| `src/resources/formats/revealjs/quarto.scss` | RevealJS callout styles | +| `src/resources/formats/html/styles-callout.html` | Standalone HTML template | +| `src/resources/formats/dashboard/quarto-dashboard.scss` | Dashboard margin overrides | + +### Lua Filters + +| File | Purpose | +|------|---------| +| `src/resources/filters/customnodes/callout.lua` | Renderer selection and AST processing | +| `src/resources/filters/modules/callouts.lua` | Bootstrap renderer implementation | +| `src/resources/filters/common/pandoc.lua` | `hasBootstrap()` function | + +### Tests + +| File | Purpose | +|------|---------| +| `tests/docs/callouts.qmd` | All callout types and appearances | +| `tests/docs/playwright/html/callouts/` | Playwright test documents | +| `tests/integration/playwright/tests/html-callouts.spec.ts` | Playwright CSS tests | From 674a513320d4d99a80eddbb195a870871298e710 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 22 Jan 2026 13:24:48 +0100 Subject: [PATCH 3/4] test: Add Playwright tests for callout spacing Test symmetric spacing in simple untitled callouts and verify titled/default callouts use padding compensation correctly. Co-Authored-By: Claude Opus 4.5 --- .../html/callouts/callout-spacing.qmd | 29 ++++++++++++++ .../playwright/tests/html-callouts.spec.ts | 39 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/docs/playwright/html/callouts/callout-spacing.qmd create mode 100644 tests/integration/playwright/tests/html-callouts.spec.ts diff --git a/tests/docs/playwright/html/callouts/callout-spacing.qmd b/tests/docs/playwright/html/callouts/callout-spacing.qmd new file mode 100644 index 00000000000..b4d074343a1 --- /dev/null +++ b/tests/docs/playwright/html/callouts/callout-spacing.qmd @@ -0,0 +1,29 @@ +--- +title: Callout Spacing Test +--- + +## Simple Untitled {#simple-untitled} + +::: {.callout-note appearance="simple"} +Content without title to test spacing symmetry. +::: + +## Simple Titled {#simple-titled} + +::: {.callout-note appearance="simple"} +## Note Title +Content with title. +::: + +## Default Untitled {#default-untitled} + +::: {.callout-note appearance="default"} +Content without title in default appearance. +::: + +## Default Titled {#default-titled} + +::: {.callout-note appearance="default"} +## Note Title +Content with title in default appearance. +::: diff --git a/tests/integration/playwright/tests/html-callouts.spec.ts b/tests/integration/playwright/tests/html-callouts.spec.ts new file mode 100644 index 00000000000..595f29afc88 --- /dev/null +++ b/tests/integration/playwright/tests/html-callouts.spec.ts @@ -0,0 +1,39 @@ +import { test, expect } from '@playwright/test'; +import { getCSSProperty } from '../src/utils'; + +test('Simple untitled callout has symmetric spacing', async ({ page }) => { + await page.goto('./html/callouts/callout-spacing.html'); + + // Simple untitled callout structure: + // .callout-style-simple > .callout-body > .callout-body-container > p + const simpleUntitledSection = page.locator('#simple-untitled'); + const lastChild = simpleUntitledSection.locator('.callout-style-simple:not(.callout-titled) .callout-body-container > p:last-child'); + + // Verify margin-bottom > 0 (compensation for -0.4em body margin is applied) + const marginBottom = await getCSSProperty(lastChild, 'margin-bottom', true) as number; + expect(marginBottom).toBeGreaterThan(0); +}); + +test('Simple titled callout spacing is handled by padding', async ({ page }) => { + await page.goto('./html/callouts/callout-spacing.html'); + + // Simple titled callout structure: + // .callout-style-simple.callout-titled > .callout-body-container.callout-body > p + const simpleTitledSection = page.locator('#simple-titled'); + const lastChild = simpleTitledSection.locator('.callout-style-simple.callout-titled .callout-body > p:last-child'); + + const paddingBottom = await getCSSProperty(lastChild, 'padding-bottom', true) as number; + expect(paddingBottom).toBeGreaterThan(0); +}); + +test('Default callout spacing is handled by padding', async ({ page }) => { + await page.goto('./html/callouts/callout-spacing.html'); + + // Default callouts always have .callout-titled class (even without explicit title) + // Structure: .callout-style-default.callout-titled > .callout-body > p + const defaultSection = page.locator('#default-untitled'); + const lastChild = defaultSection.locator('.callout-style-default .callout-body > p:last-child'); + + const paddingBottom = await getCSSProperty(lastChild, 'padding-bottom', true) as number; + expect(paddingBottom).toBeGreaterThan(0); +}); From 8addfb426b26de7e736f090306d0d122d5b77835 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 22 Jan 2026 17:40:01 +0100 Subject: [PATCH 4/4] docs: Fix date typo and add date-checking guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback from PR #13934: - Fix 2025 → 2026 date typo in callout-styling-html.md - Add guidance to verify dates from system environment Co-Authored-By: Claude Opus 4.5 --- llm-docs/CLAUDE.md | 2 ++ llm-docs/callout-styling-html.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/llm-docs/CLAUDE.md b/llm-docs/CLAUDE.md index bc556a0511b..823439367f7 100644 --- a/llm-docs/CLAUDE.md +++ b/llm-docs/CLAUDE.md @@ -37,6 +37,8 @@ git merge-base HEAD main | cut -c1-9 Then update `main_commit`, `analyzed_date`, and verify `key_files` list is complete. +**Date verification:** Before writing dates, check today's date from the system environment (shown at conversation start). This avoids year typos like writing 2025 when it's 2026. + ## Document Purpose These docs capture architectural understanding that would otherwise require extensive codebase exploration. They are NOT: diff --git a/llm-docs/callout-styling-html.md b/llm-docs/callout-styling-html.md index decec589a44..84962371896 100644 --- a/llm-docs/callout-styling-html.md +++ b/llm-docs/callout-styling-html.md @@ -1,6 +1,6 @@ --- main_commit: 50db5a5b0 -analyzed_date: 2025-01-22 +analyzed_date: 2026-01-22 key_files: - src/resources/formats/html/bootstrap/_bootstrap-rules.scss - src/resources/formats/revealjs/quarto.scss