Introduction

The ggtext package enables the rendering of complex formatted plot labels (titles, subtitles, facet labels, axis labels, etc.). Text boxes with automatic word wrap are also supported. It defines two new theme elements, element_markdown() and element_textbox() (element_textbox_simple()), which can be used in place of element_text() in ggplot2 themes.

Example and R codes

R codes for creating the plot:

# Clear our R environment: 

rm(list = ls())

# Import readxl for loading xlsx files: 

library(readxl)

library(dplyr)


# Load data (download from: https://drive.google.com/file/d/1AC70gyMQO0TufMJa1VCc5vGI72NUBoRH/view): 

read_excel("E:/storytelling/3.1 EXERCISE.xlsx", skip = 0) %>% 
  select(-c(1, 2)) -> rawData_3.1

rawData_3.1 %>% 
  slice(7) %>% 
  as.vector() %>% 
  as.numeric() -> sales

c(rep(2018, 12), rep(2019, 12)) -> year

c(rep("ACTUAL", 17), rep("FORECAST", 7)) -> type

monthLabels <- rep(month.abb, each = 1, time = 2)

monthLabels[1] <- "Jan\n2018"

monthLabels[13] <- "Jan\n2019"

# Create data: 

data.frame(sales = sales,
           year = year, 
           type = type, 
           month = monthLabels, 
           myTime = 1:24) -> dataLong


leftText <- "**2018:** Jan-Jun was a period of stability, with\nfairly steady growth (averaging +3% per\nmonth). There was a <span style = 'color:#ff7f00'>**nearly 20% decrease\nin July**</span>, when Product X was recalled and\npulled from the market. Total sales remained\nat reduced volume for the rest of the year."

rightText <- "**2019:** The year started at less than $1.6B, but\n<span style = 'color:#377eb8'>**increased markedly in February**</span>, when a new\nstudy was released. Total sales have increased\nsteadily since then and this is projected to continue.\nThe latest forecast is for $2.4B in monthly sales by the end of the year.
"

textAddted <- "**2019 FORECAST**\nThis is provided\nby ABC consultants and based on market data through June. The forecast assumes no major market changes."

dfText <- data.frame(myTime = 1, rightText = rightText, leftText = leftText, textAddted = textAddted)

dataLong %>% 
  filter(myTime %in% c(1, 6, 7, 14, 18, 24)) %>% 
  mutate(labelDollar = round(sales, 1)) %>% 
  mutate(labelDollar = paste0("$", labelDollar, "B")) %>% 
  mutate(labelDollar = case_when(myTime == 18 ~ "$2.0B", 
                                 TRUE ~ labelDollar)) -> textLabelDollar

library(ggplot2)

library(scales)

library(ggtext)

library(showtext)

# my_font <- "Poppins"

my_font <- "Open Sans"

font_add_google(name = my_font, family = my_font)

showtext_auto()


colorBar <- c("#ff7f00", "#377eb8")

dataLong %>% 
  ggplot(aes(x = myTime, y = sales)) + 
  geom_rect(aes(xmin = 18, xmax = 24, ymin = -Inf, ymax = 2.5), fill = "grey90") + 
  theme_minimal() + 
  theme(text = element_text(family = my_font)) + 
  theme(plot.title.position = "plot") + 
  geom_line(data = dataLong %>% filter(myTime >= 18), linetype = "dashed", 
            size = 1, color = "grey30") + 
  geom_line(data = dataLong %>% filter(myTime <= 18), size = 1.2, color = "grey30") + 
  geom_point(data = dataLong %>% filter(year == 2018, month %in% c("Jan", "Jun")), 
             size = 4, color = "grey30") + 
  geom_point(data = dataLong %>% filter(year == 2018, month %in% c("Jul")), 
             color = "grey30", size = 4, shape = 21, fill = "#ff7f00") + 
  geom_point(data = dataLong %>% filter(year == 2019, month %in% c("Feb")), 
             color = "grey30", size = 4, shape = 21, fill = "#377eb8") + 
  geom_point(data = dataLong %>% filter(year == 2019, month %in% c("Jun")), 
             color = "grey30", size = 4) + 
  geom_point(data = dataLong %>% filter(year == 2019, month %in% c("Dec")), 
             color = "grey30", size = 4, shape = 21, fill = "white") + 
  scale_y_continuous(limits = c(0, 3.5), breaks = seq(0, 3.5, 0.5), 
                     labels = dollar, expand = c(0, 0)) + 
  scale_x_continuous(limits = c(0.3, 24.8), breaks = 1:24, 
                     labels = monthLabels, expand = c(0, 0)) +
  geom_textbox(
    data = dfText,
    aes(x = myTime, y = 3.5, label = leftText, box.color = NA),
    width = 0.4, 
    hjust = 0, 
    vjust = 1,
    fill = NA, 
    color = "grey40", 
    family = my_font, 
    size = 3.5
  ) + 
  geom_textbox(
    data = dfText,
    aes(x = myTime + 12, y = 3.5, label = rightText, box.color = NA),
    width = 0.4, 
    hjust = 0,
    vjust = 1,
    fill = NA, 
    color = "grey40", 
    family = my_font, 
    size = 3.5
  ) + 
  theme(axis.text.x = element_text(hjust = 0, size = 10)) + 
  theme(axis.text.y = element_text(hjust = 0, size = 11)) + 
  labs(title = "Market Size over Time", 
       caption = "Source: https://www.storytellingwithdata.com/") + 
  theme(axis.title = element_blank()) + 
  theme(plot.margin = margin(0.7, 0.5, 0.5, 0.7, "cm")) + 
  theme(plot.caption = element_text(color = "grey40")) + 
  theme(plot.title = element_text(size = 18, color = "grey20", vjust = 3)) + 
  theme(panel.grid = element_blank()) -> plotDraft 



plotDraft + 
  geom_text(data = textLabelDollar %>% filter(myTime != 7), aes(label = labelDollar), 
            vjust = -1, family = my_font, size = 4, color = "grey30") + 
  geom_text(data = textLabelDollar %>% filter(myTime == 7), aes(label = labelDollar), 
            vjust = 1.8, family = my_font, size = 4, color = "grey30")