The tab_style() function

Let’s use the exibble dataset to create a simple, two-column gt table (keeping only the num and currency columns). With tab_style() (called twice), we’ll selectively add style to the values formatted by fmt_number(). In the style argument of each tab_style() call, we can define multiple types of styling with cell_fill() and cell_text() (enclosed in a list). The cells to be targeted for styling require the use of helpers like cells_body(), which is used here with different columns and rows being targeted.

exibble |>
  dplyr::select(num, currency) |>
  gt() |>
  fmt_number(decimals = 1) |>
  tab_style(
    style = list(
      cell_fill(color = "lightcyan"),
      cell_text(weight = "bold")
      ),
    locations = cells_body(
      columns = num,
      rows = num >= 5000
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#F9E3D6"),
      cell_text(style = "italic")
      ),
    locations = cells_body(
      columns = currency,
      rows = currency < 100
    )
  )
num currency
0.1 50.0
2.2 17.9
33.3 1.4
444.4 65,100.0
5,550.0 1,325.8
NA 13.3
777,000.0 NA
8,880,000.0 0.4

With a subset of the sp500 dataset, we’ll create a different gt table. Here, we’ll color the background of entire rows of body cells and do so on the basis of value expressions involving the open and close columns.

sp500 |>
  dplyr::filter(
    date >= "2015-12-01" &
    date <= "2015-12-15"
  ) |>
  dplyr::select(-c(adj_close, volume)) |>
  gt() |>
  tab_style(
    style = cell_fill(color = "lightgreen"),
    locations = cells_body(rows = close > open)
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "red"),
      cell_text(color = "white")
      ),
    locations = cells_body(rows = open > close)
  )
date open high low close
2015-12-15 2025.55 2053.87 2025.55 2043.41
2015-12-14 2013.37 2022.92 1993.26 2021.94
2015-12-11 2047.27 2047.27 2008.80 2012.37
2015-12-10 2047.93 2067.65 2045.67 2052.23
2015-12-09 2061.17 2080.33 2036.53 2047.62
2015-12-08 2073.39 2073.85 2052.32 2063.59
2015-12-07 2090.42 2090.42 2066.78 2077.07
2015-12-04 2051.24 2093.84 2051.24 2091.69
2015-12-03 2080.71 2085.00 2042.35 2049.62
2015-12-02 2101.71 2104.27 2077.11 2079.51
2015-12-01 2082.93 2103.37 2082.93 2102.63

With another two-column table based on the exibble dataset, let’s create a gt table. First, we’ll replace missing values with sub_missing(). Next, we’ll add styling to the char column. This styling will be HTML-specific and it will involve (all within a list): (1) a cell_fill() call (to set a "lightcyan" background), and (2) a string containing a CSS style declaration ("font-variant: small-caps;").

exibble |>
  dplyr::select(char, fctr) |>
  gt() |>
  sub_missing() |>
  tab_style(
    style = list(
      cell_fill(color = "lightcyan"),
      "font-variant: small-caps;"
    ),
    locations = cells_body(columns = char)
  )
char fctr
apricot one
banana two
coconut three
durian four
five
fig six
grapefruit seven
honeydew eight

In the following table based on the towny dataset, we’ll use a larger number of tab_style() calls with the aim of styling each location available in the table. Over six separate uses of tab_style(), different body cells are styled with background colors, the header and the footer also receive background color fills, borders are applied to a column of body cells and also to the column labels, and, the row labels in the stub receive a custom text treatment.

towny |>
  dplyr::filter(csd_type == "city") |>
  dplyr::select(
    name, land_area_km2, density_2016, density_2021,
    population_2016, population_2021
  ) |>
  dplyr::slice_max(population_2021, n = 5) |>
  gt(rowname_col = "name") |>
  tab_header(
    title = md(paste("Largest Five", fontawesome::fa("city") , "in `towny`")),
    subtitle = "Changes in vital numbers from 2016 to 2021."
  ) |>
  fmt_number(
    columns = starts_with("population"),
    n_sigfig = 3,
    suffixing = TRUE
  ) |>
  fmt_integer(columns = starts_with("density")) |>
  fmt_number(columns = land_area_km2, decimals = 1) |>
  cols_merge(
    columns = starts_with("density"),
    pattern = paste("{1}", fontawesome::fa("arrow-right"), "{2}")
  ) |>
  cols_merge(
    columns = starts_with("population"),
    pattern = paste("{1}", fontawesome::fa("arrow-right"), "{2}")
  ) |>
  cols_label(
    land_area_km2 = md("Area, km^2^"),
    starts_with("density") ~ md("Density, ppl/km^2^"),
    starts_with("population") ~ "Population"
  ) |>
  cols_align(align = "center", columns = -name) |>
  cols_width(
    stub() ~ px(125),
    everything() ~ px(150)
  ) |>
  tab_footnote(
    footnote = "Data was used from their respective census-year publications.",
    locations = cells_title(groups = "subtitle")
  ) |>
  tab_source_note(source_note = md(
    "All figures are compiled in the `towny` dataset (in the **gt** package)."
  )) |>
  opt_footnote_marks(marks = "letters") |>
  tab_style(
    style = list(
      cell_fill(color = "gray95"),
      cell_borders(sides = c("l", "r"), color = "gray50", weight = px(3))
    ),
    locations = cells_body(columns = land_area_km2)
  ) |>
  tab_style(
    style = cell_fill(color = "lightblue" |> adjust_luminance(steps = 2)),
    locations = cells_body(columns = -land_area_km2)
  ) |>
  tab_style(
    style = list(cell_fill(color = "gray35"), cell_text(color = "white")),
    locations = list(cells_footnotes(), cells_source_notes())
  ) |>
  tab_style(
    style = cell_fill(color = "gray98"),
    locations = cells_title()
  ) |>
  tab_style(
    style = cell_text(
      size = "smaller",
      weight = "bold",
      transform = "uppercase"
    ),
    locations = cells_stub()
  ) |>
  tab_style(
    style = cell_borders(
      sides = c("t", "b"),
      color = "powderblue",
      weight = px(3)
    ),
    locations = list(cells_column_labels(), cells_stubhead())
  )
Largest Five in towny
Changes in vital numbers from 2016 to 2021.a
Area, km2 Density, ppl/km2 Population
Toronto 631.1 4,328 4,428 2.73M 2.79M
Ottawa 2,788.2 335 365 934K 1.02M
Mississauga 292.7 2,465 2,453 722K 718K
Brampton 265.9 2,233 2,469 594K 656K
Hamilton 1,118.3 480 509 537K 569K
All figures are compiled in the towny dataset (in the gt package).
a Data was used from their respective census-year publications.

from_column() can be used to get values from a column. We’ll use it in the next example, which begins with a table having a color name column and a column with the associated hexadecimal color code. To show the color in a separate column, we first create one with cols_add() (ensuring that missing values are replaced with "" via sub_missing()). Then, tab_style() is used to style that column, using color = from_column() within cell_fill().

dplyr::tibble(
  name = c(
    "red", "green", "blue", "yellow", "orange",
    "cyan", "purple", "magenta", "lime", "pink"
  ),
  hex = c(
    "#E6194B", "#3CB44B", "#4363D8", "#FFE119", "#F58231",
    "#42D4F4", "#911EB4", "#F032E6", "#BFEF45", "#FABED4"
  )
) |>
  gt(rowname_col = "name") |>
  cols_add(color = rep(NA_character_, 10)) |>
  sub_missing(missing_text = "") |>
  tab_style(
    style = cell_fill(color = from_column(column = "hex")),
    locations = cells_body(columns = color)
  ) |>
  tab_style(
    style = cell_text(font = system_fonts(name = "monospace-code")),
    locations = cells_body()
  ) |>
  opt_all_caps() |>
  cols_width(everything() ~ px(100)) |>
  tab_options(table_body.hlines.style = "none")
hex color
red #E6194B
green #3CB44B
blue #4363D8
yellow #FFE119
orange #F58231
cyan #42D4F4
purple #911EB4
magenta #F032E6
lime #BFEF45
pink #FABED4

cell_text() also allows the use of from_column() for many of its arguments. Let’s take a small portion of data from sp500 and add an up or down arrow based on the values in the open and close columns. Within cols_add() we can create a new column (dir) with an expression to get either "red" or "green" text from a comparison of the open and close values. These values are transformed to up or down arrows with text_case_match(), using fontawesome icons in the end. However, the text values are still present and can be used by cell_text() within tab_style(). from_column() makes it possible to use the text in the cells of the dir column as color input values.

sp500 |>
  dplyr::filter(date > "2015-01-01") |>
  dplyr::slice_min(date, n = 5) |>
  dplyr::select(date, open, close) |>
  gt(rowname_col = "date") |>
  fmt_currency(columns = c(open, close)) |>
  cols_add(dir = ifelse(close < open, "red", "forestgreen")) |>
  cols_label(dir = "") |>
  text_case_match(
    "red" ~ fontawesome::fa("arrow-down"),
    "forestgreen" ~ fontawesome::fa("arrow-up")
  ) |>
  tab_style(
    style = cell_text(color = from_column("dir")),
    locations = cells_body(columns = dir)
  )
open close
2015-01-02 $2,058.90 $2,058.20
2015-01-05 $2,054.44 $2,020.58
2015-01-06 $2,022.15 $2,002.61
2015-01-07 $2,005.55 $2,025.90
2015-01-08 $2,030.61 $2,062.14