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:
@@ -6,3 +6,6 @@
|
||||
^Makefile$
|
||||
^Dockerfile$
|
||||
^LICENSE\.md$
|
||||
^\.claude$
|
||||
^\.playwright-mcp$
|
||||
^civilytics-site\.png$
|
||||
|
||||
+4
-2
@@ -14,13 +14,15 @@ BugReports: https://gitea.civilytics.org/Civilytics/civilyticsR/issues
|
||||
Depends:
|
||||
R (>= 4.1.0)
|
||||
Imports:
|
||||
ggplot2,
|
||||
ggplot2 (>= 4.0.0),
|
||||
jpeg,
|
||||
png,
|
||||
stringr,
|
||||
gridExtra,
|
||||
grid,
|
||||
stringdist
|
||||
stringdist,
|
||||
showtext,
|
||||
sysfonts
|
||||
Encoding: UTF-8
|
||||
Suggests:
|
||||
testthat (>= 3.0.0),
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
export(add_logo)
|
||||
export(add_logo_ga)
|
||||
export(agresti_coull_interval)
|
||||
export(civilytics_colors)
|
||||
export(civilytics_load_fonts)
|
||||
export(civilytics_pal)
|
||||
export(clopper_pearson)
|
||||
export(countCleanr)
|
||||
export(countDots)
|
||||
@@ -32,15 +35,21 @@ export(rnh)
|
||||
export(round_to_nearest_half)
|
||||
export(safe_max)
|
||||
export(safe_ratio)
|
||||
export(scale_color_civilytics)
|
||||
export(scale_fill_civilytics)
|
||||
export(simpleCap)
|
||||
export(star_subs)
|
||||
export(theme_civilytics)
|
||||
export(theme_civilytics_dark)
|
||||
export(trim_max)
|
||||
export(waldInterval)
|
||||
export(z_gap_test)
|
||||
export(z_univariate)
|
||||
import(ggplot2)
|
||||
importFrom(ggplot2,annotation_custom)
|
||||
importFrom(ggplot2,ggplot)
|
||||
importFrom(ggplot2,theme)
|
||||
importFrom(ggplot2,theme_void)
|
||||
importFrom(graphics,rasterImage)
|
||||
importFrom(grid,grid.draw)
|
||||
importFrom(grid,rasterGrob)
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
#' Civilytics brand colors
|
||||
#'
|
||||
#' A named character vector of all Civilytics brand colors, matching the CSS
|
||||
#' custom properties defined on www.civilytics.com (`--cv-*` variables).
|
||||
#'
|
||||
#' @format A named character vector of hex color codes.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' civilytics_colors["accent"]
|
||||
#' civilytics_colors[c("ink", "paper")]
|
||||
civilytics_colors <- c(
|
||||
# Paper (backgrounds) — warm off-white scale
|
||||
paper = "#FAF7F2",
|
||||
paper_2 = "#F2EDE4",
|
||||
paper_3 = "#E6DFD1",
|
||||
# Rules (borders/dividers)
|
||||
rule = "#D6CEBD",
|
||||
rule_strong = "#B8AE97",
|
||||
# Ink (text / foreground) — dark-to-light navy-grey scale
|
||||
ink = "#0E1A2B",
|
||||
ink_2 = "#2B3A52",
|
||||
ink_3 = "#5A6A82",
|
||||
ink_4 = "#8C97AB",
|
||||
# Navy — primary brand blue
|
||||
navy = "#22406A",
|
||||
navy_dark = "#1A2E4A",
|
||||
# Accent — burnt orange
|
||||
accent = "#C25311",
|
||||
accent_dark = "#E07840",
|
||||
accent_50 = "#FDF1E4",
|
||||
accent_100 = "#FBE0C6",
|
||||
accent_200 = "#F8C8A3"
|
||||
)
|
||||
|
||||
# Internal named list of visualization palettes.
|
||||
# Each entry is a character vector of hex codes ordered for visual distinction.
|
||||
.cv_palettes <- list(
|
||||
# Qualitative: distinct hues for categorical data (up to 6 categories)
|
||||
main = unname(civilytics_colors[c(
|
||||
"navy_dark", "accent", "ink_3", "rule_strong", "navy", "accent_200"
|
||||
)]),
|
||||
# Sequential: light-to-dark navy for ordered/continuous data
|
||||
sequential = unname(civilytics_colors[c(
|
||||
"paper", "ink_4", "ink_3", "navy", "navy_dark"
|
||||
)]),
|
||||
# Diverging: orange <-> neutral <-> navy for data with a meaningful midpoint
|
||||
diverging = unname(civilytics_colors[c(
|
||||
"accent", "accent_200", "paper", "ink_4", "navy_dark"
|
||||
)])
|
||||
)
|
||||
|
||||
# Internal palette generator: returns a function(n) -> character vector.
|
||||
.cv_pal_fun <- function(name) {
|
||||
pal <- .cv_palettes[[name]]
|
||||
if (is.null(pal)) {
|
||||
stop(sprintf(
|
||||
"'%s' is not a valid Civilytics palette. Choose from: %s",
|
||||
name, paste(names(.cv_palettes), collapse = ", ")
|
||||
), call. = FALSE)
|
||||
}
|
||||
function(n) {
|
||||
if (n > length(pal)) grDevices::colorRampPalette(pal)(n) else pal[seq_len(n)]
|
||||
}
|
||||
}
|
||||
|
||||
#' Civilytics brand color palette
|
||||
#'
|
||||
#' Returns a character vector of hex codes from a named Civilytics palette.
|
||||
#' Available palettes: `"main"` (qualitative, up to 6), `"sequential"` (light
|
||||
#' to dark navy), `"diverging"` (orange–neutral–navy).
|
||||
#'
|
||||
#' @param name Character. Palette name: `"main"`, `"sequential"`, or
|
||||
#' `"diverging"`. Defaults to `"main"`.
|
||||
#' @param n Integer or `NULL`. Number of colors to return. If `NULL`, returns
|
||||
#' all colors in the palette. If `n` exceeds the number of defined stops,
|
||||
#' colors are interpolated via [grDevices::colorRampPalette()].
|
||||
#'
|
||||
#' @return A character vector of hex color codes.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' civilytics_pal() # all 6 qualitative colors
|
||||
#' civilytics_pal("sequential", n = 3)
|
||||
#' civilytics_pal("diverging", n = 5)
|
||||
civilytics_pal <- function(name = "main", n = NULL) {
|
||||
f <- .cv_pal_fun(name)
|
||||
len <- length(.cv_palettes[[name]])
|
||||
f(if (is.null(n)) len else n)
|
||||
}
|
||||
|
||||
#' Civilytics color scale for ggplot2
|
||||
#'
|
||||
#' Applies a Civilytics brand palette to the `colour` aesthetic. Use
|
||||
#' `discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
||||
#' continuous gradients.
|
||||
#'
|
||||
#' @param palette Character. Palette name passed to [civilytics_pal()].
|
||||
#' Defaults to `"main"`.
|
||||
#' @param discrete Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
||||
#' a continuous gradient via [ggplot2::scale_color_gradientn()].
|
||||
#' @param ... Additional arguments passed to the underlying ggplot2 scale
|
||||
#' function.
|
||||
#'
|
||||
#' @return A ggplot2 scale object.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' library(ggplot2)
|
||||
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||
#' geom_point() +
|
||||
#' scale_color_civilytics()
|
||||
#'
|
||||
#' ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
||||
#' geom_point() +
|
||||
#' scale_color_civilytics("sequential", discrete = FALSE)
|
||||
scale_color_civilytics <- function(palette = "main", discrete = TRUE, ...) {
|
||||
if (discrete) {
|
||||
ggplot2::discrete_scale("colour", palette = .cv_pal_fun(palette), ...)
|
||||
} else {
|
||||
ggplot2::scale_color_gradientn(
|
||||
colours = grDevices::colorRampPalette(.cv_palettes[[palette]])(256),
|
||||
...
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#' Civilytics fill scale for ggplot2
|
||||
#'
|
||||
#' Applies a Civilytics brand palette to the `fill` aesthetic. Use
|
||||
#' `discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
||||
#' continuous gradients.
|
||||
#'
|
||||
#' @param palette Character. Palette name passed to [civilytics_pal()].
|
||||
#' Defaults to `"main"`.
|
||||
#' @param discrete Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
||||
#' a continuous gradient via [ggplot2::scale_fill_gradientn()].
|
||||
#' @param ... Additional arguments passed to the underlying ggplot2 scale
|
||||
#' function.
|
||||
#'
|
||||
#' @return A ggplot2 scale object.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' library(ggplot2)
|
||||
#' ggplot(mpg, aes(class, fill = class)) +
|
||||
#' geom_bar() +
|
||||
#' scale_fill_civilytics()
|
||||
#'
|
||||
#' ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||
#' geom_tile() +
|
||||
#' scale_fill_civilytics("sequential", discrete = FALSE)
|
||||
scale_fill_civilytics <- function(palette = "main", discrete = TRUE, ...) {
|
||||
if (discrete) {
|
||||
ggplot2::discrete_scale("fill", palette = .cv_pal_fun(palette), ...)
|
||||
} else {
|
||||
ggplot2::scale_fill_gradientn(
|
||||
colours = grDevices::colorRampPalette(.cv_palettes[[palette]])(256),
|
||||
...
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Font family name constants used as defaults in theme functions.
|
||||
# These match the --cv-font-* CSS custom properties on www.civilytics.com.
|
||||
CV_FONT_DISPLAY <- "Libre Franklin" # headings / display text
|
||||
CV_FONT_SANS <- "Inter" # axis text, legends, UI elements
|
||||
CV_FONT_SERIF <- "Source Serif 4" # body prose (not used in default theme)
|
||||
|
||||
# Internal flag so civilytics_load_fonts() is idempotent within a session.
|
||||
.cv_fonts_loaded <- FALSE
|
||||
|
||||
#' Load Civilytics brand fonts
|
||||
#'
|
||||
#' Downloads Inter and Libre Franklin from Google Fonts via
|
||||
#' [sysfonts::font_add_google()], then calls [showtext::showtext_auto()] so
|
||||
#' that all graphics devices render text with those fonts. This is called
|
||||
#' automatically when the package loads; use this function to retry if the
|
||||
#' initial load failed (e.g., the machine was offline at load time).
|
||||
#'
|
||||
#' @return Invisibly returns `NULL`.
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' civilytics_load_fonts()
|
||||
#' }
|
||||
civilytics_load_fonts <- function() {
|
||||
sysfonts::font_add_google("Inter", family = "Inter")
|
||||
sysfonts::font_add_google("Libre Franklin", family = "Libre Franklin")
|
||||
showtext::showtext_auto()
|
||||
.cv_fonts_loaded <<- TRUE
|
||||
invisible(NULL)
|
||||
}
|
||||
|
||||
.onLoad <- function(libname, pkgname) {
|
||||
tryCatch(
|
||||
civilytics_load_fonts(),
|
||||
error = function(e) {
|
||||
# Store the failure so .onAttach can surface a message to the user.
|
||||
options(.civilytics_fonts_failed = TRUE)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
.onAttach <- function(libname, pkgname) {
|
||||
if (isTRUE(getOption(".civilytics_fonts_failed"))) {
|
||||
packageStartupMessage(
|
||||
"[civilytics] Brand fonts (Inter, Libre Franklin) could not be loaded ",
|
||||
"from Google Fonts. Charts will fall back to system fonts. ",
|
||||
"Call civilytics_load_fonts() once you have an internet connection."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -121,12 +121,14 @@ has_caption <- function(gg) {
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' library(ggplot2); library(grid)
|
||||
#' tmp_plot <- ggplot(mtcars) + aes(x = hp, y = disp) + geom_point() + theme_civilytics()
|
||||
#' tmp_logo <- make_logo_grob()
|
||||
#' plot_and_logo <- add_logo(tmp_plot, tmp_logo)
|
||||
#' grid.draw(plot_and_logo)
|
||||
#' dev.off()
|
||||
#' }
|
||||
add_logo_ga <- function(plot_list, logo, nrow = 1, widths = NULL, margin_param = NULL) {
|
||||
# Change position of logo depending on if plot has a caption
|
||||
if (!is.null(margin_param)) {
|
||||
@@ -157,17 +159,28 @@ add_logo_ga <- function(plot_list, logo, nrow = 1, widths = NULL, margin_param =
|
||||
|
||||
#' Get a Civilytics Logo grob
|
||||
#'
|
||||
#' @return a gg object which contains the logo file stored as a Grob suitable for manipulating in
|
||||
#' grid
|
||||
#' Returns a ggplot object containing the Civilytics logo as a rasterGrob,
|
||||
#' ready to compose with plots via [add_logo()] or [add_logo_ga()].
|
||||
#'
|
||||
#' @param variant Character. `"light"` (default) uses the standard dark logo
|
||||
#' suitable for light backgrounds. `"dark"` uses the white logo suitable for
|
||||
#' dark backgrounds (pairs with [theme_civilytics_dark()]).
|
||||
#'
|
||||
#' @return A ggplot object (class `"gg"`) containing the logo grob.
|
||||
#' @export
|
||||
#' @import ggplot2
|
||||
#' @importFrom ggplot2 ggplot theme_void annotation_custom
|
||||
#' @examples
|
||||
#' logo <- make_logo_grob()
|
||||
#' class(logo) # gg
|
||||
make_logo_grob <- function() {
|
||||
ggplot() +
|
||||
theme_void() +
|
||||
annotation_custom(get_png(system.file("img", "civilytics_logo.png",
|
||||
package = "civilytics")), xmin = 0.7, xmax = 1)
|
||||
#' logo <- make_logo_grob() # light background
|
||||
#' logo_dark <- make_logo_grob("dark") # dark background
|
||||
#' class(logo) # "gg" "ggplot"
|
||||
make_logo_grob <- function(variant = c("light", "dark")) {
|
||||
variant <- match.arg(variant)
|
||||
img_file <- if (variant == "dark") "civilytics_logo_white.png" else "civilytics_logo.png"
|
||||
ggplot2::ggplot() +
|
||||
ggplot2::theme_void() +
|
||||
ggplot2::annotation_custom(
|
||||
get_png(system.file("img", img_file, package = "civilytics")),
|
||||
xmin = 0.7, xmax = 1
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ Add a logo to a ggplot2 object
|
||||
The resulting object needs to be drawn to the screen using grid.draw()
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
library(ggplot2); library(grid)
|
||||
tmp_plot <- ggplot(mtcars) + aes(x = hp, y = disp) + geom_point() + theme_civilytics()
|
||||
tmp_logo <- make_logo_grob()
|
||||
@@ -34,3 +35,4 @@ plot_and_logo <- add_logo(tmp_plot, tmp_logo)
|
||||
grid.draw(plot_and_logo)
|
||||
dev.off()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/colors.R
|
||||
\name{civilytics_colors}
|
||||
\alias{civilytics_colors}
|
||||
\title{Civilytics brand colors}
|
||||
\format{
|
||||
A named character vector of hex color codes.
|
||||
}
|
||||
\usage{
|
||||
civilytics_colors
|
||||
}
|
||||
\description{
|
||||
A named character vector of all Civilytics brand colors, matching the CSS
|
||||
custom properties defined on www.civilytics.com (`--cv-*` variables).
|
||||
}
|
||||
\examples{
|
||||
civilytics_colors["accent"]
|
||||
civilytics_colors[c("ink", "paper")]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/fonts.R
|
||||
\name{civilytics_load_fonts}
|
||||
\alias{civilytics_load_fonts}
|
||||
\title{Load Civilytics brand fonts}
|
||||
\usage{
|
||||
civilytics_load_fonts()
|
||||
}
|
||||
\value{
|
||||
Invisibly returns `NULL`.
|
||||
}
|
||||
\description{
|
||||
Downloads Inter and Libre Franklin from Google Fonts via
|
||||
[sysfonts::font_add_google()], then calls [showtext::showtext_auto()] so
|
||||
that all graphics devices render text with those fonts. This is called
|
||||
automatically when the package loads; use this function to retry if the
|
||||
initial load failed (e.g., the machine was offline at load time).
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
civilytics_load_fonts()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/colors.R
|
||||
\name{civilytics_pal}
|
||||
\alias{civilytics_pal}
|
||||
\title{Civilytics brand color palette}
|
||||
\usage{
|
||||
civilytics_pal(name = "main", n = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{name}{Character. Palette name: `"main"`, `"sequential"`, or
|
||||
`"diverging"`. Defaults to `"main"`.}
|
||||
|
||||
\item{n}{Integer or `NULL`. Number of colors to return. If `NULL`, returns
|
||||
all colors in the palette. If `n` exceeds the number of defined stops,
|
||||
colors are interpolated via [grDevices::colorRampPalette()].}
|
||||
}
|
||||
\value{
|
||||
A character vector of hex color codes.
|
||||
}
|
||||
\description{
|
||||
Returns a character vector of hex codes from a named Civilytics palette.
|
||||
Available palettes: `"main"` (qualitative, up to 6), `"sequential"` (light
|
||||
to dark navy), `"diverging"` (orange–neutral–navy).
|
||||
}
|
||||
\examples{
|
||||
civilytics_pal() # all 6 qualitative colors
|
||||
civilytics_pal("sequential", n = 3)
|
||||
civilytics_pal("diverging", n = 5)
|
||||
}
|
||||
+12
-6
@@ -4,16 +4,22 @@
|
||||
\alias{make_logo_grob}
|
||||
\title{Get a Civilytics Logo grob}
|
||||
\usage{
|
||||
make_logo_grob()
|
||||
make_logo_grob(variant = c("light", "dark"))
|
||||
}
|
||||
\arguments{
|
||||
\item{variant}{Character. `"light"` (default) uses the standard dark logo
|
||||
suitable for light backgrounds. `"dark"` uses the white logo suitable for
|
||||
dark backgrounds (pairs with [theme_civilytics_dark()]).}
|
||||
}
|
||||
\value{
|
||||
a gg object which contains the logo file stored as a Grob suitable for manipulating in
|
||||
grid
|
||||
A ggplot object (class `"gg"`) containing the logo grob.
|
||||
}
|
||||
\description{
|
||||
Get a Civilytics Logo grob
|
||||
Returns a ggplot object containing the Civilytics logo as a rasterGrob,
|
||||
ready to compose with plots via [add_logo()] or [add_logo_ga()].
|
||||
}
|
||||
\examples{
|
||||
logo <- make_logo_grob()
|
||||
class(logo) # gg
|
||||
logo <- make_logo_grob() # light background
|
||||
logo_dark <- make_logo_grob("dark") # dark background
|
||||
class(logo) # "gg" "ggplot"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/colors.R
|
||||
\name{scale_color_civilytics}
|
||||
\alias{scale_color_civilytics}
|
||||
\title{Civilytics color scale for ggplot2}
|
||||
\usage{
|
||||
scale_color_civilytics(palette = "main", discrete = TRUE, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{palette}{Character. Palette name passed to [civilytics_pal()].
|
||||
Defaults to `"main"`.}
|
||||
|
||||
\item{discrete}{Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
||||
a continuous gradient via [ggplot2::scale_color_gradientn()].}
|
||||
|
||||
\item{...}{Additional arguments passed to the underlying ggplot2 scale
|
||||
function.}
|
||||
}
|
||||
\value{
|
||||
A ggplot2 scale object.
|
||||
}
|
||||
\description{
|
||||
Applies a Civilytics brand palette to the `colour` aesthetic. Use
|
||||
`discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
||||
continuous gradients.
|
||||
}
|
||||
\examples{
|
||||
library(ggplot2)
|
||||
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||
geom_point() +
|
||||
scale_color_civilytics()
|
||||
|
||||
ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
||||
geom_point() +
|
||||
scale_color_civilytics("sequential", discrete = FALSE)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/colors.R
|
||||
\name{scale_fill_civilytics}
|
||||
\alias{scale_fill_civilytics}
|
||||
\title{Civilytics fill scale for ggplot2}
|
||||
\usage{
|
||||
scale_fill_civilytics(palette = "main", discrete = TRUE, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{palette}{Character. Palette name passed to [civilytics_pal()].
|
||||
Defaults to `"main"`.}
|
||||
|
||||
\item{discrete}{Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
||||
a continuous gradient via [ggplot2::scale_fill_gradientn()].}
|
||||
|
||||
\item{...}{Additional arguments passed to the underlying ggplot2 scale
|
||||
function.}
|
||||
}
|
||||
\value{
|
||||
A ggplot2 scale object.
|
||||
}
|
||||
\description{
|
||||
Applies a Civilytics brand palette to the `fill` aesthetic. Use
|
||||
`discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
||||
continuous gradients.
|
||||
}
|
||||
\examples{
|
||||
library(ggplot2)
|
||||
ggplot(mpg, aes(class, fill = class)) +
|
||||
geom_bar() +
|
||||
scale_fill_civilytics()
|
||||
|
||||
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||
geom_tile() +
|
||||
scale_fill_civilytics("sequential", discrete = FALSE)
|
||||
}
|
||||
+53
-11
@@ -2,33 +2,75 @@
|
||||
% Please edit documentation in R/theme.R
|
||||
\name{theme_civilytics}
|
||||
\alias{theme_civilytics}
|
||||
\title{Make the Civilytics plot theme}
|
||||
\title{Civilytics ggplot2 theme}
|
||||
\usage{
|
||||
theme_civilytics(
|
||||
font_size = 14,
|
||||
font_family = "",
|
||||
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
|
||||
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"])
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{font_size}{default 14, a number representing the base font for the theme}
|
||||
\item{font_size}{Numeric. Base font size in points. Default `14`.}
|
||||
|
||||
\item{font_family}{default "", a character for the font family to use in the theme}
|
||||
\item{font_family}{Character. Font family for body/axis text. Default
|
||||
`"Inter"` (loaded via showtext).}
|
||||
|
||||
\item{line_size}{default 0.5, the line size to use for the theme}
|
||||
\item{title_family}{Character. Font family for plot titles and strip labels.
|
||||
Default `"Libre Franklin"` (loaded via showtext).}
|
||||
|
||||
\item{rel_small}{default 12/14, the scale factor to create a small font from the base font_size}
|
||||
\item{line_size}{Numeric. Base line width. Default `0.5`.}
|
||||
|
||||
\item{rel_tiny}{default 11/14, the scale factor to create a tiny font from the base font_size}
|
||||
\item{rel_small}{Numeric. Scale factor for small text relative to
|
||||
`font_size`. Default `12/14`.}
|
||||
|
||||
\item{rel_large}{default 16/14, the scale factor to create a large font from the base font_size}
|
||||
\item{rel_tiny}{Numeric. Scale factor for tiny text relative to `font_size`.
|
||||
Default `11/14`.}
|
||||
|
||||
\item{rel_large}{Numeric. Scale factor for large text (titles) relative to
|
||||
`font_size`. Default `16/14`.}
|
||||
|
||||
\item{ink}{Character. Hex code for foreground/text color. Defaults to
|
||||
[civilytics_colors]`["ink"]` (`#0E1A2B`).}
|
||||
|
||||
\item{paper}{Character. Hex code for background color. Defaults to
|
||||
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
||||
|
||||
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
||||
[civilytics_colors]`["accent"]` (`#C25311`).}
|
||||
|
||||
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
||||
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
||||
}
|
||||
\value{
|
||||
a ggplot2 theme object suitable for combining with ggplot objects to theme them
|
||||
A complete ggplot2 [ggplot2::theme()] object.
|
||||
}
|
||||
\description{
|
||||
Make the Civilytics plot 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.
|
||||
}
|
||||
\details{
|
||||
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.
|
||||
}
|
||||
\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()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/theme.R
|
||||
\name{theme_civilytics_dark}
|
||||
\alias{theme_civilytics_dark}
|
||||
\title{Dark variant of the Civilytics ggplot2 theme}
|
||||
\usage{
|
||||
theme_civilytics_dark(
|
||||
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"])
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{font_size}{Numeric. Base font size in points. Default `14`.}
|
||||
|
||||
\item{font_family}{Character. Font family for body/axis text. Default
|
||||
`"Inter"` (loaded via showtext).}
|
||||
|
||||
\item{title_family}{Character. Font family for plot titles and strip labels.
|
||||
Default `"Libre Franklin"` (loaded via showtext).}
|
||||
|
||||
\item{line_size}{Numeric. Base line width. Default `0.5`.}
|
||||
|
||||
\item{rel_small}{Numeric. Scale factor for small text relative to
|
||||
`font_size`. Default `12/14`.}
|
||||
|
||||
\item{rel_tiny}{Numeric. Scale factor for tiny text relative to `font_size`.
|
||||
Default `11/14`.}
|
||||
|
||||
\item{rel_large}{Numeric. Scale factor for large text (titles) relative to
|
||||
`font_size`. Default `16/14`.}
|
||||
|
||||
\item{ink}{Character. Hex code for foreground/text color. Defaults to
|
||||
[civilytics_colors]`["ink"]` (`#0E1A2B`).}
|
||||
|
||||
\item{paper}{Character. Hex code for background color. Defaults to
|
||||
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
||||
|
||||
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
||||
[civilytics_colors]`["accent"]` (`#C25311`).}
|
||||
|
||||
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
||||
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
||||
}
|
||||
\value{
|
||||
A complete ggplot2 [ggplot2::theme()] object.
|
||||
}
|
||||
\description{
|
||||
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.
|
||||
}
|
||||
\details{
|
||||
All parameters from [theme_civilytics()] are accepted and override the dark
|
||||
defaults.
|
||||
}
|
||||
\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()
|
||||
}
|
||||
+179
-12
@@ -1,12 +1,179 @@
|
||||
# context("Histograms")
|
||||
#
|
||||
# library(ggplot2)
|
||||
# disp_hist_base <- function() hist(mtcars$disp)
|
||||
# disp_hist_ggplot <- ggplot(mtcars, aes(disp)) + geom_histogram()
|
||||
#
|
||||
# test_that({
|
||||
# skip("Not currently working")
|
||||
# vdiffr::expect_doppelganger("Base graphics histogram", disp_hist_base)
|
||||
# vdiffr::expect_doppelganger("ggplot2 histogram", disp_hist_ggplot)
|
||||
#
|
||||
# })
|
||||
library(ggplot2)
|
||||
|
||||
# --- civilytics_colors -------------------------------------------------------
|
||||
|
||||
test_that("civilytics_colors is a named character vector of hex codes", {
|
||||
expect_type(civilytics_colors, "character")
|
||||
expect_named(civilytics_colors)
|
||||
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", civilytics_colors)))
|
||||
})
|
||||
|
||||
test_that("civilytics_colors contains expected brand keys", {
|
||||
expected <- c("paper", "ink", "accent", "navy", "navy_dark",
|
||||
"accent_dark", "paper_2", "rule")
|
||||
expect_true(all(expected %in% names(civilytics_colors)))
|
||||
})
|
||||
|
||||
test_that("civilytics_colors values match brand specification", {
|
||||
expect_equal(unname(civilytics_colors["paper"]), "#FAF7F2")
|
||||
expect_equal(unname(civilytics_colors["ink"]), "#0E1A2B")
|
||||
expect_equal(unname(civilytics_colors["accent"]), "#C25311")
|
||||
expect_equal(unname(civilytics_colors["navy_dark"]), "#1A2E4A")
|
||||
expect_equal(unname(civilytics_colors["accent_dark"]),"#E07840")
|
||||
})
|
||||
|
||||
# --- civilytics_pal ----------------------------------------------------------
|
||||
|
||||
test_that("civilytics_pal returns character vector of hex codes", {
|
||||
result <- civilytics_pal()
|
||||
expect_type(result, "character")
|
||||
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
||||
})
|
||||
|
||||
test_that("civilytics_pal respects n argument", {
|
||||
expect_length(civilytics_pal("main", n = 3), 3)
|
||||
expect_length(civilytics_pal("sequential", n = 5), 5)
|
||||
})
|
||||
|
||||
test_that("civilytics_pal interpolates when n exceeds palette size", {
|
||||
result <- civilytics_pal("main", n = 10)
|
||||
expect_length(result, 10)
|
||||
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
||||
})
|
||||
|
||||
test_that("civilytics_pal errors on unknown palette name", {
|
||||
expect_error(civilytics_pal("nope"), "'nope' is not a valid Civilytics palette")
|
||||
})
|
||||
|
||||
test_that("civilytics_pal supports all three named palettes", {
|
||||
expect_no_error(civilytics_pal("main"))
|
||||
expect_no_error(civilytics_pal("sequential"))
|
||||
expect_no_error(civilytics_pal("diverging"))
|
||||
})
|
||||
|
||||
# --- scale_color/fill_civilytics ---------------------------------------------
|
||||
|
||||
test_that("scale_color_civilytics returns a Scale object (discrete)", {
|
||||
sc <- scale_color_civilytics()
|
||||
expect_s3_class(sc, "Scale")
|
||||
})
|
||||
|
||||
test_that("scale_color_civilytics returns a Scale object (continuous)", {
|
||||
sc <- scale_color_civilytics("sequential", discrete = FALSE)
|
||||
expect_s3_class(sc, "Scale")
|
||||
})
|
||||
|
||||
test_that("scale_fill_civilytics returns a Scale object (discrete)", {
|
||||
sc <- scale_fill_civilytics()
|
||||
expect_s3_class(sc, "Scale")
|
||||
})
|
||||
|
||||
test_that("scale_fill_civilytics returns a Scale object (continuous)", {
|
||||
sc <- scale_fill_civilytics("sequential", discrete = FALSE)
|
||||
expect_s3_class(sc, "Scale")
|
||||
})
|
||||
|
||||
test_that("scale_color_civilytics works in a ggplot", {
|
||||
p <- ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||
geom_point() +
|
||||
scale_color_civilytics()
|
||||
expect_no_error(ggplot_build(p))
|
||||
})
|
||||
|
||||
test_that("scale_fill_civilytics works in a ggplot", {
|
||||
p <- ggplot(mpg, aes(class, fill = class)) +
|
||||
geom_bar() +
|
||||
scale_fill_civilytics()
|
||||
expect_no_error(ggplot_build(p))
|
||||
})
|
||||
|
||||
# --- theme_civilytics --------------------------------------------------------
|
||||
|
||||
test_that("theme_civilytics returns a complete ggplot2 theme", {
|
||||
th <- theme_civilytics()
|
||||
expect_s3_class(th, "theme")
|
||||
expect_true(attr(th, "complete"))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics applies to a ggplot without error", {
|
||||
p <- ggplot(mpg, aes(displ, hwy)) +
|
||||
geom_point() +
|
||||
theme_civilytics()
|
||||
expect_no_error(ggplot_build(p))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics uses brand ink color for text", {
|
||||
th <- theme_civilytics()
|
||||
expect_equal(th$text$colour, unname(civilytics_colors["ink"]))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics uses brand paper color for plot background", {
|
||||
th <- theme_civilytics()
|
||||
expect_equal(th$plot.background$fill, unname(civilytics_colors["paper"]))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics uses paper_2 for strip background by default", {
|
||||
th <- theme_civilytics()
|
||||
expect_equal(th$strip.background$fill, unname(civilytics_colors["paper_2"]))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics strip_color parameter is respected", {
|
||||
th <- theme_civilytics(strip_color = "#FF0000")
|
||||
expect_equal(th$strip.background$fill, "#FF0000")
|
||||
})
|
||||
|
||||
test_that("theme_civilytics font_size parameter scales text elements", {
|
||||
th_big <- theme_civilytics(font_size = 20)
|
||||
th_sml <- theme_civilytics(font_size = 10)
|
||||
expect_gt(th_big$text$size, th_sml$text$size)
|
||||
})
|
||||
|
||||
# --- theme_civilytics_dark ---------------------------------------------------
|
||||
|
||||
test_that("theme_civilytics_dark returns a complete ggplot2 theme", {
|
||||
th <- theme_civilytics_dark()
|
||||
expect_s3_class(th, "theme")
|
||||
expect_true(attr(th, "complete"))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics_dark applies to a ggplot without error", {
|
||||
p <- ggplot(mpg, aes(displ, hwy)) +
|
||||
geom_point() +
|
||||
theme_civilytics_dark()
|
||||
expect_no_error(ggplot_build(p))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics_dark uses paper color as ink (light text on dark bg)", {
|
||||
th <- theme_civilytics_dark()
|
||||
expect_equal(th$text$colour, unname(civilytics_colors["paper"]))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics_dark uses navy_dark as background", {
|
||||
th <- theme_civilytics_dark()
|
||||
expect_equal(th$plot.background$fill, unname(civilytics_colors["navy_dark"]))
|
||||
})
|
||||
|
||||
test_that("theme_civilytics_dark uses navy for strip background", {
|
||||
th <- theme_civilytics_dark()
|
||||
expect_equal(th$strip.background$fill, unname(civilytics_colors["navy"]))
|
||||
})
|
||||
|
||||
# --- make_logo_grob ----------------------------------------------------------
|
||||
|
||||
test_that("make_logo_grob returns a gg object for light variant", {
|
||||
logo <- make_logo_grob("light")
|
||||
expect_s3_class(logo, "gg")
|
||||
})
|
||||
|
||||
test_that("make_logo_grob returns a gg object for dark variant", {
|
||||
logo <- make_logo_grob("dark")
|
||||
expect_s3_class(logo, "gg")
|
||||
})
|
||||
|
||||
test_that("make_logo_grob defaults to light variant", {
|
||||
expect_no_error(make_logo_grob())
|
||||
})
|
||||
|
||||
test_that("make_logo_grob errors on invalid variant", {
|
||||
expect_error(make_logo_grob("purple"))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user