From basic to ‘Wow’

Every time, in no time, with your own ggplot2 theme

Cara R Thompson, Building Stories with Data LTD
IDWSDS | 6th October 2026

Hello 👋

👩🏻

Cara Thompson

👩🏻‍💻

Love for patterns in music & language, and a fascination with the human brain |>
       Psychology PhD |>
       Analysis of postgraduate medical examinations |>
       Data Visualisation Consultant

💙

Helping others maximise the impact of their expertise

How did I end up here?

  • Research
  • Music notation
  • Art galleries
  • Psychology
  • Cross-specialism communication
  • “There must be a better way…”

One important question…

Which baking ingredient gets your vote?

  • A = Chocolate
  • B = Raspberries
  • C = Oranges

What’s the plan?

What’s the plan?

viewers_plot

viewers_plot +
  theme_wow()

On your marks, get set, bake!

Let’s make a graph

How did the ingredients evolve? Did Mary Berry have any influence?

all_the_bakes
# A tibble: 30 × 4
# Groups:   series [10]
   series mary_berry ingredient value
    <int> <chr>      <chr>      <int>
 1      1 yes        chocolate     20
 2      1 yes        raspberry      6
 3      1 yes        orange         8
 4      2 yes        chocolate     30
 5      2 yes        raspberry     12
 6      2 yes        orange         6
 7      3 yes        chocolate     25
 8      3 yes        raspberry      8
 9      3 yes        orange         7
10      4 yes        chocolate     21
# ℹ 20 more rows

Let’s make a graph!

library(ggplot2)

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  geom_point(aes(shape = mary_berry), size = 5) +
  theme_minimal(base_size = 20)

Let’s make a graph

Let’s make a graph

Set one end of the Y limits - limits = c(0, NA)

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  geom_point(aes(shape = mary_berry), size = 5) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20)

Let’s make a graph

Tie the dots together

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  geom_line(show.legend = FALSE) +
  geom_point(aes(shape = mary_berry), size = 5) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20)

Let’s make a graph

Annotate more helpfully - I()

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  geom_line(show.legend = FALSE) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_point(size = 5) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20)

Let’s make a graph

Annotate more helpfully - I()

all_the_bakes |>
  filter(ingredient %in% c("orange", "raspberry")) |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20)

Annotate more helpfully

Let’s give everyone some context

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value / 2,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(legend.position = "bottom")

Annotate more helpfully

And wrap the text - I ❤️ {munch}

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Annotate more helpfully

And wrap the text - I ❤️ {munch}

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Annotate more helpfully

Series 7.5? - scales::breaks_width(1) 💙

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

#1 - Improve the colours

Improve the colours

bakeoff_palette <- c(
  "chocolate" = "#561c2f",
  "orange" = "#e9a403",
  "raspberry" = "#d33b66",
  "mary" = "#f0c9df",
  "default" = "#187194"
)

monochromeR::view_palette(bakeoff_palette)

Improve the colours

From this…

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = "grey20",
    alpha = 0.1
  ) +
  geom_vline(xintercept = 7.5, linetype = 3) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Improve the colours

… to this!

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = bakeoff_palette["mary"]
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(values = bakeoff_palette) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

#2 - Fix the colour accessibility issue

Fix the colour accessibility issue

Wait, what?

colorblindr::cvd_grid(
  all_the_bakes |>
    ggplot(aes(
      x = series,
      y = value,
      colour = ingredient
    )) +
    annotate(
      geom = "rect",
      xmin = -Inf,
      xmax = 7.5,
      ymin = -Inf,
      ymax = Inf,
      fill = bakeoff_palette["mary"],
      alpha = 0.2
    ) +
    geom_vline(
      xintercept = 7.5,
      linetype = 1,
      linewidth = 0.5,
      colour = bakeoff_palette["mary"]
    ) +
    ggtext::geom_textbox(
      data = data.frame(),
      # Y coordinate is relative to the plot
      aes(x = 7.5, y = I(0.95), label = "Mary Berry years"),
      hjust = 1,
      halign = 1,
      colour = "black",
      box.colour = NA,
      fill = NA
    ) +
    geom_line(show.legend = FALSE) +
    geom_point(size = 5) +
    scale_colour_manual(values = bakeoff_palette) +
    scale_x_continuous(breaks = scales::breaks_width(1)) +
    scale_y_continuous(
      limits = c(0, NA),
      expand = expansion(add = c(0, 2))
    ) +
    theme_minimal() +
    theme(
      axis.title = element_blank(),
      axis.text = element_blank(),
      legend.position = "top",
      legend.justification = 0,
      legend.location = "plot",
      legend.title = element_blank(),
      plot.title.position = "plot",
      plot.title = munch::element_md(),
      plot.subtitle = munch::element_md()
    )
)

Fix the colour accessibility issue

“a contrast ratio of 3:1 for meaningful graphical elements”

Fix the colour accessibility issue

There’s a simple solution!

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = bakeoff_palette["mary"]
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(values = bakeoff_palette) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

There’s a simple solution!

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5, shape = 21) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(values = bakeoff_palette) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

There’s a simple solution!

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    colour = ingredient,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = bakeoff_palette["mary"]
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(show.legend = FALSE) +
  geom_point(size = 5, shape = 21) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

There’s a simple solution!

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(
    aes(
      colour = ingredient
    ),
    show.legend = FALSE
  ) +
  geom_point(size = 5, shape = 21) +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

Wait, wasn’t this talk about themes? - theme(geom = element_geom())

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(
    aes(
      colour = ingredient
    ),
    show.legend = FALSE
  ) +
  geom_point() +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 2,
      colour = "red"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

Wait, wasn’t this talk about themes? - theme(geom = element_geom())

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    colour = "transparent",
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(
    aes(
      colour = ingredient
    ),
    show.legend = FALSE
  ) +
  geom_point() +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 2,
      colour = "red"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Fix the colour accessibility issue

Wait, wasn’t this talk about themes? - theme(geom = element_geom())

all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    colour = "transparent",
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(
    aes(
      colour = ingredient
    ),
    show.legend = FALSE
  ) +
  geom_point() +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  ) +
  theme_minimal(base_size = 20) +
  theme(
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

#3 - Add your own font for personality

Add your own font for personality

All about the theme from here onwards!

bakeoff_plot <-
  all_the_bakes |>
  ggplot(aes(
    x = series,
    y = value,
    fill = ingredient
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = bakeoff_palette["mary"],
    colour = "transparent",
    alpha = 0.2
  ) +
  geom_vline(
    xintercept = 7.5,
    linetype = 1,
    linewidth = 0.5,
    colour = "#d76ea9"
  ) +
  annotate(
    "text",
    x = 7.4,
    y = I(0.95),
    label = "Mary Berry years",
    hjust = 1,
    size = 5
  ) +
  geom_line(
    aes(
      colour = ingredient
    ),
    show.legend = FALSE
  ) +
  geom_point() +
  labs(
    title = "Chocolate has remained the most commonly used ingredient, despite being overtaken by Orange in series 8",
    subtitle = "The number of bakes using chocolate was far higher in the Mary Berry years. We can expect about twice as many chocolate bakes if she returns as judge.",
    x = "Series",
    y = "Number of bakes"
  ) +
  scale_colour_manual(
    aesthetics = c("colour", "fill"),
    values = bakeoff_palette
  ) +
  scale_x_continuous(breaks = scales::breaks_width(1)) +
  scale_y_continuous(
    limits = c(0, NA),
    expand = expansion(add = c(0, 2))
  )

Add your font for personality

Where we were

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Add your font for personality

Hello you!

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Add your font for personality

Hello you!

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

#4 - Optimise the text hierarchy

Optimise the text hierarchy

Starting point

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Optimise the text hierarchy

Tout est relatif!

bakeoff_plot +
  theme_minimal(base_size = 10) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Optimise the text hierarchy

Tout est relatif!

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(),
    plot.subtitle = munch::element_md()
  )

Optimise the text hierarchy

Tout est relatif!

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.6)
    ),
    plot.subtitle = munch::element_md()
  )

Optimise the text hierarchy

Go bold

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a",
      family = "Cabin"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.6),
      face = "bold"
    ),
    plot.subtitle = munch::element_md()
  )

A workaround…

Go bold

systemfonts::register_variant(
  "Cabin bold",
  family = "Cabin",
  weight = "bold"
)
👉 Getting fonts to work in R

Optimise the text hierarchy

Go bold

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(family = "Cabin"),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = "#36444a"
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      family = "Cabin bold",
      size = rel(1.5)
    ),
    plot.subtitle = munch::element_md()
  )

Optimise the text hierarchy

Expanding the palette with some useful colours

bakeoff_palette <- c(
  "chocolate" = "#561c2f",
  "orange" = "#e9a403",
  "raspberry" = "#d33b66",
  "mary" = "#f0c9df",
  "default" = "#187194",
  "dark_text" = "#04161D",
  "light_text" = "#36444A",
  "background" = "#f7f5f1"
)

monochromeR::view_palette(bakeoff_palette)

Optimise the text hierarchy

Expanding the palette with some useful colours

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(
      family = "Cabin",
      colour = bakeoff_palette["light_text"]
    ),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = bakeoff_palette["light_text"]
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.5),
      family = "Cabin bold",
      colour = bakeoff_palette["dark_text"]
    ),
    plot.subtitle = munch::element_md()
  )

#5 - Give everything some space to breathe

Give everything some space to breathe

Backgrounds make life easier

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(
      family = "Cabin",
      colour = bakeoff_palette["light_text"]
    ),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = bakeoff_palette["light_text"]
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.5),
      family = "Cabin bold",
      colour = bakeoff_palette["dark_text"]
    ),
    plot.subtitle = munch::element_md()
  )

Give everything some space to breathe

Backgrounds make life easier

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(
      family = "Cabin",
      colour = bakeoff_palette["light_text"]
    ),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = bakeoff_palette["light_text"]
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.5),
      family = "Cabin bold",
      colour = bakeoff_palette["dark_text"]
    ),
    plot.subtitle = munch::element_md(),
    plot.background = element_rect(fill = bakeoff_palette["background"]),
    panel.grid = element_line(colour = "#ffffff"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  )

Give everything some space to breathe

The things no one sees… but you will never unsee

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(
      family = "Cabin",
      colour = bakeoff_palette["light_text"]
    ),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = bakeoff_palette["light_text"]
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.5),
      family = "Cabin bold",
      colour = bakeoff_palette["dark_text"],
      lineheight = 1.3
    ),
    plot.subtitle = munch::element_md(lineheight = 1.3),
    plot.background = element_rect(fill = bakeoff_palette["background"]),
    panel.grid = element_line(colour = "#ffffff"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  )

Give everything some space to breathe

The things no one sees… but you will never unsee

bakeoff_plot +
  theme_minimal(base_size = 20) +
  theme(
    text = element_text(
      family = "Cabin",
      colour = bakeoff_palette["light_text"]
    ),
    geom = element_geom(
      pointsize = 5,
      pointshape = 21,
      borderwidth = 0.5,
      colour = bakeoff_palette["light_text"]
    ),
    legend.position = "top",
    legend.justification = 0,
    legend.location = "plot",
    legend.title = element_blank(),
    plot.title.position = "plot",
    plot.title = munch::element_md(
      size = rel(1.5),
      family = "Cabin bold",
      colour = bakeoff_palette["dark_text"],
      lineheight = 1.3
    ),
    plot.subtitle = munch::element_md(lineheight = 1.3),
    plot.background = element_rect(fill = bakeoff_palette["background"]),
    panel.grid = element_line(colour = "#ffffff"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    plot.margin = margin_auto(30)
  )

Reuse it

Reuse it

Turn all the theming into a custom theme

theme_wow <- function() {
  theme_minimal(base_size = 20) +
    theme(
      text = element_text(
        family = "Cabin",
        colour = bakeoff_palette["light_text"]
      ),
      geom = element_geom(
        pointsize = 5,
        pointshape = 21,
        borderwidth = 0.5,
        colour = bakeoff_palette["light_text"]
      ),
      legend.position = "top",
      legend.justification = 0,
      legend.location = "plot",
      legend.title = element_blank(),
      plot.title.position = "plot",
      plot.title = munch::element_md(
        size = rel(1.5),
        family = "Cabin bold",
        colour = bakeoff_palette["dark_text"],
        lineheight = 1.3
      ),
      plot.subtitle = munch::element_md(lineheight = 1.3),
      plot.background = element_rect(fill = bakeoff_palette["background"]),
      panel.grid = element_line(colour = "#ffffff"),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.x = element_blank(),
      plot.margin = margin_auto(30)
    )
}

Reuse it

Turn all the theming into a custom theme

theme_wow <- function(base_text_size = 20) {
  theme_minimal(base_size = base_text_size) +
    theme(
      text = element_text(
        family = "Cabin",
        colour = bakeoff_palette["light_text"]
      ),
      geom = element_geom(
        pointsize = 5,
        pointshape = 21,
        borderwidth = 0.5,
        colour = bakeoff_palette["light_text"]
      ),
      legend.position = "top",
      legend.justification = 0,
      legend.location = "plot",
      legend.title = element_blank(),
      plot.title.position = "plot",
      plot.title = munch::element_md(
        size = rel(1.5),
        family = "Cabin bold",
        colour = bakeoff_palette["dark_text"],
        lineheight = 1.3
      ),
      plot.subtitle = munch::element_md(lineheight = 1.3),
      plot.background = element_rect(fill = bakeoff_palette["background"]),
      panel.grid = element_line(colour = "#ffffff"),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.x = element_blank(),
      plot.margin = margin_auto(base_text_size * 1.5)
    )
}

Reuse it

Turn all the theming into a custom theme

theme_wow <- function(base_text_size = 20, my_palette = bakeoff_palette) {
  theme_minimal(base_size = base_text_size) +
    theme(
      text = element_text(
        family = "Cabin",
        colour = my_palette["light_text"]
      ),
      geom = element_geom(
        pointsize = 5,
        pointshape = 21,
        borderwidth = 0.5,
        colour = my_palette["light_text"]
      ),
      legend.position = "top",
      legend.justification = 0,
      legend.location = "plot",
      legend.title = element_blank(),
      plot.title.position = "plot",
      plot.title = munch::element_md(
        size = rel(1.5),
        family = "Cabin bold",
        colour = my_palette["dark_text"],
        lineheight = 1.3
      ),
      plot.subtitle = munch::element_md(lineheight = 1.3),
      plot.background = element_rect(fill = my_palette["background"]),
      panel.grid = element_line(colour = "#ffffff"),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.x = element_blank(),
      plot.margin = margin_auto(base_text_size * 1.5)
    )
}

Reuse it

Turn all the theming into a custom theme

theme_wow <- function(base_text_size = 20, my_palette = bakeoff_palette) {
  theme_minimal(base_size = base_text_size) +
    theme(
      text = element_text(
        family = "Cabin",
        colour = my_palette["light_text"]
      ),
      geom = element_geom(
        fill = my_palette["default"],
        pointsize = 5,
        pointshape = 21,
        borderwidth = 0.5,
        colour = my_palette["light_text"]
      ),
      legend.position = "top",
      legend.justification = 0,
      legend.location = "plot",
      legend.title = element_blank(),
      plot.title.position = "plot",
      plot.title = munch::element_md(
        size = rel(1.5),
        family = "Cabin bold",
        colour = my_palette["dark_text"],
        lineheight = 1.3
      ),
      plot.subtitle = munch::element_md(lineheight = 1.3),
      plot.background = element_rect(fill = my_palette["background"]),
      panel.grid = element_line(colour = "#ffffff"),
      panel.grid.major.x = element_blank(),
      panel.grid.minor.x = element_blank(),
      plot.margin = margin_auto(base_text_size * 1.5)
    )
}

Reuse it

Did we miss anything?

bakeoff_plot

bakeoff_plot + theme_wow()

Reuse it

From basic to wow in one line

How old were the participants?

age_plot

From basic to wow in one line

How old were the participants?

age_plot +
  theme_wow()

From basic to wow in one line

How many viewers saw each first episode in a the series?

viewers_plot

From basic to wow in one line

How many viewers saw each first episode in a the series?

viewers_plot +
  theme_wow()

From basic to wow in one line

How many viewers saw each first episode in a the series?

viewers_plot +
  theme_wow() +
  theme(panel.grid = element_blank())

From basic to wow in one line

How much did we all love Mel & Sue?

viewers_plot

From basic to wow in one line

How much did we all love Mel & Sue?

viewers_plot +
  scale_fill_manual(values = c("Mel & Sue" = "pink", "Noel & Co." = "black")) +
  theme_wow() +
  theme(panel.grid = element_blank())

From basic to wow in one line

From this…

bakeoff_plot

From basic to wow in one line

… to this!

bakeoff_plot +
  theme_wow()

From basic to wow in one line

And so much more!

Over to you!


👉 cararthompson.com/talks

👋 hello@cararthompson.com


Happy datavizing!