# Styles

# Introduction

In this vignette, we explain how to customise the visualisation of tables and plots. The vignette reviews the structure of the `.yml` files that define styles, and demonstrates how to create and apply custom styles. It also shows how to style tables and plots programmatically, without the need to create a `.yml` file.

    library(visOmopResults)
    library(here)
    library(gt)
    library(ggplot2)
    library(dplyr)
    library(officer)

The package currently includes two built-in styles for tables and plots. Styles are defined using `.yml` files. To list the available styles, use:

    tableStyle()
    #> [1] "darwin"  "default"
    plotStyle()
    #> [1] "darwin"  "default"

# Branding styles using `.yml`

The package contains two built-in styles: `"default"` and `"darwin"`. The `.yml` files for these styles can be found here.

## `.yml` structure

We use the `"darwin"` style as an example. The code chunk below shows the structure of its `.yml` file:

    brand:
      color:
        palette:
          white: '#ffffff'
          darwin-blue: '#003399'
        background: white
        foreground: darwin-blue
      typography:
        base: Calibri
        base-font-size: 11
        plot: Calibri
        plot-font-size: 11
        table: Calibri
        table-font-size: 9
      plot:
        background-color: white
        header-color: darwin-blue
        header-text-color: white
        grid-major-color: '#d9d9d9'
        axis-color: '#252525'
        border-color: '#595959'
        legend-position: right
      table:
        border-color: darwin-blue
        border-width: 1
        header:
          background-color: darwin-blue
          text-bold: yes
          align: center
          text-color: white
          border-color: white
          font-size: 11
        header-name:
          background-color: darwin-blue
          text-bold: yes
          align: center
          text-color: white
          border-color: white
          font-size: 11
        header-level:
          background-color: darwin-blue
          text-bold: yes
          align: center
          text-color: white
          border-color: white
          font-size: 11
        column-name:
          background-color: darwin-blue
          text-bold: yes
          align: center
          text-color: white
          border-color: white
          font-size: 11
        group-label:
          background-color: darwin-blue
          text-bold: yes
          text-color: white
          border-color: white
        title:
          text-bold: yes
          align: center
          font-size: 15
        subtitle:
          text-bold: yes
          align: center
          font-size: 12
        body:
          border-width: 0.5
          border-color: darwin-blue

The `.yml` structure can be divided into four main sections:

- Color: Defines the color palette and the default `background` and `foreground` colors used when a plot/table section does not override them.
- Typography: Defines default font families and sizes for base text, plots, and tables (these can be overridden in the plot/table sections).
- Plot: Plot-specific settings such as background color, facet header color, header text color, grid color, axis color, border color, and legend position. Font settings are taken from the typography section unless overridden here.
- Table: Table-specific settings. You can set an overall `border-color` and `border-width`, or override settings per table section. Table sections include: `header`, `header-name`, `header-level`, `column-name`, `group-label`, `title`, `subtitle`, and `body`. For each section you can set properties such as `background-color`, `text-color`, `text-bold`, `align`, `font-size`, `border-color`, and `border-width`.

## Style hierarchy

Each plot and table element follows a style hierarchy. If a value isn’t specified at the most specific level, it inherits from higher-level entries; if none are defined, the default `ggplot2` (for plots) or the default for the specific table type is used. The table below shows the priority order for common plot and table options.

Part | Option 1 | Option 2 | Option 3
--- | --- | --- | ---
Plot
Background color | plot:background-color | color:background | -
Header (facet) color | plot:header-color | color:foreground | -
Header (facet) text color | plot:header-text-color | - | -
Border color | plot:border-color | color:foreground | -
Grid color | plot:grid-major-color | color:foreground | -
Axis color | plot:axis-color | - | -
Legend position | plot:legend-position | - | -
Font family | typography:plot | typography:base | -
Font size | plot:font-size | typography:plot-font-size | typography:base-font-size
Table section
Background color | table:[section_name]:background-color | color:background | -
Text bold | table:[section_name]:text-bold | - | -
Text color | table:[section_name]:text-color | - | -
Text align | table:[section_name]:align | - | -
Font size | table:[section_name]:font-size | typography:table-font-size | typography:base-font-size
Font family | table:[section_name]:font-family | typography:table | typography:base
Border color | table:[section_name]:border-color | table:border-color | -
Border width | table:[section_name]:border-width | table:border-width | -

In the examples above the YML path is represented with colon separators. For example, `plot:background-color` refers to the `background-color` key inside the `plot` section.

## Applying styles to tables and plots

The table-formatting functions (`visTable()`, `visOmopTable()`, and `formatTable()`) and plot functions accept a `style` argument. The `style` argument can be:

- the name of a built-in style (e.g. `"darwin"`), or
- the path to a user `.yml` file that defines a custom style, or
- a programmatic list that mirrors the `.yml` structure (only tables - see next section).

Example: apply the built-in `"darwin"` style to a plot:

    result <- mockSummarisedResult() |>
      filter(variable_name == "age")

    barPlot(
      result = result,
      x = "cohort_name",
      y = "mean",
      facet = c("age_group", "sex"),
      colour = "sex",
      style = "darwin"
    )

Example: use a custom `.yml` file (path provided):

    barPlot(
      result = result,
      x = "cohort_name",
      y = "mean",
      facet = c("age_group", "sex"),
      colour = "sex",
      style = here("MyStyleFolder", "MyStyle.yml")
    )

## Use of `_brand.yml`

If `style = NULL` and no global options are provided (via `setGlobalPlotOptions()` or `setGlobalTableOptions()`), the built-in “default” style is used. However, if a `_brand.yml` file is present in the project directory, that file’s style will be used.

# Alternative style customisation

You can customise styles programmatically without creating a `.yml` file by passing a named list to the `style` argument. The list should follow the same table section structure as the `.yml`.

## Tables

Below is an example that sets table section styles for `gt`.

    result |>
      visOmopTable(
        estimateName = c("Mean (SD)" = "<mean> (<sd>)"),
        groupColumn = "cohort_name",
        header = c("This is an overall header", "sex"),
        type = "gt",
        style = list(
          header = list(
            cell_text(weight = "bold"),
            cell_fill(color = "red")
          ),
          header_name = list(
            cell_text(weight = "bold"),
            cell_fill(color = "orange")
          ),
          header_level = list(
            cell_text(weight = "bold"),
            cell_fill(color = "yellow")
          ),
          column_name = list(
            cell_text(weight = "bold")
          ),
          group_label = list(
            cell_fill(color = "blue"),
            cell_text(color = "white", weight = "bold")
          ),
          title = list(
            cell_text(size = 20, weight = "bold")
          ),
          subtitle = list(
            cell_text(size = 15)
          ),
          body = list(
            cell_text(color = "red")
          )
        ),
        .options = list(
          title = "My formatted table!",
          subtitle = "Created with the `visOmopResults` R package.",
          groupAsColumn = FALSE,
          groupOrder = c("cohort2", "cohort1")
        )
      )

My formatted table!
Created with the `visOmopResults` R package.
| This is an overall header
--- | ---
CDM name | Age group | Variable name | Variable level | Estimate name | Sex
overall | Male | Female
cohort2
mock | overall | age | – | Mean (SD) | 38.24 (7.89) | 49.35 (4.78) | 18.62 (8.61)
| <40 | age | – | Mean (SD) | 82.74 (4.38) | 86.97 (0.23) | 48.21 (7.32)
| >=40 | age | – | Mean (SD) | 66.85 (2.45) | 34.03 (4.77) | 59.96 (6.93)
cohort1
mock | overall | age | – | Mean (SD) | 38.00 (7.94) | 12.56 (6.47) | 26.72 (7.83)
| <40 | age | – | Mean (SD) | 38.61 (5.53) | 77.74 (1.08) | 21.21 (4.11)
| >=40 | age | – | Mean (SD) | 1.34 (5.30) | 93.47 (7.24) | 65.17 (8.21)

Note that style objects differ across table engines, so the code must be adapted to the engine you use.

For `flextable`, styling objects come from the `officer` package. The structure is similar, but the style objects differ:

    result |>
      visOmopTable(
        estimateName = c("Mean (SD)" = "<mean> (<sd>)"),
        groupColumn = "cohort_name",
        header = c("This is an overall header", "sex"),
        type = "flextable",
        style = list(
          header = list(
            cell = fp_cell(background.color = "red"),
            text = fp_text(bold = TRUE)
          ),
          header_level = list(
            cell = fp_cell(background.color = "orange"),
            text = fp_text(bold = TRUE)
          ),
          header_name = list(
            cell = fp_cell(background.color = "yellow"),
            text = fp_text(bold = TRUE)
          ),
          column_name = list(
            text = fp_text(bold = TRUE)
          ),
          group_label = list(
            cell = fp_cell(background.color = "blue"),
            text = fp_text(bold = TRUE, color = "white")
          ),
          title = list(
            text = fp_text(bold = TRUE, font.size = 20)
          ),
          subtitle = list(
            text = fp_text(font.size = 15)
          ),
          body = list(
            text = fp_text(color = "red")
          )
        ),
        .options = list(
          title = "My formatted table!",
          subtitle = "Created with the `visOmopResults` R package.",
          groupAsColumn = FALSE,
          groupOrder = c("cohort2", "cohort1")
        )
      )

My formatted table!
Created with the `visOmopResults` R package.
CDM name | Age group | Variable name | Variable level | Estimate name | This is an overall header
--- | --- | --- | --- | --- | ---
Sex
overall | Male | Female
cohort2
mock | overall | age | – | Mean (SD) | 38.24 (7.89) | 49.35 (4.78) | 18.62 (8.61)
| <40 | age | – | Mean (SD) | 82.74 (4.38) | 86.97 (0.23) | 48.21 (7.32)
| >=40 | age | – | Mean (SD) | 66.85 (2.45) | 34.03 (4.77) | 59.96 (6.93)
cohort1
mock | overall | age | – | Mean (SD) | 38.00 (7.94) | 12.56 (6.47) | 26.72 (7.83)
| <40 | age | – | Mean (SD) | 38.61 (5.53) | 77.74 (1.08) | 21.21 (4.11)
| >=40 | age | – | Mean (SD) | 1.34 (5.30) | 93.47 (7.24) | 65.17 (8.21)

## Plots

Plot helpers return `ggplot2` objects, so you can further modify them using `+` and regular `ggplot2` calls:

    library(ggplot2)

    barPlot(
      result = result,
      x = "cohort_name",
      y = "mean",
      facet = c("age_group", "sex"),
      colour = "sex"
    ) +
      theme(
        strip.background = element_rect(fill = "#ffeb99", colour = "#ffcc00"),
        legend.position = "top",
        panel.grid.major = element_line(color = "transparent", linewidth = 0.25)
      ) +
      scale_color_manual(values = c("black", "black", "black")) +
      scale_fill_manual(values = c("#999999", "#E69F00", "#56B4E9"))

# Using non-supported font families for `ggplot`

If you want to use a font that is not already installed on your system, follow these steps:

## 1. Download and install the font (system-level)

- macOS: double-click the `.ttf` file and click Install Font. Example Calibri TTF (if not available on your system): https://www.freefontdownload.org/en/calibri.font
- Windows: right-click the `.ttf` file and choose Install (or Install for all users).

After installing, restart RStudio so the system font registry is updated.

## 2. Register the font with the `extrafont` package (R)

    library(extrafont)

    # import system fonts (may take several minutes) - just needs to be done when
    # a new font is installed
    font_import(paths = NULL, prompt = FALSE)

    # make fonts available for graphic devices using R
    loadfonts(device = "win")

The previous process is embeded in the function `requireExtrafont()` from visOmopResults, so the user could run that single function instead.

Notes: - `font_import()` scans system font directories. If you want to import fonts from a custom folder, pass `paths = "path/to/folder"`.

- `font_import()` can take a long time the first time it runs.

- `extrafont::loadfonts()` makes fonts available to R’s graphics devices. For PDFs, call `loadfonts(device = "pdf")` before saving plots.

## 3. Create plots with styles that use the font

Set the font family in the `.yml` file, or in `element_text()` calls within `theme`. As an example we run with “darwin” style, which if “Calibri” is loaded will use it (otherwise it defaults to “sans”).

    barPlot(
      result = result,
      x = "cohort_name",
      y = "mean",
      facet = c("age_group", "sex"),
      colour = "sex",
      style = "darwin"
    )

Remember the following:

- After installing system fonts, restart R/RStudio so R sees the new fonts.

- Use `extrafont::font_import()` once (or when you add new fonts) — it populates the extrafont database.

# Final remarks

The `.yml` customisation system allows you to control most aspects of the visual appearance of your tables and plots. To learn more about `brand.yml` and how it interacts with other elements such as Shiny apps and Quarto/R Markdown documents, refer to https://posit-dev.github.io/brand-yml/.