quarto-dev / quarto-dev/quarto-cli
HTML table processing strips inline SVG styles, causing black backgrounds in gt/svglite plots
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 6k
- Forks
- 458
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 41
Description
Inline SVGs generated by svglite (used by gt/gtExtras for density plots, sparklines, bar charts, etc.) get black backgrounds when rendered in Quarto HTML output. The same HTML renders correctly outside Quarto.
Root cause
svglite embeds a CSS rule inside each SVG's <defs> block:
<svg>
<g class="svglite">
<defs>
<style type="text/css"><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
...
}
]]></style>
</defs>
<rect width="100%" height="100%" style="stroke: none; fill: none;"/>
<rect x="0" y="0" width="85" height="14" style="stroke-width: 0; stroke: none;"/>
<!-- ↑ no fill attribute → SVG default is black, but .svglite rule sets fill:none -->
<polygon ... style="fill: #FFFF00;"/>
</g>
</svg>
The .svglite rect { fill: none } rule makes background rects transparent. Without it, SVG spec default fill: black takes effect.
handle_raw_html_as_table() in astpipeline.lua (line 150) extracts the <table>...</table> substring and passes it to pandoc.read(tableHtml, "html+raw_html") at line 167. Pandoc's HTML-to-AST conversion drops the <style> tags inside SVG <defs> — they are not part of Pandoc's table model.
The rendered HTML ends up with empty <defs> </defs> blocks and all SVG rects revert to fill: black.
Why gt's own CSS survives
gt's <style> block sits outside the <table> element (in the wrapper <div>). astpipeline.lua only extracts the <table> to </table> substring for pandoc.read(), so gt's CSS stays in the raw HTML and gets re-specified by table-rawhtml.lua. The svglite <style> tags are inside <td> cells, so they go through pandoc.read() and are lost.
Evidence
Computed fill values on the background rect:
- Standalone HTML (same gt output, no Quarto CSS):
computedFill: "none"— svglite styles intact - Quarto-rendered HTML:
computedFill: "rgb(0, 0, 0)"— styles stripped
Setting html-table-processing: none bypasses pandoc.read() and fixes the rendering.
Scope
This affects any inline SVG with embedded <style> tags inside HTML table cells — not just gt density plots. Any R package using svglite output inside tables (gtExtras sparklines, bar plots, nanoplots, etc.) would hit this.
Repro
Reprex repo: https://github.com/topepo/quarto-gt-reprex
Minimal case: any gt table using gtExtras::gt_plt_dist() or similar svglite-based plot functions, rendered in a Quarto HTML document.
Workaround: html-table-processing: none at document or cell level, or gt::tab_options(quarto.disable_processing = TRUE).
Possible fixes
- In
handle_raw_html_as_table(), detect<style>tags inside SVG elements beforepandoc.read(), extract them, and re-inject into the output HTML after conversion - Skip table processing entirely for tables that contain SVGs with embedded
<style>blocks - Promote svglite CSS to a document-level
<style>block (fragile — assumes uniform svglite rules)
Filed a companion issue on gt for the upstream side: will link once created.
Related issues
- #8624 — gt tables not maintaining CSS styling with html table processing
- #8233 — gt table sizing broken after Quarto table processing
- #8491 — tables opaque white instead of transparent with column layouts
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in astpipeline.lua at handle_raw_html_as_table() and trace the pandoc.read(tableHtml, "html+raw_html") call; compare how table-rawhtml.lua handles the converted output. Use the linked reprex or an inline svglite table to reproduce the black backgrounds. Done means embedded SVG styles survive HTML table processing without regressing existing table handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- css, html, lua
- Domain
- tooling, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100