Jeopardy!

Jeopardy board (static)

Overview

This is my entry into the ⚡R Studio 2022 Table Contest. It uses reactable and scss in Quarto to style a Jeopardy! game board.

My aims for this project were three-fold:

  1. To think about creative uses of tables! ✅

  2. Learn how to use some of reactable’s custom rendering capabilities ✅

  3. Continue to investigate and experiment (s)css and Quarto. ✅

Approach

I start by pulling archived Jeopardy! game data using the whatr 📦. What’s nice is that each row of data is indexed by the row and column it appears on in the actual board 😝. This makes it simple to ensure the right content is in the right place.

In the next step, I borrow some preexisting css for creating flip cards 🔍. I ♻️, 🔨 and 🗑 elements in order to match the aesthetic of Jeopardy! This was relatively straightforward, though researching colors and fonts took a bit of time.

The last step is to arrange the data once it’s been read into R into a format serviceable for display. I knew I planned to use reactable as the display table package and leverage escaping html.

Method 1

My first approach was to bake the html/css stuff directly into each cell, before passing it to reactable for escaping. It looked something like this:

👉️ See Code
raw_data %>%
  .... %>%
  mutate(content = if_else(row == 1,
                           # html for header
                           glue::glue('<div class="flip-card">
                                        <div class="flip-card-inner">
                                          <div class="flip-card-head">
                                            <p>{category}</p>
                                          </div>
                                          <div class="flip-card-head">
                                            {category}
                                          </div>
                                        </div>
                                      </div>'),
                           # html for clues
                           glue::glue('<div class="flip-card">
                                        <div class="flip-card-inner">
                                          <div class="flip-card-front">
                                            {value}
                                          </div>
                                          <div class="flip-card-back">
                                            <p>{clue}</p>
                                            <details>
                                            <summary>Answer</summary>
                                            <p>{answer}</p>
                                          </details>
                                          </div>
                                        </div>
                                      </div>'))) %>%
 .... %>%
 reactable(.,
           defaultColDef = colDef(html = TRUE))

While this certainly worked (it has in the past for me), I knew reactable had more to offer in terms of custom rendering. It was time to have a closer look at specifics. 😨

Method 2

It turns reactables defaultColDef argument works great for custom rendering in this case since every cell in my Jeopardy! board needs the same html/css treatment. It looks something like this:

👉️ See Code
reactable(table_data,
  sortable = FALSE,
  defaultColDef = colDef(
    html = TRUE,
    align = "center",
    # Header Rendering
    header = function(value) {
      tags$div(
        class = "flip-card flip-card-head",
        value
      )
    },
    # Cell Rendering
    cell = function(value, index) {
      # parse clue, answer from table cell
      content <- str_split(value, ";", simplify = TRUE)
      clue <- content[1]
      answer <- content[2]
      # cell content
      tags$div(
        class = "flip-card",
        tags$div(
          class = "flip-card-inner",
          tags$div(
            class = "flip-card-front",
            # multiply row index by 200 for tile value
            paste("$", index * 200)
          ),
          tags$div(
            class = "flip-card-back",
            clue,
            tags$details(
              tags$summary("Answer"),
              answer
            )
          )
        )
      )
    }
  )
)

I like this approach much better than the first because I’m keeping table and data wrangling parts of my workflow separate and hopefully more clear for readers. 🤓

Forward

It’s been an awesome ride so far experimenting with Quarto and continuing to learn how to create and modify existing CSS. I’m happy that the R Studio 2022 Table Contest is currently taking place because it was a constructive outlet to build something that uses things I’ve recently learned.

🍻✌ Enjoy!

Matthew Kumar
Matthew Kumar
Associate Director, Lead Computational Scientist

Related