rshinybslib

How to rearrange value boxes at a certain screen size?


Goal:

I want value boxes to appear in 1 row if they are within the bootstrap breakpoints of extra large and extra extra large i.e. any screen size >= 1200px. For other screen sizes i.e., < 1200px, I want the value boxes to appear in multiple rows as the third image shows below.

Details

I am using value boxes with long titles. The default behaviour is that the titles would wrap in smaller screen sizes. That does not look professional:

enter image description here

So, I use the white-space: nowrap; css property to keep the titles intact. But that cuts them off:

enter image description here

I notice that at screen sizes of medium or less (bootstrap breakpoints), the value boxes are rearranged on multiple rows:

enter image description here

I want to do the same but at a breakpoint of large screen size. Where can I make that change?

Code Example

library(shiny)
library(bslib)

ui <- page_navbar(
  header = tags$style(HTML("
        .bslib-value-box .value-box-title {
    font-size: 1.1rem;
    color: #000000;
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
}
    ")),
  sidebar = sidebar(),
  nav_panel(
    title = "Value Boxes",
    layout_columns(
      value_box(title = "Openness", value = 50),
      value_box(title = "Conscientiousness", value = 50),
      value_box(title = "Extroversion", value = 50),
      value_box(title = "Agreeableness", value = 50),
      value_box(title = "Neuroticism", value = 50)
    )
  )
)

server <- function(input, output, session) {
  
}

shinyApp(ui, server)

Solution

  • The current bslib default is to auto-fit the column-widths (respectively the number of columns) at the sm and lg breakpoints. This is what you observe, that at >= 992px, the five columns are shown in one row, which comes from Bootstrap itself:

    .grid .g-col-lg-2 {
        grid-column:auto/span 2
    }
    

    Now in order to fulfill the requirement

    They shall be in one row only if the screen size is ≥ 1200px

    we have to redefine the column-widths at the breakpoints. bslib::layout_columns() has a col_widths parameter and this is allowed to be a breakpoints() object. We can define breakpoints() by use of the logic which is described in the documentation (see ?bslib::layout_columns):

    A numeric vector of integers between 1 and 12, where each element represents the number of columns for the relevant UI element. Elements that happen to go beyond 12 columns wrap onto the next row. For example, col_widths = c(4, 8, 12) allocates 4 columns to the first element, 8 columns to the second element, and 12 columns to the third element (which wraps to the next row).

    So in order to reach the requirement above, you can use

    layout_columns(
      value_box(title = "Openness", value = 50),
      value_box(title = "Conscientiousness", value = 50),
      value_box(title = "Extroversion", value = 50),
      value_box(title = "Agreeableness", value = 50),
      value_box(title = "Neuroticism", value = 50),
      col_widths = breakpoints(xl = c(2, 2, 2, 2, 2)) # <----
    )
    

    The col_widths parameter allocates two columns for each of your five value_box elements and since this does not exceed 12, we will have them in one row at >= 1200px.

    What remains is the question why this also works below 1200px, i.e., why there will be at least two rows. The reason is that bslib has implemented a fallback item span for breakpoints not provided by the user. There is the variable --_item-column-span defined (set to 6) and this invokes the style

    .bslib-grid > * {
        grid-column: auto/span var(--_item-column-span, 1); /* = auto/span 6 */
    }
    

    below the lg breakpoint. Since this exceeds 12 columns after the second element, we see the second row occurring below 1200px (and in particular also a third one).

    Putting all together, the following minimal example should achieve your goal, however, one still may have to tweak the titles slightly for a better display.

    library(shiny)
    library(bslib)
    
    ui <- page_navbar(
      header = tags$style(HTML("
            .bslib-value-box .value-box-title {
              font-size: 1.1rem;
              color: #000000;
              font-weight: 500;
              line-height: 1.2;
              white-space: nowrap;
            }
        ")),
      sidebar = sidebar(),
      nav_panel(
        title = "Value Boxes",
        layout_columns(
          value_box(title = "Openness", value = 50),
          value_box(title = "Conscientiousness", value = 50),
          value_box(title = "Extroversion", value = 50),
          value_box(title = "Agreeableness", value = 50),
          value_box(title = "Neuroticism", value = 50),
          col_widths = breakpoints(xl = c(2, 2, 2, 2, 2))
        )
      )
    )
    
    shinyApp(ui, \(...) {})