feat: expand color system to 53 colors/10 palettes, add grid/slide themes, and README gallery
R-CMD-check / R CMD check (push) Successful in 3m41s
Replace the 16-color / 3-palette system with the full Civilytics design system: 53 named colors (full navy, ember, violet ramps plus teal, plum, moss, brass supporting hues) and 10 visualization palettes (qual, qual_warm, qual_cool, seq_ember, seq_navy, seq_violet, seq_paper_ink, div_navy_ember, div_violet_ember). Enrich theme_civilytics() with grid and paper_bg parameters, add theme_civilytics_slide() for presentations, and load all four brand fonts (Inter, Libre Franklin, Source Serif 4, JetBrains Mono). Add README.Rmd with rendered gallery showing all palettes, theme variants, grid options, dark/slide modes, facets, and scale usage.
@@ -1,13 +1,15 @@
|
|||||||
Package: civilytics
|
Package: civilytics
|
||||||
Type: Package
|
Type: Package
|
||||||
Title: Utilities Functions for Civilytics
|
Title: Brand Themes, Color Palettes, and Utility Functions for Civilytics
|
||||||
Version: 0.2.0
|
Version: 0.2.0
|
||||||
Authors@R:
|
Authors@R:
|
||||||
person("Jared", "E. Knowles", email = "jared@civilytics.com",
|
person("Jared", "E. Knowles", email = "jared@civilytics.com",
|
||||||
role = c("aut", "cre"))
|
role = c("aut", "cre"))
|
||||||
Description: House R functions for Civilytics Consulting LLC. This package
|
Description: Provides a complete ggplot2 brand theme system for Civilytics
|
||||||
implements a variety of useful functions for creating and branding analyses
|
Consulting LLC, including editorial light, dark, and slide-optimized themes;
|
||||||
produced by Civilytics Consulting LLC.
|
10 curated color palettes for qualitative, sequential, and diverging data;
|
||||||
|
logo composition utilities; and data-wrangling helpers for public-sector
|
||||||
|
analysis.
|
||||||
License: LGPL (>= 3)
|
License: LGPL (>= 3)
|
||||||
URL: https://gitea.civilytics.org/Civilytics/civilyticsR
|
URL: https://gitea.civilytics.org/Civilytics/civilyticsR
|
||||||
BugReports: https://gitea.civilytics.org/Civilytics/civilyticsR/issues
|
BugReports: https://gitea.civilytics.org/Civilytics/civilyticsR/issues
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export(civilytics_colors)
|
|||||||
export(civilytics_load_fonts)
|
export(civilytics_load_fonts)
|
||||||
export(civilytics_logo)
|
export(civilytics_logo)
|
||||||
export(civilytics_pal)
|
export(civilytics_pal)
|
||||||
|
export(civilytics_palette)
|
||||||
|
export(civilytics_palettes)
|
||||||
export(clopper_pearson)
|
export(clopper_pearson)
|
||||||
export(countCleanr)
|
export(countCleanr)
|
||||||
export(countDots)
|
export(countDots)
|
||||||
@@ -42,6 +44,7 @@ export(simpleCap)
|
|||||||
export(star_subs)
|
export(star_subs)
|
||||||
export(theme_civilytics)
|
export(theme_civilytics)
|
||||||
export(theme_civilytics_dark)
|
export(theme_civilytics_dark)
|
||||||
|
export(theme_civilytics_slide)
|
||||||
export(trim_max)
|
export(trim_max)
|
||||||
export(waldInterval)
|
export(waldInterval)
|
||||||
export(z_gap_test)
|
export(z_gap_test)
|
||||||
|
|||||||
@@ -1,3 +1,24 @@
|
|||||||
|
#' @description
|
||||||
|
#' Provides a complete ggplot2 brand theme system for Civilytics Consulting,
|
||||||
|
#' including editorial light, dark, and slide-optimized themes; 10 curated
|
||||||
|
#' color palettes; logo composition; and data-wrangling helpers for
|
||||||
|
#' public-sector analysis.
|
||||||
|
#'
|
||||||
|
#' @section Themes:
|
||||||
|
#' \itemize{
|
||||||
|
#' \item [theme_civilytics()] -- editorial theme with warm paper background
|
||||||
|
#' \item [theme_civilytics_dark()] -- navy background variant
|
||||||
|
#' \item [theme_civilytics_slide()] -- transparent background, larger text
|
||||||
|
#' }
|
||||||
|
#'
|
||||||
|
#' @section Color palettes:
|
||||||
|
#' \itemize{
|
||||||
|
#' \item [civilytics_colors] -- 53 named brand colors
|
||||||
|
#' \item [civilytics_palettes] -- 10 visualization palettes
|
||||||
|
#' \item [civilytics_palette()] -- extract colors by palette name
|
||||||
|
#' \item [scale_color_civilytics()] / [scale_fill_civilytics()] -- ggplot2 scales
|
||||||
|
#' }
|
||||||
|
#'
|
||||||
#' @keywords internal
|
#' @keywords internal
|
||||||
#' @importFrom stats qbeta
|
#' @importFrom stats qbeta
|
||||||
#' @importFrom stats qnorm
|
#' @importFrom stats qnorm
|
||||||
@@ -8,7 +29,5 @@
|
|||||||
NULL
|
NULL
|
||||||
|
|
||||||
# state.abb and state.name are lazy data from the datasets package (base R).
|
# state.abb and state.name are lazy data from the datasets package (base R).
|
||||||
# They cannot be imported via @importFrom — suppress the R CMD check NOTE here.
|
# They cannot be imported via @importFrom -- suppress the R CMD check NOTE here.
|
||||||
utils::globalVariables(c("state.abb", "state.name"))
|
utils::globalVariables(c("state.abb", "state.name"))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,106 +1,229 @@
|
|||||||
#' Civilytics brand colors
|
#' Civilytics brand colors
|
||||||
#'
|
#'
|
||||||
#' A named character vector of all Civilytics brand colors, matching the CSS
|
#' A named character vector of all Civilytics brand colors, matching the CSS
|
||||||
#' custom properties defined on www.civilytics.com (`--cv-*` variables).
|
#' custom properties in the Civilytics design system (`--cv-*` variables).
|
||||||
|
#' Includes full ramps for navy, ember, and violet, plus supporting hues
|
||||||
|
#' (teal, plum, moss, brass) and semantic status colors.
|
||||||
#'
|
#'
|
||||||
#' @format A named character vector of hex color codes.
|
#' @format A named character vector of hex color codes.
|
||||||
#' @export
|
#' @export
|
||||||
#'
|
#'
|
||||||
#' @examples
|
#' @examples
|
||||||
#' civilytics_colors["accent"]
|
#' civilytics_colors["ink"]
|
||||||
#' civilytics_colors[c("ink", "paper")]
|
#' civilytics_colors[c("navy_600", "ember_600", "teal_600")]
|
||||||
civilytics_colors <- c(
|
civilytics_colors <- c(
|
||||||
# Paper (backgrounds) — warm off-white scale
|
# Neutrals — warm paper -> civic ink
|
||||||
paper = "#FAF7F2",
|
paper = "#FAF7F2",
|
||||||
paper_2 = "#F2EDE4",
|
paper_2 = "#F2EDE4",
|
||||||
paper_3 = "#E6DFD1",
|
paper_3 = "#E6DFD1",
|
||||||
# Rules (borders/dividers)
|
|
||||||
rule = "#D6CEBD",
|
rule = "#D6CEBD",
|
||||||
rule_strong = "#B8AE97",
|
rule_strong = "#B8AE97",
|
||||||
# Ink (text / foreground) — dark-to-light navy-grey scale
|
|
||||||
ink = "#0E1A2B",
|
ink = "#0E1A2B",
|
||||||
ink_2 = "#2B3A52",
|
ink_2 = "#2B3A52",
|
||||||
ink_3 = "#5A6A82",
|
ink_3 = "#5A6A82",
|
||||||
ink_4 = "#8C97AB",
|
ink_4 = "#8C97AB",
|
||||||
# Navy — primary brand blue
|
|
||||||
navy = "#22406A",
|
# Navy — civic authority (primary brand blue)
|
||||||
navy_dark = "#1A2E4A",
|
navy_900 = "#0E1A2B", navy_800 = "#132339", navy_700 = "#1A2E4A",
|
||||||
# Accent — burnt orange
|
navy_600 = "#22406A", navy_500 = "#2E5590", navy_400 = "#4A74B0",
|
||||||
accent = "#C25311",
|
navy_300 = "#7A9BCA", navy_200 = "#B3C6E0", navy_100 = "#DDE6F2",
|
||||||
accent_dark = "#E07840",
|
navy_50 = "#EEF3FA",
|
||||||
accent_50 = "#FDF1E4",
|
|
||||||
accent_100 = "#FBE0C6",
|
# Ember — warm orange accent
|
||||||
accent_200 = "#F8C8A3"
|
ember_900 = "#451A00", ember_800 = "#6B2B00", ember_700 = "#923D00",
|
||||||
|
ember_600 = "#C25311", ember_500 = "#DB6C25", ember_400 = "#EA8A49",
|
||||||
|
ember_300 = "#F2A976", ember_200 = "#F8C8A3", ember_100 = "#FBE0C6",
|
||||||
|
ember_50 = "#FDF1E4",
|
||||||
|
|
||||||
|
# Violet — extended supporting
|
||||||
|
|
||||||
|
violet_900 = "#19102E", violet_800 = "#2A1B4D", violet_700 = "#3D2A6B",
|
||||||
|
violet_600 = "#5C3A8A", violet_500 = "#7556A8", violet_400 = "#9A7EC2",
|
||||||
|
violet_300 = "#BDA8DA", violet_200 = "#DCD0EC", violet_100 = "#F1ECF8",
|
||||||
|
|
||||||
|
# Supporting — muted editorial hues for data viz
|
||||||
|
teal_600 = "#1F6F70", teal_300 = "#7CB3B3", teal_100 = "#D3E6E6",
|
||||||
|
plum_600 = "#6B3A5E", plum_300 = "#B392A7", plum_100 = "#E7DAE1",
|
||||||
|
moss_600 = "#4A6B2F", moss_300 = "#9CB47D", moss_100 = "#DEE8CF",
|
||||||
|
brass_600 = "#B8751C", brass_100 = "#F9E6C8",
|
||||||
|
|
||||||
|
# Semantic status colors
|
||||||
|
success = "#4A6B2F",
|
||||||
|
warning = "#9A5F18",
|
||||||
|
|
||||||
|
danger = "#A6271D",
|
||||||
|
info = "#2E5590"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Internal named list of visualization palettes.
|
|
||||||
# Each entry is a character vector of hex codes ordered for visual distinction.
|
#' Civilytics visualization palettes
|
||||||
.cv_palettes <- list(
|
#'
|
||||||
# Qualitative: distinct hues for categorical data (up to 6 categories)
|
#' Named list of curated color palettes for data visualization. Qualitative
|
||||||
main = unname(civilytics_colors[c(
|
#' palettes use distinct hues for categorical data; sequential palettes ramp
|
||||||
"navy_dark", "accent", "ink_3", "rule_strong", "navy", "accent_200"
|
#' through a single hue for ordered data; diverging palettes fan out from a
|
||||||
)]),
|
#' neutral midpoint.
|
||||||
# Sequential: light-to-dark navy for ordered/continuous data
|
#'
|
||||||
sequential = unname(civilytics_colors[c(
|
#' @format A named list of character vectors of hex color codes.
|
||||||
"paper", "ink_4", "ink_3", "navy", "navy_dark"
|
#'
|
||||||
)]),
|
#' @section Qualitative (categorical data):
|
||||||
# Diverging: orange <-> neutral <-> navy for data with a meaningful midpoint
|
#' \describe{
|
||||||
diverging = unname(civilytics_colors[c(
|
#' \item{`qual`}{7 distinct hues: navy, ember, plum, violet, red, teal, ink-3}
|
||||||
"accent", "accent_200", "paper", "ink_4", "navy_dark"
|
#' \item{`qual_warm`}{Warm-leaning: ember, red, magenta, plum, violet}
|
||||||
)])
|
#' \item{`qual_cool`}{Cool-leaning: navy, violet, plum, teal}
|
||||||
|
#' }
|
||||||
|
#'
|
||||||
|
#' @section Sequential (ordered/continuous data):
|
||||||
|
#' \describe{
|
||||||
|
#' \item{`seq_ember`}{Light to dark ember (9 stops)}
|
||||||
|
#' \item{`seq_navy`}{Light to dark navy (9 stops)}
|
||||||
|
#' \item{`seq_violet`}{Light to dark violet (9 stops)}
|
||||||
|
#' \item{`seq_paper_ink`}{Paper through ember/plum to ink (8 stops, good for heatmaps)}
|
||||||
|
#' }
|
||||||
|
#'
|
||||||
|
#' @section Diverging (data with a meaningful midpoint):
|
||||||
|
#' \describe{
|
||||||
|
#' \item{`div_navy_ember`}{Navy <-> paper <-> ember (9 stops)}
|
||||||
|
#' \item{`div_violet_ember`}{Violet <-> paper <-> ember (9 stops)}
|
||||||
|
#' }
|
||||||
|
#'
|
||||||
|
#' @export
|
||||||
|
#'
|
||||||
|
#' @examples
|
||||||
|
#' names(civilytics_palettes)
|
||||||
|
#' civilytics_palettes[["qual"]]
|
||||||
|
civilytics_palettes <- list(
|
||||||
|
# -- Qualitative --
|
||||||
|
qual = c(
|
||||||
|
"#22406A",
|
||||||
|
"#C25311",
|
||||||
|
"#6B3A5E",
|
||||||
|
"#3D2A6B",
|
||||||
|
"#A6271D",
|
||||||
|
"#1F6F70",
|
||||||
|
"#5A6A82"
|
||||||
|
),
|
||||||
|
qual_warm = c(
|
||||||
|
"#C25311",
|
||||||
|
"#A6271D",
|
||||||
|
"#923D00",
|
||||||
|
"#B8366B",
|
||||||
|
"#6B3A5E",
|
||||||
|
"#DB6C25",
|
||||||
|
"#3D2A6B"
|
||||||
|
),
|
||||||
|
qual_cool = c(
|
||||||
|
"#22406A",
|
||||||
|
"#3D2A6B",
|
||||||
|
"#6B3A5E",
|
||||||
|
"#1F6F70",
|
||||||
|
"#2E5590",
|
||||||
|
"#7556A8",
|
||||||
|
"#5A6A82"
|
||||||
|
),
|
||||||
|
# -- Sequential --
|
||||||
|
seq_ember = c(
|
||||||
|
"#FDF1E4", "#FBE0C6", "#F8C8A3", "#F2A976",
|
||||||
|
"#EA8A49", "#DB6C25", "#C25311", "#923D00", "#451A00"
|
||||||
|
),
|
||||||
|
seq_navy = c(
|
||||||
|
"#EEF3FA", "#DDE6F2", "#B3C6E0", "#7A9BCA",
|
||||||
|
"#4A74B0", "#2E5590", "#22406A", "#1A2E4A", "#0E1A2B"
|
||||||
|
),
|
||||||
|
seq_violet = c(
|
||||||
|
"#F1ECF8", "#DCD0EC", "#BDA8DA", "#9A7EC2",
|
||||||
|
"#7556A8", "#5C3A8A", "#3D2A6B", "#2A1B4D", "#19102E"
|
||||||
|
),
|
||||||
|
seq_paper_ink = c(
|
||||||
|
"#FAF7F2", "#F8C8A3", "#EA8A49", "#C25311",
|
||||||
|
"#A6271D", "#6B3A5E", "#3D2A6B", "#0E1A2B"
|
||||||
|
),
|
||||||
|
# -- Diverging --
|
||||||
|
div_navy_ember = c(
|
||||||
|
"#0E1A2B", "#22406A", "#4A74B0", "#B3C6E0",
|
||||||
|
"#FAF7F2",
|
||||||
|
"#F8C8A3", "#EA8A49", "#C25311", "#6B2B00"
|
||||||
|
),
|
||||||
|
div_violet_ember = c(
|
||||||
|
"#19102E", "#3D2A6B", "#7556A8", "#BDA8DA",
|
||||||
|
"#FAF7F2",
|
||||||
|
"#F8C8A3", "#EA8A49", "#C25311", "#6B2B00"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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
|
#' Get colors from a Civilytics palette
|
||||||
#'
|
#'
|
||||||
#' Returns a character vector of hex codes from a named Civilytics palette.
|
#' Returns a character vector of hex codes from a named Civilytics palette.
|
||||||
#' Available palettes: `"main"` (qualitative, up to 6), `"sequential"` (light
|
#' For qualitative palettes, colors beyond the palette length recycle with a
|
||||||
#' to dark navy), `"diverging"` (orange–neutral–navy).
|
#' warning. For sequential and diverging palettes, colors are interpolated
|
||||||
|
#' via [grDevices::colorRampPalette()].
|
||||||
#'
|
#'
|
||||||
#' @param name Character. Palette name: `"main"`, `"sequential"`, or
|
#' @param name Character. Palette name. See `names(civilytics_palettes)`.
|
||||||
#' `"diverging"`. Defaults to `"main"`.
|
|
||||||
#' @param n Integer or `NULL`. Number of colors to return. If `NULL`, returns
|
#' @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,
|
#' all defined stops.
|
||||||
#' colors are interpolated via [grDevices::colorRampPalette()].
|
#' @param reverse Logical. Reverse the palette order. Default `FALSE`.
|
||||||
#'
|
#'
|
||||||
#' @return A character vector of hex color codes.
|
#' @return A character vector of hex color codes.
|
||||||
#' @export
|
#' @export
|
||||||
#'
|
#'
|
||||||
#' @examples
|
#' @examples
|
||||||
#' civilytics_pal() # all 6 qualitative colors
|
#' civilytics_palette() # all 7 qualitative colors
|
||||||
#' civilytics_pal("sequential", n = 3)
|
#' civilytics_palette("seq_navy", n = 5) # 5-stop navy ramp
|
||||||
#' civilytics_pal("diverging", n = 5)
|
#' civilytics_palette("div_navy_ember", n = 11, reverse = TRUE)
|
||||||
civilytics_pal <- function(name = "main", n = NULL) {
|
civilytics_palette <- function(name = "qual", n = NULL, reverse = FALSE) {
|
||||||
f <- .cv_pal_fun(name)
|
pal <- civilytics_palettes[[name]]
|
||||||
len <- length(.cv_palettes[[name]])
|
if (is.null(pal)) {
|
||||||
f(if (is.null(n)) len else n)
|
stop(
|
||||||
|
"'", name, "' is not a valid palette. Choose from: ",
|
||||||
|
paste(names(civilytics_palettes), collapse = ", "),
|
||||||
|
call. = FALSE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (reverse) pal <- rev(pal)
|
||||||
|
if (is.null(n)) return(pal)
|
||||||
|
if (startsWith(name, "qual")) {
|
||||||
|
if (n > length(pal)) {
|
||||||
|
warning(
|
||||||
|
"Requested ", n, " colors from '", name, "' palette (max ",
|
||||||
|
length(pal), "); recycling.",
|
||||||
|
call. = FALSE
|
||||||
|
)
|
||||||
|
pal <- rep_len(pal, n)
|
||||||
|
}
|
||||||
|
return(pal[seq_len(n)])
|
||||||
|
}
|
||||||
|
grDevices::colorRampPalette(pal)(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#' Civilytics palette function (closure)
|
||||||
|
#'
|
||||||
|
#' Returns a closure `function(n)` suitable for passing to
|
||||||
|
#' [ggplot2::discrete_scale()] or similar scale constructors.
|
||||||
|
#'
|
||||||
|
#' @param name Character. Palette name. See `names(civilytics_palettes)`.
|
||||||
|
#' @param reverse Logical. Reverse the palette order. Default `FALSE`.
|
||||||
|
#'
|
||||||
|
#' @return A function that takes integer `n` and returns `n` hex color codes.
|
||||||
|
#' @export
|
||||||
|
#'
|
||||||
|
#' @examples
|
||||||
|
#' pal_fn <- civilytics_pal("qual")
|
||||||
|
#' pal_fn(4)
|
||||||
|
civilytics_pal <- function(name = "qual", reverse = FALSE) {
|
||||||
|
function(n) civilytics_palette(name, n = n, reverse = reverse)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#' Civilytics color scale for ggplot2
|
#' Civilytics color scale for ggplot2
|
||||||
#'
|
#'
|
||||||
#' Applies a Civilytics brand palette to the `colour` aesthetic. Use
|
#' Applies a Civilytics brand palette to the `colour` aesthetic.
|
||||||
#' `discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
|
||||||
#' continuous gradients.
|
|
||||||
#'
|
#'
|
||||||
#' @param palette Character. Palette name passed to [civilytics_pal()].
|
#' @param palette Character. Palette name. Defaults to `"qual"`.
|
||||||
#' Defaults to `"main"`.
|
#' @param discrete Logical. `TRUE` (default) for categorical data; `FALSE`
|
||||||
#' @param discrete Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
#' for a continuous gradient via [ggplot2::scale_color_gradientn()].
|
||||||
#' a continuous gradient via [ggplot2::scale_color_gradientn()].
|
#' @param reverse Logical. Reverse the palette order. Default `FALSE`.
|
||||||
#' @param ... Additional arguments passed to the underlying ggplot2 scale
|
#' @param ... Additional arguments passed to the ggplot2 scale function.
|
||||||
#' function.
|
|
||||||
#'
|
#'
|
||||||
#' @return A ggplot2 scale object.
|
#' @return A ggplot2 scale object.
|
||||||
#' @export
|
#' @export
|
||||||
@@ -113,30 +236,34 @@ civilytics_pal <- function(name = "main", n = NULL) {
|
|||||||
#'
|
#'
|
||||||
#' ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
#' ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
||||||
#' geom_point() +
|
#' geom_point() +
|
||||||
#' scale_color_civilytics("sequential", discrete = FALSE)
|
#' scale_color_civilytics("seq_navy", discrete = FALSE)
|
||||||
scale_color_civilytics <- function(palette = "main", discrete = TRUE, ...) {
|
scale_color_civilytics <- function(palette = "qual", discrete = TRUE,
|
||||||
|
reverse = FALSE, ...) {
|
||||||
if (discrete) {
|
if (discrete) {
|
||||||
ggplot2::discrete_scale("colour", palette = .cv_pal_fun(palette), ...)
|
ggplot2::discrete_scale(
|
||||||
|
"colour",
|
||||||
|
palette = civilytics_pal(palette, reverse = reverse),
|
||||||
|
...
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
pal <- civilytics_palette(palette, reverse = reverse)
|
||||||
ggplot2::scale_color_gradientn(
|
ggplot2::scale_color_gradientn(
|
||||||
colours = grDevices::colorRampPalette(.cv_palettes[[palette]])(256),
|
colours = grDevices::colorRampPalette(pal)(256),
|
||||||
...
|
...
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#' Civilytics fill scale for ggplot2
|
#' Civilytics fill scale for ggplot2
|
||||||
#'
|
#'
|
||||||
#' Applies a Civilytics brand palette to the `fill` aesthetic. Use
|
#' Applies a Civilytics brand palette to the `fill` aesthetic.
|
||||||
#' `discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
|
||||||
#' continuous gradients.
|
|
||||||
#'
|
#'
|
||||||
#' @param palette Character. Palette name passed to [civilytics_pal()].
|
#' @param palette Character. Palette name. Defaults to `"qual"`.
|
||||||
#' Defaults to `"main"`.
|
#' @param discrete Logical. `TRUE` (default) for categorical data; `FALSE`
|
||||||
#' @param discrete Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
#' for a continuous gradient via [ggplot2::scale_fill_gradientn()].
|
||||||
#' a continuous gradient via [ggplot2::scale_fill_gradientn()].
|
#' @param reverse Logical. Reverse the palette order. Default `FALSE`.
|
||||||
#' @param ... Additional arguments passed to the underlying ggplot2 scale
|
#' @param ... Additional arguments passed to the ggplot2 scale function.
|
||||||
#' function.
|
|
||||||
#'
|
#'
|
||||||
#' @return A ggplot2 scale object.
|
#' @return A ggplot2 scale object.
|
||||||
#' @export
|
#' @export
|
||||||
@@ -149,13 +276,19 @@ scale_color_civilytics <- function(palette = "main", discrete = TRUE, ...) {
|
|||||||
#'
|
#'
|
||||||
#' ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
#' ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||||
#' geom_tile() +
|
#' geom_tile() +
|
||||||
#' scale_fill_civilytics("sequential", discrete = FALSE)
|
#' scale_fill_civilytics("seq_ember", discrete = FALSE)
|
||||||
scale_fill_civilytics <- function(palette = "main", discrete = TRUE, ...) {
|
scale_fill_civilytics <- function(palette = "qual", discrete = TRUE,
|
||||||
|
reverse = FALSE, ...) {
|
||||||
if (discrete) {
|
if (discrete) {
|
||||||
ggplot2::discrete_scale("fill", palette = .cv_pal_fun(palette), ...)
|
ggplot2::discrete_scale(
|
||||||
|
"fill",
|
||||||
|
palette = civilytics_pal(palette, reverse = reverse),
|
||||||
|
...
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
pal <- civilytics_palette(palette, reverse = reverse)
|
||||||
ggplot2::scale_fill_gradientn(
|
ggplot2::scale_fill_gradientn(
|
||||||
colours = grDevices::colorRampPalette(.cv_palettes[[palette]])(256),
|
colours = grDevices::colorRampPalette(pal)(256),
|
||||||
...
|
...
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
# These match the --cv-font-* CSS custom properties on www.civilytics.com.
|
# These match the --cv-font-* CSS custom properties on www.civilytics.com.
|
||||||
CV_FONT_DISPLAY <- "Libre Franklin" # headings / display text
|
CV_FONT_DISPLAY <- "Libre Franklin" # headings / display text
|
||||||
CV_FONT_SANS <- "Inter" # axis text, legends, UI elements
|
CV_FONT_SANS <- "Inter" # axis text, legends, UI elements
|
||||||
CV_FONT_SERIF <- "Source Serif 4" # body prose (not used in default theme)
|
CV_FONT_SERIF <- "Source Serif 4" # body prose / editorial long-form
|
||||||
|
CV_FONT_MONO <- "JetBrains Mono" # code, data tables, numeric callouts
|
||||||
|
|
||||||
# Internal flag so civilytics_load_fonts() is idempotent within a session.
|
# Internal flag so civilytics_load_fonts() is idempotent within a session.
|
||||||
.cv_fonts_loaded <- FALSE
|
.cv_fonts_loaded <- FALSE
|
||||||
@@ -25,6 +26,8 @@ CV_FONT_SERIF <- "Source Serif 4" # body prose (not used in default theme)
|
|||||||
civilytics_load_fonts <- function() {
|
civilytics_load_fonts <- function() {
|
||||||
sysfonts::font_add_google("Inter", family = "Inter")
|
sysfonts::font_add_google("Inter", family = "Inter")
|
||||||
sysfonts::font_add_google("Libre Franklin", family = "Libre Franklin")
|
sysfonts::font_add_google("Libre Franklin", family = "Libre Franklin")
|
||||||
|
sysfonts::font_add_google("Source Serif 4", family = "Source Serif 4")
|
||||||
|
sysfonts::font_add_google("JetBrains Mono", family = "JetBrains Mono")
|
||||||
showtext::showtext_auto()
|
showtext::showtext_auto()
|
||||||
.cv_fonts_loaded <<- TRUE
|
.cv_fonts_loaded <<- TRUE
|
||||||
invisible(NULL)
|
invisible(NULL)
|
||||||
@@ -43,8 +46,9 @@ civilytics_load_fonts <- function() {
|
|||||||
.onAttach <- function(libname, pkgname) {
|
.onAttach <- function(libname, pkgname) {
|
||||||
if (isTRUE(getOption(".civilytics_fonts_failed"))) {
|
if (isTRUE(getOption(".civilytics_fonts_failed"))) {
|
||||||
packageStartupMessage(
|
packageStartupMessage(
|
||||||
"[civilytics] Brand fonts (Inter, Libre Franklin) could not be loaded ",
|
"[civilytics] Brand fonts (Inter, Libre Franklin, Source Serif 4, ",
|
||||||
"from Google Fonts. Charts will fall back to system fonts. ",
|
"JetBrains Mono) could not be loaded from Google Fonts. ",
|
||||||
|
"Charts will fall back to system fonts. ",
|
||||||
"Call civilytics_load_fonts() once you have an internet connection."
|
"Call civilytics_load_fonts() once you have an internet connection."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
#' Civilytics brand color palette and typography. Requires ggplot2 >= 4.0.0
|
#' Civilytics brand color palette and typography. Requires ggplot2 >= 4.0.0
|
||||||
#' for the `ink`, `paper`, and `accent` base-theme parameters.
|
#' for the `ink`, `paper`, and `accent` base-theme parameters.
|
||||||
#'
|
#'
|
||||||
|
#' Produces an editorial, Pew-style layout: visible x-axis line to ground
|
||||||
|
#' the data, light horizontal gridlines for reference, no panel border or
|
||||||
|
#' y-axis line. Plot title and caption are left-aligned to the full plot
|
||||||
|
#' region.
|
||||||
|
#'
|
||||||
#' Brand fonts (Inter for UI text, Libre Franklin for titles) are loaded
|
#' Brand fonts (Inter for UI text, Libre Franklin for titles) are loaded
|
||||||
#' automatically via [showtext] when the package is attached. Call
|
#' automatically via [showtext] when the package is attached. Call
|
||||||
#' [civilytics_load_fonts()] to reload them if needed.
|
#' [civilytics_load_fonts()] to reload them if needed.
|
||||||
@@ -25,9 +30,15 @@
|
|||||||
#' @param paper Character. Hex code for background color. Defaults to
|
#' @param paper Character. Hex code for background color. Defaults to
|
||||||
#' [civilytics_colors]`["paper"]` (`#FAF7F2`).
|
#' [civilytics_colors]`["paper"]` (`#FAF7F2`).
|
||||||
#' @param accent Character. Hex code for accent/highlight color. Defaults to
|
#' @param accent Character. Hex code for accent/highlight color. Defaults to
|
||||||
#' [civilytics_colors]`["accent"]` (`#C25311`).
|
#' [civilytics_colors]`["ember_600"]` (`#C25311`).
|
||||||
#' @param strip_color Character. Hex code for facet strip background. Defaults
|
#' @param strip_color Character. Hex code for facet strip background. Defaults
|
||||||
#' to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).
|
#' to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).
|
||||||
|
#' @param grid Character. Which major gridlines to draw: `"y"` (default,
|
||||||
|
#' horizontal only), `"x"` (vertical only), `"both"`, or `"none"`.
|
||||||
|
#' @param paper_bg Logical. If `TRUE` (default), fill the plot and panel
|
||||||
|
#' backgrounds with the warm `paper` color. Set to `FALSE` for a
|
||||||
|
#' transparent background (useful for slides or overlay on colored
|
||||||
|
#' surfaces).
|
||||||
#'
|
#'
|
||||||
#' @return A complete ggplot2 [ggplot2::theme()] object.
|
#' @return A complete ggplot2 [ggplot2::theme()] object.
|
||||||
#' @export
|
#' @export
|
||||||
@@ -35,14 +46,22 @@
|
|||||||
#' @examples
|
#' @examples
|
||||||
#' \dontrun{
|
#' \dontrun{
|
||||||
#' library(ggplot2)
|
#' library(ggplot2)
|
||||||
|
#'
|
||||||
|
#' # Default editorial theme
|
||||||
#' ggplot(mpg, aes(displ, hwy)) +
|
#' ggplot(mpg, aes(displ, hwy)) +
|
||||||
#' geom_point() +
|
#' geom_point() +
|
||||||
#' theme_civilytics()
|
#' theme_civilytics()
|
||||||
#'
|
#'
|
||||||
|
#' # With both gridlines and brand colors
|
||||||
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
#' geom_point() +
|
#' geom_point() +
|
||||||
#' scale_color_civilytics() +
|
#' scale_color_civilytics() +
|
||||||
#' theme_civilytics()
|
#' theme_civilytics(grid = "both")
|
||||||
|
#'
|
||||||
|
#' # Transparent background for embedding
|
||||||
|
#' ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
#' geom_point() +
|
||||||
|
#' theme_civilytics(paper_bg = FALSE)
|
||||||
#' }
|
#' }
|
||||||
theme_civilytics <- function(
|
theme_civilytics <- function(
|
||||||
font_size = 14,
|
font_size = 14,
|
||||||
@@ -54,11 +73,22 @@ theme_civilytics <- function(
|
|||||||
rel_large = 16 / 14,
|
rel_large = 16 / 14,
|
||||||
ink = unname(civilytics_colors["ink"]),
|
ink = unname(civilytics_colors["ink"]),
|
||||||
paper = unname(civilytics_colors["paper"]),
|
paper = unname(civilytics_colors["paper"]),
|
||||||
accent = unname(civilytics_colors["accent"]),
|
accent = unname(civilytics_colors["ember_600"]),
|
||||||
strip_color = unname(civilytics_colors["paper_2"])) {
|
strip_color = unname(civilytics_colors["paper_2"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = TRUE) {
|
||||||
|
|
||||||
|
grid <- match.arg(grid)
|
||||||
half_line <- font_size / 2
|
half_line <- font_size / 2
|
||||||
small_size <- rel_small * font_size
|
small_size <- rel_small * font_size
|
||||||
|
rule_color <- unname(civilytics_colors["rule"])
|
||||||
|
ink_2 <- unname(civilytics_colors["ink_2"])
|
||||||
|
ink_3 <- unname(civilytics_colors["ink_3"])
|
||||||
|
bg_color <- if (isTRUE(paper_bg)) paper else NA
|
||||||
|
|
||||||
|
# Grid line elements
|
||||||
|
grid_line <- ggplot2::element_line(color = rule_color, linewidth = 0.35)
|
||||||
|
no_line <- ggplot2::element_blank()
|
||||||
|
|
||||||
ggplot2::theme_grey(
|
ggplot2::theme_grey(
|
||||||
base_size = font_size,
|
base_size = font_size,
|
||||||
@@ -92,16 +122,16 @@ theme_civilytics <- function(
|
|||||||
margin = ggplot2::margin(),
|
margin = ggplot2::margin(),
|
||||||
debug = FALSE
|
debug = FALSE
|
||||||
),
|
),
|
||||||
# Axes
|
# -- Axes --
|
||||||
axis.line = ggplot2::element_line(
|
axis.line = ggplot2::element_blank(),
|
||||||
|
axis.line.x = ggplot2::element_line(
|
||||||
color = ink,
|
color = ink,
|
||||||
linewidth = line_size,
|
linewidth = 0.6,
|
||||||
lineend = "square"
|
lineend = "square"
|
||||||
),
|
),
|
||||||
axis.line.x = NULL,
|
axis.line.y = ggplot2::element_blank(),
|
||||||
axis.line.y = NULL,
|
|
||||||
axis.text = ggplot2::element_text(
|
axis.text = ggplot2::element_text(
|
||||||
color = ink,
|
color = ink_2,
|
||||||
size = small_size
|
size = small_size
|
||||||
),
|
),
|
||||||
axis.text.x = ggplot2::element_text(
|
axis.text.x = ggplot2::element_text(
|
||||||
@@ -121,66 +151,80 @@ theme_civilytics <- function(
|
|||||||
hjust = 0
|
hjust = 0
|
||||||
),
|
),
|
||||||
axis.ticks = ggplot2::element_line(
|
axis.ticks = ggplot2::element_line(
|
||||||
color = ink,
|
color = ink_3,
|
||||||
linewidth = line_size
|
linewidth = 0.4
|
||||||
),
|
),
|
||||||
axis.ticks.length = ggplot2::unit(half_line / 2, "pt"),
|
axis.ticks.length = ggplot2::unit(4, "pt"),
|
||||||
axis.title.x = ggplot2::element_text(
|
axis.title.x = ggplot2::element_text(
|
||||||
margin = ggplot2::margin(t = half_line / 2),
|
size = ggplot2::rel(rel_small),
|
||||||
|
color = ink_3,
|
||||||
|
margin = ggplot2::margin(t = 10),
|
||||||
vjust = 1
|
vjust = 1
|
||||||
),
|
),
|
||||||
axis.title.x.top = ggplot2::element_text(
|
axis.title.x.top = ggplot2::element_text(
|
||||||
|
size = ggplot2::rel(rel_small),
|
||||||
|
color = ink_3,
|
||||||
margin = ggplot2::margin(b = half_line / 2),
|
margin = ggplot2::margin(b = half_line / 2),
|
||||||
vjust = 0
|
vjust = 0
|
||||||
),
|
),
|
||||||
axis.title.y = ggplot2::element_text(
|
axis.title.y = ggplot2::element_text(
|
||||||
|
size = ggplot2::rel(rel_small),
|
||||||
|
color = ink_3,
|
||||||
angle = 90,
|
angle = 90,
|
||||||
margin = ggplot2::margin(r = half_line / 2),
|
margin = ggplot2::margin(r = 10),
|
||||||
vjust = 1
|
vjust = 1
|
||||||
),
|
),
|
||||||
axis.title.y.right = ggplot2::element_text(
|
axis.title.y.right = ggplot2::element_text(
|
||||||
|
size = ggplot2::rel(rel_small),
|
||||||
|
color = ink_3,
|
||||||
angle = -90,
|
angle = -90,
|
||||||
margin = ggplot2::margin(l = half_line / 2),
|
margin = ggplot2::margin(l = half_line / 2),
|
||||||
vjust = 0
|
vjust = 0
|
||||||
),
|
),
|
||||||
# Legend
|
# -- Legend --
|
||||||
legend.background = ggplot2::element_blank(),
|
legend.background = ggplot2::element_blank(),
|
||||||
legend.spacing = ggplot2::unit(font_size, "pt"),
|
legend.spacing = ggplot2::unit(font_size, "pt"),
|
||||||
legend.spacing.x = NULL,
|
legend.spacing.x = NULL,
|
||||||
legend.spacing.y = NULL,
|
legend.spacing.y = NULL,
|
||||||
legend.margin = ggplot2::margin(0, 0, 0, 0),
|
legend.margin = ggplot2::margin(0, 0, 4, 0),
|
||||||
legend.key = ggplot2::element_blank(),
|
legend.key = ggplot2::element_blank(),
|
||||||
legend.key.size = ggplot2::unit(1.1 * font_size, "pt"),
|
legend.key.size = ggplot2::unit(12, "pt"),
|
||||||
legend.key.height = NULL,
|
legend.key.height = NULL,
|
||||||
legend.key.width = NULL,
|
legend.key.width = NULL,
|
||||||
legend.text = ggplot2::element_text(size = ggplot2::rel(rel_small)),
|
legend.text = ggplot2::element_text(
|
||||||
legend.title = ggplot2::element_text(hjust = 0),
|
size = ggplot2::rel(rel_small),
|
||||||
legend.position = "right",
|
color = ink_2
|
||||||
|
),
|
||||||
|
legend.title = ggplot2::element_text(
|
||||||
|
hjust = 0,
|
||||||
|
face = "bold",
|
||||||
|
size = ggplot2::rel(rel_tiny),
|
||||||
|
color = ink_3
|
||||||
|
),
|
||||||
|
legend.position = "top",
|
||||||
legend.direction = NULL,
|
legend.direction = NULL,
|
||||||
legend.justification = c("left", "center"),
|
legend.justification = c("left", "center"),
|
||||||
legend.box = NULL,
|
legend.box = NULL,
|
||||||
legend.box.margin = ggplot2::margin(0, 0, 0, 0),
|
legend.box.margin = ggplot2::margin(0, 0, 0, 0),
|
||||||
legend.box.background = ggplot2::element_blank(),
|
legend.box.background = ggplot2::element_blank(),
|
||||||
legend.box.spacing = ggplot2::unit(font_size, "pt"),
|
legend.box.spacing = ggplot2::unit(font_size, "pt"),
|
||||||
# Panel
|
# -- Panel --
|
||||||
panel.background = ggplot2::element_blank(),
|
panel.background = ggplot2::element_rect(fill = bg_color, color = NA),
|
||||||
panel.border = ggplot2::element_blank(),
|
panel.border = ggplot2::element_blank(),
|
||||||
panel.grid = ggplot2::element_blank(),
|
panel.grid.minor = ggplot2::element_blank(),
|
||||||
panel.grid.major = NULL,
|
panel.grid.major.x = if (grid %in% c("x", "both")) grid_line else no_line,
|
||||||
panel.grid.minor = NULL,
|
panel.grid.major.y = if (grid %in% c("y", "both")) grid_line else no_line,
|
||||||
panel.grid.major.x = NULL,
|
panel.spacing = ggplot2::unit(16, "pt"),
|
||||||
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.x = NULL,
|
||||||
panel.spacing.y = NULL,
|
panel.spacing.y = NULL,
|
||||||
panel.ontop = FALSE,
|
panel.ontop = FALSE,
|
||||||
# Facet strips — use brand paper_2 tint (overridden for dark variant)
|
# -- Facet strips --
|
||||||
strip.background = ggplot2::element_rect(fill = strip_color),
|
strip.background = ggplot2::element_rect(fill = strip_color, color = NA),
|
||||||
strip.text = ggplot2::element_text(
|
strip.text = ggplot2::element_text(
|
||||||
family = title_family,
|
family = font_family,
|
||||||
|
face = "bold",
|
||||||
size = ggplot2::rel(rel_small),
|
size = ggplot2::rel(rel_small),
|
||||||
|
color = ink,
|
||||||
margin = ggplot2::margin(
|
margin = ggplot2::margin(
|
||||||
half_line / 2, half_line / 2,
|
half_line / 2, half_line / 2,
|
||||||
half_line / 2, half_line / 2
|
half_line / 2, half_line / 2
|
||||||
@@ -193,49 +237,55 @@ theme_civilytics <- function(
|
|||||||
strip.placement.y = NULL,
|
strip.placement.y = NULL,
|
||||||
strip.switch.pad.grid = ggplot2::unit(half_line / 2, "pt"),
|
strip.switch.pad.grid = ggplot2::unit(half_line / 2, "pt"),
|
||||||
strip.switch.pad.wrap = ggplot2::unit(half_line / 2, "pt"),
|
strip.switch.pad.wrap = ggplot2::unit(half_line / 2, "pt"),
|
||||||
# Plot-level
|
# -- Plot-level --
|
||||||
plot.background = ggplot2::element_rect(fill = paper, color = NA),
|
plot.background = ggplot2::element_rect(fill = bg_color, color = NA),
|
||||||
plot.title = ggplot2::element_text(
|
plot.title = ggplot2::element_text(
|
||||||
family = title_family,
|
family = title_family,
|
||||||
face = "bold",
|
face = "bold",
|
||||||
size = ggplot2::rel(rel_large),
|
size = ggplot2::rel(rel_large),
|
||||||
hjust = 0,
|
hjust = 0,
|
||||||
vjust = 1,
|
vjust = 1,
|
||||||
margin = ggplot2::margin(b = half_line)
|
margin = ggplot2::margin(b = 4)
|
||||||
),
|
),
|
||||||
|
plot.title.position = "plot",
|
||||||
plot.subtitle = ggplot2::element_text(
|
plot.subtitle = ggplot2::element_text(
|
||||||
size = ggplot2::rel(rel_small),
|
size = ggplot2::rel(rel_small),
|
||||||
hjust = 0,
|
color = ink_2,
|
||||||
vjust = 1,
|
hjust = 0,
|
||||||
margin = ggplot2::margin(b = half_line)
|
vjust = 1,
|
||||||
|
lineheight = 1.3,
|
||||||
|
margin = ggplot2::margin(b = 14)
|
||||||
),
|
),
|
||||||
plot.caption = ggplot2::element_text(
|
plot.caption = ggplot2::element_text(
|
||||||
size = ggplot2::rel(rel_tiny),
|
size = ggplot2::rel(rel_tiny),
|
||||||
|
color = ink_3,
|
||||||
hjust = 0,
|
hjust = 0,
|
||||||
vjust = 1,
|
vjust = 1,
|
||||||
lineheight = 1,
|
lineheight = 1.3,
|
||||||
margin = ggplot2::margin(t = half_line)
|
margin = ggplot2::margin(t = 14)
|
||||||
),
|
),
|
||||||
|
plot.caption.position = "plot",
|
||||||
plot.tag = ggplot2::element_text(
|
plot.tag = ggplot2::element_text(
|
||||||
face = "bold",
|
face = "bold",
|
||||||
|
color = accent,
|
||||||
|
size = ggplot2::rel(rel_tiny),
|
||||||
hjust = 0,
|
hjust = 0,
|
||||||
vjust = 0.7
|
vjust = 0.7
|
||||||
),
|
),
|
||||||
plot.tag.position = c(0, 1),
|
plot.tag.position = c(0, 1),
|
||||||
plot.margin = ggplot2::margin(half_line, half_line, half_line, half_line),
|
plot.margin = ggplot2::margin(16, 18, 16, 16),
|
||||||
complete = TRUE
|
complete = TRUE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#' Dark variant of the Civilytics ggplot2 theme
|
#' Dark variant of the Civilytics ggplot2 theme
|
||||||
#'
|
#'
|
||||||
#' A convenience wrapper around [theme_civilytics()] with dark-background
|
#' Convenience wrapper around [theme_civilytics()] with dark-background
|
||||||
#' defaults: navy (`#1A2E4A`) background, warm off-white (`#FAF7F2`) text, and
|
#' defaults: navy (`#1A2E4A`) paper, warm off-white (`#FAF7F2`) ink, and
|
||||||
#' a lighter orange accent (`#E07840`) suitable for dark backgrounds. Pair with
|
#' a lighter ember accent (`#E07840`). Facet strips use primary navy.
|
||||||
#' `make_logo_grob(variant = "dark")` to use the white logo.
|
|
||||||
#'
|
#'
|
||||||
#' All parameters from [theme_civilytics()] are accepted and override the dark
|
#' Pair with `make_logo_grob(variant = "dark")` for the white logo.
|
||||||
#' defaults.
|
|
||||||
#'
|
#'
|
||||||
#' @inheritParams theme_civilytics
|
#' @inheritParams theme_civilytics
|
||||||
#'
|
#'
|
||||||
@@ -245,10 +295,6 @@ theme_civilytics <- function(
|
|||||||
#' @examples
|
#' @examples
|
||||||
#' \dontrun{
|
#' \dontrun{
|
||||||
#' library(ggplot2)
|
#' library(ggplot2)
|
||||||
#' ggplot(mpg, aes(displ, hwy)) +
|
|
||||||
#' geom_point() +
|
|
||||||
#' theme_civilytics_dark()
|
|
||||||
#'
|
|
||||||
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
#' geom_point() +
|
#' geom_point() +
|
||||||
#' scale_color_civilytics() +
|
#' scale_color_civilytics() +
|
||||||
@@ -263,9 +309,11 @@ theme_civilytics_dark <- function(
|
|||||||
rel_tiny = 11 / 14,
|
rel_tiny = 11 / 14,
|
||||||
rel_large = 16 / 14,
|
rel_large = 16 / 14,
|
||||||
ink = unname(civilytics_colors["paper"]),
|
ink = unname(civilytics_colors["paper"]),
|
||||||
paper = unname(civilytics_colors["navy_dark"]),
|
paper = unname(civilytics_colors["navy_700"]),
|
||||||
accent = unname(civilytics_colors["accent_dark"]),
|
accent = unname(civilytics_colors["ember_400"]),
|
||||||
strip_color = unname(civilytics_colors["navy"])) {
|
strip_color = unname(civilytics_colors["navy_600"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = TRUE) {
|
||||||
|
|
||||||
theme_civilytics(
|
theme_civilytics(
|
||||||
font_size = font_size,
|
font_size = font_size,
|
||||||
@@ -278,6 +326,68 @@ theme_civilytics_dark <- function(
|
|||||||
ink = ink,
|
ink = ink,
|
||||||
paper = paper,
|
paper = paper,
|
||||||
accent = accent,
|
accent = accent,
|
||||||
strip_color = strip_color
|
strip_color = strip_color,
|
||||||
|
grid = grid,
|
||||||
|
paper_bg = paper_bg
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#' Slide-friendly Civilytics ggplot2 theme
|
||||||
|
#'
|
||||||
|
#' Variant of [theme_civilytics()] sized for Reveal.js slides or PowerPoint
|
||||||
|
#' exports: larger base font (18pt), transparent background, wider margins,
|
||||||
|
#' and a heavier x-axis line. Gridlines default to horizontal only.
|
||||||
|
#'
|
||||||
|
#' @inheritParams theme_civilytics
|
||||||
|
#'
|
||||||
|
#' @return A complete ggplot2 [ggplot2::theme()] object.
|
||||||
|
#' @export
|
||||||
|
#'
|
||||||
|
#' @examples
|
||||||
|
#' \dontrun{
|
||||||
|
#' library(ggplot2)
|
||||||
|
#' ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
|
#' geom_point() +
|
||||||
|
#' scale_color_civilytics() +
|
||||||
|
#' theme_civilytics_slide()
|
||||||
|
#' }
|
||||||
|
theme_civilytics_slide <- function(
|
||||||
|
font_size = 18,
|
||||||
|
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["ember_600"]),
|
||||||
|
strip_color = unname(civilytics_colors["paper_2"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = FALSE) {
|
||||||
|
|
||||||
|
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,
|
||||||
|
grid = grid,
|
||||||
|
paper_bg = paper_bg
|
||||||
|
) +
|
||||||
|
ggplot2::theme(
|
||||||
|
axis.line.x = ggplot2::element_line(
|
||||||
|
color = ink,
|
||||||
|
linewidth = 0.8,
|
||||||
|
lineend = "square"
|
||||||
|
),
|
||||||
|
plot.margin = ggplot2::margin(24, 24, 24, 24)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
---
|
||||||
|
output: github_document
|
||||||
|
---
|
||||||
|
|
||||||
|
```{r setup, include = FALSE}
|
||||||
|
knitr::opts_chunk$set(
|
||||||
|
collapse = TRUE,
|
||||||
|
comment = "#>",
|
||||||
|
fig.path = "man/figures/README-",
|
||||||
|
fig.retina = 2,
|
||||||
|
dpi = 150,
|
||||||
|
out.width = "100%"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# civilytics
|
||||||
|
|
||||||
|
Brand themes, color palettes, and utility functions for
|
||||||
|
[Civilytics Consulting](https://www.civilytics.com). The package provides a
|
||||||
|
complete ggplot2 theme system drawn from the Civilytics design system ---
|
||||||
|
warm paper backgrounds, civic-navy ink, and editorial typography --- along
|
||||||
|
with 10 curated color palettes, logo composition helpers, and data-wrangling
|
||||||
|
utilities for public-sector analysis.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install from the Civilytics Gitea server:
|
||||||
|
|
||||||
|
```r
|
||||||
|
# install.packages("remotes")
|
||||||
|
remotes::install_gitea(
|
||||||
|
"Civilytics/civilyticsR",
|
||||||
|
gitea_url = "https://gitea.civilytics.org"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```{r quickstart, fig.height = 4, fig.width = 7, message = FALSE}
|
||||||
|
library(civilytics)
|
||||||
|
library(ggplot2)
|
||||||
|
|
||||||
|
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
|
geom_point(size = 2.5) +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
labs(
|
||||||
|
title = "Fuel economy by engine displacement",
|
||||||
|
subtitle = "Highway MPG vs. engine size for 234 vehicles",
|
||||||
|
caption = "Source: EPA fuel economy data (ggplot2::mpg)",
|
||||||
|
x = "Engine displacement (litres)",
|
||||||
|
y = "Highway MPG"
|
||||||
|
) +
|
||||||
|
theme_civilytics()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color palettes
|
||||||
|
|
||||||
|
The package ships 53 named brand colors in `civilytics_colors` and 10
|
||||||
|
curated palettes in `civilytics_palettes`. Use `civilytics_palette()` to
|
||||||
|
retrieve colors by name, or pass palettes directly to the ggplot2 scales.
|
||||||
|
|
||||||
|
### Palette gallery
|
||||||
|
|
||||||
|
```{r palette-gallery, echo = FALSE, fig.height = 9, fig.width = 7}
|
||||||
|
show_palette <- function(name, colors) {
|
||||||
|
n <- length(colors)
|
||||||
|
df <- data.frame(
|
||||||
|
x = seq_len(n),
|
||||||
|
fill = factor(seq_len(n), levels = seq_len(n))
|
||||||
|
)
|
||||||
|
ggplot(df, aes(x, y = 1, fill = fill)) +
|
||||||
|
geom_tile(width = 0.9, height = 0.9, show.legend = FALSE) +
|
||||||
|
scale_fill_manual(values = colors) +
|
||||||
|
scale_x_continuous(expand = expansion(add = 0.5)) +
|
||||||
|
labs(title = name) +
|
||||||
|
theme_void() +
|
||||||
|
theme(
|
||||||
|
plot.title = element_text(
|
||||||
|
family = "Libre Franklin", face = "bold", size = 11,
|
||||||
|
hjust = 0, margin = margin(b = 2)
|
||||||
|
),
|
||||||
|
plot.margin = margin(4, 4, 4, 4)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
plots <- mapply(
|
||||||
|
show_palette,
|
||||||
|
names(civilytics_palettes),
|
||||||
|
civilytics_palettes,
|
||||||
|
SIMPLIFY = FALSE
|
||||||
|
)
|
||||||
|
|
||||||
|
gridExtra::grid.arrange(grobs = plots, ncol = 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using palettes
|
||||||
|
|
||||||
|
```{r palette-usage, fig.height = 3.5, fig.width = 7}
|
||||||
|
# Discrete fill with the qualitative palette
|
||||||
|
ggplot(mpg, aes(class, fill = class)) +
|
||||||
|
geom_bar(show.legend = FALSE) +
|
||||||
|
scale_fill_civilytics() +
|
||||||
|
labs(title = "Vehicle counts by class", x = NULL, y = NULL) +
|
||||||
|
theme_civilytics(grid = "y")
|
||||||
|
|
||||||
|
# Continuous fill with a sequential palette
|
||||||
|
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||||
|
geom_tile() +
|
||||||
|
scale_fill_civilytics("seq_ember", discrete = FALSE) +
|
||||||
|
labs(title = "Old Faithful eruption density") +
|
||||||
|
theme_civilytics(grid = "none")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Themes
|
||||||
|
|
||||||
|
Three theme variants cover the most common output contexts. All share the
|
||||||
|
same typographic structure and accept `grid` and `paper_bg` parameters.
|
||||||
|
|
||||||
|
### Editorial (default)
|
||||||
|
|
||||||
|
The default theme uses a warm paper background with horizontal gridlines ---
|
||||||
|
an editorial, Pew-style layout.
|
||||||
|
|
||||||
|
```{r theme-editorial, fig.height = 4, fig.width = 7}
|
||||||
|
base_plot <- ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point(aes(colour = factor(cyl)), size = 2) +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
labs(
|
||||||
|
title = "Engine size vs. highway fuel economy",
|
||||||
|
subtitle = "Colored by number of cylinders",
|
||||||
|
caption = "Source: ggplot2::mpg",
|
||||||
|
colour = "Cylinders",
|
||||||
|
x = "Displacement (L)", y = "Highway MPG"
|
||||||
|
)
|
||||||
|
|
||||||
|
base_plot + theme_civilytics()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grid options
|
||||||
|
|
||||||
|
The `grid` parameter controls which major gridlines are drawn.
|
||||||
|
|
||||||
|
```{r theme-grids, echo = FALSE, fig.height = 6.5, fig.width = 7}
|
||||||
|
grid_opts <- c("y", "x", "both", "none")
|
||||||
|
grid_plots <- lapply(grid_opts, function(g) {
|
||||||
|
base_plot +
|
||||||
|
theme_civilytics(grid = g) +
|
||||||
|
labs(title = paste0("grid = \"", g, "\""), subtitle = NULL, caption = NULL)
|
||||||
|
})
|
||||||
|
gridExtra::grid.arrange(grobs = grid_plots, ncol = 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dark
|
||||||
|
|
||||||
|
Dark navy background with light text, suitable for presentations or
|
||||||
|
dashboards on dark surfaces.
|
||||||
|
|
||||||
|
```{r theme-dark, fig.height = 4, fig.width = 7}
|
||||||
|
base_plot + theme_civilytics_dark()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slide
|
||||||
|
|
||||||
|
Transparent background and larger base font (18 pt), sized for Reveal.js
|
||||||
|
slides or PowerPoint exports.
|
||||||
|
|
||||||
|
```{r theme-slide, fig.height = 4, fig.width = 7}
|
||||||
|
base_plot + theme_civilytics_slide()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Facets
|
||||||
|
|
||||||
|
Facet strips use the `paper_2` tint, with the title font.
|
||||||
|
|
||||||
|
```{r theme-facets, fig.height = 5, fig.width = 7}
|
||||||
|
ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point(colour = civilytics_colors["navy_600"], size = 1.5) +
|
||||||
|
facet_wrap(~class, ncol = 4) +
|
||||||
|
labs(
|
||||||
|
title = "Highway MPG by vehicle class",
|
||||||
|
x = "Displacement (L)", y = "Highway MPG"
|
||||||
|
) +
|
||||||
|
theme_civilytics(grid = "y")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logo utilities
|
||||||
|
|
||||||
|
Add the Civilytics logo to any ggplot using the pipe-friendly
|
||||||
|
`civilytics_logo()` or the lower-level `add_logo()` / `make_logo_grob()`.
|
||||||
|
|
||||||
|
```r
|
||||||
|
library(civilytics)
|
||||||
|
library(ggplot2)
|
||||||
|
|
||||||
|
(ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point() +
|
||||||
|
theme_civilytics()) |>
|
||||||
|
civilytics_logo()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Other utilities
|
||||||
|
|
||||||
|
The package also includes helpers for public-sector data analysis:
|
||||||
|
|
||||||
|
| Function | Purpose |
|
||||||
|
|:---------|:--------|
|
||||||
|
| `pretty_count()` / `pretty_per()` | Format numbers and percentages |
|
||||||
|
| `grade_level_to_num()` | Convert grade labels (KG, 01--12) to numeric |
|
||||||
|
| `race_short_names()` | Standardize NCES race/ethnicity categories |
|
||||||
|
| `get_fips()` / `get_stabbr()` | State FIPS code lookups |
|
||||||
|
| `clopper_pearson()` / `agresti_coull_interval()` | Proportion confidence intervals |
|
||||||
|
| `match_test()` / `trunc_match()` | Fuzzy join diagnostics |
|
||||||
|
| `perturb_count()` / `random_round()` | Privacy-preserving data perturbation |
|
||||||
@@ -1,25 +1,181 @@
|
|||||||
|
|
||||||
# civilytics
|
# civilytics
|
||||||
|
|
||||||
<!-- badges: start -->
|
Brand themes, color palettes, and utility functions for [Civilytics
|
||||||
<!-- badges: end -->
|
Consulting](https://www.civilytics.com). The package provides a complete
|
||||||
|
ggplot2 theme system drawn from the Civilytics design system — warm
|
||||||
The goal of civilytics is to ...
|
paper backgrounds, civic-navy ink, and editorial typography — along with
|
||||||
|
10 curated color palettes, logo composition helpers, and data-wrangling
|
||||||
|
utilities for public-sector analysis.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can install the released version of civilytics from [CRAN](https://CRAN.R-project.org) with:
|
Install from the Civilytics Gitea server:
|
||||||
|
|
||||||
``` r
|
``` r
|
||||||
install.packages("civilytics")
|
# install.packages("remotes")
|
||||||
|
remotes::install_gitea(
|
||||||
|
"Civilytics/civilyticsR",
|
||||||
|
gitea_url = "https://gitea.civilytics.org"
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example
|
## Quick start
|
||||||
|
|
||||||
This is a basic example which shows you how to solve a common problem:
|
|
||||||
|
|
||||||
``` r
|
``` r
|
||||||
library(civilytics)
|
library(civilytics)
|
||||||
## basic example code
|
library(ggplot2)
|
||||||
|
|
||||||
|
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
|
geom_point(size = 2.5) +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
labs(
|
||||||
|
title = "Fuel economy by engine displacement",
|
||||||
|
subtitle = "Highway MPG vs. engine size for 234 vehicles",
|
||||||
|
caption = "Source: EPA fuel economy data (ggplot2::mpg)",
|
||||||
|
x = "Engine displacement (litres)",
|
||||||
|
y = "Highway MPG"
|
||||||
|
) +
|
||||||
|
theme_civilytics()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-quickstart-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
## Color palettes
|
||||||
|
|
||||||
|
The package ships 53 named brand colors in `civilytics_colors` and 10
|
||||||
|
curated palettes in `civilytics_palettes`. Use `civilytics_palette()` to
|
||||||
|
retrieve colors by name, or pass palettes directly to the ggplot2
|
||||||
|
scales.
|
||||||
|
|
||||||
|
### Palette gallery
|
||||||
|
|
||||||
|
<img src="man/figures/README-palette-gallery-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
### Using palettes
|
||||||
|
|
||||||
|
``` r
|
||||||
|
# Discrete fill with the qualitative palette
|
||||||
|
ggplot(mpg, aes(class, fill = class)) +
|
||||||
|
geom_bar(show.legend = FALSE) +
|
||||||
|
scale_fill_civilytics() +
|
||||||
|
labs(title = "Vehicle counts by class", x = NULL, y = NULL) +
|
||||||
|
theme_civilytics(grid = "y")
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-palette-usage-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
``` r
|
||||||
|
|
||||||
|
# Continuous fill with a sequential palette
|
||||||
|
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||||
|
geom_tile() +
|
||||||
|
scale_fill_civilytics("seq_ember", discrete = FALSE) +
|
||||||
|
labs(title = "Old Faithful eruption density") +
|
||||||
|
theme_civilytics(grid = "none")
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-palette-usage-2.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
## Themes
|
||||||
|
|
||||||
|
Three theme variants cover the most common output contexts. All share
|
||||||
|
the same typographic structure and accept `grid` and `paper_bg`
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
### Editorial (default)
|
||||||
|
|
||||||
|
The default theme uses a warm paper background with horizontal gridlines
|
||||||
|
— an editorial, Pew-style layout.
|
||||||
|
|
||||||
|
``` r
|
||||||
|
base_plot <- ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point(aes(colour = factor(cyl)), size = 2) +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
labs(
|
||||||
|
title = "Engine size vs. highway fuel economy",
|
||||||
|
subtitle = "Colored by number of cylinders",
|
||||||
|
caption = "Source: ggplot2::mpg",
|
||||||
|
colour = "Cylinders",
|
||||||
|
x = "Displacement (L)", y = "Highway MPG"
|
||||||
|
)
|
||||||
|
|
||||||
|
base_plot + theme_civilytics()
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-theme-editorial-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
### Grid options
|
||||||
|
|
||||||
|
The `grid` parameter controls which major gridlines are drawn.
|
||||||
|
|
||||||
|
<img src="man/figures/README-theme-grids-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
### Dark
|
||||||
|
|
||||||
|
Dark navy background with light text, suitable for presentations or
|
||||||
|
dashboards on dark surfaces.
|
||||||
|
|
||||||
|
``` r
|
||||||
|
base_plot + theme_civilytics_dark()
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-theme-dark-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
### Slide
|
||||||
|
|
||||||
|
Transparent background and larger base font (18 pt), sized for Reveal.js
|
||||||
|
slides or PowerPoint exports.
|
||||||
|
|
||||||
|
``` r
|
||||||
|
base_plot + theme_civilytics_slide()
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-theme-slide-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
### Facets
|
||||||
|
|
||||||
|
Facet strips use the `paper_2` tint, with the title font.
|
||||||
|
|
||||||
|
``` r
|
||||||
|
ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point(colour = civilytics_colors["navy_600"], size = 1.5) +
|
||||||
|
facet_wrap(~class, ncol = 4) +
|
||||||
|
labs(
|
||||||
|
title = "Highway MPG by vehicle class",
|
||||||
|
x = "Displacement (L)", y = "Highway MPG"
|
||||||
|
) +
|
||||||
|
theme_civilytics(grid = "y")
|
||||||
|
```
|
||||||
|
|
||||||
|
<img src="man/figures/README-theme-facets-1.png" alt="" width="100%" />
|
||||||
|
|
||||||
|
## Logo utilities
|
||||||
|
|
||||||
|
Add the Civilytics logo to any ggplot using the pipe-friendly
|
||||||
|
`civilytics_logo()` or the lower-level `add_logo()` /
|
||||||
|
`make_logo_grob()`.
|
||||||
|
|
||||||
|
``` r
|
||||||
|
library(civilytics)
|
||||||
|
library(ggplot2)
|
||||||
|
|
||||||
|
(ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point() +
|
||||||
|
theme_civilytics()) |>
|
||||||
|
civilytics_logo()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Other utilities
|
||||||
|
|
||||||
|
The package also includes helpers for public-sector data analysis:
|
||||||
|
|
||||||
|
| Function | Purpose |
|
||||||
|
|:---|:---|
|
||||||
|
| `pretty_count()` / `pretty_per()` | Format numbers and percentages |
|
||||||
|
| `grade_level_to_num()` | Convert grade labels (KG, 01–12) to numeric |
|
||||||
|
| `race_short_names()` | Standardize NCES race/ethnicity categories |
|
||||||
|
| `get_fips()` / `get_stabbr()` | State FIPS code lookups |
|
||||||
|
| `clopper_pearson()` / `agresti_coull_interval()` | Proportion confidence intervals |
|
||||||
|
| `match_test()` / `trunc_match()` | Fuzzy join diagnostics |
|
||||||
|
| `perturb_count()` / `random_round()` | Privacy-preserving data perturbation |
|
||||||
|
|||||||
@@ -4,10 +4,32 @@
|
|||||||
\name{civilytics-package}
|
\name{civilytics-package}
|
||||||
\alias{civilytics}
|
\alias{civilytics}
|
||||||
\alias{civilytics-package}
|
\alias{civilytics-package}
|
||||||
\title{civilytics: Utilities Functions for Civilytics}
|
\title{civilytics: Brand Themes, Color Palettes, and Utility Functions for Civilytics}
|
||||||
\description{
|
\description{
|
||||||
House R functions for Civilytics Consulting LLC. This package implements a variety of useful functions for creating and branding analyses produced by Civilytics Consulting LLC.
|
Provides a complete ggplot2 brand theme system for Civilytics Consulting,
|
||||||
|
including editorial light, dark, and slide-optimized themes; 10 curated
|
||||||
|
color palettes; logo composition; and data-wrangling helpers for
|
||||||
|
public-sector analysis.
|
||||||
}
|
}
|
||||||
|
\section{Themes}{
|
||||||
|
|
||||||
|
\itemize{
|
||||||
|
\item [theme_civilytics()] -- editorial theme with warm paper background
|
||||||
|
\item [theme_civilytics_dark()] -- navy background variant
|
||||||
|
\item [theme_civilytics_slide()] -- transparent background, larger text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\section{Color palettes}{
|
||||||
|
|
||||||
|
\itemize{
|
||||||
|
\item [civilytics_colors] -- 53 named brand colors
|
||||||
|
\item [civilytics_palettes] -- 10 visualization palettes
|
||||||
|
\item [civilytics_palette()] -- extract colors by palette name
|
||||||
|
\item [scale_color_civilytics()] / [scale_fill_civilytics()] -- ggplot2 scales
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
\seealso{
|
\seealso{
|
||||||
Useful links:
|
Useful links:
|
||||||
\itemize{
|
\itemize{
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ civilytics_colors
|
|||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
A named character vector of all Civilytics brand colors, matching the CSS
|
A named character vector of all Civilytics brand colors, matching the CSS
|
||||||
custom properties defined on www.civilytics.com (`--cv-*` variables).
|
custom properties in the Civilytics design system (`--cv-*` variables).
|
||||||
|
Includes full ramps for navy, ember, and violet, plus supporting hues
|
||||||
|
(teal, plum, moss, brass) and semantic status colors.
|
||||||
}
|
}
|
||||||
\examples{
|
\examples{
|
||||||
civilytics_colors["accent"]
|
civilytics_colors["ink"]
|
||||||
civilytics_colors[c("ink", "paper")]
|
civilytics_colors[c("navy_600", "ember_600", "teal_600")]
|
||||||
}
|
}
|
||||||
\keyword{datasets}
|
\keyword{datasets}
|
||||||
|
|||||||
@@ -2,28 +2,23 @@
|
|||||||
% Please edit documentation in R/colors.R
|
% Please edit documentation in R/colors.R
|
||||||
\name{civilytics_pal}
|
\name{civilytics_pal}
|
||||||
\alias{civilytics_pal}
|
\alias{civilytics_pal}
|
||||||
\title{Civilytics brand color palette}
|
\title{Civilytics palette function (closure)}
|
||||||
\usage{
|
\usage{
|
||||||
civilytics_pal(name = "main", n = NULL)
|
civilytics_pal(name = "qual", reverse = FALSE)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
\item{name}{Character. Palette name: `"main"`, `"sequential"`, or
|
\item{name}{Character. Palette name. See `names(civilytics_palettes)`.}
|
||||||
`"diverging"`. Defaults to `"main"`.}
|
|
||||||
|
|
||||||
\item{n}{Integer or `NULL`. Number of colors to return. If `NULL`, returns
|
\item{reverse}{Logical. Reverse the palette order. Default `FALSE`.}
|
||||||
all colors in the palette. If `n` exceeds the number of defined stops,
|
|
||||||
colors are interpolated via [grDevices::colorRampPalette()].}
|
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
A character vector of hex color codes.
|
A function that takes integer `n` and returns `n` hex color codes.
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
Returns a character vector of hex codes from a named Civilytics palette.
|
Returns a closure `function(n)` suitable for passing to
|
||||||
Available palettes: `"main"` (qualitative, up to 6), `"sequential"` (light
|
[ggplot2::discrete_scale()] or similar scale constructors.
|
||||||
to dark navy), `"diverging"` (orange–neutral–navy).
|
|
||||||
}
|
}
|
||||||
\examples{
|
\examples{
|
||||||
civilytics_pal() # all 6 qualitative colors
|
pal_fn <- civilytics_pal("qual")
|
||||||
civilytics_pal("sequential", n = 3)
|
pal_fn(4)
|
||||||
civilytics_pal("diverging", n = 5)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
% Generated by roxygen2: do not edit by hand
|
||||||
|
% Please edit documentation in R/colors.R
|
||||||
|
\name{civilytics_palette}
|
||||||
|
\alias{civilytics_palette}
|
||||||
|
\title{Get colors from a Civilytics palette}
|
||||||
|
\usage{
|
||||||
|
civilytics_palette(name = "qual", n = NULL, reverse = FALSE)
|
||||||
|
}
|
||||||
|
\arguments{
|
||||||
|
\item{name}{Character. Palette name. See `names(civilytics_palettes)`.}
|
||||||
|
|
||||||
|
\item{n}{Integer or `NULL`. Number of colors to return. If `NULL`, returns
|
||||||
|
all defined stops.}
|
||||||
|
|
||||||
|
\item{reverse}{Logical. Reverse the palette order. Default `FALSE`.}
|
||||||
|
}
|
||||||
|
\value{
|
||||||
|
A character vector of hex color codes.
|
||||||
|
}
|
||||||
|
\description{
|
||||||
|
Returns a character vector of hex codes from a named Civilytics palette.
|
||||||
|
For qualitative palettes, colors beyond the palette length recycle with a
|
||||||
|
warning. For sequential and diverging palettes, colors are interpolated
|
||||||
|
via [grDevices::colorRampPalette()].
|
||||||
|
}
|
||||||
|
\examples{
|
||||||
|
civilytics_palette() # all 7 qualitative colors
|
||||||
|
civilytics_palette("seq_navy", n = 5) # 5-stop navy ramp
|
||||||
|
civilytics_palette("div_navy_ember", n = 11, reverse = TRUE)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
% Generated by roxygen2: do not edit by hand
|
||||||
|
% Please edit documentation in R/colors.R
|
||||||
|
\docType{data}
|
||||||
|
\name{civilytics_palettes}
|
||||||
|
\alias{civilytics_palettes}
|
||||||
|
\title{Civilytics visualization palettes}
|
||||||
|
\format{
|
||||||
|
A named list of character vectors of hex color codes.
|
||||||
|
}
|
||||||
|
\usage{
|
||||||
|
civilytics_palettes
|
||||||
|
}
|
||||||
|
\description{
|
||||||
|
Named list of curated color palettes for data visualization. Qualitative
|
||||||
|
palettes use distinct hues for categorical data; sequential palettes ramp
|
||||||
|
through a single hue for ordered data; diverging palettes fan out from a
|
||||||
|
neutral midpoint.
|
||||||
|
}
|
||||||
|
\section{Qualitative (categorical data)}{
|
||||||
|
|
||||||
|
\describe{
|
||||||
|
\item{`qual`}{7 distinct hues: navy, ember, plum, violet, red, teal, ink-3}
|
||||||
|
\item{`qual_warm`}{Warm-leaning: ember, red, magenta, plum, violet}
|
||||||
|
\item{`qual_cool`}{Cool-leaning: navy, violet, plum, teal}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\section{Sequential (ordered/continuous data)}{
|
||||||
|
|
||||||
|
\describe{
|
||||||
|
\item{`seq_ember`}{Light to dark ember (9 stops)}
|
||||||
|
\item{`seq_navy`}{Light to dark navy (9 stops)}
|
||||||
|
\item{`seq_violet`}{Light to dark violet (9 stops)}
|
||||||
|
\item{`seq_paper_ink`}{Paper through ember/plum to ink (8 stops, good for heatmaps)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\section{Diverging (data with a meaningful midpoint)}{
|
||||||
|
|
||||||
|
\describe{
|
||||||
|
\item{`div_navy_ember`}{Navy <-> paper <-> ember (9 stops)}
|
||||||
|
\item{`div_violet_ember`}{Violet <-> paper <-> ember (9 stops)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
\examples{
|
||||||
|
names(civilytics_palettes)
|
||||||
|
civilytics_palettes[["qual"]]
|
||||||
|
}
|
||||||
|
\keyword{datasets}
|
||||||
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 96 KiB |
@@ -4,25 +4,23 @@
|
|||||||
\alias{scale_color_civilytics}
|
\alias{scale_color_civilytics}
|
||||||
\title{Civilytics color scale for ggplot2}
|
\title{Civilytics color scale for ggplot2}
|
||||||
\usage{
|
\usage{
|
||||||
scale_color_civilytics(palette = "main", discrete = TRUE, ...)
|
scale_color_civilytics(palette = "qual", discrete = TRUE, reverse = FALSE, ...)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
\item{palette}{Character. Palette name passed to [civilytics_pal()].
|
\item{palette}{Character. Palette name. Defaults to `"qual"`.}
|
||||||
Defaults to `"main"`.}
|
|
||||||
|
|
||||||
\item{discrete}{Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
\item{discrete}{Logical. `TRUE` (default) for categorical data; `FALSE`
|
||||||
a continuous gradient via [ggplot2::scale_color_gradientn()].}
|
for a continuous gradient via [ggplot2::scale_color_gradientn()].}
|
||||||
|
|
||||||
\item{...}{Additional arguments passed to the underlying ggplot2 scale
|
\item{reverse}{Logical. Reverse the palette order. Default `FALSE`.}
|
||||||
function.}
|
|
||||||
|
\item{...}{Additional arguments passed to the ggplot2 scale function.}
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
A ggplot2 scale object.
|
A ggplot2 scale object.
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
Applies a Civilytics brand palette to the `colour` aesthetic. Use
|
Applies a Civilytics brand palette to the `colour` aesthetic.
|
||||||
`discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
|
||||||
continuous gradients.
|
|
||||||
}
|
}
|
||||||
\examples{
|
\examples{
|
||||||
library(ggplot2)
|
library(ggplot2)
|
||||||
@@ -32,5 +30,5 @@ ggplot(mpg, aes(displ, hwy, colour = class)) +
|
|||||||
|
|
||||||
ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
ggplot(mpg, aes(displ, hwy, colour = cty)) +
|
||||||
geom_point() +
|
geom_point() +
|
||||||
scale_color_civilytics("sequential", discrete = FALSE)
|
scale_color_civilytics("seq_navy", discrete = FALSE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,23 @@
|
|||||||
\alias{scale_fill_civilytics}
|
\alias{scale_fill_civilytics}
|
||||||
\title{Civilytics fill scale for ggplot2}
|
\title{Civilytics fill scale for ggplot2}
|
||||||
\usage{
|
\usage{
|
||||||
scale_fill_civilytics(palette = "main", discrete = TRUE, ...)
|
scale_fill_civilytics(palette = "qual", discrete = TRUE, reverse = FALSE, ...)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
\item{palette}{Character. Palette name passed to [civilytics_pal()].
|
\item{palette}{Character. Palette name. Defaults to `"qual"`.}
|
||||||
Defaults to `"main"`.}
|
|
||||||
|
|
||||||
\item{discrete}{Logical. `TRUE` (default) for a discrete scale; `FALSE` for
|
\item{discrete}{Logical. `TRUE` (default) for categorical data; `FALSE`
|
||||||
a continuous gradient via [ggplot2::scale_fill_gradientn()].}
|
for a continuous gradient via [ggplot2::scale_fill_gradientn()].}
|
||||||
|
|
||||||
\item{...}{Additional arguments passed to the underlying ggplot2 scale
|
\item{reverse}{Logical. Reverse the palette order. Default `FALSE`.}
|
||||||
function.}
|
|
||||||
|
\item{...}{Additional arguments passed to the ggplot2 scale function.}
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
A ggplot2 scale object.
|
A ggplot2 scale object.
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
Applies a Civilytics brand palette to the `fill` aesthetic. Use
|
Applies a Civilytics brand palette to the `fill` aesthetic.
|
||||||
`discrete = TRUE` for categorical variables and `discrete = FALSE` for
|
|
||||||
continuous gradients.
|
|
||||||
}
|
}
|
||||||
\examples{
|
\examples{
|
||||||
library(ggplot2)
|
library(ggplot2)
|
||||||
@@ -32,5 +30,5 @@ ggplot(mpg, aes(class, fill = class)) +
|
|||||||
|
|
||||||
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
ggplot(faithfuld, aes(waiting, eruptions, fill = density)) +
|
||||||
geom_tile() +
|
geom_tile() +
|
||||||
scale_fill_civilytics("sequential", discrete = FALSE)
|
scale_fill_civilytics("seq_ember", discrete = FALSE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ theme_civilytics(
|
|||||||
rel_large = 16/14,
|
rel_large = 16/14,
|
||||||
ink = unname(civilytics_colors["ink"]),
|
ink = unname(civilytics_colors["ink"]),
|
||||||
paper = unname(civilytics_colors["paper"]),
|
paper = unname(civilytics_colors["paper"]),
|
||||||
accent = unname(civilytics_colors["accent"]),
|
accent = unname(civilytics_colors["ember_600"]),
|
||||||
strip_color = unname(civilytics_colors["paper_2"])
|
strip_color = unname(civilytics_colors["paper_2"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = TRUE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
@@ -45,10 +47,18 @@ Default `11/14`.}
|
|||||||
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
||||||
|
|
||||||
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
||||||
[civilytics_colors]`["accent"]` (`#C25311`).}
|
[civilytics_colors]`["ember_600"]` (`#C25311`).}
|
||||||
|
|
||||||
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
||||||
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
||||||
|
|
||||||
|
\item{grid}{Character. Which major gridlines to draw: `"y"` (default,
|
||||||
|
horizontal only), `"x"` (vertical only), `"both"`, or `"none"`.}
|
||||||
|
|
||||||
|
\item{paper_bg}{Logical. If `TRUE` (default), fill the plot and panel
|
||||||
|
backgrounds with the warm `paper` color. Set to `FALSE` for a
|
||||||
|
transparent background (useful for slides or overlay on colored
|
||||||
|
surfaces).}
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
A complete ggplot2 [ggplot2::theme()] object.
|
A complete ggplot2 [ggplot2::theme()] object.
|
||||||
@@ -59,6 +69,11 @@ Civilytics brand color palette and typography. Requires ggplot2 >= 4.0.0
|
|||||||
for the `ink`, `paper`, and `accent` base-theme parameters.
|
for the `ink`, `paper`, and `accent` base-theme parameters.
|
||||||
}
|
}
|
||||||
\details{
|
\details{
|
||||||
|
Produces an editorial, Pew-style layout: visible x-axis line to ground
|
||||||
|
the data, light horizontal gridlines for reference, no panel border or
|
||||||
|
y-axis line. Plot title and caption are left-aligned to the full plot
|
||||||
|
region.
|
||||||
|
|
||||||
Brand fonts (Inter for UI text, Libre Franklin for titles) are loaded
|
Brand fonts (Inter for UI text, Libre Franklin for titles) are loaded
|
||||||
automatically via [showtext] when the package is attached. Call
|
automatically via [showtext] when the package is attached. Call
|
||||||
[civilytics_load_fonts()] to reload them if needed.
|
[civilytics_load_fonts()] to reload them if needed.
|
||||||
@@ -66,13 +81,21 @@ automatically via [showtext] when the package is attached. Call
|
|||||||
\examples{
|
\examples{
|
||||||
\dontrun{
|
\dontrun{
|
||||||
library(ggplot2)
|
library(ggplot2)
|
||||||
|
|
||||||
|
# Default editorial theme
|
||||||
ggplot(mpg, aes(displ, hwy)) +
|
ggplot(mpg, aes(displ, hwy)) +
|
||||||
geom_point() +
|
geom_point() +
|
||||||
theme_civilytics()
|
theme_civilytics()
|
||||||
|
|
||||||
|
# With both gridlines and brand colors
|
||||||
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
geom_point() +
|
geom_point() +
|
||||||
scale_color_civilytics() +
|
scale_color_civilytics() +
|
||||||
theme_civilytics()
|
theme_civilytics(grid = "both")
|
||||||
|
|
||||||
|
# Transparent background for embedding
|
||||||
|
ggplot(mpg, aes(displ, hwy)) +
|
||||||
|
geom_point() +
|
||||||
|
theme_civilytics(paper_bg = FALSE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ theme_civilytics_dark(
|
|||||||
rel_tiny = 11/14,
|
rel_tiny = 11/14,
|
||||||
rel_large = 16/14,
|
rel_large = 16/14,
|
||||||
ink = unname(civilytics_colors["paper"]),
|
ink = unname(civilytics_colors["paper"]),
|
||||||
paper = unname(civilytics_colors["navy_dark"]),
|
paper = unname(civilytics_colors["navy_700"]),
|
||||||
accent = unname(civilytics_colors["accent_dark"]),
|
accent = unname(civilytics_colors["ember_400"]),
|
||||||
strip_color = unname(civilytics_colors["navy"])
|
strip_color = unname(civilytics_colors["navy_600"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = TRUE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
@@ -45,31 +47,33 @@ Default `11/14`.}
|
|||||||
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
[civilytics_colors]`["paper"]` (`#FAF7F2`).}
|
||||||
|
|
||||||
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
\item{accent}{Character. Hex code for accent/highlight color. Defaults to
|
||||||
[civilytics_colors]`["accent"]` (`#C25311`).}
|
[civilytics_colors]`["ember_600"]` (`#C25311`).}
|
||||||
|
|
||||||
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
||||||
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
||||||
|
|
||||||
|
\item{grid}{Character. Which major gridlines to draw: `"y"` (default,
|
||||||
|
horizontal only), `"x"` (vertical only), `"both"`, or `"none"`.}
|
||||||
|
|
||||||
|
\item{paper_bg}{Logical. If `TRUE` (default), fill the plot and panel
|
||||||
|
backgrounds with the warm `paper` color. Set to `FALSE` for a
|
||||||
|
transparent background (useful for slides or overlay on colored
|
||||||
|
surfaces).}
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
A complete ggplot2 [ggplot2::theme()] object.
|
A complete ggplot2 [ggplot2::theme()] object.
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
A convenience wrapper around [theme_civilytics()] with dark-background
|
Convenience wrapper around [theme_civilytics()] with dark-background
|
||||||
defaults: navy (`#1A2E4A`) background, warm off-white (`#FAF7F2`) text, and
|
defaults: navy (`#1A2E4A`) paper, warm off-white (`#FAF7F2`) ink, and
|
||||||
a lighter orange accent (`#E07840`) suitable for dark backgrounds. Pair with
|
a lighter ember accent (`#E07840`). Facet strips use primary navy.
|
||||||
`make_logo_grob(variant = "dark")` to use the white logo.
|
|
||||||
}
|
}
|
||||||
\details{
|
\details{
|
||||||
All parameters from [theme_civilytics()] are accepted and override the dark
|
Pair with `make_logo_grob(variant = "dark")` for the white logo.
|
||||||
defaults.
|
|
||||||
}
|
}
|
||||||
\examples{
|
\examples{
|
||||||
\dontrun{
|
\dontrun{
|
||||||
library(ggplot2)
|
library(ggplot2)
|
||||||
ggplot(mpg, aes(displ, hwy)) +
|
|
||||||
geom_point() +
|
|
||||||
theme_civilytics_dark()
|
|
||||||
|
|
||||||
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
geom_point() +
|
geom_point() +
|
||||||
scale_color_civilytics() +
|
scale_color_civilytics() +
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
% Generated by roxygen2: do not edit by hand
|
||||||
|
% Please edit documentation in R/theme.R
|
||||||
|
\name{theme_civilytics_slide}
|
||||||
|
\alias{theme_civilytics_slide}
|
||||||
|
\title{Slide-friendly Civilytics ggplot2 theme}
|
||||||
|
\usage{
|
||||||
|
theme_civilytics_slide(
|
||||||
|
font_size = 18,
|
||||||
|
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["ember_600"]),
|
||||||
|
strip_color = unname(civilytics_colors["paper_2"]),
|
||||||
|
grid = c("y", "x", "both", "none"),
|
||||||
|
paper_bg = FALSE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
\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]`["ember_600"]` (`#C25311`).}
|
||||||
|
|
||||||
|
\item{strip_color}{Character. Hex code for facet strip background. Defaults
|
||||||
|
to [civilytics_colors]`["paper_2"]` (`#F2EDE4`).}
|
||||||
|
|
||||||
|
\item{grid}{Character. Which major gridlines to draw: `"y"` (default,
|
||||||
|
horizontal only), `"x"` (vertical only), `"both"`, or `"none"`.}
|
||||||
|
|
||||||
|
\item{paper_bg}{Logical. If `TRUE` (default), fill the plot and panel
|
||||||
|
backgrounds with the warm `paper` color. Set to `FALSE` for a
|
||||||
|
transparent background (useful for slides or overlay on colored
|
||||||
|
surfaces).}
|
||||||
|
}
|
||||||
|
\value{
|
||||||
|
A complete ggplot2 [ggplot2::theme()] object.
|
||||||
|
}
|
||||||
|
\description{
|
||||||
|
Variant of [theme_civilytics()] sized for Reveal.js slides or PowerPoint
|
||||||
|
exports: larger base font (18pt), transparent background, wider margins,
|
||||||
|
and a heavier x-axis line. Gridlines default to horizontal only.
|
||||||
|
}
|
||||||
|
\examples{
|
||||||
|
\dontrun{
|
||||||
|
library(ggplot2)
|
||||||
|
ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
|
geom_point() +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
theme_civilytics_slide()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,46 +9,67 @@ test_that("civilytics_colors is a named character vector of hex codes", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_colors contains expected brand keys", {
|
test_that("civilytics_colors contains expected brand keys", {
|
||||||
expected <- c("paper", "ink", "accent", "navy", "navy_dark",
|
expected <- c("paper", "ink", "navy_600", "ember_600", "teal_600",
|
||||||
"accent_dark", "paper_2", "rule")
|
"plum_600", "moss_600", "paper_2", "rule")
|
||||||
expect_true(all(expected %in% names(civilytics_colors)))
|
expect_true(all(expected %in% names(civilytics_colors)))
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_colors values match brand specification", {
|
test_that("civilytics_colors values match brand specification", {
|
||||||
expect_equal(unname(civilytics_colors["paper"]), "#FAF7F2")
|
expect_equal(unname(civilytics_colors["paper"]), "#FAF7F2")
|
||||||
expect_equal(unname(civilytics_colors["ink"]), "#0E1A2B")
|
expect_equal(unname(civilytics_colors["ink"]), "#0E1A2B")
|
||||||
expect_equal(unname(civilytics_colors["accent"]), "#C25311")
|
expect_equal(unname(civilytics_colors["ember_600"]), "#C25311")
|
||||||
expect_equal(unname(civilytics_colors["navy_dark"]), "#1A2E4A")
|
expect_equal(unname(civilytics_colors["navy_700"]), "#1A2E4A")
|
||||||
expect_equal(unname(civilytics_colors["accent_dark"]),"#E07840")
|
expect_equal(unname(civilytics_colors["ember_400"]), "#EA8A49")
|
||||||
})
|
})
|
||||||
|
|
||||||
# --- civilytics_pal ----------------------------------------------------------
|
# --- civilytics_palettes / civilytics_palette --------------------------------
|
||||||
|
|
||||||
test_that("civilytics_pal returns character vector of hex codes", {
|
test_that("civilytics_palettes contains all expected palettes", {
|
||||||
result <- civilytics_pal()
|
expected <- c("qual", "qual_warm", "qual_cool",
|
||||||
|
"seq_ember", "seq_navy", "seq_violet", "seq_paper_ink",
|
||||||
|
"div_navy_ember", "div_violet_ember")
|
||||||
|
expect_true(all(expected %in% names(civilytics_palettes)))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("civilytics_palette returns character vector of hex codes", {
|
||||||
|
result <- civilytics_palette()
|
||||||
expect_type(result, "character")
|
expect_type(result, "character")
|
||||||
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_pal respects n argument", {
|
test_that("civilytics_palette respects n argument", {
|
||||||
expect_length(civilytics_pal("main", n = 3), 3)
|
expect_length(civilytics_palette("qual", n = 3), 3)
|
||||||
expect_length(civilytics_pal("sequential", n = 5), 5)
|
expect_length(civilytics_palette("seq_navy", n = 5), 5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_pal interpolates when n exceeds palette size", {
|
test_that("civilytics_palette interpolates sequential palettes", {
|
||||||
result <- civilytics_pal("main", n = 10)
|
result <- civilytics_palette("seq_ember", n = 20)
|
||||||
expect_length(result, 10)
|
expect_length(result, 20)
|
||||||
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_pal errors on unknown palette name", {
|
test_that("civilytics_palette warns when recycling qualitative", {
|
||||||
expect_error(civilytics_pal("nope"), "'nope' is not a valid Civilytics palette")
|
expect_warning(civilytics_palette("qual", n = 10), "recycling")
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("civilytics_pal supports all three named palettes", {
|
test_that("civilytics_palette reverse works", {
|
||||||
expect_no_error(civilytics_pal("main"))
|
fwd <- civilytics_palette("seq_navy")
|
||||||
expect_no_error(civilytics_pal("sequential"))
|
rev <- civilytics_palette("seq_navy", reverse = TRUE)
|
||||||
expect_no_error(civilytics_pal("diverging"))
|
expect_equal(fwd, rev(rev))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("civilytics_palette errors on unknown palette name", {
|
||||||
|
expect_error(civilytics_palette("nope"), "'nope' is not a valid palette")
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- civilytics_pal (closure) ------------------------------------------------
|
||||||
|
|
||||||
|
test_that("civilytics_pal returns a function", {
|
||||||
|
f <- civilytics_pal()
|
||||||
|
expect_type(f, "closure")
|
||||||
|
result <- f(4)
|
||||||
|
expect_length(result, 4)
|
||||||
|
expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", result)))
|
||||||
})
|
})
|
||||||
|
|
||||||
# --- scale_color/fill_civilytics ---------------------------------------------
|
# --- scale_color/fill_civilytics ---------------------------------------------
|
||||||
@@ -59,7 +80,7 @@ test_that("scale_color_civilytics returns a Scale object (discrete)", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test_that("scale_color_civilytics returns a Scale object (continuous)", {
|
test_that("scale_color_civilytics returns a Scale object (continuous)", {
|
||||||
sc <- scale_color_civilytics("sequential", discrete = FALSE)
|
sc <- scale_color_civilytics("seq_navy", discrete = FALSE)
|
||||||
expect_s3_class(sc, "Scale")
|
expect_s3_class(sc, "Scale")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -69,7 +90,12 @@ test_that("scale_fill_civilytics returns a Scale object (discrete)", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test_that("scale_fill_civilytics returns a Scale object (continuous)", {
|
test_that("scale_fill_civilytics returns a Scale object (continuous)", {
|
||||||
sc <- scale_fill_civilytics("sequential", discrete = FALSE)
|
sc <- scale_fill_civilytics("seq_ember", discrete = FALSE)
|
||||||
|
expect_s3_class(sc, "Scale")
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("scale_color_civilytics reverse parameter works", {
|
||||||
|
sc <- scale_color_civilytics("qual", reverse = TRUE)
|
||||||
expect_s3_class(sc, "Scale")
|
expect_s3_class(sc, "Scale")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -128,6 +154,42 @@ test_that("theme_civilytics font_size parameter scales text elements", {
|
|||||||
expect_gt(th_big$text$size, th_sml$text$size)
|
expect_gt(th_big$text$size, th_sml$text$size)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics grid parameter controls gridlines", {
|
||||||
|
th_y <- theme_civilytics(grid = "y")
|
||||||
|
th_x <- theme_civilytics(grid = "x")
|
||||||
|
th_both <- theme_civilytics(grid = "both")
|
||||||
|
th_none <- theme_civilytics(grid = "none")
|
||||||
|
|
||||||
|
# grid="y" shows y gridlines, blanks x
|
||||||
|
expect_s3_class(th_y$panel.grid.major.y, "element_line")
|
||||||
|
expect_s3_class(th_y$panel.grid.major.x, "element_blank")
|
||||||
|
|
||||||
|
# grid="x" shows x gridlines, blanks y
|
||||||
|
expect_s3_class(th_x$panel.grid.major.x, "element_line")
|
||||||
|
expect_s3_class(th_x$panel.grid.major.y, "element_blank")
|
||||||
|
|
||||||
|
# grid="both" shows both
|
||||||
|
|
||||||
|
expect_s3_class(th_both$panel.grid.major.x, "element_line")
|
||||||
|
expect_s3_class(th_both$panel.grid.major.y, "element_line")
|
||||||
|
|
||||||
|
# grid="none" blanks both
|
||||||
|
expect_s3_class(th_none$panel.grid.major.x, "element_blank")
|
||||||
|
expect_s3_class(th_none$panel.grid.major.y, "element_blank")
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics paper_bg=FALSE gives transparent background", {
|
||||||
|
th <- theme_civilytics(paper_bg = FALSE)
|
||||||
|
expect_true(is.na(th$plot.background$fill))
|
||||||
|
expect_true(is.na(th$panel.background$fill))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics has plot-aligned title and caption", {
|
||||||
|
th <- theme_civilytics()
|
||||||
|
expect_equal(th$plot.title.position, "plot")
|
||||||
|
expect_equal(th$plot.caption.position, "plot")
|
||||||
|
})
|
||||||
|
|
||||||
# --- theme_civilytics_dark ---------------------------------------------------
|
# --- theme_civilytics_dark ---------------------------------------------------
|
||||||
|
|
||||||
test_that("theme_civilytics_dark returns a complete ggplot2 theme", {
|
test_that("theme_civilytics_dark returns a complete ggplot2 theme", {
|
||||||
@@ -148,14 +210,47 @@ test_that("theme_civilytics_dark uses paper color as ink (light text on dark bg)
|
|||||||
expect_equal(th$text$colour, unname(civilytics_colors["paper"]))
|
expect_equal(th$text$colour, unname(civilytics_colors["paper"]))
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("theme_civilytics_dark uses navy_dark as background", {
|
test_that("theme_civilytics_dark uses navy_700 as background", {
|
||||||
th <- theme_civilytics_dark()
|
th <- theme_civilytics_dark()
|
||||||
expect_equal(th$plot.background$fill, unname(civilytics_colors["navy_dark"]))
|
expect_equal(th$plot.background$fill, unname(civilytics_colors["navy_700"]))
|
||||||
})
|
})
|
||||||
|
|
||||||
test_that("theme_civilytics_dark uses navy for strip background", {
|
test_that("theme_civilytics_dark uses navy_600 for strip background", {
|
||||||
th <- theme_civilytics_dark()
|
th <- theme_civilytics_dark()
|
||||||
expect_equal(th$strip.background$fill, unname(civilytics_colors["navy"]))
|
expect_equal(th$strip.background$fill, unname(civilytics_colors["navy_600"]))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics_dark inherits grid parameter", {
|
||||||
|
th <- theme_civilytics_dark(grid = "both")
|
||||||
|
expect_s3_class(th$panel.grid.major.x, "element_line")
|
||||||
|
expect_s3_class(th$panel.grid.major.y, "element_line")
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- theme_civilytics_slide --------------------------------------------------
|
||||||
|
|
||||||
|
test_that("theme_civilytics_slide returns a complete ggplot2 theme", {
|
||||||
|
th <- theme_civilytics_slide()
|
||||||
|
expect_s3_class(th, "theme")
|
||||||
|
expect_true(attr(th, "complete"))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics_slide has transparent background by default", {
|
||||||
|
th <- theme_civilytics_slide()
|
||||||
|
expect_true(is.na(th$plot.background$fill))
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics_slide uses larger base font", {
|
||||||
|
th_slide <- theme_civilytics_slide()
|
||||||
|
th_base <- theme_civilytics()
|
||||||
|
expect_gt(th_slide$text$size, th_base$text$size)
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("theme_civilytics_slide applies to a ggplot without error", {
|
||||||
|
p <- ggplot(mpg, aes(displ, hwy, colour = class)) +
|
||||||
|
geom_point() +
|
||||||
|
scale_color_civilytics() +
|
||||||
|
theme_civilytics_slide()
|
||||||
|
expect_no_error(ggplot_build(p))
|
||||||
})
|
})
|
||||||
|
|
||||||
# --- make_logo_grob ----------------------------------------------------------
|
# --- make_logo_grob ----------------------------------------------------------
|
||||||
|
|||||||