quarto-dev / quarto-dev/quarto-cli

Table Attr (id, classes, attributes) is silently dropped when tbl-colwidths triggers a SimpleTable round-trip

Open
#14,778 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
JavaScript
Stars
6k
Forks
458
Avg merge
1d 9h
Merged PRs (30d)
41

Description

I have:
  • searched the issue tracker for similar issues
  • installed the latest version of Quarto CLI
  • formatted my issue following the Bug Reports guide
Bug description
Bug description

resolve_table_colwidths_scoped (in share/filters/modules/tablecolwidths.lua) applies column widths by round-tripping the table through pandoc.utils.to_simple_table / from_simple_table. SimpleTable has no Attr, so the rebuilt table loses its identifier, classes and attributes.

This is invisible for tables labelled the normal Quarto way ({#tbl-x} in the caption), because crossref carries the id on a wrapping div. It bites when a user Lua filter sets el.identifier (or el.attr) on a Table: the attribute is present in the AST until the pre-scope-resolution step and gone right after it.

With the HTML default tbl-colwidths: auto, the round-trip is triggered by the mere presence of a Link anywhere in the table — the auto branch scans for links and, if it finds one, zeroes the computed widths. So the symptom looks arbitrary from the outside: tables with links lose their id, tables without links keep it.

With tbl-colwidths: false or an explicit list, the attributes are lost for every table, link or no link.

Notes

Related but not the same:

  • #7072 (closed): {#tbl-x tbl-colwidths="[…]"} declared in the caption of a book project.
  • #13776 (closed by #14096): the same to_simple_table / from_simple_table round-trip, losing a different thing — merged cells. The fix added an is_simple(tbl) guard that bails out when the table has spans, but the round-trip is still lossy for Attr on every other table, so the guard doesn't cover this case.
    A guard for spans and a silent Attr loss suggest the round-trip itself is the wrong tool here; setting the widths on tbl.colspecs directly would avoid both.
Steps to reproduce

filter.lua:

function Table(el)
  el.attr = pandoc.Attr("my-id", {"my-class"}, {{"data-mine", "1"}})
  return el
end

_quarto.yml:

project:
  type: default
filters:
  - filter.lua
format:
  html:
    toc: false

with-link.qmd:

---
title: With link
---
 
| A | B |
|---|---|
| one | [two](https://example.org) |
 
: Caption.

without-link.qmd — the same file, with the cell two instead of the link.

Then quarto render with-link.qmd without-link.qmd --to html.

Actual behavior
<!-- without-link.html -->
<table id="my-id" class="my-class caption-top table" data-mine="1">
 
<!-- with-link.html -->
<table class="caption-top table">

QUARTO_TRACE_FILTERS=trace quarto render with-link.qmd shows the Attr intact through pre-output-location and empty at pre-scope-resolution, which is where resolve_scoped_elements runs resolve_table_colwidths_scoped.

Setting tbl-colwidths: true keeps the Attr in both files — that branch returns nil and never rebuilds the table. Setting tbl-colwidths: false or [50,50] loses it in both.

Expected behavior

Applying column widths should preserve the table's Attr. Something like reassigning result.attr = tbl.attr after from_simple_table, or setting the widths on tbl.colspecs directly instead of going through SimpleTable.

Your environment

Quarto 1.10.18 (also reproduced on 1.5.57)

  • macOS
  • Not R/Python specific: plain .qmd, no engine.
Quarto check output

❯ quarto check
Quarto 1.10.18
[✓] Checking environment information...
Quarto cache location: /Volumes/macmini external ssd/eukeni_external/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
Pandoc version 3.10.0: OK
Dart Sass version 1.101.0: OK
Deno version 2.7.14: OK
Typst version 0.15.1: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
Version: 1.10.18
Path: /Applications/quarto/bin

[✓] Checking tools....................OK
TinyTeX: (not installed)
Chrome Headless Shell: (not installed)
VeraPDF: (not installed)

[✓] Checking LaTeX....................OK
Using: Installation From Path
Path: /Library/TeX/texbin
Version: 2025

[✓] Checking Chrome Headless....................OK
Using: Chrome found on system
Path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
Source: MacOS known location

[✓] Checking basic markdown render....OK

[✓] Checking R installation...........(None)

  Unable to locate an installed version of R.
  Install R from https://cloud.r-project.org/

[✓] Checking Python 3 installation....OK
Version: 3.14.6
Path: /opt/homebrew/opt/python@3.14/bin/python3.14
Jupyter: (None)

  Jupyter is not available in this Python installation.
  Install with python3 -m pip install jupyter

[✓] Checking Julia installation...

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 share/filters/modules/tablecolwidths.lua at resolve_table_colwidths_scoped and trace the to_simple_table/from_simple_table round-trip used for tbl-colwidths. Reproduce the issue with filter.lua and the with-link.qmd and without-link.qmd examples, then verify that rendered tables preserve their identifier, classes, and attributes across the affected tbl-colwidths settings.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.