A custom {ggplot2} theme for Cornell

Application exercise
Create a custom {ggplot2} theme that adheres to Cornell University’s brand identity guidelines.
Modified

May 24, 2026

ImportantGetting started

This application exercise is designed to be run in your web browser using the {webr} framework. Simply work through the exercises and use the provided code cells to execute live R code in your browser.

Cornell University brand identity

Organizational branding is a set of visual and verbal elements that represent an organization. It is “how your audience perceives you” and created through “many elements, including your name, logo, tagline, website, colors, collateral, messaging, positioning, graphic elements, social media, and other outreach platforms.”1

Cornell University maintains detailed guidelines for its brand identity. The design center provides explicit instructions for using the Cornell logo, color palette, and typography, as well as downloadable materials such as a PowerPoint template.

Suppose we wish to create a series of statistical charts to be used in a Cornell Bowers2 presentation, but we want to ensure they are both reproducible as well as consistent with the university’s branding. We can use the {ggplot2} package to create a custom theme that adheres to the Cornell brand identity.

Basic bar chart

Let’s create a basic chart to assist us with generating an appropriate {ggplot2} theme. Here we will use graduation trends from Cornell University from College Scorecard. In this instance, we will use a bar chart to visualize the percentage of degrees awarded for three fields of study from 2020-24.

cornell_degrees <- read_csv("data/cornell-degrees.csv")
cornell_degrees
# A tibble: 140 × 3
   field_of_study  year    pct
   <chr>          <dbl>  <dbl>
 1 Computer        1997 0.0421
 2 Computer        1998 0.0497
 3 Computer        1999 0.0581
 4 Computer        2000 0.0716
 5 Computer        2001 0.0684
 6 Computer        2002 0.0859
 7 Computer        2003 0.0745
 8 Computer        2004 0.0463
 9 Computer        2005 0.0327
10 Computer        2006 0.032 
# ℹ 130 more rows
cornell_degrees_plot <- cornell_degrees |>
  filter(year >= 2020) |>
  mutate(
    year = factor(year),
    field_of_study = fct_reorder2(.f = field_of_study, .x = year, .y = pct)
  ) |>
  ggplot(mapping = aes(x = field_of_study, y = pct, fill = year)) +
  geom_col(position = position_dodge2(padding = 0.2)) +
  scale_x_discrete(labels = label_wrap(width = 15)) +
  scale_y_continuous(labels = label_percent()) +
  labs(
    title = "Computer degrees have surpassed Engineering at Cornell",
    subtitle = "Percentage of degrees awarded by field of study",
    x = NULL,
    y = NULL,
    fill = NULL,
    caption = "Source: College Scorecard"
  )
cornell_degrees_plot

Develop a custom Cornell theme for {ggplot2}

Based on Cornell’s brand identity, we want to ensure our theme adheres to the following requirements:

Your turn: Implement a Cornell-branded theme for {ggplot2} that meets the requirements outlined above.

Tip

Replace the ______ placeholders with the appropriate values. Use the {ggplot2} theme_sub_*() documentation as a reference.

TipSuggested solution
cornell_degrees_plot +
  theme_minimal(
    base_family = "Spectral",
    base_size = 12
  ) +
  theme_sub_plot(
    title.position = "plot",
    title = element_text(hjust = 0.5),
    subtitle = element_text(hjust = 0.5)
  ) +
  theme_sub_legend(
    position = "bottom",
    text = element_text(size = rel(1.0))
  ) +
  theme_sub_panel(
    grid.major.x = element_blank(),
    grid.minor.x = element_blank(),
    grid.major.y = element_line(color = "grey", linewidth = 0.3),
    grid.minor.y = element_line(color = "grey", linewidth = 0.15)
  ) +
  theme_sub_axis(
    text = element_text(size = rel(1.2))
  ) +
  theme(
    palette.fill.discrete = cornell_pal
  )
1
Use theme_minimal() as a starting point instead of building everything from scratch. Ensures a white background.
2
Align the title using the entire plot, not just the plot panel.
3
Horizontally align the title and subtitle centered over the plot.
4
Move the legend to the bottom of the plot.
5
No grid lines on the \(x\)-axis.
6
Increase visibility of grid lines on the \(y\)-axis.
7
Increase font size of axis and legend labels.
8
Use the Google font “Spectral” as the base font family (open-source alternative to Palantino).
9
Increase the size of all text elements of the theme.
10
Use the Cornell color palette via theme() instead of scale_fill_manual().

Turn into a reusable function

Demo: Convert your Cornell theme into a reusable theme_cornell() function and test it on two different charts.

Show the code
theme_cornell <- function(
  base_size = 12,
  base_family = "Spectral",
  ...
) {
  cornell_pal <- c(
    "#b41b1b",
    "#4d4f53",
    "#a2998b",
    "#ef9595",
    "#7d7364",
    "#a8b2c4"
  )

  theme_minimal(
    base_family = base_family,
    base_size = base_size,
    ...
  ) +
    theme_sub_plot(
      title.position = "plot",
      title = element_text(hjust = 0.5),
      subtitle = element_text(hjust = 0.5)
    ) +
    theme_sub_legend(
      position = "bottom",
      text = element_text(size = rel(1.0))
    ) +
    theme_sub_panel(
      grid.major.x = element_blank(),
      grid.minor.x = element_blank(),
      grid.major.y = element_line(color = "grey", linewidth = 0.3),
      grid.minor.y = element_line(color = "grey", linewidth = 0.15)
    ) +
    theme_sub_axis(
      text = element_text(size = rel(1.2))
    ) +
    theme(
      palette.color.discrete = cornell_pal,
      palette.fill.discrete = cornell_pal
    )
}
# existing bar chart
cornell_degrees_plot +
  theme_cornell()

# line graph
cornell_degrees |>
  mutate(
    field_of_study = fct_reorder2(
      .f = field_of_study,
      .x = year,
      .y = pct
    )
  ) |>
  ggplot(aes(x = year, y = pct, color = field_of_study)) +
  geom_point() +
  geom_line() +
  scale_x_continuous(limits = c(1996, 2024), breaks = seq(1996, 2024, 4)) +
  scale_y_continuous(labels = label_percent()) +
  labs(
    x = "Graduation year",
    y = "Percent of degrees awarded",
    color = "Field of study",
    title = "Cornell University degrees awarded from 1997-2024",
    subtitle = "Only the top five fields as of 2024",
    caption = "Source: Department of Education\nhttps://collegescorecard.ed.gov/"
  ) +
  theme_cornell() +
  theme_sub_legend(position = "right")

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.2 (2025-10-31)
 os       macOS Tahoe 26.5
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/New_York
 date     2026-05-24
 pandoc   3.8.3 @ /Applications/Positron.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
 quarto   1.10.3 @ /Applications/quarto/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 ! package      * version date (UTC) lib source
 P bit            4.6.0   2025-03-06 [?] RSPM (R 4.5.0)
 P bit64          4.8.0   2026-04-21 [?] RSPM
 P cli            3.6.6   2026-04-09 [?] RSPM
 P crayon         1.5.3   2024-06-20 [?] RSPM (R 4.5.0)
 P digest         0.6.39  2025-11-19 [?] RSPM (R 4.5.0)
 P dplyr        * 1.2.1   2026-04-03 [?] RSPM
 P evaluate       1.0.5   2025-08-27 [?] RSPM (R 4.5.0)
 P farver         2.1.2   2024-05-13 [?] RSPM (R 4.5.0)
 P fastmap        1.2.0   2024-05-15 [?] RSPM (R 4.5.0)
 P forcats      * 1.0.1   2025-09-25 [?] RSPM (R 4.5.0)
 P generics       0.1.4   2025-05-09 [?] RSPM (R 4.5.0)
 P ggplot2      * 4.0.3   2026-04-22 [?] RSPM
 P glue           1.8.1   2026-04-17 [?] RSPM
 P gtable         0.3.6   2024-10-25 [?] RSPM (R 4.5.0)
 P here           1.0.2   2025-09-15 [?] CRAN (R 4.5.0)
 P hms            1.1.4   2025-10-17 [?] RSPM (R 4.5.0)
 P htmltools      0.5.9   2025-12-04 [?] RSPM (R 4.5.0)
 P htmlwidgets    1.6.4   2023-12-06 [?] RSPM (R 4.5.0)
 P jsonlite       2.0.0   2025-03-27 [?] RSPM (R 4.5.0)
 P knitr          1.51    2025-12-20 [?] RSPM (R 4.5.0)
 P labeling       0.4.3   2023-08-29 [?] RSPM (R 4.5.0)
 P lifecycle      1.0.5   2026-01-08 [?] RSPM (R 4.5.0)
 P lubridate    * 1.9.5   2026-02-04 [?] RSPM
 P magrittr       2.0.5   2026-04-04 [?] RSPM
 P otel           0.2.0   2025-08-29 [?] RSPM (R 4.5.0)
 P pillar         1.11.1  2025-09-17 [?] RSPM (R 4.5.0)
 P pkgconfig      2.0.3   2019-09-22 [?] RSPM (R 4.5.0)
 P purrr        * 1.2.2   2026-04-10 [?] RSPM
 P R6             2.6.1   2025-02-15 [?] RSPM (R 4.5.0)
 P ragg           1.5.0   2025-09-02 [?] RSPM (R 4.5.0)
 P RColorBrewer   1.1-3   2022-04-03 [?] RSPM (R 4.5.0)
 P readr        * 2.2.0   2026-02-19 [?] RSPM
 P renv           1.2.3   2026-05-16 [?] RSPM
 P rlang          1.2.0   2026-04-06 [?] RSPM
 P rmarkdown      2.31    2026-03-26 [?] RSPM
 P rprojroot      2.1.1   2025-08-26 [?] RSPM (R 4.5.0)
 P S7             0.2.2   2026-04-22 [?] RSPM
 P scales       * 1.4.0   2025-04-24 [?] RSPM (R 4.5.0)
 P sessioninfo    1.2.3   2025-02-05 [?] RSPM (R 4.5.0)
 P stringi        1.8.7   2025-03-27 [?] RSPM (R 4.5.0)
 P stringr      * 1.6.0   2025-11-04 [?] RSPM (R 4.5.0)
 P systemfonts    1.3.2   2026-03-05 [?] RSPM
 P textshaping    1.0.5   2026-03-06 [?] RSPM
 P tibble       * 3.3.1   2026-01-11 [?] RSPM (R 4.5.0)
 P tidyr        * 1.3.2   2025-12-19 [?] RSPM (R 4.5.0)
 P tidyselect     1.2.1   2024-03-11 [?] RSPM (R 4.5.0)
 P tidyverse    * 2.0.0   2023-02-22 [?] RSPM (R 4.5.0)
 P timechange     0.4.0   2026-01-29 [?] RSPM
 P tzdb           0.5.0   2025-03-15 [?] RSPM (R 4.5.0)
 P utf8           1.2.6   2025-06-08 [?] RSPM (R 4.5.0)
 P vctrs          0.7.3   2026-04-11 [?] RSPM
 P vroom          1.7.1   2026-03-31 [?] RSPM
 P withr          3.0.2   2024-10-28 [?] RSPM (R 4.5.0)
 P xfun           0.57    2026-03-20 [?] RSPM
 P yaml           2.3.12  2025-12-10 [?] RSPM (R 4.5.0)

 [1] /Users/bcs88/Projects/info-3312/course-site/renv/library/macos/R-4.5/aarch64-apple-darwin20
 [2] /Users/bcs88/Library/Caches/org.R-project.R/R/renv/sandbox/macos/R-4.5/aarch64-apple-darwin20/4cd76b74

 * ── Packages attached to the search path.
 P ── Loaded and on-disk path mismatch.

──────────────────────────────────────────────────────────────────────────────

Footnotes

  1. Source: What is Organizational Branding?↩︎

  2. According to the Cornell Bowers brand guidelines, always refer to it as Cornell Bowers or the more formal Cornell Bowers College of Computing and Information Science, never Bowers CIS or just CIS.↩︎