NISRA Technology and Support Lab RAP Skeleton Demo Report

Status: Accredited Official Statistics

Publication date: 10 June 2024

Last updated: 08 December 2025

1. Key findings (h2)

This section includes the key points of your publication (in an unordered list). The logos used in the banner are intentionally placed in order. NISRA logo on left and department on right, if an Accredited Official Statistics publication then this logo will be in the middle. Logo placement is automated in the code depending on which logos are required.

There are some key accessibility features to highlight here:

  • the language element is set to English, which is done in the YAML section of the code
  • there is a named accessibility contact at the end of the doc
  • you will see from the code that the main sections of the html are enclosed in div elements

It can help automation of reports to include sections of text that contain numerical values calculated by code. These will then update automatically when the data changes. This has been implemented below. For key points with accompanying charts or graphics, a two column structure can be used.

Population change

# create a bullet point in unicode called 'b'
b <- "\U2022"

point_1 <- paste0(
  b, " The Northern Ireland population grew from ",
  round_half_up(ni_earliest_total, 2), " million in ",
  earliest_year, " to ", round_half_up(ni_latest_total, 2),
  " million in ", latest_year, ", an increase of ",
  round_half_up(ni_earliest_latest_change, 0), " percent."
)

point_2 <- paste0(
  b, " Under 25s make up the largest age group in the ",
  "population."
)

point_3 <- paste0(
  b, " Derry City and Strabane has seen the largest increase ",
  "of all the LGDs (Local Government Districts)."
)


change_chart <- here(
  "code/demo/demo_data/images/mini_population.png"
)

div(
  class = "row",
  div(
    class = "col-sm-6", style = "margin-top: 10px; font-size:120%;",
    point_1, p(), point_2, p(), point_3
  ),
  div(
    class = "col-sm-6",
    img(
      src = change_chart,
      alt = "Column chart showing relative increase in population"
    )
  )
)
• The Northern Ireland population grew from 1.69 million in 2001 to 1.91 million in 2022, an increase of 13 percent.

• Under 25s make up the largest age group in the population.

• Derry City and Strabane has seen the largest increase of all the LGDs (Local Government Districts).
Column chart showing relative increase in population

2. Introduction (h2)

This is a section with some basic paragraphs of text like you would use for an introduction or commentary. The data used in Demo Report are from the NISRA - 2022 Mid Year Population Estimates for Northern Ireland. It includes LGD2014, single year of age and sex mid-year estimates for years 2001 to 2022. We are using it to outline some accessibility features. If a report uses a number of different data sources, they should be provided along with their respective figures or tables. For example:

Source: NISRA - 2022 Mid Year Population Estimates for Northern Ireland

The source can be specified in the ‘fig.cap’ option of R Markdown blocks to be included underneath figures.

Header tags (h3)

A key element is using header tags to structure your document. Screen readers use these to scan and navigate documents so it’s important that headings are tagged correctly. The colour used in these headings is #1460aa rather than NISRA blue to meet accessibility standards.

  • the title of the document is tagged as h1, don’t use elsewhere
  • don’t use heading styles to format text that isn’t a heading (for example to emphasise a point) - use bold or italic styles
  • conversely, don’t use bold styles for headings - use proper heading styles
  • don’t use bold, colour or text size to create visual headings without tagging at the appropriate level
  • don’t skip any heading levels, follow order h1, h2, h3, h4

More detailed guidance on writing for web is available in the Drupal user manual published by Dissemination Branch. You can find the Drupal user manual and other accessibility guidance on MS Teams.

3. Infographics and images (h2)

An image should not replace a clear text description so ensure any information presented in an infographic is presented elsewhere. Add descriptive alt text for anyone using a screen reader - even images with no substantive content should have empty alt text added to indicate that they are decorative.

Do not use images that only contain text - images can contain explanatory text but should never exclusively be of text. Use real text and style appropriately instead.

The reading order of an infographic should always be left-to-right.

For more specific guidance about accessible infographics refer to Dissemination Branch document ‘Creating an accessible infographic’. For more general guidance you can also refer to Government Statistical Service (GSS) guidance on infographics but note that as of the 20th of August 2021 it does not include accessibility requirements.

Important note: If SVG images are used for images these should be marked up properly. It is important that all text is clear on these images and are properly tested for accessibility. If there are still issues with the screen reader reading text within an SVG image, it is recommended to use a PNG image format instead.

Type the title of infographic here (h3)

# file is in image folder
knitr::include_graphics("../demo/demo_data/images/PopInfographic.png")

Population increase from 2018 to 2043 due to relative contributions of natural change and net migration.

Source: 2019 Mid-year population estimates

4. Tables (h2)

This section has a number of basic tables. The UK Analysis function guidance recommends that only small demonstration tables should be included in the publication itself. Demonstration tables are small tables which highlight one or two messages clearly and are supported by commentary.

When it comes to accessibility, a good rule of thumb is to use a simple structure. Use a simple table matrix with a single row of column headers describing what is in each column. Other considerations are as follows:

  • don’t merge cells
  • don’t have blank rows or columns
  • every column should have a heading
  • make sure all headings and labels are visible
  • use consistent rounding
  • left align text, or row labels, right align numbers
  • use thousand comma separator
  • avoid abbreviations or make sure they are explained fully when first used
  • use caption for table titles
  • put any data labels in the headings such as % or millions or STG rather than in all column cells
  • don’t use symbols such as * or superscripts to create notes or explanations

Basic table (h3)

Table 1: Mid ulster had the highest proportion of under 25s (h4)

Select the tab to view population of young or elderly people.

1a Population of young people by LGD
# kable code needs to be created as a string to allow correct rendering of
# source code on second tab

kable(df_mye_latest_year_youthrate,

  # left align text, right align numbers
  align = ("lrrr"),

  # full and descriptive headings
  col.names = c(
    "Local Government District (LGD)",
    "Young people in LGD",
    "Total population of LGD",
    "Young people as percentage"
  ),

  # use caption to keep text associated with table
  caption = paste0("Population of young people by Local Government District in
                   NI, ", latest_year),

  # add thousand separator
  format.args = list(big.mark = ","),
  label = NA
) %>%
  kable_styling()
Population of young people by Local Government District in NI, 2022
Local Government District (LGD) Young people in LGD Total population of LGD Young people as percentage
Antrim and Newtownabbey 43,399 146,148 29.7
Ards and North Down 43,686 164,223 26.6
Armagh City, Banbridge and Craigavon 69,994 220,271 31.8
Belfast 113,076 348,005 32.5
Causeway Coast and Glens 41,392 141,316 29.3
Derry City and Strabane 47,552 150,836 31.5
Fermanagh and Omagh 36,180 116,994 30.9
Lisburn and Castlereagh 43,265 149,915 28.9
Mid Ulster 50,409 151,001 33.4
Mid and East Antrim 39,019 139,200 28.0
Newry, Mourne and Down 58,475 182,634 32.0

Commentary describing the key messages you want readers to take from the table could go here.

1b Population of elderly people by LGD
# kable code needs to be created as a string to allow correct rendering of
# source code on fourth tab

kable(df_mye_latest_year_elderlyrate,

  # left align text, right align numbers
  align = ("lrrr"),

  # full and descriptive headings
  col.names = c(
    "Local Government District (LGD)",
    "Elderly people in LGD",
    "Total population of LGD",
    "Elderly people as percentage"
  ),

  # use caption to keep text associated with table
  caption = paste0("Population of elderly people by Local Government District
                   in NI, ", latest_year),

  # add thousand separator
  format.args = list(big.mark = ","),
  label = NA
) %>%
  kable_styling()
Population of elderly people by Local Government District in NI, 2022
Local Government District (LGD) Elderly people in LGD Total population of LGD Elderly people as percentage
Antrim and Newtownabbey 25,751 146,148 17.6
Ards and North Down 37,193 164,223 22.6
Armagh City, Banbridge and Craigavon 36,034 220,271 16.4
Belfast 51,989 348,005 14.9
Causeway Coast and Glens 28,091 141,316 19.9
Derry City and Strabane 24,759 150,836 16.4
Fermanagh and Omagh 22,148 116,994 18.9
Lisburn and Castlereagh 26,910 149,915 18.0
Mid Ulster 23,137 151,001 15.3
Mid and East Antrim 27,964 139,200 20.1
Newry, Mourne and Down 31,473 182,634 17.2

Commentary describing the key messages you want readers to take from the table could go here.

Pagination (h3)

Table 2: Northern Ireland population is growing year on year (h4)

datatable(df_mye_year_gender_t,
  # DT creates row numbers by default. Suppress to ensure all cols have a header
  rownames = FALSE,
  # Rename column headers to make them descriptive
  colnames = c(
    "Mid Year Ending",
    "Females",
    "Males",
    "NI Population Total"
  ),
  caption = paste0("Number of males and females making up the total NI
                   population from ", earliest_year, -latest_year),
  # left align first column and add thousand separator to numbers
  options = list(columnDefs = list(list(className = "dt-left", targets = 0)))
) %>%
  formatRound(c("Females", "Males", "All persons"),
    digits = 0, interval = 3,
    mark = ","
  )

Descriptive commentary to highlight key point from this table. Females outnumber males year on year and population growth is driven by both sexes

Cross-tabulation (h3)

Table 3: Under 25s were the largest age group in Northern Ireland

cross_tab_data <- df_mye_latest_year_agegrp_gend %>%
  pivot_wider(names_from = gender, values_from = pop_total) %>%
  mutate(persons = Males + Females)

cross_tab_data["Female_Male_ratio_100"] <-
  round_half_up(cross_tab_data$Females / cross_tab_data$Males * 100, 0)

# Adding thousand separator
cross_tab_data$Females <- prettyNum(cross_tab_data$Females, big.mark = ",")
cross_tab_data$Males <- prettyNum(cross_tab_data$Males, big.mark = ",")
cross_tab_data$persons <- prettyNum(cross_tab_data$persons, big.mark = ",")

kable(cross_tab_data,
  # left row labels, right align numbers
  align = "lrrrr",
  # make column headings clear and descriptive
  col.names = c(
    "Age group",
    "Females",
    "Males",
    "Total persons",
    "Number of females to every 100 males"
  ),
  # use caption for descriptive title
  caption = paste0("Northern Ireland population by broad age band and sex ", latest_year),
) %>%
  kable_styling()
Northern Ireland population by broad age band and sex 2022
Age group Females Males Total persons Number of females to every 100 males
0-24 285,014 301,433 586,447 95
25-44 252,835 241,141 493,976 105
45-64 252,234 242,437 494,671 104
65plus 180,513 154,936 335,449 117

If we group the population into broad age bands we can compare the proportion of males to females. Males outnumber females in the under 25 age group only. As age increases, the ratio of females to males increases.

Table with colour scales

Table 4: Belfast saw greatest increase in population from 2021 to 2022

# function to add thousand separator
mycomma <- function(digits = 0) {
  require(formattable)
  formatter("span", x ~ comma(x, digits = digits))
}

# up and down arrows added to column four
# colour tiles added to column five. Colour selected is a lighter shade of
# nisra_blue (which failed accessibility check). Seems that colour specified in
# code is the mid point of the tiles generated so needs to be pale to ensure
# contrast for higher levels is sufficient.
t4 <- formattable(df_t4_html,
  align = c("lrrrr"),
  list(
    area(col = 2:3) ~ mycomma(digits = 0), # apply mycomma fn
    area(col = 5) ~ color_tile("transparent", "#7ca6da"),
    `Change` = formatter("span",
      style = ~ style(
        color = ifelse(
          `Change` > 0, "black", "#e60000"
        )
      ),
      ~ icontext(ifelse(
        `Change` > 0, "arrow-up",
        "arrow-down"
      ), format(`Change`, big.mark = ","))
    )
  )
)

t4
LGD 2022 population 2021 population Change Change (%)
Antrim and Newtownabbey 146,148 145,852 296 0.2
Ards and North Down 164,223 163,827 396 0.2
Armagh City, Banbridge and Craigavon 220,271 219,127 1,144 0.5
Belfast 348,005 344,992 3,013 0.9
Causeway Coast and Glens 141,316 141,664 -348 -0.2
Derry City and Strabane 150,836 150,834 2 0.0
Fermanagh and Omagh 116,994 116,926 68 0.1
Lisburn and Castlereagh 149,915 149,272 643 0.4
Mid Ulster 151,001 150,598 403 0.3
Mid and East Antrim 139,200 139,127 73 0.1
Newry, Mourne and Down 182,634 182,345 289 0.2

Ensure sufficient contrast between background colours and the text when using colour scales.

5. Charts (h2)

This section includes a line chart with a single line, a line chart with multiple lines, a bar chart, a column chart, a pie chart, tree maps and a donut chart.

Considerations when creating charts:

Accessibility

  • Do not rely on colour alone to convey information; include data labels and be mindful of legends
  • Ensure colour contrast ratios between adjacent chart elements and against background is min 3:1. There is an accessible colour palette developed by Dissemination Branch available on the Dissemination Tab of the NISRA Teams channel
  • ONS recommend that you link to the raw data in csv or xls format or provide a way for users to request it
  • Chart titles should be descriptive. A main title and statistical subtitle should be used (e.g. main title ‘Figure 1: The sex pay gap fell to 8.6% among full-time employees in 2018’) and statistical subtitle should include statistical measure, geographic coverage and time period (e.g. ‘Sex pay gap for median gross hourly earnings (excluding overtime), UK, April 1997 to 2019’)
  • Charts should include a line of alt text (to include chart type, type of data used and summary of main trend); all detail should be in main text
  • All text on a chart should be presented horizontally (including axis labels)
  • Axis labels should be displayed in full (i.e. not abbreviated)
  • Where abbreviations are included they should be in full, or explained in footnotes
  • Try to avoid a legend (especially if they rely on colour alone to convey meaning), but if legend is needed place above the chart
  • Where possible, charts should be created in HTML (i.e. interactive), and where not possible, in SVG image format (this exemplar contains examples of both)
  • Main message(s) of charts should be clearly explained in text above or below the chart ( i.e. provide a clear text alternative)
  • Avoid using dual axis charts
  • Consider punctuation on labels of interactive charts and how a screen reader will vocalise them
  • Consider how charts will resize for different display sizes e.g. mobile, laptop or desktop; a message has been added for mobile users to suggest rotating phone to landscape for viewing charts
  • If using Plotly package to create charts users must keep plotly logo and icons switched off. This is to avoid multiple links going to same destination (poor accessibility) and the icons create visual clutter. Instructions on how to do this are provided in the code.

For further information, please refer to the UK Government Analysis Function (UKAF) guidance on best practice for designing charts.

Single line chart (h3)

This section includes a static and reactive version of a line chart.

The line has to meet colour contrast standards for adjacent colours (3:1). In this case it applies to the line against the background.

The value labels have to meet the colour contrast requirements for normal sized text which is 4.5:1. Here, the labels are placed at the ends of the line to make them easier to read and to avoid any problems with overlap if the chart is resized when viewed on different devices or browsers.

To add the broken axis symbol in a ‘plot_ly’ chart pipe the function “fn_break_axis” with “linecolor” matching the colour given in the “y-axis” attributes in the layout section. This function is a NISRA created function that can be found in the functions folder. This function is for use only with a ‘plot_ly’ chart.

To add the broken axis symbol to a ‘ggplot’ chart, it must be added as an annotation to give the illusion of a broken axis as the ‘ggplot’ package does not provide a built-in axis-break feature.

The code below shows examples of how a broken axis symbol can be added to both ‘plot_ly’ and ‘ggplot’ charts.

Include descriptive text before or after the chart.

Figure 1: Mid year population estimates continue to increase

Northern Ireland mid year population estimate (total) between 2001 and 2022
Figure 1 tabs
Figure 1 plotly
year1_lab <- list(
  x = ~ mid_year_ending[1],
  y = ~ ni_pop_total[1], # sets position of label
  xanchor = "right",
  yanchor = "middle", # alignment of label
  text = ~ paste0(
    earliest_year, ": \n",
    round_half_up(ni_pop_total[1] / 1e6, 2), "m"
  ),
  # punctuated and formatted label text
  showarrow = FALSE, # turn off otherwise get line between label and chart
  font = list(size = 14, color = nisra_blue)
)

year2_lab <- list(
  x = ~ tail(mid_year_ending, n = 1), # position at last value of x variable
  y = ~ tail(ni_pop_total, n = 1), # position at last value of y variable
  xanchor = "left",
  yanchor = "middle", # alignment
  # punctuated and formatted label text
  text = ~ paste0(
    latest_year, ": \n",
    round_half_up(tail(ni_pop_total, 1) / 1e6, 2), "m"
  ),
  showarrow = FALSE,
  font = list(size = 14, color = nisra_blue)
)
# create annotations
# annotation on vertical line
note_1 <- list(
  x = 0.41, y = 1, # position of annotation
  text = "2009",
  showarrow = FALSE, xref = "paper", yref = "paper",
  xanchor = "left", align = "left",
  font = list(size = 14, color = nisra_navy, family = "Arial black")
)
# use annotation rather than y axis title variable for horizontal text
yaxistitle <- list(
  x = 0.02, y = 1.06,
  text = paste0("Population (Millions)"),
  showarrow = FALSE,
  xref = "paper", yref = "paper",
  xanchor = "center", yanchor = "centre",
  xshift = 0, yshift = 0,
  font = list(size = 14, color = "black")
)
# Create the caption - Note the non-zero axis
note_2 <- list(
  x = 0.75, y = -0.13, # position of annotation
  text = "Note the non-zero axis",
  showarrow = FALSE, xref = "paper", yref = "paper",
  xanchor = "left", align = "left",
  font = list(size = 14, color = nisra_navy)
)
# aesthetic definitions for vertical line used in fig1
vline <- function(x = 0, color = nisra_navy) {
  list(
    type = "line",
    y0 = 0,
    y1 = 1,
    yref = "paper",
    x0 = x,
    x1 = x,
    line = list(color = color, dash = "dot")
  )
}

# create line chart (type = scatter and mode = line)
plot_ly(df_annual_pop_tot,
  x = ~mid_year_ending,
  y = ~ni_pop_total,
  name = "Northern Ireland population",
  line = list(color = nisra_blue),
  type = "scatter",
  mode = "line",
  hoverinfo = "text",
  # formatting hover text to millions(m) to 2dp
  text = ~ paste0(
    mid_year_ending, ": ",
    format(round_half_up(ni_pop_total / 1e6, 2), nsmall = 2), "m"
  )
) %>%
  layout(
    showlegend = FALSE,
    font = list(family = "Arial", size = 14),
    xaxis = list(
      title = "Year",
      showline = TRUE,
      linecolor = "#000000",
      showgrid = FALSE,
      showticklabels = TRUE,
      range = c(1999, 2024),
      ticks = "outside",
      tickwidth = 2
    ),
    yaxis = list(
      title = "",
      showline = TRUE,
      linecolor = "#000000",
      showgrid = FALSE
    ),
    annotations = list(
      year1_lab,
      year2_lab,
      note_1,
      yaxistitle,
      note_2
    )
  ) %>%
  # To add the broken axis symbol pipe the function "fn_break_axis" with
  # "linecolor" matching the colour given above. This function is a NISRA
  # created function that can be found in the functions folder.
  fn_break_axis(
    linecolor = "#000000",
    ref_line = vline(2009)
  ) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)
Figure 1 plotly zero axis
year1_lab <- list(
  x = ~ mid_year_ending[1],
  y = ~ ni_pop_total[1], # sets position of label
  xanchor = "right",
  yanchor = "middle", # alignment of label
  text = ~ paste0(
    earliest_year, ": \n",
    round_half_up(ni_pop_total[1] / 1e6, 2), "m"
  ),
  # punctuated and formatted label text
  showarrow = FALSE, # turn off otherwise get line between label and chart
  font = list(size = 14, color = nisra_blue)
)

year2_lab <- list(
  x = ~ tail(mid_year_ending, n = 1), # position at last value of x variable
  y = ~ tail(ni_pop_total, n = 1), # position at last value of y variable
  xanchor = "left",
  yanchor = "middle", # alignment
  # punctuated and formatted label text
  text = ~ paste0(
    latest_year, ": \n",
    round_half_up(tail(ni_pop_total, 1) / 1e6, 2), "m"
  ),
  showarrow = FALSE,
  font = list(size = 14, color = nisra_blue)
)
# create annotations
# annotation on vertical line
note_1 <- list(
  x = 0.41, y = 1, # position of annotation
  text = "2009",
  showarrow = FALSE, xref = "paper", yref = "paper",
  xanchor = "left", align = "left",
  font = list(size = 14, color = nisra_navy, family = "Arial black")
)
# use annotation rather than y axis title variable for horizontal text
yaxistitle <- list(
  x = 0.02, y = 1.06, text = paste0("Population (Millions)"),
  showarrow = FALSE, xref = "paper", yref = "paper",
  xanchor = "center", yanchor = "centre", xshift = 0, yshift = 0,
  font = list(size = 14, color = "black")
)

# aesthetic definitions for vertical line used in fig1
vline <- function(x = 0, color = nisra_navy) {
  list(
    type = "line",
    y0 = 0,
    y1 = 1,
    yref = "paper",
    x0 = x,
    x1 = x,
    line = list(color = color, dash = "dot")
  )
}

# create line chart (type = scatter and mode = line)
plot_ly(df_annual_pop_tot,
  x = ~mid_year_ending,
  y = ~ni_pop_total,
  name = "Northern Ireland population",
  line = list(color = nisra_blue),
  type = "scatter",
  mode = "line",
  hoverinfo = "text",
  # formatting hover text to millions(m) to 2dp
  text = ~ paste0(
    mid_year_ending, ": ",
    format(round_half_up(ni_pop_total / 1e6, 2), nsmall = 2), "m"
  )
) %>%
  layout(
    showlegend = FALSE,
    font = list(family = "Arial", size = 14),
    xaxis = list(
      title = "Year",
      showline = TRUE,
      linecolor = "#000000",
      showgrid = FALSE,
      showticklabels = TRUE,
      range = c(1999, 2024),
      ticks = "outside",
      tickwidth = 2
    ),
    yaxis = list(
      title = "",
      showline = TRUE,
      range = c(0, 2100000),
      linecolor = "#000000",
      showgrid = FALSE
    ),
    annotations = list(
      year1_lab,
      year2_lab,
      note_1,
      yaxistitle
    )
  ) %>%
  layout(shapes = list(vline(2009))) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)
Figure 1 static
ggline(df_annual_pop_tot,
  x = "mid_year_ending",
  y = "ni_pop_total",
  plot_type = "l",
  color = nisra_blue,
  linetype = "solid",
  size = 1,
  caption = "Note the non-zero axis",
  xlim = c(2000, 2026),
  xlab = "Mid year ending",
  ylab = "Population"
) +
  font("ylab", angle = 0, vjust = 1.02) +
  scale_y_continuous(
    limits = c(1650000, 2000000),
    labels = scales::label_number(suffix = "m", scale = 1e-6, big.mark = ",")
  ) +
  theme(text = element_text(family = "Arial")) +
  theme(
    axis.title = element_text(size = 11),
    axis.text = element_text(size = 10),
    # give a bit of room at the left for the slashes if needed
    plot.margin = margin(5.5, 20, 5.5, 20)
  ) +
  geom_text(
    data = df_annual_pop_tot %>% dplyr::filter(mid_year_ending == latest_year),
    aes(
      label = paste0(latest_year, ": \n", format(ni_pop_total,
        big.mark = ","
      )),
      x = mid_year_ending + 1.5, y = ni_pop_total
    )
  ) +
  geom_text(
    data = df_annual_pop_tot %>% dplyr::filter(
      mid_year_ending == earliest_year
    ),
    aes(
      label = paste0(earliest_year, ": ", format(ni_pop_total, big.mark = ",")),
      x = mid_year_ending + 2.5,
      y = ni_pop_total - 7000
    )
  ) +
  geom_vline(xintercept = 2009, linetype = "dashed", color = nisra_blue) +
  annotate("text",
    x = 2009.5, y = 1880000, hjust = 0,
    label = paste(strwrap("2009: Growth rate starts to slow", width = 18),
      collapse = "\n"
    )
  ) +
  # --- Add a “fake” axis-break mark (two slashes) near the left axis ---
  coord_cartesian(clip = "off") +
  annotate("text",
    x = min(df_annual_pop_tot$mid_year_ending),
    y = 1.67e6,
    label = "//",
    hjust = 2.55, # nudges further left
    vjust = 0.5, size = 5
  )

line chart showing growth in population from 2001 to 2019. More detail on trends is included in text immediately above or below the chart

End tabs

Further information on the chart download buttons can be found in section 7. Download data of the demo report.

Multiple lines chart (h3)

This section includes a line chart with more than one series which are not overlapping.

The navy and mid-blues meet the 3:1 contrast and the text for both of these colours meets the 4.5:1 contrast for text against the background. It is difficult to ensure sufficient contrast for more than 2 lines especially when they overlap or cross because the order in which they appear will change.

A static chart is used here so it views well on mobile as well as desktop. Annotations easily get crowded and overlap when resized and the chart resizes the x axis which alters how the trend is perceived.

Include descriptive text before or after the chart.

Figure 2: Females outnumber males in total population

Northern Ireland mid year population estimate (sex) 2001 to 2022
# Create separate dataframe for selected datapoints from main dataframe
df_mye_selected_years_gender <- df_mye_year_gender %>%
  filter(mid_year_ending %in% c(2001, 2012, 2022)) %>%
  # format numbers
  mutate(label = paste0(
    mid_year_ending, ": ",
    round_half_up(mye_pop / 1e6, 2), "m"
  ))


# code to create interpolated points with blank labels along the line of the
# line plot to cause repulsion of labels from line and avoid overlap - the
# final df, df_fig2_labs_inter is used in the geom_text_repel statement


df_mye_interp_male <- (filter(df_mye_year_gender, gender == "Males"))
df_mye_interp_male <- as.data.frame(do.call(
  cbind, approx(df_mye_interp_male$mid_year_ending,
    df_mye_interp_male$mye_pop,
    n = 30
  )
))
df_mye_interp_female <- (filter(df_mye_year_gender, gender == "Females"))
df_mye_interp_female <- as.data.frame(do.call(
  cbind, approx(df_mye_interp_female$mid_year_ending,
    df_mye_interp_female$mye_pop,
    n = 30
  )
))

df_mye_interp_male$label <- ""
df_mye_interp_male$Gender <- "Males"
df_mye_interp_male <- df_mye_interp_male %>% select(x, Gender, y, label)
colnames(df_mye_interp_male) <- colnames(df_mye_selected_years_gender)
df_mye_lab_select_male <- (filter(
  df_mye_selected_years_gender,
  gender == "Males"
))
df_mye_interp_male <- rbind(df_mye_lab_select_male, df_mye_interp_male)

df_mye_interp_female$label <- ""
df_mye_interp_female$Gender <- "Females"
df_mye_interp_female <- df_mye_interp_female %>% select(x, Gender, y, label)
colnames(df_mye_interp_female) <- colnames(df_mye_selected_years_gender)
df_mye_lab_select_female <- (filter(
  df_mye_selected_years_gender,
  gender == "Females"
))
df_mye_interp_female <- rbind(df_mye_lab_select_female, df_mye_interp_female)


df_fig2_labs_inter <- rbind(df_mye_interp_male, df_mye_interp_female)
df_fig2_labs_inter <- arrange(df_fig2_labs_inter, mid_year_ending, gender)



fig2 <- ggplot(
  df_mye_year_gender,
  aes(
    x = mid_year_ending,
    y = mye_pop,
    color = gender
  )
) +
  geom_line(linewidth = 1) +
  # ACC: Change to line colours with sufficient contrast
  scale_color_manual(values = c(nisra_blue, nisra_navy)) +


  # Using NITotals_lab_select_multiple from above to pick the years that will
  # have "points", specifying the size of the dots
  geom_point(
    data = df_mye_selected_years_gender,
    aes(x = mid_year_ending, y = mye_pop), size = 3
  ) +
  labs(
    x = "Year",
    y = "Population",
    caption = "Note the non-zero axis"
  ) +
  theme_bw() +
  theme( # removes grid lines from chart
    panel.grid = element_blank(),
    # removes chart border top and right
    panel.border = element_blank(),
    axis.line.x = element_line(colour = "#000000"),
    axis.line.y = element_line(colour = "#000000"),
    # ACC: make y axis title horizontal and position it further from y axis line
    axis.title.y = element_text(
      angle = 0, vjust = 1, hjust = 0,
      margin = margin(t = 0, r = 5, b = 0, l = 0)
    ),
    # align title and subtitle to left
    plot.title.position = "plot",
    # align caption to left
    plot.caption = element_text(hjust = 0),
    # ACC: change all fonts to 12
    text = element_text(size = 12, family = "Arial"),
    # bolds title and subtitle + adds margin below subtitle and plot
    plot.title = element_text(face = "bold"),
    plot.subtitle = element_text(face = "bold", margin = margin(0, 0, 15, 0)),
    # ACC: Legend to top and tidy up
    legend.position = "top",
    legend.title = element_blank(),
    legend.text = element_text(size = 12)
  ) +
  geom_text_repel( # ACC: show value label for specified years
    data = df_fig2_labs_inter,
    aes(
      x = mid_year_ending,
      y = mye_pop,
      label = label
    ), force = 10
  ) +
  # ACC: make x axis labels easier to read by removing every other year
  scale_x_continuous(breaks = seq(earliest_year, latest_year, by = 2)) +
  # add comma to y labels
  scale_y_continuous(labels = label_number(scale = 1e0, big.mark = ",")) +
  # indicate non zero axis
  labs(tag = "//") +
  theme(plot.tag.position = c(0.203, 0.2))


fig2

Figure 2 alt text example. More detail on the trends are included in the text directly above or below the chart, and data can be downloaded using the button below.

Multiple single line charts (h3)

This section changes from multiple lines on one chart to include a small chart for each grouping specified (i.e. LGD2014 as below).

In situations where you have a larger number of lines, consider using small multiples to either highlight trends against noisy backgrounds or present trends side by side. You could highlight a single line in colour and have the other lines in grey or just show individual lines as demonstrated below.

This is a static chart as it is more mobile friendly.

Include descriptive text before or after the chart.

Figure 3: In 2022, Belfast Local Government District (LGD) has more younger people than other areas

Northern Ireland mid year population estimate by LGD in 2022
ggplot(
  df_mye_latest_5yr_age_lgd,
  aes(
    x = age_group_5,
    y = population_latest
  )
) +
  geom_line(linewidth = 1, group = 1, color = nisra_blue) +
  # ACC: Specify line colour with sufficient contrast
  facet_wrap(vars(str_wrap(lgd2014name, 30)), ncol = 3) +
  # ACC: Use clear and descriptive labels
  labs(
    x = "Age",
    y = "Population"
  ) +
  theme_bw() +
  theme( # removes grid lines from chart
    panel.grid = element_blank(),
    # panel margin
    panel.spacing.x = unit(0.7, "lines"),
    axis.line.x = element_line(colour = "#000000"),
    axis.line.y = element_line(colour = "#000000"),
    # ACC: make y axis title horizontal and position it further from y axis line
    axis.title.y = element_text(
      angle = 1, vjust = 1, hjust = 0,
      margin = margin(t = 0, r = 5, b = 0, l = 0)
    ),
    # ACC: change all fonts to 12
    text = element_text(size = 12, family = "Arial"),
    # facet only legend hide bg
    legend.title = element_blank(),
    legend.position = "none",
    strip.background = element_blank()
  ) +
  scale_x_discrete(breaks = c("0-4", "45-49", "90-94"), labels = c(
    "0-4",
    "45-49",
    "90+"
  )) +
  # ACC: add comma to y labels
  scale_y_continuous(labels = label_number(scale = 1e0, big.mark = ","))

Figure 3 alt text example. More detail on the trends are included in the text directly above or below the chart, and data can be downloaded using the button below.

Bar chart (a-z) (h3)

This section includes a bar chart sorted in alphabetical Local Government District (LGD) order. A bar chart was chosen so that the LGD names could be displayed in full horizontally.

Include descriptive text before or after the chart.

Figure 4: Young people by LGD

Data from 2022 mid-year estimates (MYEs) showing the percentage of each LGD population that is under 25
# create amended df for chart. Reorder factors for plotting (need lgd names in
# reverse alphabetical order as coord flip reverses order).
# Remove unnecessary columns
fig4_data <- df_mye_latest_year_youthrate %>%
  mutate(lgd2014name = fct_reorder(lgd2014name, desc(lgd2014name))) %>%
  select(-under25_pop, -lgd_pop_total)

ggplot(fig4_data, aes(x = lgd2014name, y = youthrate)) +
  geom_bar(fill = nisra_blue, stat = "identity") +
  geom_text(aes(label = youthrate),
    hjust = 1.5,
    colour = "white", fontface = "bold"
  ) +
  # adds value labels to the bars, sets text to colour and weight that meet
  # contrast standards.
  theme_bw() +
  # axis labels
  labs(
    x = paste(strwrap("LGD", width = 0.2 * getOption("width")),
      collapse = "\n"
    ),
    y = "Percentage of population under 25"
  ) +
  # makes y axis title horizontal
  theme(
    axis.title.y = element_text(
      angle = 0,
      vjust = 1,
      hjust = 1
    ),
    # removes grids from chart
    panel.grid = element_blank(),
    # added vertical grid at major ticks
    panel.grid.major.x = element_line(color = "#BEBEBE", linewidth = 0.1),
    # removes chart border top and right
    panel.border = element_blank(),
    # xaxis line black
    axis.line.x = element_line(colour = "#000000"),
    # yaxis line black
    axis.line.y = element_line(colour = "#000000"),
  ) +
  coord_flip()

Figure 4 alt text example. More detail on the trends are included in the text directly above or below the chart, and data can be downloaded using the button below.

The chart shows that LGD with the greatest percentage of population less than 25 is Mid Ulster. Ards and North down is the LGD with the smallest percentage.

Interactive bar chart (h3)

This bar chart is created using the same data but using plotly to create an interactive html chart. Title is used for statistical title and h4 for descriptive title.

When resized for mobile the bars shorten but the labels remain a more readable size. Keep the x axis title short so it won’t get cut off on a smaller display.

Include descriptive text before or after the chart highlighting the key message.

Figure 5: Proportion of under 25s was higher in 2001.

Percentage of population aged under 25 by LGD, 2001 MYEs

Dashed line represents Northern Ireland mean.

# create annotation for y axis title so it can be horizontal.
yaxistitle <- list(
  x = -0.3, y = 1.06, # position of y axis title
  text = "LGD", # title
  showarrow = FALSE, xref = "paper", yref = "paper",
  xanchor = "center", yanchor = "centre", # alignment
  xshift = 0, yshift = 0,
  font = list(family = "Arial Black", size = 14, color = "black")
)

fig5 <- plot_ly(df_mye_earliest_youthrate,
                x = ~earliest_youthrate,
                y = ~lgd2014name,
                type = "bar",
                orientation = "h", # horizontal bars. Default is vertical
                hoverinfo = "text",
                # adds value labels to the bars and sets hover text
                text = ~earliest_youthrate, textposition = "auto",
                # sets colour of bars to navy
                marker = list(color = nisra_navy)
) %>%
  layout( # add annotation
    font = list(family = "Arial", size = 14),
    annotations = list(yaxistitle),
    shapes = list(vline(~ mean(earliest_youthrate), color = nisra_blue)),
    xaxis = list(title = "Under 25s (%)"),
    yaxis = list(
      title = "",
      ticks = "outside",
      tickcolor = "#ffffff",
      ticklen = 10,
      categoryorder = "category descending"
    )
  )

# displays lgds in alphabetic order
# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig5, displayModeBar = FALSE)  # hide the modebar

The chart shows that LGD with the greatest percentage of population under 25 in 2001 was Derry City and Strabane. Ards and North down was the LGD with the smallest percentage.

Column chart (h3)

This section includes two versions of a column chart (one static, one interactive) and two stacked bar charts. To ensure colour is not the only means of conveying information, contrast is used to differentiate between male and female columns and data labels have been added.

In this instance, the plotly grouped bar chart resizes well because the x axis labels are short and don’t overlap when resized. The data labels will change to vertical on mobile which is not ideal (should be horizontal) but the hover labels will be horizontal and legible.

The stacked bars have been created in plotly. They resize well for small screens because the age group labels are short and don’t overlap. The horizontal stacked bar also resizes well because there are only two groups. If there were many more a static chart would likely be more appropriate.

Include descriptive text before or after the chart.

Figure 6: Females outnumber males in all age groups except the under 25s

Northern Ireland population in 2022 by age group
Figure 6 tabs
ggplot static
fig6 <- ggplot(
  df_mye_latest_year_agegrp_gend,
  aes(
    x = age_group,
    y = pop_total,
    fill = gender
  )
) +
  geom_col(position = position_dodge()) +
  # colour bars that meet contrast for adjacent colours
  scale_fill_manual(values = c(nisra_navy, nisra_blue)) +
  # add value labels, and add thousand separator
  geom_text(aes(label = prettyNum(pop_total, big.mark = ",")),
    position = position_dodge(width = 1), vjust = 1.1,
    colour = "white", size = 3.2
  ) +
  theme_bw() +
  # axis labels
  labs(
    x = "Age group",
    y = "Population",
    fill = "Gender"
  ) +
  theme(
    axis.title.y = element_text( # makes y axis title horizontal
      angle = 0,
      vjust = 1.05,
      hjust = 1
    ),
    # removes grids from chart
    panel.grid = element_blank(),
    # added horizontal grid at major ticks
    panel.grid.major.y = element_line(color = "#BEBEBE", linewidth = 0.1),
    # removes chart border top and right
    panel.border = element_blank(),
    # xaxis line black
    axis.line.x = element_line(colour = "#000000"),
    # yaxis line black
    axis.line.y = element_line(colour = "#000000"),
    legend.position = "top"
  ) +
  # add thousand separator to y axis labels
  scale_y_continuous(labels = label_number(scale = 1e0, big.mark = ","))

fig6

Figure 6 alt text example. More detail on the trends are included in the text directly above or below the chart, and data can be downloaded using the button below.

plotly
df_fig6 <- pivot_wider(df_mye_latest_year_agegrp_gend,
  names_from = gender,
  values_from = pop_total
)

df_fig6 <- as.data.frame(df_fig6)
df_fig6 <- gather(df_fig6, key = "variable", value = "value", 2:3)

nisra_blue <- "#3878c5"
nisra_navy <- "#00205b"

colormap <- setNames(
  object = c("#00205b", "#3878c5"),
  nm = unique(df_fig6$variable)
)

df_fig6 <- df_fig6 %>%
  mutate(variable = factor(variable, levels = c("Females", "Males"))) %>%
  plotly::plot_ly(
    x = ~age_group, y = ~value, color = ~variable, colors = colormap,
    type = "bar",
    text = format(df_fig6$value, big.mark = ","),
    textposition = "inside",
    textangle = "horizontal", # for accessibility
    insidetextfont = list(size = 12), # to fit
    hovertemplate = "%{text}", # hover text = text
    textfont = list(color = "white")
  ) %>%
  layout( # makes it a clustered bar
    barmode = "group",
    font = list(family = "Arial", size = 14),
    xaxis = list(title = "Age group"),
    yaxis = list(
      title = "",
      tickformat = ",d",
      showgrid = FALSE
    ),
    legend = list(
      orientation = "h",
      xanchor = "center",
      x = 0.5,
      y = 1.1,
      # so order is same as legend
      traceorder = "normal"
    ),
    annotations = list(list(
      text = "Population",
      x = -0.03,
      xref = "paper",
      xanchor = "center",
      y = 1.02,
      yref = "paper",
      yanchor = "bottom",
      showarrow = FALSE,
      font = list(size = 16)
    ))
  ) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)

df_fig6
Stacked bar
df_fig6 <- pivot_wider(df_mye_latest_year_agegrp_gend,
  names_from = gender,
  values_from = pop_total
)
df_fig6 <- as.data.frame(df_fig6)
df_fig6 <- gather(df_fig6, key = "variable", value = "value", 2:3)

fig6c <- df_fig6 %>%
  mutate(variable = factor(variable, levels = c("Females", "Males"))) %>%
  plotly::plot_ly(
    x = ~age_group, y = ~value, color = ~variable, colors = colormap,
    type = "bar",
    text = format(df_fig6$value, big.mark = ","),
    textposition = "inside",
    textangle = "horizontal", # for accessibility
    insidetextfont = list(size = 12), # to fit
    hovertemplate = "%{text}", # hover text = text
    textfont = list(color = "white")
  ) %>%
  layout(
    # stacked rather than clustered/grouped
    barmode = "stack",
    font = list(family = "Arial"),
    xaxis = list(title = "Age group"),
    yaxis = list(
      title = "",
      tickformat = ",d",
      showgrid = FALSE
    ),
    legend = list(
      orientation = "h",
      xanchor = "center",
      x = 0.5,
      y = 1.1,
      font = list(family = "Arial", size = 14),
      traceorder = "normal"
    ),
    font = list(family = "Arial", size = 14),
    annotations = list(list(
      text = "Population",
      x = -0.03,
      xref = "paper",
      xanchor = "center",
      y = 1.02,
      yref = "paper",
      yanchor = "bottom",
      showarrow = FALSE,
      font = list(size = 16)
    ))
  )
# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig6c, displayModeBar = FALSE)
Stacked horizontal bar (%)
# for stacked horizontal bar set orientation to "h" (horizontal)
# and barmode to stack
fig6d <- plot_ly(df_latest_year_agegrp_gend_pct,
  x = ~Females,
  y = ~age_group,
  marker = list(color = nisra_navy),
  type = "bar",
  orientation = "h",
  name = "Females",
  text = ~ paste0(format(round_half_up(Females, 1), nsmall = 1), "%"),
  # use excel rounding function, display to 1dp
  insidetextanchor = "middle",
  hoverinfo = "y+text"
) %>%
  add_trace(
    x = ~Males,
    marker = list(color = nisra_blue),
    name = "Males",
    text = ~ paste0(format(round_half_up(Males, 1), nsmall = 1), "%")
  ) %>%
  layout(
    barmode = "stack",
    font = list(family = "Arial", size = 14),
    yaxis = list(
      title = "",
      ticks = "outside",
      tickcolor = "#ffffff",
      ticklen = 5,
      # default was 65+ at top
      autorange = "reversed"
    ),
    xaxis = list(title = "Percentage (%)"),
    legend = list(
      orientation = "h",
      xanchor = "center",
      x = 0.42,
      y = 1.1,
      traceorder = "normal"
    ),
    annotations = list(list(
      text = "Age Group",
      x = 0,
      xref = "paper",
      xanchor = "center",
      y = 1.02,
      yref = "paper",
      yanchor = "bottom",
      showarrow = FALSE,
      font = list(size = 16)
    ))
  )
# ensure legend is same order as stacks
# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig6d, displayModeBar = FALSE)
End tabs

Column chart 5 categories

This chart has been included to show how the 5 colour palette can be used for categorical data. The order in which the colours are used is important as are the colours of the data labels on each and should be preserved. These can be used for fewer categories as long as the order is retained.

Figure 7: Over 85s remain the smallest age group

Northern Ireland population in 2022 in 5 age bands
yaxistitle <- list(
  text = "Population",
  x = 0.01, y = 1.05,
  xref = "paper", yref = "paper",
  xanchor = "right", yanchor = "top",
  showarrow = FALSE,
  font = list(size = 14)
)


fig7 <- plot_ly(df_mye_latest_5_agegroups,
  x = ~age_grp_5,
  y = ~agegroup_total,
  type = "bar",
  marker = list(color = c(
    nisra_blue, nisra_navy,
    nisra_col3_green, nisra_col4_purple, nisra_col5_lilac
  )),
  # order of colours is important: need medium and
  # dark colours beside one another
  text = ~ format(agegroup_total, big.mark = ","),
  textfont = list(color = c(
    "white", "white",
    "black", "white", "black"
  )),
  # need to specify black or white labels. plotly default
  # is grey and fails contrast required
  textangle = "horizontal",
  hovertemplate = ~ paste0(
    age_group, ": ", format(agegroup_total, big.mark = ","),
    "<extra></extra>"
  ),
  hoverlabel = list(font = list(color = c(
    "white", "white",
    "black", "white", "black"
  )))
) %>%
  layout(
    yaxis = list(
      title = "",
      tickformat = ",d"
    ), # adds thousand separator
    xaxis = list(title = "Age Group"),
    annotations = list(yaxistitle)
  )

# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig7, displayModeBar = FALSE)

Pie chart (h3)

Ensure that colour is not the only means of conveying meaning. Here value labels are added for alternative source of information.

These pie charts have been placed in two columns so that when resized for mobile they will wrap one under the other and remain legible.

Please note, pie charts should only have 5 or fewer categories.

Include descriptive text before or after the chart.

Figure 8: Higher percentage of females than males in the over 65 age group in NI

Percentage of Northern Ireland population aged 65 plus by sex, 2022
fig8a <- plot_ly(df_mye_latest_over_under_65,
  labels = ~age_65split,
  values = ~Females,
  type = "pie",
  # create better punctuated labels for screen readers,
  # default value failed accessibility audit
  text = ~ paste0(
    age_65split, " \n (",
    round_half_up(Females / sum(Females) * 100, 1), "%)"
  ),
  # increase label size
  insidetextfont = list(size = 16),
  insidetextorientation = "horizontal",
  textinfo = "text",
  hoverinfo = "text",
  # hover font colour for contrast
  hoverlabel = list(font = list(color = c(
    "black",
    "white"
  ))),
  marker = list(
    colors = c(nisra_col3_green, nisra_navy)
  ),
  textfont = list(color = c("black", "white")),
  showlegend = FALSE
) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE) %>%
  layout(
    annotations = list(
      x = 0.5, y = 1.08, xanchor = "center",
      text = ~ paste("<b> Females </b>"),
      showarrow = FALSE,
      font = list(family = "Arial", size = 16)
    ),
    font = list(family = "Arial", size = 14)
  )

fig8b <- plot_ly(df_mye_latest_over_under_65,
  labels = ~age_65split,
  values = ~Males,
  type = "pie",
  text = ~ paste0(
    age_65split, " \n (",
    round_half_up(Males / sum(Males) * 100, 1), "%)"
  ),
  textinfo = "text",
  hoverinfo = "text",
  # increase label size
  insidetextfont = list(size = 16),
  insidetextorientation = "horizontal",
  marker = list(
    colors = c(nisra_col3_green, nisra_navy)
  ),
  # plotly default is grey which is insufficient contrast
  textfont = list(color = c(
    "black",
    "white"
  )),
  showlegend = FALSE
) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE) %>%
  layout(
    annotations = list(
      x = 0.5, y = 1.08,
      xanchor = "center",
      text = ~ paste("<b> Males </b>"),
      showarrow = FALSE,
      font = list(family = "Arial", size = 16)
    ),
    font = list(family = "Arial", size = 14)
  )

# creates a two column div placing a chart in each. Usual plotly method of
# creating a row with 2 columns did not resize well. This way, the pies display
# one below the other when viewed on mobile.
div(
  class = "row",
  div(
    class = "col-sm-6", style = "margin-top: 10px;",
    fig8a
  ),
  div(class = "col-sm-6", fig8b)
)

Treemap - basic (h3)

Tile size and value labels are sources of meaning in addition to colour in the treemap. Again, text must meet 4.5:1 contrast and adjacent colours 3:1.

Borders are used around the tiles to ensure the contrast requirements are met. All of these colours can be used against a white background for chart elements but because the tiles are adjacent to multiple colours, not all the contrasts are OK (green against NISRA mid-blue for example). In this instance, a white border is used around the tiles.

Include descriptive text before or after the chart.

Figure 9: Females aged 0-24 make up the largest age group in NI.

Female population of Northern Ireland by age group, 2022
fig9 <- plot_ly(df_fig9,
  type = "treemap",
  labels = ~age_grp_5,
  parents = ~gender,
  values = ~pop_total,
  # punctuated labels so that readers do not rely solely on colour
  # or shape to understand the chart and screen readers vocalise ok
  text = ~ paste0(
    "Age ", age_grp_5, ":",
    "\n", format(pop_total, big.mark = ",")
  ),
  textinfo = "text",
  hoverinfo = "text",
  hoverlabel = list(font = list(color = c(
    "white", "white",
    "black", "white", "black"
  ))),
  branchvalues = "total",
  # set tile colours, border and label colours to meet accessibility standards
  marker = list(
    colors = c(
      nisra_blue, nisra_navy,
      nisra_col3_green, nisra_col4_purple, nisra_col5_lilac
    ),
    line = list(color = "black", width = 1)
  ),
  textfont = list(family = "Arial", size = 16, color = c(
    "white", "white",
    "black", "white",
    "black"
  ))
)

# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig9, displayModeBar = FALSE)

Treemap - nested (3 levels) (h3)

To demonstrate a more complex tree map with three levels of nesting and multiple tiles, a tree map (Figure 3) from the Economic and Labour Market Statistics report Structure and Performance of the Northern Ireland Economy 2018 and 2019 is shown below. These colours have not been checked for accessibility.

Figure 10: Split of Northern Ireland GVA by industry, 2019

Agriculture accounted for the largest share of GVA

To view the sub-industries in more detail by industry, click on the industry header at the top of each box. To reset the chart, click on the industry header again to get back to the original view.

2019
tmap_textcolours[1] <- "black"

fig10 <- plot_ly(
  type = "treemap",
  labels  = ~tmap_labels_9,
  parents = ~tmap_parents_9,
  values  = ~tmap_values_9,
  textinfo = "label",
  text = ~ paste0(tmap_labels_9, "\n", round_half_up(tmap_values_9_lab, 1), "%"),
  hoverinfo = "text",

  # ⬇️ put per-tile hover colours HERE (trace-level)
  hoverlabel = list(font = list(color = tmap_textcolours)),

  marker = list(colors = c(
    "white", nisra_green_decoration, nisra_navy, nisra_col9_pink,
    nisra_col3_green, nisra_col4_purple, nisra_col5_lilac,
    nisra_col6_olive, nisra_col8_slate, nisra_blue,
    nista_col10_burgandy
  )),
  textfont = list(color = tmap_textcolours)
) %>%
  layout(
    # leave layout.hoverlabel without color (size/bg ok if you want)
    hoverlabel = list(font = list(size = 20)),
    font = list(family = "Arial", size = 14),
    margin = list(l = 0, r = 0, b = 12, t = 0)
  ) %>%
  config(displayModeBar = FALSE)

fig10
2018
fig10prev <- plot_ly(
  type = "treemap",
  labels = ~tmap_labels_9prev,
  parents = ~tmap_parents_9prev,
  values = ~tmap_values_9prev,
  textinfo = "label",
  text = ~ paste0(
    tmap_labels_9prev,
    "\n", round_half_up(tmap_values_9prev_lab, 1), "%"
  ),
  hoverinfo = "text",
    # ⬇️ put per-tile hover colours HERE (trace-level)
  hoverlabel = list(font = list(color = tmap_textcolours)),
  marker = list(colors = c(
    "white", nisra_green_decoration, nisra_navy, nisra_col9_pink,
    nisra_col3_green, nisra_col4_purple, nisra_col5_lilac,
    nisra_col6_olive, nisra_col8_slate, nisra_blue,
    nista_col10_burgandy
  )),
    textfont = list(
    color = tmap_textcolours
  )
) %>%
  layout(
    hoverlabel = list(
      font = list(size = 20)),
    font = list(family = "Arial", size = 14),
    margin = list(
      l = 0,
      r = 0,
      b = 12,
      t = 0
    )
  ) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)

fig10prev

Donut chart (h3)

This donut chart has 4 sections. Please note, donut charts should only have 5 or fewer categories. It’s important that these colours appear in order to retain required contrast and the correct colour is used for the labels. The plotly default label colour is grey or white so these are set manually to ensure sufficient contrast. The default grey is too pale against the green.

Keep labels short and concise when using plotly for donut so that labels remain visible on mobile.

Figure 11: Under 25s largest age group in Northern Ireland

Northern Ireland population by age group 2022
fig11 <- plot_ly(df_mye_latest_year_agegroup,
  type = "pie",
  labels = ~age_group,
  values = ~agegroup_total,
  # default orders segments by size
  sort = FALSE,
  # accompanies sort argument
  direction = "clockwise",
  # turns pie into donut
  hole = 0.5,
  # create labels with age group and percentage values in brackets
  text = ~ paste0(
    age_group, ": \n (",
    round_half_up(agegroup_total / sum(agegroup_total) * 100, 1), "%)"
  ),
  hoverinfo = "text",
  textinfo = "text",
  # keeps labels horizontal
  insidetextorientation = "horizontal",
  # specify colours in correct order.
  marker = list(
    colors = c(
      nisra_blue, nisra_navy, nisra_col3_green, nisra_col4_purple
    ),
    # border required to ensure contrast between nisra blue and lilac
    line = list(color = "black", width = 1)
  ),
  # specify label colours to meet contrast standards
  textfont = list(
    family = "Arial",
    color = c("white", "white", "black", "white"),
    size = 14
  )
) %>%
  layout(
    # legend removed for accessibility (relies on colour to be able to
    # understand it. Labels added to segments instead)
    showlegend = FALSE,
    font = list(family = "Arial", size = 12)
  )

# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
# modebar is series of icons and plotly logo linked to plotly site. Turned off
# throughout to avoid multiple links going to same destination (poor
# accessibility) and icons can create visual clutter.
config(fig11, displayModeBar = FALSE)

Population pyramid (h3)

This population pyramid shows populations by age for two sexes, in two years a decade apart, with the most recent represented by the shaded area and the comparison year represented by the lines. The colours of both the shaded areas and the lines have been chosen to ensure sufficient contrast, and the text manually set to white to ensure visibility. A unified hovermode has been applied to show all data for the relevant age in the hover label at once.

Figure 12: The population of Northern Ireland is aging

Norhern Ireland population by age and sex (2012 and 2022)
plot_ly(fig_12_data,
  y = ~Age,
  orientation = "h",
  type = "bar",
  showlegend = FALSE,
  hoverinfo = "none"
) %>%
  add_trace(
    x = as.formula(paste0("~`Males ", latest_year, "`")),
    name = paste("Males", latest_year),
    marker = list(color = nisra_navy),
    hoverinfo = "text+y",
    hovertext = paste0(
      "Males ", latest_year, ": ",
      prettyNum(fig_12_data[[paste("Males", latest_year)]],
        big.mark = ","
      )
    )
  ) %>%
  add_trace(
    x = as.formula(paste0("~`Females ", latest_year, "`")),
    name = paste("Females", latest_year),
    marker = list(color = nisra_col3_green),
    hoverinfo = "text+y",
    hovertext = paste0(
      "Females ", latest_year, ": ",
      prettyNum(
        abs(fig_12_data[[paste(
          "Females",
          latest_year
        )]]),
        big.mark = ","
      )
    )
  ) %>%
  add_trace(
    x = as.formula(paste0("~`Males ", latest_year - 10, "`")),
    type = "scatter",
    mode = "lines",
    name = paste("Males", latest_year - 10),
    line = list(
      color = "#00b0f0",
      width = 4
    ),
    showlegend = TRUE,
    hoverinfo = "text+y",
    hovertext = paste0(
      "Males", latest_year - 10, ": ",
      prettyNum(
        fig_12_data[[paste(
          "Males",
          latest_year - 10
        )]],
        big.mark = ","
      )
    )
  ) %>%
  add_trace(
    x = as.formula(paste0("~`Females ", latest_year - 10, "`")),
    type = "scatter",
    mode = "lines",
    name = paste("Females", latest_year - 10),
    line = list(
      color = "#2c523b",
      width = 4
    ),
    showlegend = TRUE,
    hoverinfo = "text+y",
    hovertext = paste0(
      "Females",
      latest_year - 10, ": ",
      prettyNum(
        abs(fig_12_data[[paste(
          "Females",
          latest_year - 10
        )]]),
        big.mark = ","
      )
    )
  ) %>%
layout(
  barmode = "overlay",
  bargap = 0,
  font = list(family = "Arial", size = 14),
  xaxis = list(
    title = "Persons (Thousands)",
    tickformat = ",",
    tickmode = "array",
    tickvals = seq(-15000, 15000, by = 5000),
    ticktext = as.character(abs(seq(-15, 15, by = 5))),
    range = c(-15000, 15000),
    showgrid = FALSE,
    zeroline = FALSE,        # Remove the internal vertical zero line
    showline = TRUE,         # Draw the solid x-axis line
    linecolor = "black",
    linewidth = 1,
    ticks = "outside",       # Tick marks on the axis
    ticklen = 8,
    tickwidth = 1,
    tickcolor = "black",
    mirror = FALSE,          # Axis line only on the bottom
    anchor = "y",            # Tie axis to y-axis (removes gap)
    fixedrange = TRUE,
    domain = c(0, 1),          # Ensures the plot spans full width
    constrain = "domain"       # Keeps data flush to axes (no extra padding)
  ),
  yaxis = list(
    title = "",
    categoryorder = "category ascending",
    tickvals = seq(0, 90, by = 10),
    ticktext = c(as.character(seq(0, 80, by = 10)), "90+"),
    showgrid = FALSE,
    zeroline = FALSE,        # Avoid extra horizontal line
    showline = TRUE,         # Draw solid y-axis line
    linecolor = "black",
    linewidth = 1,
    ticks = "outside",
    ticklen = 6,
    tickwidth = 1,
    tickcolor = "black",
    range = c(-0.5, 90.5)
  ),
  legend = list(
    x = 1,
    y = 1,
    xanchor = "right"
  ),
  annotations = list(
    list(
      text = paste0("<b>Females\n", latest_year, "</b>"),
      x = -5000,
      xref = "x",
      xanchor = "center",
      y = 30,
      yref = "y",
      yanchor = "middle",
      showarrow = FALSE,
      font = list(color = "#fff", size = 16)
    ),
    list(
      text = paste0("<b>Males\n", latest_year, "</b>"),
      x = 5000,
      xref = "x",
      xanchor = "center",
      y = 30,
      yref = "y",
      yanchor = "middle",
      showarrow = FALSE,
      font = list(color = "#fff", size = 16)
    )
  ),
  hovermode = "y unified"
) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)
f_make_tables(fig_12_xl,
  title = paste0(
    "Figure 12: Population by age and sex (",
    latest_year - 10, " and ", latest_year, ")"
  ),
  data_dir = here("code/demo/demo_outputs/figdata")
)

Flow chart (h3)

This flow chart shows components of change to the population in Northern Ireland from 2021 to 2022. The chart has been constructed directly in the Markdown document, using shapes that are defined in the CSS document.

Black text has been used against light colours with a darker border line, as using light backgrounds with dark text has been shown to reduce eyestrain. If the colours are changed, it is important to ensure that contrast continues to meet accessibility requirements.

Figure 13: Natural change is the main driver of population growth

Components of population change in Northern Ireland (2021 and 2022)
cat(sprintf('
::::::::: div-row
::: {.navy-box style="font-weight: bold"}
%s\\
Population\\
(%s)
:::

::: arrow

:::

::: green-circle
Natural\\
Change\\
%s
:::

::: blue-circle
Net\\
Migration\\
%s
:::

::: green-circle
Other\\
Changes\\
%s
:::

::: arrow

:::

::: {.navy-box style="font-weight: bold"}
%s\\
Population\\
(%s)
:::
:::::::::
',
prev_year, pop_ni_last_rounded,
natural_change_sign,
net_migration_sign,
other_changes_sign,
latest_year, pop_ni_current_rounded))

Natural
Change
+3,800

Net
Migration
+2,300

Other
Changes
-100

Filled multiple line chart (h3)

This chart shows population change in Northern Ireland over the course of five years by five broad age bands. Labels are placed at the start and end of each time series to avoid visual clutter.

Figure 14: Over 65s see largest population growth

Northern Ireland population change by age group (2018 to 2022)
fig_14_plot <- plot_ly(
  data = fig_14_data,
  x = ~index,
  y = ~pct,
  fill = "tozeroy",
  type = "scatter",
  mode = "none",
  color = ~age_grp_5,
  fillcolor = nisra_blue,
  hoverinfo = "text",
  text = ~ paste(mid_year_ending,
    age_grp_5, paste0(round_half_up(pct, 1), "%"),
    sep = "\n"
  )
) %>%
  layout(
    font = list(family = "Arial", size = 12),
    title = "",
    xaxis = list(
      title = "",
      showticklabels = FALSE,
      showgrid = FALSE
    ),
    yaxis = list(
      title = "",
      range = c(0, 35),
      showgrid = FALSE,    # No vertical gridlines
      zeroline = TRUE,     # Keep the main x-axis line
      showline = TRUE,     # Ensure axis line is drawn
      linecolor = "black", # Axis line color
      linewidth = 1
    ),
    showlegend = FALSE,
    margin = list(b = 100),
    hovermode = "x unified"
  )
fig_14_data$age_grp_5 <- as.factor(fig_14_data$age_grp_5)
age_groups <- c("Under 24", "25 to 44", "45 to 64", "65 to 84", "85 and over")

x_positions <- seq(1 / (length(age_groups) * 2),
  (length(age_groups) * 2 - 1) / (length(age_groups) * 2),
  length.out = length(age_groups)
)

annotations <- lapply(seq_along(age_groups), function(i) {
  list(
    text = age_groups[i],
    x = x_positions[i],
    xref = "paper",
    xanchor = "center",
    y = -0.1,
    yref = "paper",
    yanchor = "top",
    showarrow = FALSE
  )
})

annotations_left <- list()
annotations_right <- list()

for (i in seq_len(nrow(fig_14_data))) {
  if (fig_14_data$index[i] %% 5 == 1) {
    annotations_left[[i]] <- list(
      x = fig_14_data$index[i],
      y = 0,
      text = fig_14_data$label[i],
      xanchor = "left",
      yanchor = "top",
      xref = "x",
      yref = "paper",
      showarrow = FALSE
    )
  } else if (fig_14_data$index[i] %% 5 == 0) {
    annotations_right[[i]] <- list(
      x = fig_14_data$index[i],
      y = 0,
      text = fig_14_data$label[i],
      xanchor = "right",
      yanchor = "top",
      xref = "x",
      yref = "paper",
      showarrow = FALSE
    )
  }
}

fig_14_plot <- fig_14_plot %>%
  layout(annotations = c(annotations, annotations_left, annotations_right)) %>%
  # modebar is series of icons and plotly logo linked to plotly site. Turned off
  # throughout to avoid multiple links going to same destination (poor
  # accessibility) and icons can create visual clutter.
  config(displayModeBar = FALSE)

for (i in seq_len(nrow(fig_14_data))) {
  if ((fig_14_data$mid_year_ending[i]) == year_minus_four) {
    fig_14_plot <- fig_14_plot %>%
      add_annotations(
        text = paste0(sprintf("%0.1f", fig_14_data$pct[i]), "%"),
        x = fig_14_data$index[i],
        y = fig_14_data$pct[i] + 1.4,
        xanchor = "left",
        yanchor = "bottom",
        showarrow = FALSE
      )
  }

  if ((fig_14_data$mid_year_ending[i]) == latest_year) {
    fig_14_plot <- fig_14_plot %>%
      add_annotations(
        text = paste0(sprintf("%0.1f", fig_14_data$pct[i]), "%"),
        x = fig_14_data$index[i],
        y = fig_14_data$pct[i] + 1.4,
        xanchor = "right",
        yanchor = "bottom",
        showarrow = FALSE
      )
  }
}

fig_14_plot
f_make_tables(fig_14_data_xl,
  title = paste0(
    "Figure 14: Northern Ireland population change by age group (",
    latest_year - 4, " to ", latest_year, ")"
  ),
  data_dir = here("code/demo/demo_outputs/figdata")
)

Diverging stacked bar

This chart has been included to show how to display a dataset with positive and negative change.

Figure 15: Hawaii had the largest positive population change

Population change in selected US States in 2022
plot_ly(fig_15_data_filtered,
        x = ~`Net Migration & Other Changes`,
        y = ~`Local Government District`,
        type = "bar",
        orientation = "h",
        marker = list(color = nisra_col3_green),
        name = "Net Migration and Other Changes",
        hoverinfo = "text+x",
        hovertext = ~paste("Net Migration:", `Net Migration & Other Changes`)) %>%
  add_trace(x = ~`Natural Change (births minus deaths)`,
            y = ~`Local Government District`,
            type = "bar",
            orientation = "h",
            marker = list(color = nisra_navy),
            name = "Natural Change (births minus deaths)",
            hovertext = ~paste("Natural Change:", `Natural Change (births minus deaths)`)) %>%
  add_trace(x = ~`Total Percentage Change`,
            y = ~`Local Government District`,
            type = "scatter",
            mode = "markers",
            marker = list(color = nisra_blue,
                          size = 9,
                          symbol = "diamond",
                          line = list(color = "#fff", width = 2)),
            name = "Total Percentage Change",
            hovertext = ~paste("Total Change:", `Total Percentage Change`)) %>%
  layout(
  font = list(family = "Arial", size = 14),
  barmode = "relative",
  xaxis = list(
    title = "Percentage change",
    range = c(-50, 50),
    tickmode = "array",                   # manually define ticks
    tickvals = c(-50, -25, 0, 25, 50),    # exact tick positions
    ticktext = c("-50%", "-25%", "0%", "25%", "50%"),  # custom labels (optional)
    automargin = TRUE,
    tickfont = list(size = 10)
  ),
  yaxis = list(title = "", automargin = TRUE),
  legend = list(orientation = "h", y = 1.35, xanchor = "center"),
  hovermode = "y unified",
  margin = list(l = 10, r = 10, t = 10, b = 60)
  ) %>%
  config(displayModeBar = FALSE, responsive = TRUE)
f_make_tables(fig_15_data_filtered,
  title = paste0(
    "Figure 15: Population change in selected US States in 2022"),
  data_dir = here("code/demo/demo_outputs/figdata")
)

Other chart types (h3)

There are many other potential charts, maps and data visualisations that can be coded in R and included in html reports. More information and examples can be explored in the following links:

Plotly R Open Source Graphing Library

Plotly R Library Basic Charts

Plotly R Library Statistical Charts

The R Graph Gallery

The R Graph Gallery - Interactive Charts

Plotly cheat sheet for R

ggplot2 charts

Data visualization with ggplot2

Making Maps with R

Using ggplot2 to create maps

Sankey Diagram

Building a flowchart

6. Maps (h2)

This section includes a static map and an interactive map. If you create a map in another package and import as an image, ensure it meets accessibility requirements for an infographic.

Static map (h3)

This section includes a map generated in R. General principles to consider are:

  • ensure contrast between text labels and background colour is at least 4.5:1
  • ensure contrast between adjacent colours is at least 3:1, if they don’t use borders that do
  • verify using online tools like WebAIM contrast checker
  • ensure colour is not the only way of conveying meaning
  • use clear and descriptive labels

Here the data labels are displayed on the map, alt text is included and the shapefile has been simplified to reduce the file size.

We have used three colours. More than three colours increases the difficulty of creating sufficient contrast.

Ensure the data underlying the maps is accessible to users who cannot perceive images. In this case, the data is in a table in a previous section. You could also make it available as a download.

Include descriptive text before or after the map.

Map 1: Proportion of people aged under 25 in Northern Ireland by LGD

df_map_data$xmod <- c(0.8, 0, 0, 0.5, 0, 1.1, 0.5, 0.5, 0, 1, -1)
df_map_data$ymod <- c(-0.5, 0, 0.2, 0.3, 0, 0, 1.4, -0.55, 0.3, 0, -1)

map1_static_map <- tm_shape(df_map_data,
  # Default is km
  unit = "imperial"
) +
  tm_polygons( # Colours the map sections based on these values
    col = "lgd_young_perc",
    # Legend title
    title = "Percentage of under 25s",
    # Number of different colours to use
    n = 3,
    # Quantiles gives even sized groupings
    style = "quantile",
    # Colour palette. Execute the command tmaptools::palette_explorer() in R
    # console to see range of options
    palette = "Blues",
    # Add transparency to polygons improving contrast with text
    alpha = 0.5,
    legend.show = FALSE,
    border.col = "black"
  ) +

  tm_text( # add labels to polygons, change text colour to black and move
    # labels to fit within shapes
    "map_label",
    size = 0.8,
    col = "#000000",
    auto.placement = FALSE,
    xmod = "xmod",
    ymod = "ymod"
  ) +
  tm_add_legend("fill",
    fill = RColorBrewer::brewer.pal(3, "Blues"),
    border.col = "#454A58",
    labels = c("26.6  to 29.5", "29.6 to 31.5", "31.6 to 33.5"),
    title = "Percentage of under 25s"
  ) +
  tm_add_legend( # add manual legend as a key
    "symbol",
    fill = "#00205b",
    title = "Key",
    labels = c(
      "AN = Antrim and Newtownabbey",
      "AND = Ards and North Dow",
      "B = Belfast"
    )
  ) +
  tm_format( # setting format to worldwide creates a wider rectangle shape that
    # fits width of page
    "World_wide"
  ) +
  tm_layout(legend.position = c("left", "top"))

# Plotting map:
tmap_mode("plot") # Set to "view" for interactive map or "plot" for static

map1_static_map

Map of Northern Ireland shaded by percentage of under 25s living in each Local Government District. More detail on the trends are included in the text directly above or below the map

Interactive maps (h3)

This section shows the same map produced as an interactive plot. The figures are displayed on the map, council names show on hover and the council name and figures display on click. The shapefile has been simplified to reduce file size and not slow page load speed.

Ensure the data underlying the maps is accessible to users who cannot perceive images. In this case, the data is in a table in a previous section. You could also make it available as a download.

The default rendering tabs through the LGDs but the focus area is not visible for keyboard users. This is an accessibility fail. Additional code has been added to remove the map from the tab order and to add alt text to the LGDs for screen reader users. See sections of code in the final code chunk (download buttons) which creates alt text and in the script section at the very end of the code to create an array of alt texts for the map and remove tabs from the LGDs.

Include descriptive text before or after the map.

Map 2: Interactive shapefile map with hover text

# Reduce the size of the shapefile using the simplify command in rmaptools.


#### interactive map ####

# df_map_data$xmod <- c(3, 1, 0, 1.5, 0, 1.5, 0, 0, 0, 1, 1)
# df_map_data$ymod <- c(0, 3, 0, 0, 0, 0, 1, 0, 0, 0, -3)

df_map_data$xmod <- c(0, -1.5, 0, -1.1, 0, 0, 0, -1, 0, 0, -1)
df_map_data$ymod <- c(0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -1)

map2_interactive_map <- tm_shape(
  df_map_data %>%
    select(lgd2014name, lgd_young_perc, map_label_value_only, ymod, xmod),
  name = "Show data",
  # Default is km
  unit = "imperial"
) +
  tm_basemap( # Adds background map.
    "Esri.WorldGrayCanvas"
  ) +
  tm_polygons( # Colours the map sections based on these values
    col = "lgd_young_perc",
    # Number of different colours to use
    n = 3,
    # Quantiles gives even sized groupings
    style = "quantile",
    # Colour palette
    palette = "Blues",
    # Add transparency to polygons improving contrast with text
    alpha = 0.5,
    border.alpha = 1,
    legend.show = FALSE,
    popup.vars = c("Percentage of under 25s: " = "lgd_young_perc")
  ) +
  tm_add_legend("fill",
    fill = RColorBrewer::brewer.pal(3, "Blues"),
    border.col = "#454A58",
    labels = c("26.6  to 29.5", "29.6 to 31.5", "31.6 to 33.5"),
    title = "Percentage of under 25s"
  ) +
  tm_view( # Re-positioning legend
    view.legend.position = c("left", "top"),
    # Re-positioning layer controls
    control.position = c("left", "bottom"),
    leaflet.options = c(
      zoomControl = FALSE,
      # Removes zoom and drag options
      dragging = FALSE,
      keyboard = FALSE,
      boxZoom = FALSE,
      doubleClickZoom = FALSE,
      scrollWheelZoom = FALSE,
      tap = FALSE,
      touchZoom = FALSE,
      zoom = 8,
      minZoom = 8,
      maxZoom = 8,
      attributionControl = FALSE
    )
  ) +
  tm_text( # Label polygons, increase font size, set colour to black (better
    # contrast) and adjust positioning so they are contained as much as
    # possible within the polygons
    "map_label_value_only",
    size = 1.2,
    col = "#000000",
    auto.placement = FALSE,
    xmod = "xmod",
    ymod = "ymod"
  ) +
  tm_scale_bar(width = 250) +
  tmap_mode( # Set to "view" for interactive map or "plot" for static
    "view"
  )

map2_interactive_map
# please also refer to the "alt text for maps" chunk in the source code of this
# section for additional configurations

Map 3: Interactive point map with zoom and hover text

Map 3 shows another example of an interactive map that plots specific points instead of using a shapefile. This map uses a data-set containing information on the location of benefits offices (JBO) and the number of available vacancies.

The location points are displayed on the map and JBO names as well as available vacancies show on hover or click. This interactive map also includes zoom functionality and a view reset button. Points on the map can be resized according to the data or also grouped together.

The data underlying the map is available to view and download in a tab for users who cannot perceive images.

Map (interactive) output
#### interactive map ####

map3_interactive_map <- leaflet(options = leafletOptions(
  minZoom = 8,
  maxZoom = 12
)) %>%
  setView(lng = -6.5, lat = 54.6, zoom = 8.15, ) %>%
  addProviderTiles("Esri.WorldGrayCanvas")


map3_interactive_map <- addCircleMarkers(
  map = map3_interactive_map, data = df_jbo, lng = ~Longitude, lat = ~Latitude,
  color = "blue",
  radius = 10,
  label = lapply(paste0(
    df_jbo$JBO, ": ", df_jbo$Vacancies,
    " vacancies"
  ), HTML),
  labelOptions = labelOptions(style = list("font-size" = "14px"))
) %>%
  addEasyButton(easyButton(
    icon = "fa-globe",
    title = "Reset Zoom",
    onClick = JS("function(btn, map){ map.setView([54.6,lng = -6.5],8.15); }")
  ))


map3_interactive_map
df_jbo <- df_jbo[1:2]
Map (interactive) data
kable(df_jbo)
JBO Vacancies
Shaftesbury Square 6,431
Holywood Road 4,411
Antrim 4,325
Foyle/Lisnagelvin 4,174
Belfast North 3,664
Lisburn 3,410
Bangor 2,921
Portadown 2,912
Ballymena 2,736
Knockbreda 2,258
Newry 2,041
Newtownards 1,992
Coleraine 1,872
Magherafelt 1,619
Armagh 1,612
Enniskillen 1,567
Omagh 1,255
Dungannon 1,198
Lurgan 1,098
Banbridge 863
Falls Road 850
Newtownabbey 834
Cookstown 804
Downpatrick 759
Ballymoney 754
Limavady 739
Carrickfergus 701
Strabane 670
Andersonstown 573
Ballynahinch 569
Larne 538
Newcastle 462
Kilkeel 426
Shankill 410

7. Download data (h2)

Download data buttons linked to charts and maps (h3)

In the functions folder the ‘make_tables’ R Script contains a function called ‘f_make_tables’ that creates the Excel and CSV download buttons that are under each of the figures in section six. The function generates a hyper-link that sits behind the download button and allows a user to click the button and download data from the corresponding chart into csv or excel format.

This function can be called after each figure and has 4 parameters fed into it - data (the data frame), title (the title to be given to the downloaded file), data_style (style applied to the excel file - styles can be viewed in the Style.R script) and data_dir (the file path the downloaded files should be stored in - in the example below the file path is set in the demo config file).

When the download buttons are clicked the excel or csv files are saved out to the ‘figdata’ folder. For figure 1 the code to call the f_make_tables function can be seen below when you select the show code button:

# call make tables function to create chart download buttons
f_make_tables(
  data = df_fig1_xls,
  title = fig1_title,
  data_style = ns_comma,
  data_dir = here("code/demo/demo_outputs/figdata"),
)

Embed spreadsheet into report (h3)

Another option to include downloadable data in a html report is to embed a spreadsheet into the report by adding a download data button as below. For this option edit and run the demo_excel_tables.R script first to create the required spreadsheet in the demo output folder or add the already prepared spreadsheet to the folder. Using the code below (click on show code) parameters can be set in the embed_file function to set the file path, file name and text to appear on the download button. Run this code to create the download data button with the embedded spreadsheet.

It is not recommended to use this option unless necessary. This is due to the large file sizes of the embedded files. This has the potential to slow the processing and knitting time of the report and generate a html page that is very large and slow to load. Due to the large file sizes of the HTML, it is not possible to attain metrics for the embedded files within the report. It is recommended to host spreadsheets elsewhere and link to them in your HTML. This keeps the HTML file size down for quicker loading and allows metrics to be gathered on the associated spreadsheets.

# embeds the spreadsheets
embexcel <- embed_file(
  here("code/demo/demo_outputs/RAP_demo_tables.xlsx"),
  text = "Publication tables: 15KB"
)

div(
  div(
    class = "row", style = "display: flex;",
    div(class = "row-indent"),
    div(class = "download", "Download data: ", style = "font-size: 12pt"),
    div(class = "row-indent"),
    div(class = "xl-button2", HTML(embexcel)),  # <-- wrap with HTML()
    div(class = "download", style = "padding-top: 7px; padding-left: 5px;
        padding-right: 5px;")
  )
)

8. Notes for readers (h2)

This section has been separated from the main section of the document to aid navigation. Normally a publication should contain a separate section of this nature and can be named ‘Notes for Readers’, ‘Background Notes’ or ‘Supporting Information’. It should include:

  • definitions (or a link to definitions) of all relevant concepts and terms. Whether the definition is included in the text, the Notes section or a link provided depends on the importance of the reader knowing the correct definition
  • key quality information (including any limitations or key caveats) and a link to the Background Quality Report (or the equivalent). Where appropriate, limitations or caveats should also be flagged in the text alongside the data, as not all users will read the Notes section
  • links to the equivalent statistics in England, Scotland and Wales (and if appropriate RoI) or indicate that equivalent statistics are not available (with an explanation or guidance on comparison if appropriate) - assuming this information is not included elsewhere in the publication

It may include (if relevant):

  • a series of methodology notes (or a link to the methodology used to produce the statistics). This may include details of the data sources used to produce the statistics.
  • information on how the statistics are used, for example if they are used to measure key performance targets; and advice on the appropriate use of the statistics
  • links to related statistics, including where appropriate guidance on the coherence and comparability of the statistics
  • links to social media accounts X (Twitter) and Facebook
  • information on supplementary statistical services
  • date of the next release
  • links to previous publications
  • email addresses should all be lowercase and it is good practice to include a named individual (some exemptions apply)
  • links to downloads should have file type and size in the link text 2022 Mid-year population estimates statistical bulletin pdf 545KB

With regards to adding ‘Contact Details’, they may be included in this section. However the RAP skeleton has a contact details function called f_contact in the html_formatting R script within the functions folder. This function is called at the bottom of a report (Section 13) above the footer and is the recommended way to add contact details to a report as it ensures consistency for users. Information for specific contact details can be added and edited in the config file

Requests for PDF

It is not encouraged to produced NISRA publications in PDF format however if users request a HTML publication in PDF format it can be created by printing to HTML to PDF using the browser print. Please note - any PDF files created in this way will not be accessible.

9. Text presentation (h2)

This section includes a quote, more subheading levels, unordered lists, ordered lists and links.

Quote box (h3)

Mid Year Estimates:
In June each year NISRA prepares data on the size of the Northern Ireland population and includes statistics on a range of administrative and statistical geographies. A style like this could be used for definitions or for emphasis.

Administrative geographies (h3)

Local government districts (h4)

  1. Antrim and Newtownabbey (no ampersand).
  2. Ards and North Down.
  3. Armagh City, Banbridge and Craigavon.
  4. Belfast.
  5. Causeway Coast and Glens.
  6. Derry and Strabane.
  7. Fermanagh and Omagh.
  8. Lisburn and Castlereagh.
  9. Mid and East Antrim.
  10. Mid Ulster.
  11. Newry, Mourne and Down.

Health and social care trusts (h4)

  1. Belfast HSC Trust.
  2. South Eastern HSC Trust.
  3. Western HSC Trust.
  4. Southern HSC Trust.
  5. Northern HSC Trust.
  6. Northern Ireland Ambulance Service.

Statistical geographies (h3)

  • NUTS III areas.
  • Super Output Areas.
  • Census Small Areas.

Former administrative and statistical geographies (h3)

  • Parliamentary Constituencies (Assembly Areas).
  • Health and Social Care Trusts.
  • The former Education and Library Boards.
  • The former Health and Social Services Boards.
  • The former NUTS-III areas.
  • The former 582 Electoral Wards.

Bullets following a heading start with a capital letter, finish with a full stop and are short (no more than one sentence)

Bullets as a list within the text should always have a lead-in line, start in lowercase (unless it starts with a proper noun), don’t have full stops within bullets or at the end. For more guidance see the ONS punctuation guide.

Accordions

Notes can be stored in accordions, as shown below. This can be useful to reduce clutter in your HTML and allow users to access notes when needed.

htmltools::HTML("
<div class='accordion'> 
  <button class='accordion-toggle' aria-expanded='false' 
          aria-controls='panel1' id='accordion1'> 
    Local Government Districts 
    <span class='accordion-arrow'>▶</span>
  </button> 
  <div class='accordion-panel' id='panel1' role='region' 
       aria-labelledby='accordion1' hidden> 

    <ul>
      <li>Antrim and Newtownabbey (no ampersand).</li>
      <li>Ards and North Down.</li>
      <li>Armagh City, Banbridge and Craigavon.</li>
      <li>Belfast.</li>
      <li>Causeway Coast and Glens.</li>
      <li>Derry and Strabane.</li>
      <li>Fermanagh and Omagh.</li>
      <li>Lisburn and Castlereagh.</li>
      <li>Mid and East Antrim.</li>
      <li>Mid Ulster.</li>
      <li>Newry, Mourne and Down.</li>
    </ul>

  </div> 
</div> 

<script> 
document.getElementById('accordion1').addEventListener('click', function() { 
  var expanded = this.getAttribute('aria-expanded') === 'true'; 
  this.setAttribute('aria-expanded', !expanded); 
  var panel = document.getElementById(this.getAttribute('aria-controls')); 
  panel.hidden = expanded; 
}); 
</script>
")

11. Accredited official statistics and official statistics (h2)

Outputs should include a chapter detailing regulatory information regarding the Statistic type of the report. Example text for each of Accredited Official Statistics, Official Statistics and Official Statistics in Development can be found on the NISRA Teams Code Of Practice page.

It is recommended to use these templates and update them with your respective information accordingly.

12.Contact details (h2)

Published by: Dissemination Branch, Northern Ireland Statistics and Research Agency

Lead Statistician: John Smith

Telephone: 028 XX XXXXXX

Email: contact@nisra.gov.uk

Accessibility contact (h3)

Please contact Dissemination Branch for assistance with accessibility requirements or alternative formats. Contact details are:

Email:

Telephone: +44 (0)300 200 7836

Dissemination Branch
NISRA
Colby House
Stranmillis Court
BELFAST
BT9 5RR