简体   繁体   中英

CSS for each page in R Shiny

I've written an R shiny application and am styling it before I complete it. I've written a small amount of HTML and want to change things such as the background colour using CSS. After consulting online I found I needed to seperate my css using the class argument, however when I specify a class for each page, it brings back no CSS at all.

Below is a shortened version of my R shiny application. Any help would be greatly appreciated.

library(shiny)

setwd("C:\\Users\\FRSAWG\\Desktop\\Application\\Shiny")

user <- shinyUI(navbarPage("",

tabPanel("Home Page",
       div(class="one",
         div(tags$style("#one body{background-color:blue;color:white;font-family:Arial}"),
            div(HTML("<h1><b><center>Home Page</center></b></h1>"))))),
tabPanel("Glossary",
       div(class="two",
           div(tags$style("#two body{background-color:red;color:white;font-family:Arial}"),
               div(HTML("<h1><b><center>Glossary</center></b></h1>")))))
))

serv <- shinyServer(function(input, output) {})

shinyApp(user, serv)

For reference I've designated one and two the class names for each of the pages.

UPDATE: Using the package shinyjs by Dean Attali ( link ), I wrote a helper function that you can call from R to create and run a jQuery function to modify the CSS element of a given object (or selector, in general) based on input from R syntax. You can use this to modify the CSS for your <body> when the tab changes.

This solves the problem with my previous suggestion - now there's no need to toggle the class of the body, which was sometimes causing flickering when for a split second all of the style classes for <body> were toggled off.

Here's the working example:

library(shiny)
library(shinyjs)

## Modify the CSS style of a given selector
modifyStyle <- function(selector, ...) {

  values <- as.list(substitute(list(...)))[-1L]
  parameters <- names(values)

  args <- Map(function(p, v) paste0("'", p,"': '", v,"'"), parameters, values)
  jsc <- paste0("$('",selector,"').css({", paste(args, collapse = ", "),"});")

  shinyjs::runjs(code = jsc)

}

# UI for the app
user <- shinyUI(
  navbarPage(title = "", id = "navtab",
             header = div(useShinyjs()),
             tabPanel("Home Page",
                      div(HTML("<h1><b><center>Home Page</center></b></h1>")),
                      "More text."
             ),

             tabPanel("Glossary",
                      div(HTML("<h1><b><center>Glossary</center></b></h1>")),
                      "More text."
             )
  )
)

# Server for the app
serv <- shinyServer(function(input, output, session) {

  observeEvent(input$navtab, {
    currentTab <- input$navtab # Name of the current tab

    if (currentTab == "Home Page") {
      modifyStyle("body", background = "blue", color = "white", 'font-family' = "Arial")
    }
    if (currentTab == "Glossary")  {
      modifyStyle("body", background = "red", color = "white", 'font-family' = "Arial")
    }
  })

})

shinyApp(user, serv)


I'm new to CSS myself, but it seems your problem can be fixed by just altering the CSS tags slightly. Changing the #one to .one and removing the body preceding the brackets will make the CSS style get applied to the divs of class one .

Using the selector #one would be changing the CSS style of a div whose id , not class, is one . Here's a link to a guide on w3shools.com explaining the use of different selectors in CSS syntax.

Some other notes:

  • You could also use a tags$head to organize your style tags in one place, instead of spreading them around the code. (This is down to personal preference, though.)
  • You can pass a class argument to tabPanel to set its CSS class - this removes the need for the inner div to set the class.

Modified example code:

library(shiny)

user <- shinyUI(navbarPage(
  tags$head(
    tags$style(HTML(".one {background-color: blue; color: white; font-family: Arial}")),
    tags$style(HTML(".two {background-color: red;  color: white; font-family: Arial}"))
  ),

  tabPanel("Home Page", class = "one",
      div(HTML("<h1><b><center>Home Page</center></b></h1>")),
      "More text."
  ),

  tabPanel("Glossary", class = "two",
      div(HTML("<h1><b><center>Glossary</center></b></h1>")),
      "More text."
  )
))

serv <- shinyServer(function(input, output) {})

shinyApp(user, serv)

Like I mentioned, I'm new to CSS, so I'm not 100% sure if this is the output you are looking for, though.

EDIT2: Here's a solution using the package shinyjs to update the class of the <body> when the selected tab changes. (Note that in order to use the functions from shinyjs , you need to include useShinyjs() in your ui.)

The idea is to make navbarPage return the name of the tab that's currently active in input$navtab by setting its id to navtab . Then we can use the toggleClass function from the package shinyjs to change the class of the <body> dynamically, and thus have the appropriate CSS styling applied.

It's not perfect, since the class change only happens after the server gets notified that the tab has changed, which sometimes causes the background to flash before changing. It can get a bit annoying. I suspect a better solution would be to use javascript to change the <body> class when clicking the link to change the tab, but I couldn't figure out how to do that with Shiny.

Here's the code:

library(shiny)
library(shinyjs)

user <- shinyUI(
  navbarPage(title = "", id = "navtab",
  header = tags$head(
    useShinyjs(),
    tags$style(HTML(".one {background: blue; color: white; font-family: Arial}")),
    tags$style(HTML(".two {background: red;  color: white; font-family: Arial}"))
  ),

    tabPanel("Home Page",
      div(HTML("<h1><b><center>Home Page</center></b></h1>")),
      "More text."
    ),

    tabPanel("Glossary",
     div(HTML("<h1><b><center>Glossary</center></b></h1>")),
     "More text."
    )
  )
)

serv <- shinyServer(function(input, output, session) {

   observeEvent(input$navtab, {
     shinyjs::toggleClass(selector = "body", class = "one",
                 condition = (input$navtab == "Home Page"))

     shinyjs::toggleClass(selector = "body", class = "two",
                 condition = (input$navtab == "Glossary"))
   })

})

shinyApp(user, serv)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM