rdashboardquarto

Remove white area between header and body in dashboard Quarto


When I'm creating a Quarto dashboard I sometimes need to adjust some layout using css code. For the following dashboard we use some css to adjust the size of the values in a valuebox. The problem is when we use css in a code chunk and place this at the top of the document, there appears white space between the header and the body of the dashboard. Here is some reproducible code:

---
title: "Gapminder"
author: "Quinten"
format: dashboard
theme: yeti
---

```{r}
#| warning: false
#| message: false
#| echo: false
library(tidyverse)
library(DT)
library(gapminder)
library(ggpmisc)
library(ggbump)
library(plotly)
library(leaflet)
library(countrycode)
library(sf)
library(rnaturalearth)
library(crosstalk)
```

```{css, echo=FALSE}
.quarto-dashboard .bslib-value-box .value-box-value {
    font-size: clamp(.1em, 10cqw, 2em) !important;
}
```

```{r}
# Calculate some values
# Average gdpPercap per year
mean_gdpPercap <- gapminder %>%
  group_by(year) %>%
  reframe(mean_gdpPercap = mean(gdpPercap)) %>%
  pull(mean_gdpPercap)

perc_growth_gdpPercap <- 100*((mean_gdpPercap[length(mean_gdpPercap)] - mean_gdpPercap[1])/mean_gdpPercap[1])

# Average lifeExp per year
mean_lifeExp <- gapminder %>%
  group_by(year) %>%
  reframe(mean_lifeExp = mean(lifeExp)) %>%
  pull(mean_lifeExp)

perc_growth_lifeExp <- 100*((mean_lifeExp[length(mean_lifeExp)] - mean_lifeExp[1])/mean_lifeExp[1])

```

```{r}
# Create dataset for leaflet map
df <- gapminder %>% #filter(year == 2007) %>% 
  mutate(iso_a3 = countrycode(country, "country.name", "iso3c"), 
         gdpPercap = round(gdpPercap, 0), 
         lifeExp = round(lifeExp, 0))

world <- ne_countries(type = "countries",  returnclass = 'sf') %>% 
  left_join(., df, by = "iso_a3") %>% 
  filter(!is.na(country)) %>% 
  select("country", "continent" = "continent.y", "year", "lifeExp", "pop", "gdpPercap", "geometry") %>% 
  as('Spatial')

world_NA <- ne_countries(type = "countries",  returnclass = 'sf')

sd <-  SharedData$new(world)
sd_df <- SharedData$new(world@data, group = sd$groupName())
```


# {.sidebar}

This is a static dashboard with some hover options to give you some insights of the Gapminder dataset. This dashboard is developed using [Quarto](https://quarto.org) [v1.4](https://quarto.org/docs/blog/posts/2024-01-24-1.4-release/).

***

Below you can select some settings to filter the map and table:

```{r}
filter_select("year", "Choose the year:", sd_df, ~year)
filter_slider("lifeExp", "Life expectancy (years):", sd_df, ~lifeExp)
filter_slider("gdpPercap", "Income per person ($):", sd_df, ~gdpPercap)
```

***

::: {.callout-note collapse="true"}
## Disclaimer
This is just a simple static dashboard as demonstration to show what is possible with the latest version of Quarto.
:::

# Analysis

## Row {height=20%}

```{r}
#| content: valuebox
#| title: "Percentage growth 1952-2007 of average lifeExp"
list(
  icon = "arrow-up",
  color = "green",
  value = paste0(round(perc_growth_lifeExp,1), "%")
)
```

```{r}
#| content: valuebox
#| title: "Percentage growth 1952-2007 of average gdpPercap"
list(
  icon = "graph-up",
  color = "light",
  value = paste0(round(perc_growth_gdpPercap,1), "%")
)
```

## Row {height=80%}

### Column {.tabset}

```{r}
#| title: "World map"
pal <- colorFactor(c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"), domain = c("Africa", "Americas", "Asia", "Europe", "Oceania"), ordered = FALSE)

leaflet(sd) %>% 
  addProviderTiles("CartoDB.Positron") %>% 
  addPolygons(data = world_NA, color = "#969696", weight = 1, fillColor = "#808080") %>% 
  addPolygons(color = "#969696", weight = 2, fillColor = ~pal(continent), fillOpacity = 0.8)
```


```{r}
#| title: "gdpPercap vs lifeExp"
gapminder %>%
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
  geom_point() +
  geom_smooth(method = loess) +
  theme_minimal()
```


### Column {.tabset}

```{r}
#| title: "Top 10 countries life expectancy per year"
df <- gapminder %>% 
  group_by(year) %>%
  arrange(desc(lifeExp)) %>% 
  slice(1:10) %>%
  mutate(rank = row_number()) %>%
  ungroup()

p <- ggplot(df, aes(year, rank, color = continent, group = country)) +
  geom_bump() +
  geom_point() +
  geom_text(data = df %>% filter(year == min(year)),
            aes(x = year - .1, label = country), size = 2, vjust = -1.5) +
  geom_text(data = df %>% filter(year == max(year)),
            aes(x = year + .1, label = country), size = 2, vjust = -1.5) +
  scale_x_continuous(breaks = c(unique(df$year))) +
  scale_y_continuous(breaks = c(1:10), trans = "reverse") +
  guides(x =  guide_axis(angle = 45)) +
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank()) +
  labs(x = "Year", y = "Rank", color = "Continent") +
  coord_cartesian(clip = "off") 
p
```

```{r}
#| title: "Average life expectancy per continent over time"
p <- gapminder %>%
  group_by(continent, year) %>%
  summarise(lifeExp=mean(lifeExp)) %>%
  ggplot(aes(x=year, y=lifeExp, color=continent)) +
  geom_line() + 
  geom_point() +
  theme_minimal()
plotly::ggplotly(p)
```

Output:

enter image description here

As you can see there is a big white area between the header and the body of the dashboard. This even happens when we use echo=FALSE for the css code. I tried to use this answer: Remove white space between header and body, but this doesn't work unfortunately. So I was wondering if anyone knows why this happens and how we could prevent the white area?


Solution

  • I think the way to do this is to add your CSS to a separate file. As you're using a theme, we can follow the docs:

    To customize an existing Bootstrap theme with your own set of variables and/or rules, just provide the base theme and then your custom theme file(s)

    In our case that means create an SCSS (rather than CSS) file, styles.scss:

    /*-- scss:rules --*/
    .quarto-dashboard .bslib-value-box .value-box-value {
        font-size: clamp(.1em, 10cqw, 2em) !important;
    }
    

    Note the apparent comment /*-- scss:rules --*/ is a layer boundary that is necessary to tell the parser that what is below is just standard CSS.

    Then include this in your header yaml:

    ---
    title: "Gapminder"
    author: "Quinten"
    format: dashboard
    theme: 
        - yeti
        - styles.scss
    ---
    

    Remove the {css} block from your document and voilĂ  - no more gap:

    enter image description here

    For more on SCSS syntax see here.