Can you conditionally choose `geom_text()` colours based on underlying `geom_bar()` fills?

I'd like black geom_text() on light geom_bar() fills, and white text on dark fills. Here's an example {ggplot2} object:


# 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:


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:

Screenshot 2020-12-27 at 14.02.52

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.

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.