quarto-dev / quarto-dev/quarto-cli

HTML table processing strips inline SVG styles, causing black backgrounds in gt/svglite plots

Open
#14,285 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug tables
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

  1. In handle_raw_html_as_table(), detect <style> tags inside SVG elements before pandoc.read(), extract them, and re-inject into the output HTML after conversion
  2. Skip table processing entirely for tables that contain SVGs with embedded <style> blocks
  3. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.