ggplot add horizontal line to grouped categorical data and share legend

One option would be to use only the fill scale and make use of custom key glyph.

  1. Set the color for the geom_hline as an argument instead of mapping on the color aes. Instead map a constant e.g. "" on the fill aes. A Drawback is that we get a warning.
  2. Add an additional color and label to scale_fill_manual.
  3. To get a line as the key glyph for the geom_hline I make use of a custom key glyph which conditionally on the fill color switches between draw_key_path and the default key glyph for geom_col. To make this work I use a "red2" as the additional fill color for the hline which I switch to "red" inside the custom key glyph function.
library(tidyverse)
data(mtcars)

y = mean(mtcars$mpg)
x = unique(mtcars$cyl)
meanDf <- data.frame(x, y )

mtcars$mean = y
mtcars$group = "mean"

draw_key_cust <- function(data, params, size) {
  if (data$fill %in% c("red2")){
    data$colour <- "red"
    data$fill <- NA
    draw_key_path(data, params, size)  
  } else
    GeomCol$draw_key(data, params, size)
}

mtcars %>%
  ggplot(aes(x = factor(cyl), y = mpg, fill = factor(carb))) +
  geom_hline(data = meanDf, aes(yintercept = y, fill = ""), color = "red") +
  geom_col(key_glyph = "cust") +
  scale_fill_manual(name = NULL, values = c("red2", "blue", "red", "green", "white", "black", "yellow"), labels = c("label", paste("myLabel", 1:6))) +
  theme(panel.background = element_rect(fill = "white")) +
  theme(legend.background = element_rect(color = "black", fill = "white"))
#> Warning: Ignoring unknown aesthetics: fill


I think for readability, it's better to separate them out. However, for formatting purpose, you sure can bring them as close as you want by dropping the legend.title (not just assigning it an empty string) and adjusting the legend.margin and legned.spacing. For instance,

library(tidyverse)
data(mtcars)

y = mean(mtcars$mpg)
x = unique(mtcars$cyl)
meanDf <- 
  data.frame(x, y )


mtcars$mean = y
mtcars$group = "mean"

mtcars %>%
  ggplot(aes(x = factor(cyl), y = mpg, fill = factor(carb))) +
  geom_col(position = "dodge") +
  geom_hline(data = meanDf, aes(yintercept = y, color = "")) +
  scale_fill_manual(name = "", values = c("blue", "red", "green", "white", "black", "yellow"), labels = paste("myLabel", 1:6)) +
  scale_color_manual(name = "", values = "red", label = "myLabel") + 
  theme(
    legend.title = element_blank(), 
    legend.margin = margin(t = 0, b = 0, r = 2, l = 2),
    legend.spacing.y = unit(.5, "pt")
  )

Output res