Skip to content

feat(sidebar): Redesign sidebar toggle and mobile layout#798

Merged
gadenbuie merged 29 commits intomainfrom
sidebar/toggle-refresh
Sep 14, 2023
Merged

feat(sidebar): Redesign sidebar toggle and mobile layout#798
gadenbuie merged 29 commits intomainfrom
sidebar/toggle-refresh

Conversation

@gadenbuie
Copy link
Copy Markdown
Member

@gadenbuie gadenbuie commented Sep 13, 2023

TODO

  • Remove top padding on always open sidebars
  • Always open sidebars in mobile go to "flow" layout
  • Use arrow-bar-left for the toggle icon
  • Should we use circle shape to match full screen card toggle?
    • Keeping current radius, but there's a --bslib-collapse-toggle-border-radius that can be used to set this.
  • Default to sidebar(position = "right")?
    • (Not now)
  • Deprecate or revisit sidebar mobile height shenanigans?
    • max_height_mobile now only applies to open = "always" sidebars and is ignored otherwise. A warning is provided if max_height_mobile is provided with another open value.

Intro

This PR revisits the sidebar toggle with a new design and some updated behavior. At a high level:

  • Toggle moves to the top of the sidebar. When collapsed, the toggle is outside the sidebar area and when expanded the toggle moves into the sidebar area.

  • To accomodate this, we hold space in two places:

    • Add padding at the top of every sidebar to accomodate the toggle button
    • When collapsed, we also increase the padding in the main area adjacent to the sidebar
  • The colors of the main content area are now controlled via CSS variables, --bslib-sidebar-main-fg and --bslib-sidebar-main-bg. I think this is a better pattern anyway, but it allows the toggle icon to be colored according to the foreground color of the region where its placed. (Previously it was static and only used the sidebar fg/bg colors.)

  • Mobile view is completely revisited, ultimately it's much simpler. The sidebar operates in the same way (expands from the side), but takes up the complete layout area as an overlay over the main content area. Only one sidebar can be expanded at a time, so nested sidebars in the mobile view are simplified.

Demo App

Here's the code for a demo app to test features, appearance, etc. None of the controls are wired up, they're just there to help motivate appearance discussions.

Demo app with bunches of sidebars
library(shiny)
pkgload::load_all()

library(htmltools)
library(plotly)
library(leaflet)

plotly_widget <-
  plot_ly(x = diamonds$cut) |>
  config(displayModeBar = FALSE) |>
  layout(margin = list(t = 0, b = 0, l = 0, r = 0))

leaflet_widget <-
  leaflet(
    options = leafletOptions(attributionControl = FALSE)
  ) |>
  setView(lng = 174.768, lat = -36.852, zoom = 5) |>
  addMarkers(lng = 174.768, lat = -36.852, popup = "The birthplace of R") |>
  addTiles()

accordions <- function(id) {
  ns <- shiny::NS(id)
  accordion(
    open = FALSE,
    accordion_panel(
      "Dropdowns",
      value = ns("Numerical"),
      icon = bsicons::bs_icon("menu-app"),
      selectInput(ns("cut"), "Cut", choices = LETTERS),
      selectInput(ns("clarity"), "Clarity", choices = LETTERS)
    ),
    accordion_panel(
      "Numerical",
      value = ns("Dropdowns"),
      icon = bsicons::bs_icon("sliders"),
      sliderInput(ns("depth"), "Depth", 0, 100, 25),
      sliderInput(ns("table"), "Table", 0, 100, 50)
    )
  )
}

ui <- page_navbar(
  title = "Sidebar Refresh",
  fillable = TRUE,
  nav_spacer(),
  nav_panel(
    "Sidebar Page",
    layout_sidebar(
      sidebar = sidebar(
        title = "Diamond in the rough",
        actionButton("btn2", "Do the thing"),
        selectInput("select", "Select the thing", choices = LETTERS),
        textInput("text", "Type the thing", value = "Hello, world!")
      ),
      card(
        height = 300,
        full_screen = TRUE,
        card_header("A sidebar layout inside a card"),
        layout_sidebar(
          fillable = TRUE,
          sidebar = sidebar(
            actionButton("btn", "A button"),
            open = FALSE
          ),
          plotly_widget
        )
      ),
      card(
        full_screen = TRUE,
        class = "bg-dark",
        card_header("A filling map"),
        layout_sidebar(
          class = "p-0",
          fg = "black",
          sidebar = sidebar(
            title = "The Birthplace of R",
            fg = "white",
            p(
              "Did you know that R was born in New Zealand?",
              "Zoom in to find out exactly where."
            ),
            position = "right",
            open = FALSE
          ),
          card_body(class = "p-0", leaflet_widget)
        )
      )
    )
  ),
  nav_panel(
    "Sidebar Accordion",
    card(
      card_header("Groups of diamond filters"),
      layout_sidebar(
        sidebar = sidebar(accordions("one")),
        plotly_widget
      )
    ),
    card(
      card_header("Groups of diamond filters"),
      layout_sidebar(
        sidebar = sidebar(title = "Diamond studded accordions", accordions("two")),
        plotly_widget
      )
    )
  ),
  nav_panel(
    "Nested Sidebars",
    card(
      card_header("Nested sidebars"),
      layout_sidebar(
        fillable = TRUE,
        class = "p-0",
        sidebar = sidebar(
          width = "20%",
          bg = "#FF7847",
          fg = "",
          "Left Sidebar 1"
        ),
        layout_sidebar(
          fillable = TRUE,
          class = "p-0",
          border = FALSE,
          border_radius = FALSE,
          sidebar = sidebar(
            width = "30%",
            bg = "#FF70A6",
            position = "right",
            "Right Sidebar"
          ),
          layout_sidebar(
            border = FALSE,
            border_radius = FALSE,
            sidebar = sidebar(
              width = "40%",
              bg = "#FFB499",
              "Left Sidebar 2",
            ),
            lorem::ipsum(3, 1)
          )
        )
      )
    ),
    card(
      card_header("Nested sidebars (right)"),
      layout_sidebar(
        fillable = TRUE,
        class = "p-0",
        sidebar = sidebar(
          width = "20%",
          bg = "#FF7847",
          fg = "",
          "Left Sidebar"
        ),
        layout_sidebar(
          fillable = TRUE,
          class = "py-0 pe-0",
          border = FALSE,
          border_radius = FALSE,
          sidebar = sidebar(
            width = "30%",
            bg = "#FF70A6",
            position = "right",
            "Right Sidebar 1"
          ),
          layout_sidebar(
            border = FALSE,
            border_radius = FALSE,
            sidebar = sidebar(
              width = "40%",
              bg = "#FFB499",
              position = "right",
              "Right Sidebar 2",
            ),
            lorem::ipsum(3, 1)
          )
        )
      )
    )
  ),
  nav_panel(
    "Static",
    card(
      card_header("Static sidebar"),
      layout_sidebar(
        sidebar = sidebar(
          title = "Static sidebar",
          "This sidebar is always open",
          open = "always"
        ),
        lorem::ipsum(3, 4)
      )
    ),
    card(
      card_header("Static sidebar (right)"),
      layout_sidebar(
        sidebar = sidebar(
          title = "Static sidebar",
          "This sidebar is always open",
          position = "right",
          open = "always"
        ),
        lorem::ipsum(3, 4)
      )
    )
  )
)

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

}

shinyApp(ui, server)

@gadenbuie gadenbuie self-assigned this Sep 13, 2023
Copy link
Copy Markdown
Collaborator

@cpsievert cpsievert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done! :shipit:

@gadenbuie gadenbuie merged commit f3d8532 into main Sep 14, 2023
@gadenbuie gadenbuie deleted the sidebar/toggle-refresh branch September 14, 2023 19:27
This was referenced Sep 26, 2023
@daattali
Copy link
Copy Markdown
Contributor

Don't feel an obligation to respond if it's a complex answer, but I'm curious: why did the sidebar toggle change? Without knowing the context of this change, the new toggle button seems to me less ideal, because now it (1) takes up precious vertical real estate from the side bar which makes the top of the sidebar look empty, and (2) takes horizontal real estate from the main content when the sidebar is collapsed, which similarly makes the main content are look a bit awkward by having the left side be non symmetric with the right side.

@gadenbuie
Copy link
Copy Markdown
Member Author

Hi @daattali! The changes were motivated by a few things all coming together. Above all, we wanted to orient the toggle at the top, rather than the bottom, and to have the sidebar expand to fill the full container width on mobile. Those both motivated the change in position and the new behavior where the toggle floats into and out of the sidebar. We too noted the symmetry issues and are planning on addressing that before release.

We also felt that the previous design was overly compact and the usability suffered. The new toggle has a much bigger touch target. Along these lines, a common design thread in our latest work is to create more breathing room and space in default Shiny apps.

We appreciate the feedback! If you're able to share specific examples from your work, either as screenshots or reprex apps, we'd love to take a look and take them into consideration.

@daattali
Copy link
Copy Markdown
Contributor

I don't have any specific examples, the screenshots from the docs website are enough - when I saw those, my first reaction was that these screenshots must be from a very old draft version. But I definitely understand it's extremely difficult or impossible to have an ideal design for this that works across all contexts.

When I saw this image:

image

I thought that it would maybe be nicer if the button the button was half in the sidebar and half in the main content, and then the sidebar content could be pushed up to the top and the main content could be centered horizontally, something like this

image

And for mobile,

image

Would become

image

This is just what came to mind, but I realize these may have the same issues you describe initially, of the button not being prominent enough

@gadenbuie
Copy link
Copy Markdown
Member Author

gadenbuie commented Oct 17, 2023

It does look like there's a bug in those screenshots. The left gutter between the sidebar and the content should not be constant, it's increased when the sidebar is collapsed, but returns to normal when the sidebar is open. Either the screenshots are missing the right edge of the app or there's a bug in the (version used for the) screenshots. Regardless, we'll be addressing the asymmetry in a follow-up PR soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants