Build-a-Dashboard Workshop
Duke University
Posit, PBC
2024-08-12
Quarto dashboards can be styled with one of the 25 themes from the Bootswatch project:
dashboard-r.qmd
---
title: "Bootswatch themes"
format:
dashboard:
theme: quartz
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Bootswatch themes"
format:
dashboard:
theme: quartz
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
You can provide a light and dark theme and Quarto will automatically place a light/dark toggle on the navbar:
dashboard-r.qmd
---
title: "Light and dark"
format:
dashboard:
theme:
light: flatly
dark: darkly
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Light and dark"
format:
dashboard:
theme:
light: flatly
dark: darkly
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Light and dark"
format:
dashboard:
theme:
light: flatly
dark: darkly
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Custom light theme"
format:
dashboard:
theme: style/custom-light.scss
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
/*-- scss:defaults --*/
// fonts
@import 'https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible&display=swap';
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@700&display=swap');
$font-family-sans-serif: "Atkinson Hyperlegible";
// colors
$navbar-bg: #0e2635;
$navbar-fg: #F0F0F0;
/*-- scss:rules --*/
.card-header {
background-color: #ae8b2d;
color: #F0F0F0;
}
dashboard-r.qmd
---
title: "Sandstone + Custom light theme"
format:
dashboard:
theme:
- sandstone
- style/custom-light.scss
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
Achieving a unified look for your dashboard requires customizing your dashboard along with your plots and tables
Choose colors thare complementary and visually appealing (at least to you) bu also following accessbility best practices
Stick to pre-defined, accessible color scales where possible
Option 1 - Cloud: Go to Posit Cloud for the workshop, open project titled 3 - Theming and styling
.
Option 2 - Local: Clone the GitHub repo posit-conf-2024/olympicdash
(https://github.com/posit-conf-2024/olympicdash) and work on olympicdash-r-3.qmd.
Python - Local: Clone the GitHub repo posit-conf-2024/olympicdash
(https://github.com/posit-conf-2024/olympicdash) and work on olympicdash-py-3.qmd.
Your goal is to create a dashboard that looks like the following:
Tip
Work with your neighbor if you’re having difficulty finding which theme to use.
05:00
Add an SCSS file for custom styling and use it to augment the Bootswatch theme to get dashboard theme elements as close to the final goal as you can.
Tip
Use a tool like the Digital Color Picker to identify colors to be set.
10:00
Polish your plots to get them as close to the final goal as you can.
10:00
A lot more to learn on theming all things Quarto!
These may or may not happen in the near / not-so-near future, but you might want to follow them:
_brand.yml
: https://github.com/quarto-dev/quarto-cli/issues/10249
Support for auto theming in quarto with the thematic R package: https://github.com/rstudio/thematic/issues/125
Improve the HTML Theming documentation page: https://github.com/quarto-dev/quarto-cli/issues/8654