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.
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 youBase 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)R Markdown — theme a whole HTML report from one line in the YAML header:
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):
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):
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:
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:
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.
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")The same scales drop into the chart types people reach for most often. Every example below uses a built-in R dataset, so each one runs without any extra data.
A line chart with one series per group suits five groups.
Orange records the trunk circumference of five orange trees
as they age:
ggplot(Orange, aes(age, circumference, color = Tree)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_color_nd() +
labs(title = "Five series, as lines", x = "age (days)",
y = "trunk circumference (mm)", color = "tree")Grouped (dodged) bars color a second factor with
scale_fill_nd(). warpbreaks gives the number
of warp breaks on a loom by wool type (A or B) and tension (low, medium,
high):
ggplot(warpbreaks, aes(tension, breaks, fill = wool)) +
geom_bar(stat = "summary", fun = "mean", position = "dodge",
width = 0.7) +
scale_fill_nd() +
labs(title = "Grouped bars", x = "tension", y = "mean breaks",
fill = "wool")Overlapping densities put the palette on a filled area, where a
little transparency keeps the groups readable. ToothGrowth
is guinea-pig tooth length at three doses of vitamin C:
ggplot(ToothGrowth, aes(len, fill = factor(dose))) +
geom_density(alpha = 0.6) +
scale_fill_nd() +
labs(title = "Overlapping densities", x = "tooth length", y = "density",
fill = "dose")A scatter plot with a fitted line per group pairs
geom_smooth() with scale_color_nd().
ChickWeight follows the weight of chicks on four diets over
their first weeks:
ggplot(ChickWeight, aes(Time, weight, color = Diet)) +
geom_point(size = 1.6, alpha = 0.5) +
geom_smooth(method = "lm", formula = y ~ x, se = FALSE) +
scale_color_nd() +
labs(title = "Fitted lines by group", x = "time (days)",
y = "weight (g)", color = "diet")A stacked bar normalized to proportions
(position = "fill") shows how a second factor divides each
category, a common way to read composition. Here the passenger class
composition of the Titanic differs sharply by sex (the crew
was almost entirely male):
titanic <- aggregate(Freq ~ Sex + Class, data = as.data.frame(Titanic),
FUN = sum)
ggplot(titanic, aes(Sex, Freq, fill = Class)) +
geom_col(position = "fill") +
scale_fill_nd() +
labs(title = "Stacked proportions", x = NULL, y = "proportion",
fill = "passenger class")A lollipop chart — a tidy alternative to bars — colors the stems and
heads from the palette with scale_color_nd(). Here it shows
the mean final weight of chicks on each of the four
ChickWeight diets:
chick <- aggregate(weight ~ Diet, data = ChickWeight, FUN = mean)
ggplot(chick, aes(Diet, weight, color = Diet)) +
geom_segment(aes(xend = Diet, yend = 0), linewidth = 1.5) +
geom_point(size = 6) +
scale_color_nd() +
labs(title = "A lollipop chart", x = "diet", y = "mean weight (g)") +
theme(legend.position = "none")A bar chart in polar coordinates makes a radial chart, painted
straight from the palette — again the count of diamonds by
cut quality:
ggplot(diamonds, aes(cut, fill = cut)) +
geom_bar(width = 1, color = "white") +
scale_fill_nd() +
coord_polar() +
labs(title = "A radial bar chart", x = NULL, y = NULL, fill = "cut") +
theme_minimal(base_size = 12)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")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")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")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.
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" # darkgoldenrodIn 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")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")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.
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)
}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"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.
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:
To add it to an existing html_document instead, point
the css field at the shipped stylesheet:
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:
These are independent open-source typefaces, not the licensed Notre Dame brand fonts.
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)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.