Theming and styling

Build-a-Dashboard Workshop

Dr. Mine Çetinkaya-Rundel

Duke University

Posit, PBC

2024-08-12

Theming

Bootswatch themes

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()
```

Bootswatch themes

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()
```

Bootswatch themes

Light and dark mode

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()
```

Light mode

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()
```

Dark mode

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()
```

Custom themes

  • You can create an entirely custom theme and apply it to your dashboard:

    format:
      theme: style.scss
  • Or you can customize an existing Bootswatch theme:

    format:
      theme:
        - cosmo
        - custom.scss

Custom light SCSS

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()
```

Custom light SCSS

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;
}

Customizing Bootswatch themes

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()
```

Styling

A unified look

  • 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

Your turn

Start

  • 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.

Goal

Your goal is to create a dashboard that looks like the following:

Step 1

  • Update the theme to the appropriate Bootswatch theme.
  • Render the dashboard and identify what other aspects of the dashboard theme and cell outputs should be modified.

Tip

Work with your neighbor if you’re having difficulty finding which theme to use.

05:00

Step 2

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

Step 3

Polish your plots to get them as close to the final goal as you can.

10:00

Learn more

Theming Quarto

A lot more to learn on theming all things Quarto!

Look forward to…

These may or may not happen in the near / not-so-near future, but you might want to follow them: