Skip to content

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

@cderv

Description

@cderv

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtablesIssues with Tables including the gt integration

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions