<- 5
ident_linewidth <- 25
ident_disk_size <- 40
ident_textsize <- sqrt(5)
ident_minor_disk_factor <- "Futura" ident_font
At GPCDS we’re purposefully provoking comparisons to things. To evoke the feeling that everything needed to be categorised and factions exist we wanted to design a cohesive colour palette that gels well but also has clear distinctions within it. Let’s meet the GPCDS palette, brand idents and the logomarks.
Our initial sketches for brand elements back in 2023.
The colours
The primary colours of each category were chosen through the excellent interactive tool https://coolors.co/generate and then the other colours were generated through experimenting with https://color.adobe.com/create/color-wheel. While iterating on the palette we continuously simulated the colours under the most common form of colour blindness - deuteranopia. The chart below shows these 25 colours:

There are 7 additional colours, primarily designed for web elements but could also be used alongside the visualisation category palettes.

RAG indicators are problematic but very popular. Our palette contains 5 colour-blind safe colours from a red-green palette recommended via Andy Kirk’s Visualising Data from this beautiful NPR chart by Alyson Hurt and Katie Park.

Category Idents
Each visualisation category has two “idents” - a major and minor ident.
Design Element | Major Ident | Minor Ident |
---|---|---|
Filled square | Each ident is a 1:1 rectangle filled in the primary colour for the category. | |
Disks | Each ident contains four disks, two in the secondary colour and the other two in the tertiary colour from the category | Each ident contains two disks, using the secondary and tertiary colour from the category |
Lines | Each ident contains one or more lines using the tertiary darker colour from the category | Each ident may contain a line using the teriary darker colour from the category |
Text | Each ident contains the number of the category using the Futura font | Minor idents don’t contain text |
These are the properties of the graphical elments:
As the idents are intended to be displayed in their “four-wise arrangement” they were individually designed to make that layout satisfying. There are notes below about how these decisions were made.




The graph idents are simple graphs. In the major graph the edges are either at 180 or 60 degrees to one another, and the closed triad is placed to draw the reader to the cente of the four-wise arrangement.
Code
<- 12.5
graph_x_first <- 50
graph_y_first <- 30
graph_y_height
<- tan(60 * pi / 180)
graph_line_1_slope <- tan(-60 * pi / 180)
graph_line_2_slope
<- - {50 + graph_line_1_slope * 50}
graph_line_1_intercept <- - {50 + graph_line_2_slope * {100-graph_x_first}}
graph_line_2_intercept
= {graph_line_2_intercept - graph_line_1_intercept} / {graph_line_1_slope - graph_line_2_slope}
graph_bottom_disk_x
= - {graph_line_1_slope * graph_bottom_disk_x + graph_line_1_intercept}
graph_bottom_disk_y
<- tibble(
gg_graph_ident_major x = c(graph_x_first, 100 - graph_x_first, 50, graph_bottom_disk_x),
y = c(graph_y_first, graph_y_first, graph_y_first, graph_bottom_disk_y),
# colour = "secondary"
colour = c("secondary", "tertiary", "tertiary", "secondary")
%>%
) ggplot() +
annotate(
"segment",
x = graph_x_first,
xend = 100 - graph_x_first,
y = graph_y_first,
yend = graph_y_first,
linewidth = ident_linewidth,
colour = cols_gpcds$graph_tertiary_darker) +
annotate(
"segment",
x = c(50, 100 - graph_x_first),
xend = c(graph_bottom_disk_x, graph_bottom_disk_x),
y = c(graph_y_first, graph_y_first),
yend = c(graph_bottom_disk_y, graph_bottom_disk_y),
linewidth = ident_linewidth,
colour = cols_gpcds$graph_tertiary_darker) +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size,
show.legend = FALSE) +
geom_text(
x = 50,
y = 70,
label = "GRAPH",
size = ident_textsize,
colour = "white",
family = "Futura",
lineheight = 0.9
+
) scale_colour_manual(
values = c(
"secondary" = cols_gpcds$graph_secondary,
"tertiary" = cols_gpcds$graph_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$graph_primary,
colour = cols_gpcds$graph_primary
)
)
ggsave(quarto_here("gg_graph_ident_major.png"),
gg_graph_ident_major,width = 8,
height = 8)
Code
<- 20
graph_minor_x_first <- 50
graph_y_first <- 30
graph_y_height
<- tibble(
gg_graph_ident_minor x = c(graph_minor_x_first, 100 - graph_minor_x_first),
y = c(graph_y_first, graph_y_first),
# colour = "secondary"
colour = c("secondary", "tertiary")
%>%
) ggplot() +
annotate(
"segment",
x = graph_minor_x_first,
xend = 100 - graph_minor_x_first,
y = graph_y_first,
yend = graph_y_first,
linewidth = ident_linewidth + 2,
colour = cols_gpcds$graph_tertiary_darker) +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size * ident_minor_disk_factor,
show.legend = FALSE) +
scale_colour_manual(
values = c(
"secondary" = cols_gpcds$graph_secondary,
"tertiary" = cols_gpcds$graph_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$graph_primary,
colour = cols_gpcds$graph_primary
)
)
ggsave(quarto_here("gg_graph_ident_minor.png"),
gg_graph_ident_minor,width = 8,
height = 8)
The plot idents are inspired by scatter plots. In the major ident the disks are placed on the line y = -x
to enclose the idents in the four-wise arrangement. The word “plot” is displayed in the top-right corner as that’s the placement of the ident in the four-wise arrangement.
Code
<- tibble(
gg_plot_ident_major x = c(12.5, 37.5, 62.5, 87.5),
y = rev(c(12.5, 37.5, 62.5, 87.5)),
colour = c("tertiary", "tertiary", "secondary", "secondary")
%>%
) ggplot() +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size,
show.legend = FALSE) +
geom_text(
x = 75,
y = 62.5,
label = "PLOT",
size = ident_textsize,
colour = "white",
family = "Futura") +
geom_vline(xintercept = 50,
colour = cols_gpcds$plot_tertiary_darker,
linewidth = ident_linewidth) +
geom_hline(yintercept = 50,
colour = cols_gpcds$plot_tertiary_darker,
linewidth = ident_linewidth) +
scale_colour_manual(values = c("secondary" = cols_gpcds$plot_secondary,
"tertiary" = cols_gpcds$plot_tertiary)) +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100),
ylim = c(0, 100)) +
theme_void() +
theme(panel.background = element_rect(fill = cols_gpcds$plot_primary,
colour = cols_gpcds$plot_primary))
ggsave(quarto_here("gg_plot_ident_major.png"),
gg_plot_ident_major,width = 8,
height = 8)
Code
<- tibble(
gg_plot_ident_minor x = c(30, 70),
y = rev(c(30, 70)),
colour = c("tertiary", "secondary")
%>%
) ggplot() +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size * ident_minor_disk_factor,
show.legend = FALSE) +
scale_colour_manual(values = c("secondary" = cols_gpcds$plot_secondary,
"tertiary" = cols_gpcds$plot_tertiary)) +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100),
ylim = c(0, 100)) +
theme_void() +
theme(panel.background = element_rect(fill = cols_gpcds$plot_primary,
colour = cols_gpcds$plot_primary))
ggsave(quarto_here("gg_plot_ident_minor.png"),
gg_plot_ident_minor,width = 8,
height = 8)
The chart idents are inspired by bubble charts, but could also be interpeted as something more complex. In the major ident all four of the smaller disks are placed on the lines y = ± x
with the top-right disk placed off the “bubble” to bring the viewer’s gaze to the centre of the four-wise arrangement.
Code
library("ggforce")
<- list(x = 50, y = 50)
chart_center_circle_coords <- 37.5
chart_center_circle_radius
# Solve (x - 50)² + (y - 50)² = 37.5² and y = x
<- 50 - 75 / {2 * sqrt(2)}
chart_equ_solution
<- tibble(
gg_chart_ident_major x = c(chart_equ_solution, 100-chart_equ_solution, 100 - 12.5, chart_equ_solution),
y = c(100-chart_equ_solution, chart_equ_solution, 100 - 12.5, chart_equ_solution),
colour = c("tertiary", "tertiary", "secondary", "secondary")
%>%
) ggplot() +
geom_circle(data = tibble(),
aes(x0 = 50, y0 = 50, r = chart_center_circle_radius),
linewidth = ident_linewidth,
colour = cols_gpcds$chart_tertiary_darker) +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size,
show.legend = FALSE) +
geom_text(
x = 50,
y = 50,
label = "CHART",
size = ident_textsize,
colour = "white",
family = "Futura"
+
) scale_colour_manual(
values = c(
"secondary" = cols_gpcds$chart_secondary,
"tertiary" = cols_gpcds$chart_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$chart_primary,
colour = cols_gpcds$chart_primary
)
)
ggsave(quarto_here("gg_chart_ident_major.png"),
gg_chart_ident_major,width = 8,
height = 8)
Code
<- 37.5
chart_minor_center_circle_radius
# Solve (x - 50)² + (y - 50)² = 37.5² and y = x
<- 50 - 75 / {2 * sqrt(2)}
chart_equ_solution
<- tibble(
gg_chart_ident_minor x = 100 - chart_equ_solution,
y = 100 - chart_equ_solution,
colour = "secondary"
%>%
) ggplot() +
geom_circle(aes(x0 = 50, y0 = 50, r = chart_minor_center_circle_radius),
linewidth = ident_linewidth,
colour = cols_gpcds$chart_tertiary_darker,
fill = cols_gpcds$chart_tertiary_darker) +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size * ident_minor_disk_factor,
show.legend = FALSE) +
scale_colour_manual(
values = c(
"secondary" = cols_gpcds$chart_secondary,
"tertiary" = cols_gpcds$chart_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$chart_primary,
colour = cols_gpcds$chart_primary
)
)
ggsave(quarto_here("gg_chart_ident_minor.png"),
gg_chart_ident_minor,width = 8,
height = 8)
The data story idents are designed to imply movement and include arrows which are not found in any other ident. In the major ident the arrows point to the centre of the ident itself instead of a position within the four-wise arrangement - to indicate that the story sometimes need to be centered over design.
Code
<- list(min = 15, max = 15 *3)
story_x_coords <- 20
story_y_coords <- 7.5
story_arrow_y_adjustment <- 2.5
story_arrow_x_adjustment
<- tibble(
gg_story_ident_major x = c(story_x_coords$min, story_x_coords$max, 100 - story_x_coords$min, 100 - story_x_coords$max),
y = c(100 - story_y_coords, 100 - story_y_coords, story_y_coords, story_y_coords),
colour = c("secondary", "tertiary", "secondary", "tertiary")
%>%
) ggplot() +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size,
show.legend = FALSE) +
annotate("curve",
x = story_x_coords$min + story_arrow_x_adjustment,
y = 100 - story_y_coords + story_arrow_y_adjustment,
xend = story_x_coords$max - story_arrow_x_adjustment,
yend = 100 - story_y_coords + story_arrow_y_adjustment,
linewidth = ident_linewidth - 2,
colour = cols_gpcds$story_tertiary_darker,
arrow = arrow(length = unit(0.6, "cm"), type = "closed"),
curvature = -0.5,
ncp = 1000) +
annotate("curve",
x = 100 - story_x_coords$min - story_arrow_x_adjustment,
y = story_y_coords - story_arrow_y_adjustment,
xend = 100 - story_x_coords$max + story_arrow_x_adjustment,
yend = story_y_coords - story_arrow_y_adjustment,
linewidth = ident_linewidth - 2,
colour = cols_gpcds$story_tertiary_darker,
arrow = arrow(length = unit(0.6, "cm"), type = "closed"),
curvature = -0.5,
ncp = 1000) +
geom_text(
x = 50,
y = 50,
label = "DATA\nSTORY",
size = ident_textsize,
colour = "white",
family = "Futura",
lineheight = 0.9
+
) scale_colour_manual(
values = c(
"secondary" = cols_gpcds$story_secondary,
"tertiary" = cols_gpcds$story_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$story_primary,
colour = cols_gpcds$story_primary
)
)
ggsave(quarto_here("gg_story_ident_major.png"),
gg_story_ident_major,width = 8,
height = 8)
Code
<- list(min = 25, max = 75)
story_minor_x_coords <- 20
story_minor_y_coords <- 15
story_arrow_y_adjustment <- 2.5
story_arrow_x_adjustment
<- tibble(
gg_story_ident_minor x = as.numeric(story_minor_x_coords),
y = c(50, 50),
colour = c("secondary", "tertiary")
%>%
) ggplot() +
geom_point(aes(x, y, colour = colour),
size = ident_disk_size * ident_minor_disk_factor,
show.legend = FALSE) +
annotate("curve",
x = story_minor_x_coords$min + story_arrow_x_adjustment,
y = 50 + story_arrow_y_adjustment,
xend = story_minor_x_coords$max - story_arrow_x_adjustment,
yend = 50 + story_arrow_y_adjustment,
linewidth = ident_linewidth,
colour = cols_gpcds$story_tertiary_darker,
arrow = arrow(length = unit(0.6, "cm"), type = "closed"),
curvature = -0.5,
ncp = 1000) +
annotate("curve",
x = 100 - story_minor_x_coords$min - story_arrow_x_adjustment,
y = 50 - story_arrow_y_adjustment,
xend = 100 - story_minor_x_coords$max + story_arrow_x_adjustment,
yend = 50 - story_arrow_y_adjustment,
linewidth = ident_linewidth,
colour = cols_gpcds$story_tertiary_darker,
arrow = arrow(length = unit(0.6, "cm"), type = "closed"),
curvature = -0.5,
ncp = 1000) +
scale_colour_manual(
values = c(
"secondary" = cols_gpcds$story_secondary,
"tertiary" = cols_gpcds$story_tertiary
)+
) scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
coord_fixed(xlim = c(0, 100), ylim = c(0, 100)) +
theme_void() +
theme(
panel.background = element_rect(
fill = cols_gpcds$story_primary,
colour = cols_gpcds$story_primary
)
)
ggsave(quarto_here("gg_story_ident_minor.png"),
gg_story_ident_minor,width = 8,
height = 8)
Logomarks
The logomark is constructed from slightly modified versions of the minor idents - the script for generating the logomarks can be found on GitHub. There are three different versions of the design:
There are two cricisms of the first generation logomarks:
In the colour logomark the arrows in the story section do not have a lineend on the non-arrow size. This is a limitation of the curve geom.
There are slight inconsistencies in the linewidth of some elements due to how they’re calculated in {ggplot2}. In an ideal world all lines would have the same width.
Lettermark
Our combination mark was designed before the lettermark in Canva:
We’ve tried to replicate the lettermark directly here with HTML and CSS but unfortunately cannot get the font-weight
to affect the letters:
G|P|C|DS
Combination Marks
The combination marks were constructed in Canva for simplicity. To provide some meaning to how the lettermark and logomark relate to one another the height of the pipe character has been set to 150px and the logomark is 2 * ϕ * 150px
The 1:1 and 4:3 ratio combination marks are crops of the same image with a vertical alignment, whereas the 16:9 combination mark is horizontally arranged:
Favicon
Favicons are miniture versions of the logo used for website icon etc, the logomark has been simplified to rectangles coloured with the primary colour of each visualisation category.