rquartogt

How to set the gt table so that it responds to theme changes in a Quarto website?


In Quarto, it is possible to set a switch for display, which shows a switch button to control the theme of the website:

_quarto.yml:

project:
  type: website

format:
  html:
    theme: 
      light: flatly
      dark: darkly

I'd like to include a gt table that changes its theme according to the Quarto settings. Given the default example from gt:

Example page.qmd:

---
title: "Example page"
format: html
---

```{r}
library(gt)

# Define the start and end dates for the data range
start_date <- "2010-06-07"
end_date <- "2010-06-14"

# Create a gt table based on preprocessed
# `sp500` table data
sp500 |>
  dplyr::filter(date >= start_date & date <= end_date) |>
  dplyr::select(-adj_close) |>
  gt() |>
  tab_header(
    title = "S&P 500",
    subtitle = glue::glue("{start_date} to {end_date}")
  ) |>
  fmt_currency() |>
  fmt_date(columns = date, date_style = "wd_m_day_year") |>
  fmt_number(columns = volume, suffixing = TRUE) 

```

When the theme is flatly, it looks fine:

enter image description here

But when the theme switches to darkly, only the code responds to dark mode, not the table:

enter image description here

I am just wondering how to make the table react to the Quarto theme?


Solution

  • Change the background-color of the table to var(--bs-table-bg) and set color to inherit:

    enter image description here

    This is implemented below by modifying the table's css.

    However, for better readability and in order to keep the striped layout, it is maybe beneficial to make further adjustments: I implemented an Event listener which toggles the Bootstrap classes table-light and table-dark on the gt table depending on the dark mode state.

    enter image description here

    ---
    title: "Example page"
    format:
      html:
        theme: 
          light: flatly
          dark: darkly
    include-before-body:
      text: |
        <script>
          document.addEventListener("DOMContentLoaded", function() {
            const attrObserver = new MutationObserver((mutations) => {
              mutations.forEach(mu => {
                if (mu.type !== "attributes" && mu.attributeName !== "class") return;
                var gt_tables = document.getElementsByClassName("gt_table");
                var table_array = [...gt_tables]; 
                table_array.forEach(tbl => { 
                  if (document.body.classList.contains("quarto-light")) {
                    tbl.classList.remove("table-dark");
                    tbl.classList.add("table-light");
                  } else {
                    tbl.classList.remove("table-light");
                    tbl.classList.add("table-dark");            
                  }
                });
              });
            });
            let targetNode = document.querySelectorAll("body");
            targetNode.forEach(el => attrObserver.observe(el, {attributes: true}));
          });
        </script>
    ---
    
    ```{r echo = FALSE}
    library(gt)
    
    modify_css <- function(x) {
      x <- gsub(
        "background-color: #FFFFFF;", 
        "background-color: var(--bs-table-bg);", 
        x)
      x <- gsub(
        "color: #333333;", 
        "color: inherit;", 
        x)   
      htmltools::HTML(x)
    }
    
    # Define the start and end dates for the data range
    start_date <- "2010-06-07"
    end_date <- "2010-06-14"
    
    # Create a gt table based on preprocessed
    # `sp500` table data
    sp500 |>
      dplyr::filter(date >= start_date & date <= end_date) |>
      dplyr::select(-adj_close) |>
      gt() |>
      tab_header(
        title = "S&P 500",
        subtitle = glue::glue("{start_date} to {end_date}")
      ) |>
      fmt_currency() |>
      fmt_date(columns = date, date_style = "wd_m_day_year") |>
      fmt_number(columns = volume, suffixing = TRUE) |> 
      as_raw_html() |>
      modify_css()
    ```