Search code examples
rshinysassnavbarbslib

Why is my bslib theme not coloring the navbar as expected?


I can't seem to get bslib style options working right. I was interested to use the pulse bootswatch for the purple (https://bootswatch.com/pulse/), but I'm not seeing it ...

library(shiny)
library(bslib)

ui <- page_navbar(
    title = "why is the background here not purple?",
    theme = bs_theme(bootswatch = "pulse")
)

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

shinyApp(ui = ui, server = server)

Using the bs_themer in the top right of the app seems to give inconsistent results, but other bootswatches do sometimes seem to use the primary color as the header background as expected, but the pulse theme doesn't seem to work.


Solution

  • bslib >= 0.90

    bslib had several changes which provide easier access to navbar attributes (see e.g. rstudio/bslib#940). The recommended way for theming the navbar is now to use the new navbar_options parameter of page_navbar. For example, the Pulse theme could be applied by providing class = "bg-primary" and theme = "dark":

    navbar_options = list(class = "bg-primary", theme = "dark")
    

    This would set up a markup similar to the one of the Bootswatch source code:

    <nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
      <!-- Navbar content -->
    </nav>
    

    Please see ?navbar_options for more details.

    library(shiny)
    library(bslib)
    
    ui <- page_navbar(
      title = "background is now purple",
      theme = bs_theme(5, "pulse"),
      navbar_options = list(class = "bg-primary", theme = "dark")
    )
    
    shinyApp(ui = ui, server = \(...) {})
    

    enter image description here


    bslib < 0.90

    The reason why you don't see the purple color when applying the Bootswatch theme Pulse (via theme = bs_theme(bootswatch = "pulse")) is because Bootstrap and bslib handle navbar styling in different ways (despite that, it is expectable that your code should suffice for setting up the color).

    Bootstrap 5

    The docs on color schemes suggest customization by using .bg-* classes. A navbar could e.g. have the HTML setup

    <nav class="navbar bg-primary" data-bs-theme="dark">
      <!-- Navbar content -->
    </nav>
    

    and would get colored with a corresponding primary color (here the purple, this is also used in the above Bootswatch example). Additionally, data-bs-theme="dark" makes the navbar using dark colors. However, bslib does not define the navbar by this means.

    bslib

    bslib uses the variables $navbar-light-bg, $navbar-dark-bg and $navbar-bg for the background color, as introduced in the PRs rstudio/bslib#253 and rstudio/bslib#271. Additionally, bslib makes use of two Bootstrap 3 classes: .navbar-default and .navbar-inverse. In your example code (generally with bslib::page_navbar() with inverse = FALSE), one would get

    <nav class="navbar navbar-default navbar-static-top navbar-inverse" role="navigation">
      <!-- Navbar content -->
    </nav>
    

    The reason why these classes are still used is backward compatibility: shiny::navbarPage() calls bslib::page_navbar() under the hood and shiny relies on Bootstrap 3. The two classes define the color variables as follows:

    So we have to see how $navbar-light-bg and $navbar-dark-bg are defined. There is a mapping function in bslib which is used for the translation from Bootstrap 3 navbar classes to higher versions, it shows the mapping definitions, e.g.

    > bslib:::bs3compat_navbar_defaults("pulse")
    [1] ""
    > bslib:::bs3compat_navbar_defaults("flatly")
    [[1]]
    [1] "$navbar-light-bg: $primary !default;"
    
    [[2]]
    [1] "$navbar-dark-bg: $success !default;"
    

    Using "pulse" returns here "" because this Bootswatch theme was added in Bootstrap 4. Therefore, the fallback value var(--bs-dark) gets used and this is the black what you see.

    How to solve it?

    The above should make clear why both of the following not convenient approaches work ("#593196" is the primary color for Pulse):

    • Overwrite $navbar-dark-bg or $navbar-light-bg:
      theme = bs_theme(bootswatch = "pulse") |>
        bs_add_variables("navbar-dark-bg" = "#593196")
      
    • Set the background-color of .navbar-default to $primary:
      theme = bs_theme(bootswatch = "pulse") |>
        bs_add_rules(
          rules = ".navbar.navbar-default {
                    background-color: $primary !important;
                   }"
        )
      

    However, the recommended solution in the sense of Bootstrap 5 is to use appropriate classes on the navbar. What we can do is:

    • Omit the .navbar-inverse and .navbar-default classes,
    • Add the .bg-primary class,
    • Add the attribute data-bs-theme="dark".

    This can be implemented via htmltools::tagQuery() (and setting inverse = FALSE in page_navbar()):

    library(shiny)
    library(bslib)
    
    ui <- htmltools::tagQuery(page_navbar(
      title = "background is now purple",
      theme = bs_theme(bootswatch = "pulse"),
      inverse = FALSE
    ))$ 
      find(".navbar")$
      toggleClass("navbar-default bg-primary")$
      addAttrs(`data-bs-theme` = "dark")$
      allTags()
    
    shinyApp(ui = ui, server = \(...) {})
    

    We obtain the purple color, as expected:

    enter image description here