Notre Dame Color Palettes

NDPalette provides color palettes that approximate the University of Notre Dame brand colors, together with ggplot2 color and fill scales. These colors are well suited to statistical visualization and psychometric analysis: the white-safe ordering keeps groups legible in plots of data, and the near-white-to-navy ramps suit continuous quantities such as correlations and factor loadings. The colors come back as plain hexadecimal strings, so they work anywhere that accepts a color: a ggplot2 scale, a base graphics call, a plotly chart, a table, or a figure rendered outside R. It opens with a quick start for every workflow (ggplot2, base R graphics, R Markdown, and Shiny), previews the palettes, shows each color with its name, walks through coloring one group up to ten, gives a gallery of common chart types in both ggplot2 and base R, shows how to mix in a color from outside the palette, and closes with a set of statistical and psychometric figures. It is an independent project, not affiliated with or endorsed by the University.

library(NDPalette)

Quick start: every workflow, the easy way

The point of NDPalette is that you should not have to choose colors by hand. In each workflow you add one thing and NDPalette applies the colors for you. Everything after this section is only for when you want more control: a set number of colors, a specific brand color by name, or mixing in a color from outside the palette.

ggplot2 — add a scale:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2) +
  scale_color_nd()                      # one line; the colors are chosen for you

Base R graphics — set the palette once, then color by a grouping factor:

palette(nd_palette(8))                  # base R now draws in Notre Dame colors
plot(Petal.Length ~ Sepal.Length, data = iris, col = Species, pch = 19)

palette("default")                      # restore base R's palette when done

R Markdown — theme a whole HTML report from one line in the YAML header:

output:
  NDPalette::html_nd_document           # navy headings, brand callouts, soft page

Shiny — color the page chrome from the palette with a bslib theme (the Theming a Shiny app with NDPalette vignette walks through a full app):

bslib::bs_theme(primary = nd_color("navy"), secondary = nd_color("bright_gold"))

The default Notre Dame palette

The default "nd" palette leads with the six Notre Dame brand colors that read clearly on a white background, then extends through seven former Notre Dame brand colors, thirteen anchors in all. show_palette() previews the whole set (it defaults to the full palette):

show_palette()

Every color also has a readable name. The card below lists the thirteen data colors with their names and hexadecimal values, so a color can be matched to its label at a glance:

pal   <- nd_palette()                            # the thirteen data colors
named <- nd_colors[match(pal, nd_colors$hex), ]  # matched to their catalog names
named$pos <- rev(seq_len(nrow(named)))

ggplot(named, aes(x = 0, y = pos)) +
  geom_tile(aes(fill = hex), width = 0.5, height = 0.82) +
  geom_text(aes(x = 0.32, label = paste0(name, "  (", hex, ")")),
            hjust = 0, size = 3.1) +
  scale_fill_identity() +
  coord_cartesian(xlim = c(-0.3, 4), clip = "off") +
  labs(x = NULL, y = NULL) +
  theme_void()

show_palette() can also print those names on the swatches themselves, in place of the hex values, by passing a character vector to labels:

show_palette(nd_palette(), labels = named$name)

The raw anchors are exported as nd_palettes for tools and for users who want the hexadecimal values directly:

nd_palettes$nd
#>  [1] "#0c2340" "#d39f10" "#0a843d" "#1c4f8f" "#c1cddd" "#ae9142" "#36deb8"
#>  [8] "#4c1c2a" "#342551" "#a8a600" "#456300" "#e3e361" "#1e1a13"

nd_palette(n) returns the first n of these. Because the colors are taken in a fixed order, asking for five groups always yields the same five colors, and those five are the first five of any larger request:

nd_palette(2)
#> [1] "#0c2340" "#d39f10"
nd_palette(5)
#> [1] "#0c2340" "#d39f10" "#0a843d" "#1c4f8f" "#c1cddd"

From one group to ten

As the number of groups grows, the palette adds one color at a time. The staircase below shows exactly which colors nd_palette(n) returns for n running from one to ten: each row is one value of n, read left to right.

ladder <- do.call(rbind, lapply(1:10, function(k) {
  data.frame(n = k, position = seq_len(k), hex = nd_palette(k))
}))
ladder$n <- factor(ladder$n, levels = 10:1)

ggplot(ladder, aes(position, n, fill = hex)) +
  geom_tile(color = "white", linewidth = 1.2) +
  scale_fill_identity() +
  scale_x_continuous(breaks = 1:10, position = "top") +
  coord_equal() +
  labs(x = "color position", y = "number of groups (n)") +
  theme(panel.grid = element_blank())

The same progression, drawn as ggplot2 bar charts colored with scale_fill_nd(). Each panel is a plot of n groups, so the panels show the palette at work for one through ten categories:

bars <- do.call(rbind, lapply(1:10, function(k) {
  data.frame(n = k, position = seq_len(k))
}))
bars$value <- 2 + sin(bars$position)
bars$group <- factor(bars$position, levels = 1:10)
bars$panel <- factor(paste0("n = ", bars$n), levels = paste0("n = ", 1:10))

ggplot(bars, aes(group, value, fill = group)) +
  geom_col(width = 0.85) +
  facet_wrap(~ panel, nrow = 2, scales = "free_x") +
  scale_fill_nd() +
  labs(x = NULL, y = NULL) +
  theme(legend.position = "none",
        axis.text = element_blank(),
        panel.grid = element_blank())

The first four colors (ND Blue, Bright Gold, Green, Bright Blue) are the most strongly separated, so two-, three-, and four-group plots read cleanly. Past six groups the palette draws on the former Notre Dame colors, which introduce teal, maroon, and purple to keep neighboring categories distinct. Two of those, maroon and purple, are dark, so for a plot with many small or thin marks (a fine line chart, a dense scatter) it is worth checking that adjacent groups stay legible, and reaching for the colorblind-friendly "nd_cvd" palette (below) when separation matters more than leading with the brand colors.

The scales in practice

scale_color_nd() and scale_fill_nd() drop into a ggplot2 plot and map the grouping levels to the Notre Dame colors automatically. scale_colour_nd() is the British-spelling alias.

Two and three groups, on the color aesthetic. The data are iris — sepal and petal measurements of 150 flowers from three species:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5, alpha = 0.9) +
  scale_color_nd() +
  labs(title = "Three groups", x = "sepal length", y = "petal length",
       color = "species")

Three groups, on the fill aesthetic — the count of the 32 mtcars cars by number of cylinders:

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
  geom_bar(width = 0.7) +
  scale_fill_nd() +
  labs(title = "Three groups", x = "cylinders", y = "count",
       fill = "cylinders")

Five groups, five well-separated colors — the count of the roughly 54,000 diamonds by cut quality:

ggplot(diamonds, aes(cut, fill = cut)) +
  geom_bar() +
  scale_fill_nd() +
  labs(title = "Five groups", x = "cut", y = "count") +
  theme(legend.position = "none")

Six groups — InsectSprays counts insects on field plots treated with each of six insecticides (A–F):

ggplot(InsectSprays, aes(spray, count, fill = spray)) +
  geom_boxplot() +
  scale_fill_nd() +
  labs(title = "Six groups", x = "spray", y = "count") +
  theme(legend.position = "none")

To reverse the color order, pass reverse = TRUE to the scale:

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
  geom_bar(width = 0.7) +
  scale_fill_nd(reverse = TRUE) +
  labs(x = "cylinders", y = "count", fill = "cylinders") +
  theme(legend.position = "none")

Base R graphics

The palettes are plain hex strings, so they drop into base graphics too. The easiest route is to set R’s active palette once with palette(nd_palette(n)); after that, base plots that color by a factor or integer use Notre Dame colors automatically, with no per-call color work.

palette(nd_palette(6))                 # base R now draws in Notre Dame colors
boxplot(count ~ spray, data = InsectSprays, col = 1:6, border = "grey30",
        main = "Six groups, base R", xlab = "spray", ylab = "count")

palette("default")                     # restore when done

Or pass nd_palette() straight to a col argument, without touching the global palette:

barplot(table(mtcars$cyl), col = nd_palette(3), border = NA,
        main = "Three groups, base R", xlab = "cylinders", ylab = "count")

A sequential ramp shades a heatmap or surface; image() is the base R counterpart of a filled ggplot2 raster. A ramp straight from a pale tint to navy passes through a washed-out blue-gray in the middle, so the ramp here runs light sky blue through Bright Blue to navy, keeping the midtones a clear blue. volcano is a grid of elevations on Maungawhau, a volcano in Auckland:

ramp <- grDevices::colorRampPalette(c(nd_tints[["light_sky_blue"]],
                                      nd_color("bright_blue"),
                                      nd_color("navy")))(20)
image(volcano, col = ramp, axes = FALSE, main = "Maungawhau elevation")

A soft tint works as a base R page background through par(bg = nd_informal_tints[["soft_white"]]), the base counterpart of setting plot.background in ggplot2; par(bg = ...) takes one color for the whole page. The grid below compares the options on one plot of mtcars (fuel economy versus weight for 32 cars from a 1974 issue of Motor Trend, one point per car): the soft white, soft yellow, and warm soft-yellow tints as the background, with navy or bright blue points. To use a tint for a real page, pass it to par(bg = ...); to change the point color, change the nd_color(...) argument.

panel <- function(bg, col, lab) {
  plot(mpg ~ wt, data = mtcars, type = "n", main = lab,
       xlab = "weight (1000 lbs)", ylab = "mpg")
  rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4],
       col = bg, border = NA)                  # the tint, shown per panel
  points(mtcars$wt, mtcars$mpg, pch = 19, col = col)
  box()
}
op <- par(mfrow = c(2, 2), mar = c(4, 4, 2, 1))
panel(nd_informal_tints[["soft_white"]],       nd_color("navy"),        "soft white + navy")
panel(nd_informal_tints[["soft_yellow"]],      nd_color("navy"),        "soft yellow + navy")
panel(nd_informal_tints[["soft_yellow_warm"]], nd_color("navy"),        "soft yellow, warm (gold) + navy")
panel(nd_informal_tints[["soft_white"]],       nd_color("bright_blue"), "soft white + bright blue")

par(op)

Statistical and psychometric visualization

The brand colors are built for plots of data, and they fit the figures that recur in statistical and psychometric work. The examples that follow are a sequential and a diverging ramp on two correlation matrices, a two-group fill for factor loadings, and a several-color scale for item characteristic curves.

A correlation heatmap puts the near-white-to-navy ramp on a matrix of item intercorrelations. Here six items measure a single construct, so the off-diagonal correlations cluster around a common value:

set.seed(113)
f     <- rnorm(250)                                    # the latent construct
load  <- 0.65                                          # common loading
items <- sapply(1:6, function(j) load * f + rnorm(250, sd = sqrt(1 - load^2)))
colnames(items) <- paste0("i", 1:6)

R <- cor(items)
dfR <- as.data.frame(as.table(R))
names(dfR) <- c("row", "col", "r")
dfR$txt <- ifelse(dfR$r > 0.5, "white", nd_color("navy"))

heat <- grDevices::colorRampPalette(
  c(nd_tints[["light_sky_blue"]], nd_palette(1)))(100)

ggplot(dfR, aes(col, row, fill = r)) +
  geom_tile(color = "white") +
  geom_text(aes(label = sprintf("%.2f", r), color = txt), size = 3) +
  scale_fill_gradientn(colors = heat, limits = c(0, 1)) +
  scale_color_identity() +
  coord_equal() +
  labs(title = "Item intercorrelations", x = NULL, y = NULL, fill = "r")

Because the off-diagonal correlations here are all similar (around 0.42), the tiles read as nearly one color. When correlations are all alike, no color scale will show much difference among them; the scale does more visible work when the values have a wider range, including negative ones, as in the next example.

A second matrix, the correlations among several mtcars variables (fuel economy, cylinders, displacement, horsepower, rear-axle ratio, and weight), has a wide range, including strong negative correlations — fuel economy falls as weight, cylinders, and displacement rise. A diverging ramp anchored at the two Notre Dame primaries (navy for negative, a light tint at zero, gold for positive) shows those differences:

vars <- c("mpg", "cyl", "disp", "hp", "drat", "wt")
Rm   <- cor(mtcars[, vars])
dfm  <- as.data.frame(as.table(Rm)); names(dfm) <- c("row", "col", "r")
dfm$txt <- ifelse(dfm$r < -0.5, "white", nd_color("navy"))

diverging <- grDevices::colorRampPalette(
  c(nd_color("navy"), nd_tints[["light_warm_white"]], nd_color("bright_gold")))(100)

ggplot(dfm, aes(col, row, fill = r)) +
  geom_tile(color = "white") +
  geom_text(aes(label = sprintf("%.2f", r), color = txt), size = 3) +
  scale_fill_gradientn(colors = diverging, limits = c(-1, 1)) +
  scale_color_identity() +
  coord_equal() +
  labs(title = "mtcars correlations (diverging ramp)", x = NULL, y = NULL,
       fill = "r")

Standardized factor loadings are a natural two-group fill: one bar per item for each of two factors, colored ND Blue and Bright Gold. The first four items load on the first factor, the last four on the second:

loadings <- rbind(
  data.frame(item = paste0("Item ", 1:8), factor = "Verbal",
             loading = c(.74, .69, .78, .81, .22, .15, .09, .25)),
  data.frame(item = paste0("Item ", 1:8), factor = "Quantitative",
             loading = c(.18, .12, .24, .07, .71, .79, .66, .80)))
loadings$item <- factor(loadings$item, levels = paste0("Item ", 8:1))

ggplot(loadings, aes(loading, item, fill = factor)) +
  geom_col(position = "dodge", width = 0.7) +
  scale_fill_nd() +
  labs(title = "Standardized factor loadings", x = "loading", y = NULL,
       fill = "factor")

Item characteristic curves from a two-parameter logistic model give each item one of the well-separated brand colors. Five items, five colors:

theta <- seq(-4, 4, length.out = 200)
pars  <- data.frame(item = paste0("Item ", 1:5),
                    a = c(1.2, 0.8, 1.6, 1.0, 1.9),     # discriminations
                    b = c(-1.5, -0.6, 0.1, 0.8, 1.7))   # difficulties
icc <- do.call(rbind, lapply(seq_len(nrow(pars)), function(i) {
  data.frame(theta = theta, item = pars$item[i],
             p = plogis(pars$a[i] * (theta - pars$b[i])))
}))

ggplot(icc, aes(theta, p, color = item)) +
  geom_line(linewidth = 1) +
  scale_color_nd() +
  labs(title = "Item characteristic curves (2PL)",
       x = expression(theta), y = "probability of a correct response",
       color = "item")

Finding a color by name and role

The palettes above cover most plotting needs, but sometimes you want one specific brand color, or you want to see the whole set at once with a note on what each color is. nd_colors is that catalog: one row per color, labeled with its brand (University or Athletics) and its role (primary, secondary, former, tint, or neutral).

nd_colors
#>                   name                key     hex      brand      role
#> 1              ND Blue               navy #0c2340 university   primary
#> 2     ND Metallic Gold      metallic_gold #ae9142 university   primary
#> 3          Medium Blue        medium_blue #143865 university secondary
#> 4          Bright Blue        bright_blue #1c4f8f university secondary
#> 5            Dark Gold          dark_gold #8c7535 university secondary
#> 6          Bright Gold        bright_gold #d39f10 university secondary
#> 7                Green              green #0a843d university secondary
#> 8          Light Green        light_green #b3dac5 university secondary
#> 9        Dark Sky Blue      dark_sky_blue #c1cddd university secondary
#> 10          Warm White         warm_white #efe9d9 university      tint
#> 11    Light Warm White   light_warm_white #f8f4ec university      tint
#> 12     Medium Sky Blue    medium_sky_blue #e1e8f2 university      tint
#> 13      Light Sky Blue     light_sky_blue #edf2f9 university      tint
#> 14                Teal               teal #36deb8 university    former
#> 15              Maroon             maroon #4c1c2a university    former
#> 16              Purple             purple #342551 university    former
#> 17         Light Olive        light_olive #a8a600 university    former
#> 18          Dark Olive         dark_olive #456300 university    former
#> 19         Pale Yellow        pale_yellow #e3e361 university    former
#> 20               Black              black #1e1a13 university    former
#> 21         Faint White        faint_white #fdfcfa       none  informal
#> 22          Soft White         soft_white #faf7f1       none  informal
#> 23        Faint Yellow       faint_yellow #fefdf3       none  informal
#> 24 Soft Yellow (light)  soft_yellow_light #fdf9e6       none  informal
#> 25         Soft Yellow        soft_yellow #faf3d7       none  informal
#> 26  Soft Yellow (warm)   soft_yellow_warm #f6edc6       none  informal
#> 27     Notre Dame Blue     athletics_blue #0c2340  athletics   primary
#> 28  Standard Dome Gold          dome_gold #c99700  athletics   primary
#> 29  Metallic Dome Gold metallic_dome_gold    <NA>  athletics   primary
#> 30         Irish Green        irish_green #00843d  athletics secondary
#> 31               White    athletics_white #ffffff  athletics   neutral
#>    white_safe   pms
#> 1        TRUE   289
#> 2        TRUE 10127
#> 3        TRUE  2154
#> 4        TRUE  2945
#> 5        TRUE  4495
#> 6        TRUE   117
#> 7        TRUE   347
#> 8        TRUE  2246
#> 9        TRUE  5376
#> 10      FALSE  4545
#> 11      FALSE  9064
#> 12      FALSE   650
#> 13      FALSE   656
#> 14       TRUE  <NA>
#> 15       TRUE  <NA>
#> 16       TRUE  <NA>
#> 17       TRUE  <NA>
#> 18       TRUE  <NA>
#> 19       TRUE  <NA>
#> 20       TRUE  <NA>
#> 21      FALSE  <NA>
#> 22      FALSE  <NA>
#> 23      FALSE  <NA>
#> 24      FALSE  <NA>
#> 25      FALSE  <NA>
#> 26      FALSE  <NA>
#> 27       TRUE   289
#> 28       TRUE   117
#> 29         NA  8642
#> 30       TRUE   348
#> 31      FALSE  <NA>
#>                                                                                            description
#> 1                                                              Primary. The signature Notre Dame Blue.
#> 2                                                               Primary. The metallic Notre Dame Gold.
#> 3                                              Secondary. A deep blue between ND Blue and Bright Blue.
#> 4                                                                        Secondary. A bright mid blue.
#> 5                                                                     Secondary. A muted, darker gold.
#> 6                                    Secondary. A saturated gold; the anchor gold of the data palette.
#> 7                                                                     Secondary. The Notre Dame green.
#> 8                                                                       Secondary. A pale, soft green.
#> 9                                      Secondary. The one sky blue dark enough to use as a data color.
#> 10                                                 Near-white tint. A warm cream for page backgrounds.
#> 11                                                              Near-white tint. A lighter warm cream.
#> 12                                                                       Near-white tint. A pale blue.
#> 13                                                            Near-white tint. The lightest pale blue.
#> 14                                                   Former secondary Notre Dame color. A bright teal.
#> 15                               Former secondary Notre Dame color. A deep maroon (dark brownish red).
#> 16 Former tertiary Notre Dame color; the only former color of the tertiary tier. A dark violet purple.
#> 17                                            Former secondary Notre Dame color. A yellow-green olive.
#> 18                                              Former secondary Notre Dame color. A dark olive green.
#> 19                                        Former secondary Notre Dame color. A soft chartreuse yellow.
#> 20                                               Former secondary Notre Dame color. A warm near-black.
#> 21                      Informal background (not an ND brand color). A warm white just off pure white.
#> 22                                     Informal background (not an ND brand color). A soft warm white.
#> 23                     Informal background (not an ND brand color). A soft yellow just off pure white.
#> 24                             Informal background (not an ND brand color). A light, soft warm yellow.
#> 25                                         Informal background (not an ND brand color). A soft yellow.
#> 26                                  Informal background (not an ND brand color). A warmer soft yellow.
#> 27                                             Athletics. Notre Dame Blue, shared with the University.
#> 28                                                           Athletics. The warmer Standard Dome Gold.
#> 29                          Athletics. Metallic Dome Gold; a Pantone metallic ink with no digital hex.
#> 30                                 Athletics. Irish Green, one Pantone step from the University green.
#> 31                                                                                   Athletics. White.

nd_color() pulls hex values out of that table by name or by role, which is easier than going through a palette name when you only want a color or two:

nd_color("navy", "green")        # two colors by name
#> [1] "#0c2340" "#0a843d"
nd_color(role = "former")        # a whole role group
#> [1] "#36deb8" "#4c1c2a" "#342551" "#a8a600" "#456300" "#e3e361" "#1e1a13"

Because the result is a plain character vector of hexadecimal strings, it drops straight into a manual scale, a base graphics call, or anywhere else a color is expected:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5, alpha = 0.9) +
  scale_color_manual(values = nd_color("navy", "bright_gold", "green")) +
  labs(title = "Picked by name", x = "sepal length", y = "petal length",
       color = "species")

The table also carries the Notre Dame Athletics colors for reference. Athletics is a separate brand system: it shares ND Blue with the University, pairs it with a warmer Standard Dome Gold and a metallic gold that has no sanctioned digital value (so its hex is NA), and uses an Irish Green a single Pantone step from the University green. These Athletics colors are reference only and never enter nd_palette() or the scales.

nd_colors[nd_colors$brand == "athletics", c("name", "hex", "pms")]
#>                  name     hex  pms
#> 27    Notre Dame Blue #0c2340  289
#> 28 Standard Dome Gold #c99700  117
#> 29 Metallic Dome Gold    <NA> 8642
#> 30        Irish Green #00843d  348
#> 31              White #ffffff <NA>

Mixing in a color from outside the palette

Sometimes one category should be a specific color that is not a Notre Dame color at all — a rival’s color, another institution’s, or a flag. Because nd_palette() and nd_color() return plain hex strings, you can append your own color and hand the combined vector to scale_color_manual() (in ggplot2) or to a col argument (in base R): keep the Notre Dame colors for the brand-relevant groups and let the outside color stand apart.

The three below are not Notre Dame colors; they are defined here only for the example:

st_louis_blues <- "#2c5196"   # St. Louis Blues blue
irish_flag     <- "#009900"   # Irish flag green
dark_goldenrod <- "#b8860b"   # darkgoldenrod

In ggplot2, build the value vector from nd_color() plus the outside color and pass it to scale_color_manual():

values <- c("setosa"     = nd_color("navy"),
            "versicolor" = nd_color("bright_gold"),
            "virginica"  = st_louis_blues)        # the non-ND color
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5) +
  scale_color_manual(values = values) +
  labs(title = "Two Notre Dame colors plus one from outside the palette",
       x = "sepal length", y = "petal length", color = "species")

The same idea in base R — the first groups take Notre Dame colors and the last takes the outside color:

cols <- c(nd_palette(2), irish_flag)              # navy, bright gold, Irish green
barplot(c(8, 6, 9), col = cols, border = NA, names.arg = c("A", "B", "C"),
        main = "Notre Dame palette plus one outside color")

Other palettes

Former Notre Dame colors

The seven colors past the six leading brand colors are former Notre Dame colors: a historical, legacy set no longer in the current brand guide. In the former brand taxonomy six were secondary colors and purple was the lone tertiary color. They are approximate, legacy colors, included here only to extend the categorical palette past six, and are not part of the current Notre Dame brand. A color such as purple appearing in this set should not be read as a current, primary, or signature Notre Dame color. Because a key like maroon does not reveal its tier, the description column of nd_colors gives each one’s tier and hue (teal, for instance, is a former secondary color):

nd_colors[nd_colors$role == "former", c("name", "hex", "description")]
#>           name     hex
#> 14        Teal #36deb8
#> 15      Maroon #4c1c2a
#> 16      Purple #342551
#> 17 Light Olive #a8a600
#> 18  Dark Olive #456300
#> 19 Pale Yellow #e3e361
#> 20       Black #1e1a13
#>                                                                                            description
#> 14                                                   Former secondary Notre Dame color. A bright teal.
#> 15                               Former secondary Notre Dame color. A deep maroon (dark brownish red).
#> 16 Former tertiary Notre Dame color; the only former color of the tertiary tier. A dark violet purple.
#> 17                                            Former secondary Notre Dame color. A yellow-green olive.
#> 18                                              Former secondary Notre Dame color. A dark olive green.
#> 19                                        Former secondary Notre Dame color. A soft chartreuse yellow.
#> 20                                               Former secondary Notre Dame color. A warm near-black.

They round out the default palette beyond six, and they are also available on their own:

nd_palette(palette = "former")
#> [1] "#36deb8" "#4c1c2a" "#342551" "#a8a600" "#456300" "#e3e361" "#1e1a13"
show_palette(nd_palette(palette = "former"))

Selectable palettes other than "nd" are not wired into the scale_*_nd() functions; reach them through scale_fill_manual() or scale_color_manual():

ggplot(InsectSprays, aes(spray, count, fill = spray)) +
  geom_boxplot() +
  scale_fill_manual(values = nd_palette(palette = "former")) +
  labs(title = "Former Notre Dame palette", x = "spray", y = "count") +
  theme(legend.position = "none")

Colorblind-friendly Notre Dame colors

When categorical separation or colorblind safety matters more than leading with the brand colors, the "nd_cvd" palette reorders the actual Notre Dame colors (current and former) so that they stay distinguishable under the common forms of color vision deficiency, rather than borrowing a palette from another source. The order was derived by a greedy search that, at each step, adds the Notre Dame color that keeps the worst-case minimum CIE-Lab Delta-E as large as possible across normal vision and simulated deuteranopia, protanopia, and tritanopia. The ten anchors stay distinguishable for two through ten categories and lead with ND Blue and Bright Gold, the Notre Dame pair furthest apart under simulation.

show_palette(nd_palette(palette = "nd_cvd"))

The scale_*_nd() scales take it through their palette argument, so it drops into a plot the same way as the default:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5) +
  scale_color_nd(palette = "nd_cvd") +
  labs(title = "Colorblind-friendly Notre Dame colors", x = "sepal length",
       y = "petal length", color = "species")

To see why the ordering is safe, the rows below show the first eight "nd_cvd" colors as they appear to normal vision and as simulated for the three dichromacies with the colorspace package. The colors stay separated across all four rows:

cvd8 <- nd_palette(8, palette = "nd_cvd")
rows <- list("normal vision" = cvd8,
             deuteranopia     = colorspace::deutan(cvd8),
             protanopia       = colorspace::protan(cvd8),
             tritanopia       = colorspace::tritan(cvd8))
op <- par(mfrow = c(4, 1), mar = c(0.3, 6.5, 0.3, 0.3))
for (nm in names(rows)) {
  plot(NA, xlim = c(0, 8), ylim = c(0, 1), axes = FALSE, xlab = "", ylab = "",
       xaxs = "i", yaxs = "i")
  rect(0:7, 0, 1:8, 1, col = rows[[nm]], border = "white")
  mtext(nm, side = 2, las = 1, line = 0.5, cex = 0.85)
}

par(op)

Brand tints, backgrounds, and sequential ramps

Four Notre Dame brand tints are too light to read as data on a white background, so nd_palette() never returns them. They remain useful away from the data layer and are exported as nd_tints:

nd_tints
#>       warm_white light_warm_white  medium_sky_blue   light_sky_blue 
#>        "#efe9d9"        "#f8f4ec"        "#e1e8f2"        "#edf2f9"

A tint makes a panel background behind a single brand color:

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(color = nd_palette(1), size = 2.5) +
  labs(title = "A tint as a panel background", x = "weight", y = "mpg") +
  theme(panel.background = element_rect(fill = nd_tints[["light_sky_blue"]],
                                        color = NA))

Warm White (#efe9d9) is the warm counterpart: a cream that works as a full-page background behind dark brand colors. Set it on both panel.background and plot.background:

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(color = nd_palette(1), size = 2.5) +
  labs(title = "Warm White as a full background", x = "weight", y = "mpg") +
  theme(
    panel.background = element_rect(fill = nd_tints[["warm_white"]],
                                    color = NA),
    plot.background  = element_rect(fill = nd_tints[["warm_white"]],
                                    color = NA)
  )

Six informal soft backgrounds are also provided, as nd_informal_tints, running from a very light warm white to a warmer soft yellow. The two faint_* tints sit just off pure white (CIE L* about 99) for the lightest touch, where you want only a hint of warmth rather than plain white. These are not Notre Dame brand colors; they are alternatives to a white background for HTML reports, vignettes, and Shiny apps, offered alongside the official Warm Whites:

nd_informal_tints
#>       faint_white        soft_white      faint_yellow soft_yellow_light 
#>         "#fdfcfa"         "#faf7f1"         "#fefdf3"         "#fdf9e6" 
#>       soft_yellow  soft_yellow_warm 
#>         "#faf3d7"         "#f6edc6"
show_palette(nd_informal_tints, labels = names(nd_informal_tints),
             border = "grey80")

Set one on both panel.background and plot.background for a soft page behind a plot — here the new, even softer soft_white:

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(color = nd_palette(1), size = 2.5) +
  labs(title = "An informal soft-white background", x = "weight", y = "mpg") +
  theme(
    panel.background = element_rect(fill = nd_informal_tints[["soft_white"]],
                                    color = NA),
    plot.background  = element_rect(fill = nd_informal_tints[["soft_white"]],
                                    color = NA)
  )

A tint also makes a natural light end for a sequential ramp running to a dark brand color, for a continuous (rather than categorical) fill. Putting Bright Blue between the pale tint and navy keeps the midtones a clear blue instead of a washed-out gray:

ramp <- grDevices::colorRampPalette(
  c(nd_tints[["light_sky_blue"]], nd_color("bright_blue"), nd_color("navy")))(7)
show_palette(ramp)

Applied to faithfuld, a smoothed density of Old Faithful eruptions over eruption length and waiting time:

ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
  geom_raster() +
  scale_fill_gradientn(colors = ramp) +
  labs(title = "Old Faithful eruption density", x = "waiting", y = "eruptions")

For a diverging quantity — a correlation that runs from negative to positive, say — a ramp anchored at the two Notre Dame primaries (navy and gold) with a light tint in the middle covers both directions:

diverging <- grDevices::colorRampPalette(
  c(nd_color("navy"), nd_tints[["light_warm_white"]], nd_color("bright_gold")))(11)
show_palette(diverging)

The Statistical and psychometric visualization section above puts this ramp on a correlation matrix.

Theming R Markdown reports

The package’s stylesheet themes an HTML report in the same colors its figures use, so the prose and the plots match. The simplest route is the html_nd_document output format, which wires the stylesheet in for you:

output: NDPalette::html_nd_document

To add it to an existing html_document instead, point the css field at the shipped stylesheet:

output:
  html_document:
    css: !expr NDPalette::nd_css_path()

Either way you get navy headings, a bright-gold accent rule, navy table headers, brand-tinted callout boxes (.note, .important, .tip), and the soft Warm White page background; nd_css() also returns the stylesheet as a string if you would rather inline it or edit a copy. The same stylesheet drops into Shiny via shiny::includeCSS(nd_css_path()), and the Theming a Shiny app with NDPalette vignette walks through a complete app.

By default the stylesheet uses a system sans-serif font and loads nothing from the network. To pull two open-source fonts close in feel to the brand typefaces — Montserrat for body text, Zilla Slab for headings — generate the stylesheet with web_fonts = TRUE, which adds a Google Fonts import:

output:
  html_document:
    css: !expr NDPalette::nd_css(tempfile(fileext = ".css"), web_fonts = TRUE)

These are independent open-source typefaces, not the licensed Notre Dame brand fonts.

Pairing with a light theme

NDPalette is a color system, not a full visual theme: it does not ship a theme_nd(). The brand colors pair best with a light built-in ggplot2 theme, which keeps the figure background white or near-white so the colors carry the meaning. This vignette sets theme_minimal() once at the top with theme_set(theme_minimal(base_size = 12)), and every figure above inherits it.

theme_light() and theme_bw() are good light alternatives:

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5) +
  scale_color_nd() +
  labs(x = "sepal length", y = "petal length", color = "species") +
  theme_light(base_size = 12)

A note on fonts

Notre Dame’s brand typefaces are licensed, so NDPalette ships colors only, not fonts. You can pair the colors with free, openly licensed fonts that have a similar feel through ggplot2’s base_family argument. Montserrat is a geometric sans close to the brand sans, and Zilla Slab or Roboto Slab is a slab serif close to the brand display face. These are independent open-source typefaces, not the Notre Dame fonts. For an HTML report, nd_css(web_fonts = TRUE) pulls the same two open-source fonts from Google Fonts and applies them to the page (see Theming R Markdown reports above).

The recipe below uses the showtext and sysfonts packages to pull two free fonts from Google Fonts and render them. It is shown for reference and is not run when this vignette is built (it would require those two packages and a network connection).

# install.packages(c("showtext", "sysfonts"))
library(showtext)
sysfonts::font_add_google("Montserrat", "nd_sans")  # geometric sans
sysfonts::font_add_google("Zilla Slab", "nd_slab")  # slab serif
showtext_auto()

ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
  geom_point(size = 2.5) +
  scale_color_nd() +
  labs(title = "Notre Dame colors with a free, ND-evoking font",
       x = "sepal length", y = "petal length", color = "species") +
  theme_minimal(base_family = "nd_sans") +
  theme(plot.title = element_text(family = "nd_slab", face = "bold"))

If you already have a free font installed on your system, the systemfonts package can register it by name without a download, and the same base_family argument then applies it.