Search code examples
rgt

Customize border colors dynamically in gt table


I have a gt table that requires borders in several columns. I'm easily able to change the color of a targeted row and/or column, as you can see in this example with changing the border of "fig" to purple.

library(dplyr)
library(gt)

exibble %>%
  gt() %>%
  cols_align(align = "center",
             columns = c("char")) %>%
  tab_style(
    style = list(
      cell_borders(
        sides = c("all"),
        color = c("purple"),
        weight = px(3)
      )
    ),
    locations = list(
      cells_body(
        columns = "char",
        rows = char == "fig"
      )
    )
  )

Targeted fig color

However, when I try to change multiple colors at once from another column containing colors, I get an error:

exibble %>%
  mutate(col = case_when(char == "fig" ~ "purple",
                         char == "banana" ~ "yellow",
                         TRUE ~ "white")) %>%
  gt() %>%
  cols_align(align = "center",
             columns = c("char")) %>%
  tab_style(
    style = list(
      cell_borders(
        sides = c("all"),
        color = from_column(col),
        weight = px(3)
      )
    ),
    locations = list(
      cells_body(
        columns = "char",
        rows = !is.na(col)
      )
    )
  )

Error in `cell_borders()`:
! `color` must have a length one, not 3.

How do I make the entire column reflect the intended border colors (white, purple, or yellow) without having to one by one target those cell values?


Solution

  • As cell_borders does not accept vectorized color input your best bet is a loop. purrr::reduce is quite handy for such cases:

    dat <- exibble %>%
      mutate(col = case_when(char == "fig" ~ "purple",
                             char == "banana" ~ "yellow",
                             TRUE ~ "white"))
    tt <- dat %>%
      gt() %>%
      cols_align(align = "center",
                 columns = c("char"))
    
    ## define color table with the row indices for targetting the right row
    col_dat <- dat %>%
      mutate(rn = 1:n(), col, char, .keep = "none") %>%
      filter(!is.na(char))
    
    ## use reduce to apply a border color to each row separately
    
    purrr::reduce(1:nrow(col_dat), function(tab, idx) {
      sl <- col_dat %>% slice(idx)
      tab %>%
        tab_style(
          style = list(
            cell_borders(
              sides = "all",
              color = sl %>% pull(col),
              weight = px(3)
            )
          ),
          locations = list(
            cells_body(
              columns = "char",
              rows = sl %>% pull(rn)
            )
          )
        )
    }, .init = tt)
    

    A table with 10 columns and 8 rows where column "char" features a border in each cell whose color is determined by the color goven in column "col"