Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify html element ids to use name rather than labels for html validity #1842

Merged
merged 8 commits into from
Aug 22, 2024

Conversation

olivroy
Copy link
Collaborator

@olivroy olivroy commented Aug 18, 2024

Summary

Opening for discussion as this PR does not fix the aftermentioned issues, but may be a good first step. (I would need advice for anything extra)

It seems difficult to get rid of the <style> error. Suggestion to use <link> in quarto-dev/quarto-cli#10512 (reply in thread)

It seems that when we are using md(), we are emitting invalid ids for spanners.

Unfortunately, it seems that the example supplied in #1839 still yields an invalid html error. Also wondering if there is a more robust valid_html_id() in the deps. I wrote a very simple version, https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id, but not sure it will work well. It still states this has to be unique?

---
title: Hello
format: html
keep-md: true
lang: en
---


https://github.com/rstudio/gt/pull/988

```{r}
#| echo: false
#| message: false
#| eval: true
devtools::load_all(".")
 # or pak::pak("rstudio/gt#1842")
# library(gt)
mtcars[1:3, 1:4] |> 
  gt::gt() |> 
  gt::tab_spanner(label = gt::md("Header"), columns = 2:3)
```

It seems that with this PR, we are down to a single html validity error

image

down from a couple from quarto-dev/quarto-cli#10534 (comment)

But, it doesn't fix the issue, I still get this when I render the docc
image

@cscheid in case you have an idea of how we could deal with this, please let me know!

Related GitHub Issues and PRs (the PR doesn't fix any issue)

Checklist

@capnrefsmmat
Copy link

I wrote a very simple version, https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id, but not sure it will work well. It still states this has to be unique?

Yes, HTML ID attributes have to be globally unique in the document. If you print the same gt table twice in one HTML file, you can't have the same IDs in it.

I suspect that's not possible without adding some kind of random value to the IDs, e.g. making the attribute id="cyl-dsklfj34", and keeping track of the IDs used in the table headers.

@rich-iannone
Copy link
Member

Just getting back into the fold here, and I looked into what might be causing this. The malformed ID through md() was bad so thank you @olivroy for addressing that in the PR! There's probably more to do there to make it truly unique (e.g., using the table ID as part of the string).

You're right that this doesn't solve the issue. If you supply a non-malformed ID through the id arg (or just through using your fix here) you run into the display bug whenever md() is used in the tab_spanner() call.

This can be traced to the <span> tag of the spanner label. It's closed early in the Quarto-processed version. Here's a screenshot of the source HTML for the correct version:

correct_span

And here is the table within a Quarto HTML output:

incorrect_span

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 21, 2024

Thanks for the clear information! Is there any way we could avoid that Quarto close that span? Maybe we don't need to emit the gt_from_md class since we emit base64 encoding?

@rich-iannone
Copy link
Member

Thanks for the clear information! Is there any way we could avoid that Quarto close that span? Maybe we don't need to emit the gt_from_md class since we emit base64 encoding?

We could certainly try that! Would you be okay with me reviewing the PR as it is? I figure we could keep the scope smaller if we try to solve with a new PR.

@cscheid
Copy link
Member

cscheid commented Aug 21, 2024

I suspect that's not possible without adding some kind of random value to the IDs, e.g. making the attribute id="cyl-dsklfj34", and keeping track of the IDs used in the table headers.

I would just like to suggest to avoid random values. You could have a globally unique stateful counter instead, and that would make consecutive document renders consistent. (Random values would also make snapshot testing super hard...)

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 21, 2024

Would you be okay with me reviewing the PR as it is?

Yes! I think this PR is fine in any case!

  • Basically, aims to remove empty ids (they are not useful?
  • Aim to take id from table variable name instead of label
  • Reduce number of html table problems

A future PR will be easier to do given #1844 is merged. You will be able to quickly visualize the diffs inside RStudio with snapshot_review(). I'd love to see your solution for this to close the issues. I think I still lack some understanding of how things interact toggether.

Agreed that random ids kind of defeat the original purpose. The idea was for accessibility when they were implemented in #988. I think my PR stays with this purpose.

@cscheid
Copy link
Member

cscheid commented Aug 21, 2024

This can be traced to the <span> tag of the spanner label.

Could we have a look at the unparsed output that gt generates? Pasting the keep-md: true output from the test document in this PR would suffice. It could easily be a Quarto bug.

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 21, 2024

Hi @cscheid, with this PR,

gt emits the following html code outside Quarto

mtcars[1:3, 1:4] |> 
  gt::gt() |> 
  gt::tab_spanner(label = gt::md("Header"), columns = 2:3)
Regular gt output

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<style>body{background-color:white;}</style>


</head>
<body>
<div id="fzpaegbbhx" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
  <style>#fzpaegbbhx table {
  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#fzpaegbbhx thead, #fzpaegbbhx tbody, #fzpaegbbhx tfoot, #fzpaegbbhx tr, #fzpaegbbhx td, #fzpaegbbhx th {
  border-style: none;
}

#fzpaegbbhx p {
  margin: 0;
  padding: 0;
}

#fzpaegbbhx .gt_table {
  display: table;
  border-collapse: collapse;
  line-height: normal;
  margin-left: auto;
  margin-right: auto;
  color: #333333;
  font-size: 16px;
  font-weight: normal;
  font-style: normal;
  background-color: #FFFFFF;
  width: auto;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #A8A8A8;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #A8A8A8;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
}

#fzpaegbbhx .gt_caption {
  padding-top: 4px;
  padding-bottom: 4px;
}

#fzpaegbbhx .gt_title {
  color: #333333;
  font-size: 125%;
  font-weight: initial;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-color: #FFFFFF;
  border-bottom-width: 0;
}

#fzpaegbbhx .gt_subtitle {
  color: #333333;
  font-size: 85%;
  font-weight: initial;
  padding-top: 3px;
  padding-bottom: 5px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-color: #FFFFFF;
  border-top-width: 0;
}

#fzpaegbbhx .gt_heading {
  background-color: #FFFFFF;
  text-align: center;
  border-bottom-color: #FFFFFF;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#fzpaegbbhx .gt_bottom_border {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#fzpaegbbhx .gt_col_headings {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#fzpaegbbhx .gt_col_heading {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 6px;
  padding-left: 5px;
  padding-right: 5px;
  overflow-x: hidden;
}

#fzpaegbbhx .gt_column_spanner_outer {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 4px;
  padding-right: 4px;
}

#fzpaegbbhx .gt_column_spanner_outer:first-child {
  padding-left: 0;
}

#fzpaegbbhx .gt_column_spanner_outer:last-child {
  padding-right: 0;
}

#fzpaegbbhx .gt_column_spanner {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 5px;
  overflow-x: hidden;
  display: inline-block;
  width: 100%;
}

#fzpaegbbhx .gt_spanner_row {
  border-bottom-style: hidden;
}

#fzpaegbbhx .gt_group_heading {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  text-align: left;
}

#fzpaegbbhx .gt_empty_group_heading {
  padding: 0.5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: middle;
}

#fzpaegbbhx .gt_from_md > :first-child {
  margin-top: 0;
}

#fzpaegbbhx .gt_from_md > :last-child {
  margin-bottom: 0;
}

#fzpaegbbhx .gt_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  margin: 10px;
  border-top-style: solid;
  border-top-width: 1px;
  border-top-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  overflow-x: hidden;
}

#fzpaegbbhx .gt_stub {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
}

#fzpaegbbhx .gt_stub_row_group {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
  vertical-align: top;
}

#fzpaegbbhx .gt_row_group_first td {
  border-top-width: 2px;
}

#fzpaegbbhx .gt_row_group_first th {
  border-top-width: 2px;
}

#fzpaegbbhx .gt_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#fzpaegbbhx .gt_first_summary_row {
  border-top-style: solid;
  border-top-color: #D3D3D3;
}

#fzpaegbbhx .gt_first_summary_row.thick {
  border-top-width: 2px;
}

#fzpaegbbhx .gt_last_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#fzpaegbbhx .gt_grand_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#fzpaegbbhx .gt_first_grand_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-style: double;
  border-top-width: 6px;
  border-top-color: #D3D3D3;
}

#fzpaegbbhx .gt_last_grand_summary_row_top {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: double;
  border-bottom-width: 6px;
  border-bottom-color: #D3D3D3;
}

#fzpaegbbhx .gt_striped {
  background-color: rgba(128, 128, 128, 0.05);
}

#fzpaegbbhx .gt_table_body {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#fzpaegbbhx .gt_footnotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#fzpaegbbhx .gt_footnote {
  margin: 0px;
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#fzpaegbbhx .gt_sourcenotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#fzpaegbbhx .gt_sourcenote {
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#fzpaegbbhx .gt_left {
  text-align: left;
}

#fzpaegbbhx .gt_center {
  text-align: center;
}

#fzpaegbbhx .gt_right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

#fzpaegbbhx .gt_font_normal {
  font-weight: normal;
}

#fzpaegbbhx .gt_font_bold {
  font-weight: bold;
}

#fzpaegbbhx .gt_font_italic {
  font-style: italic;
}

#fzpaegbbhx .gt_super {
  font-size: 65%;
}

#fzpaegbbhx .gt_footnote_marks {
  font-size: 75%;
  vertical-align: 0.4em;
  position: initial;
}

#fzpaegbbhx .gt_asterisk {
  font-size: 100%;
  vertical-align: 0;
}

#fzpaegbbhx .gt_indent_1 {
  text-indent: 5px;
}

#fzpaegbbhx .gt_indent_2 {
  text-indent: 10px;
}

#fzpaegbbhx .gt_indent_3 {
  text-indent: 15px;
}

#fzpaegbbhx .gt_indent_4 {
  text-indent: 20px;
}

#fzpaegbbhx .gt_indent_5 {
  text-indent: 25px;
}

#fzpaegbbhx .katex-display {
  display: inline-flex !important;
  margin-bottom: 0.75em !important;
}

#fzpaegbbhx div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
  height: 0px !important;
}
</style>
  <table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
  <thead>
    <tr class="gt_col_headings gt_spanner_row">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="mpg">mpg</th>
      <th class="gt_center gt_columns_top_border gt_column_spanner_outer" rowspan="1" colspan="2" scope="colgroup" id="Header">
        <span class="gt_column_spanner"><span class='gt_from_md'>Header</span></span>
      </th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="hp">hp</th>
    </tr>
    <tr class="gt_col_headings">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="cyl">cyl</th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="disp">disp</th>
    </tr>
  </thead>
  <tbody class="gt_table_body">
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">22.8</td>
<td headers="cyl" class="gt_row gt_right">4</td>
<td headers="disp" class="gt_row gt_right">108</td>
<td headers="hp" class="gt_row gt_right">93</td></tr>
  </tbody>
  
  
</table>
</div>
</body>
</html>

It emits the following code if inside quarto. (I used local_mocked_bindings(check_quarto() = TRUE)

Mocking what gt sends to Quarto

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<style>body{background-color:white;}</style>


</head>
<body>
<div id="kmxbfyyrfv" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
  <style>#kmxbfyyrfv table {
  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#kmxbfyyrfv thead, #kmxbfyyrfv tbody, #kmxbfyyrfv tfoot, #kmxbfyyrfv tr, #kmxbfyyrfv td, #kmxbfyyrfv th {
  border-style: none;
}

#kmxbfyyrfv p {
  margin: 0;
  padding: 0;
}

#kmxbfyyrfv .gt_table {
  display: table;
  border-collapse: collapse;
  line-height: normal;
  margin-left: auto;
  margin-right: auto;
  color: #333333;
  font-size: 16px;
  font-weight: normal;
  font-style: normal;
  background-color: #FFFFFF;
  width: auto;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #A8A8A8;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #A8A8A8;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
}

#kmxbfyyrfv .gt_caption {
  padding-top: 4px;
  padding-bottom: 4px;
}

#kmxbfyyrfv .gt_title {
  color: #333333;
  font-size: 125%;
  font-weight: initial;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-color: #FFFFFF;
  border-bottom-width: 0;
}

#kmxbfyyrfv .gt_subtitle {
  color: #333333;
  font-size: 85%;
  font-weight: initial;
  padding-top: 3px;
  padding-bottom: 5px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-color: #FFFFFF;
  border-top-width: 0;
}

#kmxbfyyrfv .gt_heading {
  background-color: #FFFFFF;
  text-align: center;
  border-bottom-color: #FFFFFF;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#kmxbfyyrfv .gt_bottom_border {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#kmxbfyyrfv .gt_col_headings {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#kmxbfyyrfv .gt_col_heading {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 6px;
  padding-left: 5px;
  padding-right: 5px;
  overflow-x: hidden;
}

#kmxbfyyrfv .gt_column_spanner_outer {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 4px;
  padding-right: 4px;
}

#kmxbfyyrfv .gt_column_spanner_outer:first-child {
  padding-left: 0;
}

#kmxbfyyrfv .gt_column_spanner_outer:last-child {
  padding-right: 0;
}

#kmxbfyyrfv .gt_column_spanner {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 5px;
  overflow-x: hidden;
  display: inline-block;
  width: 100%;
}

#kmxbfyyrfv .gt_spanner_row {
  border-bottom-style: hidden;
}

#kmxbfyyrfv .gt_group_heading {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  text-align: left;
}

#kmxbfyyrfv .gt_empty_group_heading {
  padding: 0.5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: middle;
}

#kmxbfyyrfv .gt_from_md > :first-child {
  margin-top: 0;
}

#kmxbfyyrfv .gt_from_md > :last-child {
  margin-bottom: 0;
}

#kmxbfyyrfv .gt_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  margin: 10px;
  border-top-style: solid;
  border-top-width: 1px;
  border-top-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  overflow-x: hidden;
}

#kmxbfyyrfv .gt_stub {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
}

#kmxbfyyrfv .gt_stub_row_group {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
  vertical-align: top;
}

#kmxbfyyrfv .gt_row_group_first td {
  border-top-width: 2px;
}

#kmxbfyyrfv .gt_row_group_first th {
  border-top-width: 2px;
}

#kmxbfyyrfv .gt_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#kmxbfyyrfv .gt_first_summary_row {
  border-top-style: solid;
  border-top-color: #D3D3D3;
}

#kmxbfyyrfv .gt_first_summary_row.thick {
  border-top-width: 2px;
}

#kmxbfyyrfv .gt_last_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#kmxbfyyrfv .gt_grand_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#kmxbfyyrfv .gt_first_grand_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-style: double;
  border-top-width: 6px;
  border-top-color: #D3D3D3;
}

#kmxbfyyrfv .gt_last_grand_summary_row_top {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: double;
  border-bottom-width: 6px;
  border-bottom-color: #D3D3D3;
}

#kmxbfyyrfv .gt_striped {
  background-color: rgba(128, 128, 128, 0.05);
}

#kmxbfyyrfv .gt_table_body {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#kmxbfyyrfv .gt_footnotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#kmxbfyyrfv .gt_footnote {
  margin: 0px;
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#kmxbfyyrfv .gt_sourcenotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#kmxbfyyrfv .gt_sourcenote {
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#kmxbfyyrfv .gt_left {
  text-align: left;
}

#kmxbfyyrfv .gt_center {
  text-align: center;
}

#kmxbfyyrfv .gt_right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

#kmxbfyyrfv .gt_font_normal {
  font-weight: normal;
}

#kmxbfyyrfv .gt_font_bold {
  font-weight: bold;
}

#kmxbfyyrfv .gt_font_italic {
  font-style: italic;
}

#kmxbfyyrfv .gt_super {
  font-size: 65%;
}

#kmxbfyyrfv .gt_footnote_marks {
  font-size: 75%;
  vertical-align: 0.4em;
  position: initial;
}

#kmxbfyyrfv .gt_asterisk {
  font-size: 100%;
  vertical-align: 0;
}

#kmxbfyyrfv .gt_indent_1 {
  text-indent: 5px;
}

#kmxbfyyrfv .gt_indent_2 {
  text-indent: 10px;
}

#kmxbfyyrfv .gt_indent_3 {
  text-indent: 15px;
}

#kmxbfyyrfv .gt_indent_4 {
  text-indent: 20px;
}

#kmxbfyyrfv .gt_indent_5 {
  text-indent: 25px;
}

#kmxbfyyrfv .katex-display {
  display: inline-flex !important;
  margin-bottom: 0.75em !important;
}

#kmxbfyyrfv div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
  height: 0px !important;
}
</style>
  <table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
  <thead>
    <tr class="gt_col_headings gt_spanner_row">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="mpg">mpg</th>
      <th class="gt_center gt_columns_top_border gt_column_spanner_outer" rowspan="1" colspan="2" scope="colgroup" id="Header">
        <span class="gt_column_spanner"><div data-qmd-base64="SGVhZGVy"><div class='gt_from_md'><p>Header</p>
</div></div></span>
      </th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="hp">hp</th>
    </tr>
    <tr class="gt_col_headings">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="cyl">cyl</th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="disp">disp</th>
    </tr>
  </thead>
  <tbody class="gt_table_body">
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">22.8</td>
<td headers="cyl" class="gt_row gt_right">4</td>
<td headers="disp" class="gt_row gt_right">108</td>
<td headers="hp" class="gt_row gt_right">93</td></tr>
  </tbody>
  
  
</table>
</div>
</body>
</html>

And the final document, it looks like this.

Inside Quarto after processing

---
title: Hello
format: html
keep-md: true
lang: en
---





https://github.com/rstudio/gt/pull/988



::: {.cell}
::: {.cell-output-display}


```{=html}
<div id="weocbcfwpn" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
<style>#weocbcfwpn table {
  font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#weocbcfwpn thead, #weocbcfwpn tbody, #weocbcfwpn tfoot, #weocbcfwpn tr, #weocbcfwpn td, #weocbcfwpn th {
  border-style: none;
}

#weocbcfwpn p {
  margin: 0;
  padding: 0;
}

#weocbcfwpn .gt_table {
  display: table;
  border-collapse: collapse;
  line-height: normal;
  margin-left: auto;
  margin-right: auto;
  color: #333333;
  font-size: 16px;
  font-weight: normal;
  font-style: normal;
  background-color: #FFFFFF;
  width: auto;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #A8A8A8;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #A8A8A8;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
}

#weocbcfwpn .gt_caption {
  padding-top: 4px;
  padding-bottom: 4px;
}

#weocbcfwpn .gt_title {
  color: #333333;
  font-size: 125%;
  font-weight: initial;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-color: #FFFFFF;
  border-bottom-width: 0;
}

#weocbcfwpn .gt_subtitle {
  color: #333333;
  font-size: 85%;
  font-weight: initial;
  padding-top: 3px;
  padding-bottom: 5px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-color: #FFFFFF;
  border-top-width: 0;
}

#weocbcfwpn .gt_heading {
  background-color: #FFFFFF;
  text-align: center;
  border-bottom-color: #FFFFFF;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#weocbcfwpn .gt_bottom_border {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#weocbcfwpn .gt_col_headings {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
}

#weocbcfwpn .gt_col_heading {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 6px;
  padding-left: 5px;
  padding-right: 5px;
  overflow-x: hidden;
}

#weocbcfwpn .gt_column_spanner_outer {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: normal;
  text-transform: inherit;
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 4px;
  padding-right: 4px;
}

#weocbcfwpn .gt_column_spanner_outer:first-child {
  padding-left: 0;
}

#weocbcfwpn .gt_column_spanner_outer:last-child {
  padding-right: 0;
}

#weocbcfwpn .gt_column_spanner {
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: bottom;
  padding-top: 5px;
  padding-bottom: 5px;
  overflow-x: hidden;
  display: inline-block;
  width: 100%;
}

#weocbcfwpn .gt_spanner_row {
  border-bottom-style: hidden;
}

#weocbcfwpn .gt_group_heading {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  text-align: left;
}

#weocbcfwpn .gt_empty_group_heading {
  padding: 0.5px;
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  vertical-align: middle;
}

#weocbcfwpn .gt_from_md > :first-child {
  margin-top: 0;
}

#weocbcfwpn .gt_from_md > :last-child {
  margin-bottom: 0;
}

#weocbcfwpn .gt_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  margin: 10px;
  border-top-style: solid;
  border-top-width: 1px;
  border-top-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 1px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 1px;
  border-right-color: #D3D3D3;
  vertical-align: middle;
  overflow-x: hidden;
}

#weocbcfwpn .gt_stub {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
}

#weocbcfwpn .gt_stub_row_group {
  color: #333333;
  background-color: #FFFFFF;
  font-size: 100%;
  font-weight: initial;
  text-transform: inherit;
  border-right-style: solid;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
  padding-left: 5px;
  padding-right: 5px;
  vertical-align: top;
}

#weocbcfwpn .gt_row_group_first td {
  border-top-width: 2px;
}

#weocbcfwpn .gt_row_group_first th {
  border-top-width: 2px;
}

#weocbcfwpn .gt_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#weocbcfwpn .gt_first_summary_row {
  border-top-style: solid;
  border-top-color: #D3D3D3;
}

#weocbcfwpn .gt_first_summary_row.thick {
  border-top-width: 2px;
}

#weocbcfwpn .gt_last_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#weocbcfwpn .gt_grand_summary_row {
  color: #333333;
  background-color: #FFFFFF;
  text-transform: inherit;
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
}

#weocbcfwpn .gt_first_grand_summary_row {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-top-style: double;
  border-top-width: 6px;
  border-top-color: #D3D3D3;
}

#weocbcfwpn .gt_last_grand_summary_row_top {
  padding-top: 8px;
  padding-bottom: 8px;
  padding-left: 5px;
  padding-right: 5px;
  border-bottom-style: double;
  border-bottom-width: 6px;
  border-bottom-color: #D3D3D3;
}

#weocbcfwpn .gt_striped {
  background-color: rgba(128, 128, 128, 0.05);
}

#weocbcfwpn .gt_table_body {
  border-top-style: solid;
  border-top-width: 2px;
  border-top-color: #D3D3D3;
  border-bottom-style: solid;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
}

#weocbcfwpn .gt_footnotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#weocbcfwpn .gt_footnote {
  margin: 0px;
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#weocbcfwpn .gt_sourcenotes {
  color: #333333;
  background-color: #FFFFFF;
  border-bottom-style: none;
  border-bottom-width: 2px;
  border-bottom-color: #D3D3D3;
  border-left-style: none;
  border-left-width: 2px;
  border-left-color: #D3D3D3;
  border-right-style: none;
  border-right-width: 2px;
  border-right-color: #D3D3D3;
}

#weocbcfwpn .gt_sourcenote {
  font-size: 90%;
  padding-top: 4px;
  padding-bottom: 4px;
  padding-left: 5px;
  padding-right: 5px;
}

#weocbcfwpn .gt_left {
  text-align: left;
}

#weocbcfwpn .gt_center {
  text-align: center;
}

#weocbcfwpn .gt_right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

#weocbcfwpn .gt_font_normal {
  font-weight: normal;
}

#weocbcfwpn .gt_font_bold {
  font-weight: bold;
}

#weocbcfwpn .gt_font_italic {
  font-style: italic;
}

#weocbcfwpn .gt_super {
  font-size: 65%;
}

#weocbcfwpn .gt_footnote_marks {
  font-size: 75%;
  vertical-align: 0.4em;
  position: initial;
}

#weocbcfwpn .gt_asterisk {
  font-size: 100%;
  vertical-align: 0;
}

#weocbcfwpn .gt_indent_1 {
  text-indent: 5px;
}

#weocbcfwpn .gt_indent_2 {
  text-indent: 10px;
}

#weocbcfwpn .gt_indent_3 {
  text-indent: 15px;
}

#weocbcfwpn .gt_indent_4 {
  text-indent: 20px;
}

#weocbcfwpn .gt_indent_5 {
  text-indent: 25px;
}

#weocbcfwpn .katex-display {
  display: inline-flex !important;
  margin-bottom: 0.75em !important;
}

#weocbcfwpn div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
  height: 0px !important;
}
</style>
<table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
  <thead>
    <tr class="gt_col_headings gt_spanner_row">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="mpg">mpg</th>
      <th class="gt_center gt_columns_top_border gt_column_spanner_outer" rowspan="1" colspan="2" scope="colgroup" id="Header">
        <span class="gt_column_spanner"><div data-qmd-base64="SGVhZGVy"><div class='gt_from_md'><p>Header</p>
</div></div></span>
      </th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="2" colspan="1" scope="col" id="hp">hp</th>
    </tr>
    <tr class="gt_col_headings">
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="cyl">cyl</th>
      <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="disp">disp</th>
    </tr>
  </thead>
  <tbody class="gt_table_body">
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">21.0</td>
<td headers="cyl" class="gt_row gt_right">6</td>
<td headers="disp" class="gt_row gt_right">160</td>
<td headers="hp" class="gt_row gt_right">110</td></tr>
    <tr><td headers="mpg" class="gt_row gt_right">22.8</td>
<td headers="cyl" class="gt_row gt_right">4</td>
<td headers="disp" class="gt_row gt_right">108</td>
<td headers="hp" class="gt_row gt_right">93</td></tr>
  </tbody>
  
  
</table>
</div>
```


:::
:::

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 21, 2024

I noticed in quarto-dev/quarto-cli#3340 (comment) that somehow Quarto processes this

In my testing, if <div class='gt_from_md'><p>...</p></div> is replaced with <span data-qmd="..."></span>, then quarto (after this bugfix) actually renders this crossref correctly.

@cscheid
Copy link
Member

cscheid commented Aug 21, 2024

This is the problem:

<span class="gt_column_spanner"><div data-qmd-base64="SGVhZGVy"><div class='gt_from_md'><p>Header</p>
</div></div></span>

A div is a block-level element, and can't exist inside a span.

image

@cscheid
Copy link
Member

cscheid commented Aug 21, 2024

(I'll reiterate our request that the HTML is valid, otherwise Quarto doesn't have a chance to do the right thing.)

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 21, 2024

Oh! Thanks

So, if we replace this div code by span, it should work? Excited to try it after this PR is merged!

gt/R/utils.R

Lines 760 to 766 in 02fef72

non_na_text <- paste0("<div data-qmd-base64=\"", non_na_text, "\">")
non_na_text <-
paste0(
non_na_text, "<div class='gt_from_md'>",
non_na_text_processed, "</div></div>"
)

@olivroy olivroy changed the title Try to make gt tables more valid in html Modify html element ids to use name rather than labels for html validity Aug 21, 2024
@rich-iannone
Copy link
Member

rich-iannone commented Aug 21, 2024

I think we should go the opposite way. Replace the outer <span> with a <div>. Some testing will need to be done but changing to htmltools::tags$div(...) in the level_1_spanners[[length(level_1_spanners) + 1]] <- assignments in utils_render_html.R resulted in the table example working properly on my machine.

htmltools::tags$span(

htmltools::tags$span(

@olivroy
Copy link
Collaborator Author

olivroy commented Aug 22, 2024

@rich-iannone
Nakes sense! Please merge this! with this PR + your proposed fix + the idea I came up with in #1854, I think we may be able to actually fix this

@olivroy olivroy mentioned this pull request Aug 22, 2024
3 tasks
@rich-iannone rich-iannone merged commit a52eb41 into rstudio:master Aug 22, 2024
11 checks passed
olivroy added a commit to olivroy/gt that referenced this pull request Aug 22, 2024
olivroy added a commit to olivroy/gt that referenced this pull request Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants