---
title: "Table Styling and Formatting"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Table Styling and Formatting}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

options(gtsummary.print_engine = "gt")
```

```{r setup}
#| eval: false
library(sumExtras)
library(gtsummary)
library(dplyr)
library(gt)

use_jama_theme()
```

```{r setup2}
#| echo: false
#| message: false
#| warning: false
library(sumExtras)
library(gtsummary)
library(dplyr)
library(gt)

use_jama_theme()
```

## Group Headers

`gtsummary::add_variable_group_header()` creates section headers in your table. `{sumExtras}` provides functions to style them.

```{r}
trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  )
```

The headers are there, but they don't stand out. That's where `add_group_styling()` comes in.

## `add_group_styling()`

Adds bold and/or italic formatting to group headers. Also restores left-justified variable label indentation that `add_variable_group_header()` changes.

:::::: {style="display: flex; gap: 15px; margin-bottom: 20px; align-items: flex-start;"}

::: {style="flex: 1; max-width: 48%;"}

#### Without styling

```{r without-styling, eval=FALSE}
trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  )
```

:::

::: {style="flex: 1; max-width: 48%;"}

#### With add_group_styling()

```{r with-styling, eval=FALSE}
trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  ) |>
  add_group_styling()
```

:::

::::::

```{r build-styling, echo=FALSE}
table_without <- trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  )

table_with <- trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Variables",
    variables = age:stage
  ) |>
  add_group_styling()
```

:::::: {style="display: flex; gap: 15px; margin-bottom: 30px; align-items: flex-start;"}

::: {style="flex: 1; max-width: 48%;"}

```{r render-without, echo=FALSE}
table_without
```

:::

::: {style="flex: 1; max-width: 48%;"}

```{r render-with, echo=FALSE}
table_with
```

:::

::::::

The `format` argument controls the text style:

```{r}
# Bold only
trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Patient Characteristics",
    variables = age:stage
  ) |>
  add_group_styling(format = "bold")
```

Options are `"bold"`, `"italic"`, or `c("bold", "italic")` (the default).

## `add_group_colors()`

Adds a background color to group header rows. This is a terminal operation in that it converts the table to `{gt}`. It must be the **last step** in your pipeline.

```{r}
trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  ) |>
  add_group_styling() |>
  add_group_colors(color = "#E3F2FD")
```

The default color is `"#E8E8E8"` (light gray). Pass any CSS color string, or a vector of colors (one per group):

```{r}
trial |>
  select(age, marker, grade, stage, response, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age
  ) |>
  add_variable_group_header(
    header = "Clinical Measures",
    variables = marker:response
  ) |>
  add_group_styling() |>
  add_group_colors(color = c("#E3F2FD", "#FFF9E6"))
```

## `get_group_rows()`

If you need more control than `add_group_colors()` provides, `get_group_rows()` returns the row indices of group headers. You can then use those with `gt::tab_style()` directly:

```{r}
my_table <- trial |>
  select(age, marker, grade, stage, trt) |>
  tbl_summary(by = trt) |>
  extras() |>
  add_variable_group_header(
    header = "Demographics",
    variables = age:marker
  ) |>
  add_variable_group_header(
    header = "Disease",
    variables = grade:stage
  ) |>
  add_group_styling()

group_rows <- get_group_rows(my_table)

my_table |>
  as_gt() |>
  gt::tab_style(
    style = list(
      gt::cell_fill(color = "#E8E8E8"),
      gt::cell_text(weight = "bold")
    ),
    locations = gt::cells_body(rows = group_rows)
  )
```

## Matching gt Tables with `theme_gt_compact()`

If you mix `{gtsummary}` tables with plain `{gt}` tables in the same document, they won't match visually. `theme_gt_compact()` applies the same JAMA compact look to `{gt}` tables so everything is consistent:

:::::: {style="display: flex; gap: 15px; margin-bottom: 20px; align-items: flex-start;"}

::: {style="flex: 1; max-width: 48%;"}

#### gtsummary with extras()

```{r match-gtsummary, eval=FALSE}
trial |>
  tbl_summary(
    by = trt,
    include = c(age, grade, marker)
  ) |>
  extras()
```

:::

::: {style="flex: 1; max-width: 48%;"}

#### gt with theme_gt_compact()

```{r match-gt, eval=FALSE}
trial |>
  select(trt, age, grade, marker) |>
  head(10) |>
  gt() |>
  theme_gt_compact()
```

:::

::::::

```{r build-match, echo=FALSE}
table_gtsummary <- trial |>
  tbl_summary(
    by = trt,
    include = c(age, grade, marker)
  ) |>
  extras()

table_gt <- trial |>
  select(trt, age, grade, marker) |>
  head(10) |>
  gt() |>
  theme_gt_compact()
```

:::::: {style="display: flex; gap: 15px; margin-bottom: 30px; align-items: flex-start;"}

::: {style="flex: 1; max-width: 48%;"}

```{r render-match-gtsummary, echo=FALSE}
table_gtsummary
```

:::

::: {style="flex: 1; max-width: 48%;"}

```{r render-match-gt, echo=FALSE}
table_gt
```

:::

::::::

See `vignette("themes")` for more on theming.

## Complete Example

```{r}
dictionary <- tibble::tribble(
  ~variable,    ~description,
  "trt",        "Treatment Assignment",
  "age",        "Age at Baseline (years)",
  "marker",     "Biomarker Level (ng/mL)",
  "stage",      "Clinical Stage",
  "grade",      "Tumor Grade",
  "response",   "Treatment Response",
  "death",      "Patient Died"
)

trial |>
  select(trt, age, marker, grade, stage, response, death) |>
  tbl_summary(by = trt, missing = "no") |>
  add_auto_labels(dictionary = dictionary) |>
  extras() |>
  add_variable_group_header(
    header = "BASELINE CHARACTERISTICS",
    variables = age:marker
  ) |>
  add_variable_group_header(
    header = "DISEASE CHARACTERISTICS",
    variables = grade:stage
  ) |>
  add_variable_group_header(
    header = "OUTCOMES",
    variables = response:death
  ) |>
  add_group_styling() |>
  add_group_colors(color = "#E8E8E8")
```

## More Vignettes

* `vignette("sumExtras-intro")` -- getting started with extras()
* `vignette("labeling")` -- dictionary-based labeling
* `vignette("themes")` -- JAMA compact themes for `{gtsummary}` and `{gt}` tables
* `vignette("options")` -- .Rprofile options for automatic labeling
