"

On-brand accessible dataviz

My contribution to the RSS panel on Accessibility at the 2025 RSS Conference in Edinburgh.

Published

September 4, 2025

In this talk we take a “basic but functional” graph and improve it in 10 successive ways, illustrating how accessibility and creativity go hand in hand in crafting intuitive data visualisations.

Slides

View full screen

The 10 steps and their associated resources

This list is a combination of 🛠️ resources and 📚 further reading. We’re using data from the 📦 {bakeoff}.


1. Choose intuitive colours - 🛠️ imagecolorpicker.com

2. Check your palette is colourblind-friendly and adjust as needed! - 🛠️ www.vis4.net/palettes

3. Blend in some of your brand colour - 🛠️ monochromeR

4. Mute your colours - 📚 Designing for neurodivergent audiences, Nightingale Issue 03

5. Fix the background-to-foreground issue - 🛠️ colourcontrast.cc

6. Improve text hierarchy - 📚 pimpmytype.com

7. Add personality and readability - 📚 pimpmytype.com

8. Optimise your text colours - 🛠️ colourcontrast.cc

9. Give everything space to breathe - 📚 The a11y project post on Dyslexia fonts

10. Give the plot a background - 📚 Designing for neurodivergent audiences, Nightingale

Final graph code

Note that for the text to look like this when you run the code, you’ll need to have installed Noto Sans on your device. Here’s a blog post to help you do that and get it working nicely within R!

library(tidyverse)

# Organise the data
all_the_bakes <- bakeoff::challenges |>
  select(-technical) |>
  pivot_longer(c(signature, showstopper)) |>
  group_by(series) |>
  mutate(
    chocolate_count = sum(grepl("[Cc]hocolat", value)),
    raspberry_count = sum(grepl("[Rr]aspberr", value)),
    orange_count = sum(grepl("[Oo]range", value))
  ) |>
  select(series, chocolate_count, raspberry_count, orange_count) |>
  unique() |>
  pivot_longer(-series) |>
  mutate(
    rank = rank(-value, ties.method = "min"),
    mary_berry = case_when(series < 8 ~ "yes", TRUE ~ "no")
  ) |>
  pivot_wider(names_from = name, values_from = c(rank, value)) |>
  rename(
    choc_bakes = value_chocolate_count,
    rasp_bakes = value_raspberry_count,
    orange_bakes = value_orange_count
  ) |>
  select(series, mary_berry, choc_bakes, rasp_bakes, orange_bakes) |>
  pivot_longer(-c(series, mary_berry))

# Set up the colours
branded_palette <- c(
  "chocolate" = "#561c2f",
  "orange" = "#e9a403",
  "raspberry" = "#d33b66",
  "mary" = "#f0c9df"
)

# Make the graph
all_the_bakes |>
  mutate(
    name = case_when(
      grepl("choc", name) ~ "chocolate",
      grepl("orange", name) ~ "orange",
      grepl("rasp", name) ~ "raspberry"
    )
  ) |>
  ggplot(aes(
    x = series,
    y = value,
    colour = name,
    fill = name
  )) +
  annotate(
    geom = "rect",
    xmin = -Inf,
    xmax = 7.5,
    ymin = -Inf,
    ymax = Inf,
    fill = branded_palette["mary"],
    alpha = 0.3
  ) +
  geom_line(show.legend = FALSE) +
  geom_vline(xintercept = 7.5, linetype = 3, colour = "#d76ea9") +
  ggtext::geom_textbox(
    data = data.frame(),
    aes(x = 7.5, y = 31, label = "**Mary Berry years**"),
    hjust = 1,
    halign = 1,
    box.colour = NA,
    colour = "#324e5a",
    family = "Noto Sans",
    fill = NA
  ) +
  geom_point(aes(shape = name), size = 5, colour = "#324e5a") +
  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_shape_manual(
    values = c("chocolate" = 22, "orange" = 21, "raspberry" = 25),
  ) +
  scale_fill_manual(values = branded_palette) +
  scale_colour_manual(values = branded_palette) +
  scale_y_continuous(limits = c(0, 31), expand = expansion(add = c(0, 2))) +
  scale_x_continuous(breaks = c(1:10)) +
  theme_minimal() +
  theme(
    text = element_text(family = "Noto Sans", colour = "#324e5a"),
    plot.title.position = "plot",
    plot.title = marquee::element_marquee(
      width = 1,
      margin = margin(c(12, 0, 12, 0)),
      size = rel(1.8),
      lineheight = 1.15,
      family = "Noto Sans",
      colour = "#002332"
    ),
    plot.subtitle = marquee::element_marquee(
      width = 1,
      vjust = 0,
      size = rel(1.2),
      family = "Noto Sans",
      colour = "#324e5a",
      lineheight = 1.2,
      margin = margin(c(6, 0, 24, 0))
    ),
    legend.title = element_blank(),
    panel.grid = element_line(colour = "white"),
    panel.grid.minor.x = element_blank(),
    plot.margin = margin(rep(24, 4)),
    plot.background = element_rect(fill = "#f8f8f8", colour = "#f8f8f8")
  )

Reuse

Citation

For attribution, please cite this work as:
“On-Brand Accessible Dataviz.” 2025. September 4, 2025. https://www.cararthompson.com/talks/on-brand-accessibility/.