Writing your own theme

In the code chunk below, we provide a function that you can use to create a custom theme for your organization. You can use this theme in your own plots by calling theme_org().

1 What this theme provides

This theme is built on the theme_minimal() theme from ggplot2, and it provides a set of default settings that we find generally useful. It allows the analyst who uses it to do the following:

  • Set the base font (base_family) for the entire plot
  • If that base font is not available, it will fall back to a set of preferred fonts (e.g., Arial or Helvetica)
  • If that base font is available, then it will attempt to use a condensed version of that font (e.g., Arial Narrow or Helvetica Condensed) for the graph text.
  • If the analyst chooses from a set of select base fonts, then the theme will attempt to use a condensed version of that font. If that font is not available, it will fall back to a set of preferred condensed fonts (e.g., Arial Narrow or Helvetica Condensed).
  • Set the base font size for the entire plot; all other font sizes are set relative to this base size so the plot can be easily adapted for a variety of use cases (e.g., presentations, reports, etc.).
  • Makes the plot title bold and much larger than the other text.

2 How to use this theme

Here’s the process for using this theme:

  1. Copy the code below into your own R script file.
  2. Save the file as theme_org.R in your project directory (or wherever you put your R scripts).
  3. Load the theme_org.R file into your R session (or into your own R script) using the source() function.
  4. Call the theme_org() function in your plots to apply the theme.
  5. When you are fully satisfied with any customization that you are going to do, then distribute this theme_org.R file to your colleagues so they can use the same theme in their plots. This is just another way that you can make it easier for your analytical team’s customers to use your work.

3 Customizing this theme

You can customize this theme in a variety of ways:

  • Change the values of dark_color, medium_color, or strip_color to use different colors for the plot title, axis labels, and strip labels.
  • Set the value of the base_family:
    • Search for base_family = NULL in the code below.
    • Change the NULL to the name of the font you want to use. For example, if you wanted to use "Avenir Next" as the base font, you would change it to base_family = "Avenir Next".
  • Set the value of the base_size:
    • Search for base_size = 12 in the code below.
    • Change the 12 to the size you want to use. For example, if you wanted to use a base size of 14, you would change it to base_size = 14.
  • If you want to add another associated condensed font, you can add it to the condensed_map in the set_condensed_font() function. For example, if you wanted to add "Scott Sans" as a base font with "Scott Sans Condensed" as the condensed font, you would add the line "Scott Sans" = "Scott Sans Condensed" to the condensed_map list.
  • If you want to change the relative sizes of any of the text, you can do so by changing the rel() values in the theme() function. For example, if you wanted to make the plot title smaller, you could change size = ggplot2::rel(1.6) to size = ggplot2::rel(1.4).
  • Finally, you can also add any other changes to the theme_minimal() settings within this call to theme(). For example, if you wanted to change the background color of the plot, you could add panel.background = ggplot2::element_rect(fill = "green") to the theme() function.

4 The code itself

The following is the code for the theme_org() function. You can copy and paste this into your own R script file to use it.

library(ggplot2)
library(grid)
library(systemfonts)

theme_org <- function(base_size = 12, base_family = NULL) {
  # Check if systemfonts package is installed and which
  #  fonts are available
  sf <- systemfonts::system_fonts()
  
  # Check if the base family is available in system fonts
  org_font <- set_org_font(base_family, sf)

  # Determine the graph font based on available fonts
  org_graph_font <- set_condensed_font(org_font, sf)

  dark_color <- "#201547"
  medium_color <- "#582c83"
  strip_color <- "#F0F0F0"
  
  ggplot2::theme_minimal(base_size = base_size, 
                         base_family = org_font) %+replace%
    ggplot2::theme(
      plot.caption = ggplot2::element_text(
                        family = org_font, 
                        size = ggplot2::rel(0.7), 
                        hjust = 1, 
                        color = medium_color
                        ),
      plot.title = ggplot2::element_text(
                        size = ggplot2::rel(1.6), 
                        face = "bold", 
                        family = org_font, 
                        color = dark_color,
                        lineheight = ggplot2::rel(1.5), 
                        hjust = 0,
                        margin = ggplot2::margin(2, 1, 4, 1, "pt")
                        ),
      plot.title.position = "panel",
      plot.caption.position = "plot",
      plot.subtitle = ggplot2::element_text(
                        size = ggplot2::rel(1.2), 
                        family = org_font, 
                        color = medium_color,
                        lineheight = ggplot2::rel(1.2), 
                        hjust = 0,
                        margin = ggplot2::margin(1, 1, 10, 1, "pt")
                        ),
      axis.title = ggplot2::element_text(
                        size = ggplot2::rel(1.1), 
                        family = org_graph_font, 
                        color = dark_color
                        ),
      axis.text = ggplot2::element_text(
                        size = ggplot2::rel(0.9), 
                        family = org_graph_font, 
                        color = medium_color
                        ),
      legend.title = ggplot2::element_text(
                        size = ggplot2::rel(0.9), 
                        family = org_graph_font, 
                        face = "bold", 
                        color = dark_color
                        ),
      legend.text = ggplot2::element_text(
                        size = ggplot2::rel(0.8), 
                        family = org_graph_font, 
                        color = dark_color, 
                        lineheight = 0.9
                        ),
      strip.text = ggplot2::element_text(
                        size = ggplot2::rel(0.9), 
                        family = org_font, 
                        face = "plain", 
                        color = dark_color,
                        margin = ggplot2::margin(3, 2, 3, 2, "pt")
                        ),
      strip.background = ggplot2::element_rect(fill = strip_color, 
                                               color = strip_color),
      legend.key.spacing.x = grid::unit(1, "pt"),
      legend.key.spacing.y = grid::unit(0, "pt")
    )
}

#' Choose a condensed font based on base font with graceful fallbacks
#'
#' @param org_font Base font name (e.g., "Roboto")
#' @param sf A systemfonts::system_fonts() data frame
#' @return Name of the best available condensed font
#' @export
set_condensed_font <- function(org_font, sf) {
  # Map base font → preferred condensed version
  condensed_map <- c(
    "Arial" = "Arial Narrow",
    "Helvetica" = "Helvetica Condensed",
    "Avenir" = "Avenir Next Condensed",
    "Avenir Next" = "Avenir Next Condensed",
    "Fira Sans" = "Fira Sans Condensed",
    "Source Sans Pro" = "Source Sans Pro Condensed",
    "Roboto" = "Roboto Condensed",
    "Noto Sans" = "Noto Sans Condensed",
    "Open Sans" = "Open Sans Condensed",
    "Lato" = "Lato Light",
    "PT Sans" = "PT Sans Narrow",
    "Ubuntu" = "Ubuntu Condensed",
    "Droid Serif" = "Droid Serif Condensed"
  )
  
  preferred <- condensed_map[[org_font]]
  
  if (!is.null(preferred) && preferred %in% sf$family) {
    return(preferred)
  } else if ("Arial Narrow" %in% sf$family) {
    return("Arial Narrow")
  } else if ("Helvetica Condensed" %in% sf$family) {
    return("Helvetica Condensed")
  } else {
    return(org_font)
  }
}

set_org_font <- function(base_family, sf) {
  has_arial <- "Arial" %in% sf$family
  has_helvetica <- "Helvetica" %in% sf$family

  # Determine the base font and graph font based on available fonts
  if (!is.null(base_family)) {
    if (base_family %in% sf$family) {
      org_font <- base_family
    } else if (has_arial) {
      org_font <- "Arial"
    } else if (has_helvetica) {
      org_font <- "Helvetica"
    } else {
      org_font <- "sans"
    }
  } else if (has_arial) {
    org_font <- "Arial"
  } else if (has_helvetica) {
    org_font <- "Helvetica"
  } else {
    org_font <- "sans"
  }
  return(org_font)
}