feat: add brand color system, showtext fonts, and dark theme
R-CMD-check / R CMD check (push) Failing after 1m39s
R-CMD-check / R CMD check (push) Failing after 1m39s
- R/colors.R: civilytics_colors named vector (from --cv-* CSS vars on civilytics.com), three palettes (main/sequential/diverging), scale_color_civilytics() and scale_fill_civilytics() for ggplot2 - R/fonts.R: auto-loads Inter + Libre Franklin from Google Fonts via showtext in .onLoad(); exports civilytics_load_fonts() for manual retry - R/theme.R: wires ggplot2 4.0 ink/paper/accent params into theme_grey() base, adds title_family + strip_color params; new theme_civilytics_dark() using navy_dark background and warm off-white text - R/logo.R: make_logo_grob(variant) accepts "light"/"dark" to select the white logo for dark-background compositions - DESCRIPTION: adds showtext, sysfonts; pins ggplot2 >= 4.0.0 - tests/testthat/test_theme.R: full test suite for colors, palettes, scales, both theme variants, and logo grob variants
This commit is contained in:
@@ -1,165 +1,279 @@
|
||||
#' Make the Civilytics plot theme
|
||||
#'
|
||||
#' @param font_size default 14, a number representing the base font for the theme
|
||||
#' @param font_family default "", a character for the font family to use in the theme
|
||||
#' @param line_size default 0.5, the line size to use for the theme
|
||||
#' @param rel_small default 12/14, the scale factor to create a small font from the base font_size
|
||||
#' @param rel_tiny default 11/14, the scale factor to create a tiny font from the base font_size
|
||||
#' @param rel_large default 16/14, the scale factor to create a large font from the base font_size
|
||||
#' @return a ggplot2 theme object suitable for combining with ggplot objects to theme them
|
||||
#' @export
|
||||
theme_civilytics <-
|
||||
function (font_size = 14,
|
||||
font_family = "",
|
||||
line_size = 0.5,
|
||||
rel_small = 12 / 14,
|
||||
rel_tiny = 11 / 14,
|
||||
rel_large = 16 / 14) {
|
||||
half_line <- font_size / 2
|
||||
small_size <- rel_small * font_size
|
||||
theme_grey(base_size = font_size, base_family = font_family) %+replace%
|
||||
theme(
|
||||
line = element_line(
|
||||
color = "black",
|
||||
linewidth = line_size,
|
||||
linetype = 1,
|
||||
lineend = "butt"
|
||||
),
|
||||
rect = element_rect(
|
||||
fill = NA,
|
||||
color = NA,
|
||||
linewidth = line_size,
|
||||
linetype = 1
|
||||
),
|
||||
text = element_text(
|
||||
family = font_family,
|
||||
face = "plain",
|
||||
color = "black",
|
||||
size = font_size,
|
||||
hjust = 0.5,
|
||||
vjust = 0.5,
|
||||
angle = 0,
|
||||
lineheight = 0.9,
|
||||
margin = margin(),
|
||||
debug = FALSE
|
||||
),
|
||||
axis.line = element_line(
|
||||
color = "black",
|
||||
linewidth = line_size,
|
||||
lineend = "square"
|
||||
),
|
||||
axis.line.x = NULL,
|
||||
axis.line.y = NULL,
|
||||
axis.text = element_text(color = "black",
|
||||
size = small_size),
|
||||
axis.text.x = element_text(margin = margin(t = small_size / 4),
|
||||
vjust = 1),
|
||||
axis.text.x.top = element_text(margin = margin(b = small_size / 4),
|
||||
vjust = 0),
|
||||
axis.text.y = element_text(margin = margin(r = small_size / 4),
|
||||
hjust = 1),
|
||||
axis.text.y.right = element_text(margin = margin(l = small_size / 4),
|
||||
hjust = 0),
|
||||
axis.ticks = element_line(color = "black",
|
||||
linewidth = line_size),
|
||||
axis.ticks.length = unit(half_line / 2,
|
||||
"pt"),
|
||||
axis.title.x = element_text(margin = margin(t = half_line / 2),
|
||||
vjust = 1),
|
||||
axis.title.x.top = element_text(margin = margin(b = half_line / 2),
|
||||
vjust = 0),
|
||||
axis.title.y = element_text(
|
||||
angle = 90,
|
||||
margin = margin(r = half_line /
|
||||
2),
|
||||
vjust = 1
|
||||
),
|
||||
axis.title.y.right = element_text(
|
||||
angle = -90,
|
||||
margin = margin(l = half_line / 2),
|
||||
vjust = 0
|
||||
),
|
||||
legend.background = element_blank(),
|
||||
legend.spacing = unit(font_size, "pt"),
|
||||
legend.spacing.x = NULL,
|
||||
legend.spacing.y = NULL,
|
||||
legend.margin = margin(0,
|
||||
0, 0, 0),
|
||||
legend.key = element_blank(),
|
||||
legend.key.size = unit(1.1 *
|
||||
font_size, "pt"),
|
||||
legend.key.height = NULL,
|
||||
legend.key.width = NULL,
|
||||
legend.text = element_text(size = rel(rel_small)),
|
||||
legend.title = element_text(hjust = 0),
|
||||
legend.position = "right",
|
||||
legend.direction = NULL,
|
||||
legend.justification = c("left",
|
||||
"center"),
|
||||
legend.box = NULL,
|
||||
legend.box.margin = margin(0,
|
||||
0, 0, 0),
|
||||
legend.box.background = element_blank(),
|
||||
legend.box.spacing = unit(font_size, "pt"),
|
||||
panel.background = element_blank(),
|
||||
panel.border = element_blank(),
|
||||
panel.grid = element_blank(),
|
||||
panel.grid.major = NULL,
|
||||
panel.grid.minor = NULL,
|
||||
panel.grid.major.x = NULL,
|
||||
panel.grid.major.y = NULL,
|
||||
panel.grid.minor.x = NULL,
|
||||
panel.grid.minor.y = NULL,
|
||||
panel.spacing = unit(half_line,
|
||||
"pt"),
|
||||
panel.spacing.x = NULL,
|
||||
panel.spacing.y = NULL,
|
||||
panel.ontop = FALSE,
|
||||
strip.background = element_rect(fill = "grey80"),
|
||||
strip.text = element_text(
|
||||
size = rel(rel_small),
|
||||
margin = margin(half_line / 2, half_line /
|
||||
2, half_line / 2,
|
||||
half_line / 2)
|
||||
),
|
||||
strip.text.x = NULL,
|
||||
strip.text.y = element_text(angle = -90),
|
||||
strip.placement = "inside",
|
||||
strip.placement.x = NULL,
|
||||
strip.placement.y = NULL,
|
||||
strip.switch.pad.grid = unit(half_line / 2,
|
||||
"pt"),
|
||||
strip.switch.pad.wrap = unit(half_line / 2,
|
||||
"pt"),
|
||||
plot.background = element_blank(),
|
||||
plot.title = element_text(
|
||||
face = "bold",
|
||||
size = rel(rel_large),
|
||||
hjust = 0,
|
||||
vjust = 1,
|
||||
margin = margin(b = half_line)
|
||||
),
|
||||
plot.subtitle = element_text(
|
||||
size = rel(rel_small),
|
||||
hjust = 0,
|
||||
vjust = 1,
|
||||
margin = margin(b = half_line)
|
||||
),
|
||||
plot.caption = element_text(
|
||||
size = rel(rel_tiny),
|
||||
hjust = 0, # set hjust to 0
|
||||
vjust = 1,
|
||||
lineheight = 1,
|
||||
margin = margin(t = half_line)
|
||||
),
|
||||
plot.tag = element_text(
|
||||
face = "bold",
|
||||
hjust = 0,
|
||||
vjust = 0.7
|
||||
),
|
||||
plot.tag.position = c(0, 1),
|
||||
plot.margin = margin(half_line,
|
||||
half_line, half_line, half_line),
|
||||
complete = TRUE
|
||||
)
|
||||
}
|
||||
#' Civilytics ggplot2 theme
|
||||
#'
|
||||
#' A complete ggplot2 theme built on [ggplot2::theme_grey()] using the
|
||||
#' Civilytics brand color palette and typography. Requires ggplot2 >= 4.0.0
|
||||
#' for the `ink`, `paper`, and `accent` base-theme parameters.
|
||||
#'
|
||||
#' Brand fonts (Inter for UI text, Libre Franklin for titles) are loaded
|
||||
#' automatically via [showtext] when the package is attached. Call
|
||||
#' [civilytics_load_fonts()] to reload them if needed.
|
||||
#'
|
||||
#' @param font_size Numeric. Base font size in points. Default `14`.
|
||||
#' @param font_family Character. Font family for body/axis text. Default
|
||||
#' `"Inter"` (loaded via showtext).
|
||||
#' @param title_family Character. Font family for plot titles and strip labels.
|
||||
#' Default `"Libre Franklin"` (loaded via showtext).
|
||||
#' @param line_size Numeric. Base line width. Default `0.5`.
|
||||
#' @param rel_small Numeric. Scale factor for small text relative to
|
||||
#' `font_size`. Default `12/14`.
|
||||
#' @param rel_tiny Numeric. Scale factor for tiny text relative to `font_size`.
|
||||
#' Default `11/14`.
|
||||
#' @param rel_large Numeric. Scale factor for large text (titles) relative to
|
||||
#' `font_size`. Default `16/14`.
|
||||
#' @param ink Character. Hex code for foreground/text color. Defaults to
|
||||
#' [civilytics_colors]`["ink"]` (`#0E1A2B`).
|
||||
#' @param paper Character. Hex code for background color. Defaults to
|
||||
#' [civilytics_colors]`["paper"]` (`#FAF7F2`).
|
||||
#' @param accent Character. Hex code for accent/highlight color. Defaults to
|
||||
#' [civilytics_colors]`["accent"]` (`#C25311`).
|
||||
#' @param strip_color Character. Hex code for facet strip background. Defaults
|
||||
#' to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).
|
||||
#'
|
||||
#' @return A complete ggplot2 [ggplot2::theme()] object.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' library(ggplot2)
|
||||
#' ggplot(mpg, aes(displ, hwy)) +
|
||||
#' geom_point() +
|
||||
#' theme_civilytics()
|
||||
#'
|
||||
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||
#' geom_point() +
|
||||
#' scale_color_civilytics() +
|
||||
#' theme_civilytics()
|
||||
theme_civilytics <- function(
|
||||
font_size = 14,
|
||||
font_family = CV_FONT_SANS,
|
||||
title_family = CV_FONT_DISPLAY,
|
||||
line_size = 0.5,
|
||||
rel_small = 12 / 14,
|
||||
rel_tiny = 11 / 14,
|
||||
rel_large = 16 / 14,
|
||||
ink = unname(civilytics_colors["ink"]),
|
||||
paper = unname(civilytics_colors["paper"]),
|
||||
accent = unname(civilytics_colors["accent"]),
|
||||
strip_color = unname(civilytics_colors["paper_2"])) {
|
||||
|
||||
half_line <- font_size / 2
|
||||
small_size <- rel_small * font_size
|
||||
|
||||
ggplot2::theme_grey(
|
||||
base_size = font_size,
|
||||
base_family = font_family,
|
||||
ink = ink,
|
||||
paper = paper,
|
||||
accent = accent
|
||||
) %+replace%
|
||||
ggplot2::theme(
|
||||
line = ggplot2::element_line(
|
||||
color = ink,
|
||||
linewidth = line_size,
|
||||
linetype = 1,
|
||||
lineend = "butt"
|
||||
),
|
||||
rect = ggplot2::element_rect(
|
||||
fill = NA,
|
||||
color = NA,
|
||||
linewidth = line_size,
|
||||
linetype = 1
|
||||
),
|
||||
text = ggplot2::element_text(
|
||||
family = font_family,
|
||||
face = "plain",
|
||||
color = ink,
|
||||
size = font_size,
|
||||
hjust = 0.5,
|
||||
vjust = 0.5,
|
||||
angle = 0,
|
||||
lineheight = 0.9,
|
||||
margin = ggplot2::margin(),
|
||||
debug = FALSE
|
||||
),
|
||||
# Axes
|
||||
axis.line = ggplot2::element_line(
|
||||
color = ink,
|
||||
linewidth = line_size,
|
||||
lineend = "square"
|
||||
),
|
||||
axis.line.x = NULL,
|
||||
axis.line.y = NULL,
|
||||
axis.text = ggplot2::element_text(
|
||||
color = ink,
|
||||
size = small_size
|
||||
),
|
||||
axis.text.x = ggplot2::element_text(
|
||||
margin = ggplot2::margin(t = small_size / 4),
|
||||
vjust = 1
|
||||
),
|
||||
axis.text.x.top = ggplot2::element_text(
|
||||
margin = ggplot2::margin(b = small_size / 4),
|
||||
vjust = 0
|
||||
),
|
||||
axis.text.y = ggplot2::element_text(
|
||||
margin = ggplot2::margin(r = small_size / 4),
|
||||
hjust = 1
|
||||
),
|
||||
axis.text.y.right = ggplot2::element_text(
|
||||
margin = ggplot2::margin(l = small_size / 4),
|
||||
hjust = 0
|
||||
),
|
||||
axis.ticks = ggplot2::element_line(
|
||||
color = ink,
|
||||
linewidth = line_size
|
||||
),
|
||||
axis.ticks.length = ggplot2::unit(half_line / 2, "pt"),
|
||||
axis.title.x = ggplot2::element_text(
|
||||
margin = ggplot2::margin(t = half_line / 2),
|
||||
vjust = 1
|
||||
),
|
||||
axis.title.x.top = ggplot2::element_text(
|
||||
margin = ggplot2::margin(b = half_line / 2),
|
||||
vjust = 0
|
||||
),
|
||||
axis.title.y = ggplot2::element_text(
|
||||
angle = 90,
|
||||
margin = ggplot2::margin(r = half_line / 2),
|
||||
vjust = 1
|
||||
),
|
||||
axis.title.y.right = ggplot2::element_text(
|
||||
angle = -90,
|
||||
margin = ggplot2::margin(l = half_line / 2),
|
||||
vjust = 0
|
||||
),
|
||||
# Legend
|
||||
legend.background = ggplot2::element_blank(),
|
||||
legend.spacing = ggplot2::unit(font_size, "pt"),
|
||||
legend.spacing.x = NULL,
|
||||
legend.spacing.y = NULL,
|
||||
legend.margin = ggplot2::margin(0, 0, 0, 0),
|
||||
legend.key = ggplot2::element_blank(),
|
||||
legend.key.size = ggplot2::unit(1.1 * font_size, "pt"),
|
||||
legend.key.height = NULL,
|
||||
legend.key.width = NULL,
|
||||
legend.text = ggplot2::element_text(size = ggplot2::rel(rel_small)),
|
||||
legend.title = ggplot2::element_text(hjust = 0),
|
||||
legend.position = "right",
|
||||
legend.direction = NULL,
|
||||
legend.justification = c("left", "center"),
|
||||
legend.box = NULL,
|
||||
legend.box.margin = ggplot2::margin(0, 0, 0, 0),
|
||||
legend.box.background = ggplot2::element_blank(),
|
||||
legend.box.spacing = ggplot2::unit(font_size, "pt"),
|
||||
# Panel
|
||||
panel.background = ggplot2::element_blank(),
|
||||
panel.border = ggplot2::element_blank(),
|
||||
panel.grid = ggplot2::element_blank(),
|
||||
panel.grid.major = NULL,
|
||||
panel.grid.minor = NULL,
|
||||
panel.grid.major.x = NULL,
|
||||
panel.grid.major.y = NULL,
|
||||
panel.grid.minor.x = NULL,
|
||||
panel.grid.minor.y = NULL,
|
||||
panel.spacing = ggplot2::unit(half_line, "pt"),
|
||||
panel.spacing.x = NULL,
|
||||
panel.spacing.y = NULL,
|
||||
panel.ontop = FALSE,
|
||||
# Facet strips — use brand paper_2 tint (overridden for dark variant)
|
||||
strip.background = ggplot2::element_rect(fill = strip_color),
|
||||
strip.text = ggplot2::element_text(
|
||||
family = title_family,
|
||||
size = ggplot2::rel(rel_small),
|
||||
margin = ggplot2::margin(
|
||||
half_line / 2, half_line / 2,
|
||||
half_line / 2, half_line / 2
|
||||
)
|
||||
),
|
||||
strip.text.x = NULL,
|
||||
strip.text.y = ggplot2::element_text(angle = -90),
|
||||
strip.placement = "inside",
|
||||
strip.placement.x = NULL,
|
||||
strip.placement.y = NULL,
|
||||
strip.switch.pad.grid = ggplot2::unit(half_line / 2, "pt"),
|
||||
strip.switch.pad.wrap = ggplot2::unit(half_line / 2, "pt"),
|
||||
# Plot-level
|
||||
plot.background = ggplot2::element_rect(fill = paper, color = NA),
|
||||
plot.title = ggplot2::element_text(
|
||||
family = title_family,
|
||||
face = "bold",
|
||||
size = ggplot2::rel(rel_large),
|
||||
hjust = 0,
|
||||
vjust = 1,
|
||||
margin = ggplot2::margin(b = half_line)
|
||||
),
|
||||
plot.subtitle = ggplot2::element_text(
|
||||
size = ggplot2::rel(rel_small),
|
||||
hjust = 0,
|
||||
vjust = 1,
|
||||
margin = ggplot2::margin(b = half_line)
|
||||
),
|
||||
plot.caption = ggplot2::element_text(
|
||||
size = ggplot2::rel(rel_tiny),
|
||||
hjust = 0,
|
||||
vjust = 1,
|
||||
lineheight = 1,
|
||||
margin = ggplot2::margin(t = half_line)
|
||||
),
|
||||
plot.tag = ggplot2::element_text(
|
||||
face = "bold",
|
||||
hjust = 0,
|
||||
vjust = 0.7
|
||||
),
|
||||
plot.tag.position = c(0, 1),
|
||||
plot.margin = ggplot2::margin(half_line, half_line, half_line, half_line),
|
||||
complete = TRUE
|
||||
)
|
||||
}
|
||||
|
||||
#' Dark variant of the Civilytics ggplot2 theme
|
||||
#'
|
||||
#' A convenience wrapper around [theme_civilytics()] with dark-background
|
||||
#' defaults: navy (`#1A2E4A`) background, warm off-white (`#FAF7F2`) text, and
|
||||
#' a lighter orange accent (`#E07840`) suitable for dark backgrounds. Pair with
|
||||
#' `make_logo_grob(variant = "dark")` to use the white logo.
|
||||
#'
|
||||
#' All parameters from [theme_civilytics()] are accepted and override the dark
|
||||
#' defaults.
|
||||
#'
|
||||
#' @inheritParams theme_civilytics
|
||||
#'
|
||||
#' @return A complete ggplot2 [ggplot2::theme()] object.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' library(ggplot2)
|
||||
#' ggplot(mpg, aes(displ, hwy)) +
|
||||
#' geom_point() +
|
||||
#' theme_civilytics_dark()
|
||||
#'
|
||||
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||
#' geom_point() +
|
||||
#' scale_color_civilytics() +
|
||||
#' theme_civilytics_dark()
|
||||
theme_civilytics_dark <- function(
|
||||
font_size = 14,
|
||||
font_family = CV_FONT_SANS,
|
||||
title_family = CV_FONT_DISPLAY,
|
||||
line_size = 0.5,
|
||||
rel_small = 12 / 14,
|
||||
rel_tiny = 11 / 14,
|
||||
rel_large = 16 / 14,
|
||||
ink = unname(civilytics_colors["paper"]),
|
||||
paper = unname(civilytics_colors["navy_dark"]),
|
||||
accent = unname(civilytics_colors["accent_dark"]),
|
||||
strip_color = unname(civilytics_colors["navy"])) {
|
||||
|
||||
theme_civilytics(
|
||||
font_size = font_size,
|
||||
font_family = font_family,
|
||||
title_family = title_family,
|
||||
line_size = line_size,
|
||||
rel_small = rel_small,
|
||||
rel_tiny = rel_tiny,
|
||||
rel_large = rel_large,
|
||||
ink = ink,
|
||||
paper = paper,
|
||||
accent = accent,
|
||||
strip_color = strip_color
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user