I'd like black geom_text()
on light geom_bar()
fills, and white text on dark fills. Here's an example {ggplot2} object:
library(tidyverse)
# Example data
d <- data.frame(
x = c("A", "A", "B", "B"),
y = c(3, 6, 4, 10),
z = c("a", "b", "a", "b")
)
# Build grouped bar plot
p <- d %>%
ggplot(aes(x, y, fill = z)) +
geom_bar(position = "dodge", stat = "identity") +
scale_fill_manual(values = c("grey10", "grey90")) +
coord_flip() + theme_minimal()
I have a function, coloratio::cr_choose_bw()
, that returns "black"
or "white"
depending on which has the best contrast with a user-supplied colour. I can use this to select geom_text()
colours based on the geom_bar()
fills:
remotes::install_github("matt-dray/coloratio")
coloratio::cr_choose_bw("grey10") # function example
## [1] "white"
p + geom_text(
aes(y = 0.5, label = y), position = position_dodge(0.9),
colour = map_chr( # make text colour dependent on bar colour
ggplot_build(p)[[1]][[1]]$fill, # access the fills from the p object
coloratio::cr_choose_bw # choose black/white text based on fill
)
)
This results in white text on the dark bars and black on the light:
This approach isn't optimal and requires that p
is created before you can extract the bar fills with ggplot_build(p)
. Is there a better approach?
I understand that I could create and refer to a fill_colour
variable in the original dataset and generate a text_colour
variable from it using cr_choose_bw()
, but I'm interested in whether it's possible to rely on geom_text()
to do the hard work.
EDIT 2021-08-20: you could use something like {ggfittext}, which has a contrast
argument.