knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library("colorpath") library("purrr") library("glue") library("ggplot2") library("tibble") library("dplyr") library("scales")
The goal of this vignette is to show a way of building a categorical color-palette:
We will use the four surfaces introduced in the surfaces vignette.
# TODO: find a more-robust way to keep this in sync. colors <- list( blues = c("#42B4E6", "#0087CD"), oranges = c("#E47F00", "#702407"), greens = c("#007626", "#70E07C"), purples = c("#3D1152", "#C530FF") ) surfaces <- purrr::map(colors, pth_new_surface, transformer = pth_to_jzazbz100)
We can look at how the outer edge of these surfaces "collapse" under color-vision deficiency:
mat_surfaces <- surfaces %>% map(pth_mat_max_chroma) %>% reduce(rbind)
Next, we can plot these colors in polar coordinates, using pth_plot_polar()
:
pth_plot_polar(mat_surfaces, cvd = pth_cvd_grid_full()) + facet_grid( rows = vars(condition), cols = vars(severity), labeller = labeller(severity = label_both) ) + ggtitle( label = "Outer (chroma) edge of surfaces, simulating color-vision deficiency", subtitle = glue("Color space: {pth_colorspace_name(mat_surfaces)}") )
I am going to propose a design frameowrk, based on deuteranopia, because:
In short, I propose to choose:
It is clear that under full deuteranopia, for blues and purples, there is no variation in hue. Therefore, we will have to make things different using luminance and chroma.
blues_purples_first <- tribble( ~name, ~luminance, ~saturation, "blues", 80.0, 1.00, "purples", 65.0, 1.00, "purples", 45.0, 1.00, "blues", 30.0, 1.00 )
get_color_cat_surfaces <- function(name, luminance, saturation = 1) { pth_color_sfclumsat(surfaces[[name]], luminance, saturation) }
blues_purples_mat <- pmap(blues_purples_first, get_color_cat_surfaces) %>% reduce(rbind)
blues_purples_cvd <- pth_data_cvd( blues_purples_mat, cvd = pth_cvd_grid( condition = c("deutan", "protan"), severity = seq(0, 1, 0.1) ) )
pth_plot_lumchroma(blues_purples_cvd)
Here, we can see the measures we can take to keep these four colors more separated. We can see that, according to the color-vision deficiency models, the colors with the purple hue drop in luminance (and increase in chroma) as the severity increases.
With this in mind, we can choose the "original" colors to improve the separation at maximum severity.
blues_purples_second <- tribble( ~name, ~luminance, ~saturation, "blues", 75.0, 0.75, "purples", 65.0, 0.77, "purples", 35.0, 1.00, "blues", 45.0, 1.00 )
blues_purples_mat_second <- pmap(blues_purples_second, get_color_cat_surfaces) %>% reduce(rbind)
blues_purples_cvd_second <- pth_data_cvd( blues_purples_mat_second, cvd = pth_cvd_grid( condition = c("deutan", "protan"), severity = seq(0, 1, 0.1) ) )
pth_plot_lumchroma(blues_purples_cvd_second)
We can do the same thing with greens and oranges:
greens_oranges <- tribble( ~name, ~luminance, ~saturation, "greens", 65.0, 0.65, "oranges", 65.0, 0.90, "oranges", 30.0, 1.00, "greens", 40.0, 0.80 )
greens_oranges_mat <- pmap(greens_oranges, get_color_cat_surfaces) %>% reduce(rbind)
greens_oranges_cvd <- pth_data_cvd( greens_oranges_mat, cvd = pth_cvd_grid( condition = c("deutan", "protan"), severity = seq(0, 1, 0.1) ) )
pth_plot_lumchroma(greens_oranges_cvd)
Let's get these colors together:
categorical_mat <- rbind(blues_purples_mat_second, greens_oranges_mat) %>% `[`(c(4, 6, 3, 5, 1, 7, 2, 8), ) categorical_hex <- pth_to_hex(categorical_mat) colorspace::swatchplot(categorical_hex, cvd = TRUE)
show_col(categorical_hex)
Our next task is to see the performance of the categorical palette, defined as the minimum distance between colors under CVD.
categorical_performance <- categorical_mat %>% pth_data_cat_euclid() %>% filter(hex_original_a < hex_original_b) %>% group_by(condition) %>% arrange(distance, .by_group = TRUE) %>% slice_head(n = 3) %>% print()
Finally, let's look at the highest and lowest luminance values for each condition. The motivation is that we want to stay away from the background color for both light mode and dark mode.
categorical_cvd <- categorical_mat %>% pth_data_cvd(pth_cvd_grid_severity(1)) %>% group_by(condition) categorical_cvd %>% arrange(luminance, .by_group = TRUE) %>% slice_head(n = 2) %>% print()
We can see that the #7b2600
color may be problematic in dark-mode.
We need to be mindful that we have adquate separation from the luminance of the background.
categorical_cvd %>% arrange(desc(luminance), .by_group = TRUE) %>% slice_head(n = 2) %>% print()
It looks like we are OK with light mode - in fact, this suggests the possibilty of using disctinct light-mode and dark-mode palettes.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.