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.
This commit is contained in:
2026-05-19 10:35:32 -06:00
parent 099fff1d1d
commit 515bae8685
29 changed files with 1209 additions and 273 deletions
+122 -27
View File
@@ -9,46 +9,67 @@ test_that("civilytics_colors is a named character vector of hex codes", {
})
test_that("civilytics_colors contains expected brand keys", {
expected <- c("paper", "ink", "accent", "navy", "navy_dark",
"accent_dark", "paper_2", "rule")
expected <- c("paper", "ink", "navy_600", "ember_600", "teal_600",
"plum_600", "moss_600", "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")
expect_equal(unname(civilytics_colors["ink"]), "#0E1A2B")
expect_equal(unname(civilytics_colors["ember_600"]), "#C25311")
expect_equal(unname(civilytics_colors["navy_700"]), "#1A2E4A")
expect_equal(unname(civilytics_colors["ember_400"]), "#EA8A49")
})
# --- civilytics_pal ----------------------------------------------------------
# --- civilytics_palettes / civilytics_palette --------------------------------
test_that("civilytics_pal returns character vector of hex codes", {
result <- civilytics_pal()
test_that("civilytics_palettes contains all expected palettes", {
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_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_palette respects n argument", {
expect_length(civilytics_palette("qual", n = 3), 3)
expect_length(civilytics_palette("seq_navy", n = 5), 5)
})
test_that("civilytics_pal interpolates when n exceeds palette size", {
result <- civilytics_pal("main", n = 10)
expect_length(result, 10)
test_that("civilytics_palette interpolates sequential palettes", {
result <- civilytics_palette("seq_ember", n = 20)
expect_length(result, 20)
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_palette warns when recycling qualitative", {
expect_warning(civilytics_palette("qual", n = 10), "recycling")
})
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"))
test_that("civilytics_palette reverse works", {
fwd <- civilytics_palette("seq_navy")
rev <- civilytics_palette("seq_navy", reverse = TRUE)
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 ---------------------------------------------
@@ -59,7 +80,7 @@ test_that("scale_color_civilytics returns a Scale object (discrete)", {
})
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")
})
@@ -69,7 +90,12 @@ test_that("scale_fill_civilytics returns a Scale object (discrete)", {
})
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")
})
@@ -128,6 +154,42 @@ test_that("theme_civilytics font_size parameter scales text elements", {
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 ---------------------------------------------------
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"]))
})
test_that("theme_civilytics_dark uses navy_dark as background", {
test_that("theme_civilytics_dark uses navy_700 as background", {
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()
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 ----------------------------------------------------------