Vintage NBA Seasons
A {reactable} tutorial
Preparing the Data
We’ll start off with the raw data from 538. The data accompanies this Rmarkdown file or, alternatively, can be downloaded directly from their github repository. The file we’re using is called ‘historical_RAPTOR_by_player.csv’. Background information on the RAPTOR metric can be found here.
We’ll load the libraries, then the raw data.
We are primarily using the dplyr
and readr
packages from the Tidyverse. We’ll create our table with the reactable
package and use htmltools
for when we need to use html
elements for table customization.
library(tidyverse)
## ── Attaching packages ─────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2 ✓ purrr 0.3.4
## ✓ tibble 3.0.3 ✓ dplyr 1.0.1
## ✓ tidyr 1.1.1 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(reactable)
library(htmltools)
# read data
df <- read_csv("./data/historical_RAPTOR_by_player.csv")
## Parsed with column specification:
## cols(
## player_name = col_character(),
## player_id = col_character(),
## season = col_double(),
## poss = col_double(),
## mp = col_double(),
## raptor_offense = col_double(),
## raptor_defense = col_double(),
## raptor_total = col_double(),
## war_total = col_double(),
## war_reg_season = col_double(),
## war_playoffs = col_double(),
## predator_offense = col_double(),
## predator_defense = col_double(),
## predator_total = col_double(),
## pace_impact = col_double()
## )
df %>% head()
## # A tibble: 6 x 15
## player_name player_id season poss mp raptor_offense raptor_defense
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Alaa Abdel… abdelal01 1991 640 303 -3.94 -0.510
## 2 Alaa Abdel… abdelal01 1992 1998 959 -2.55 -0.198
## 3 Alaa Abdel… abdelal01 1993 2754 1379 -2.37 -2.07
## 4 Alaa Abdel… abdelal01 1994 320 159 -6.14 -2.75
## 5 Alaa Abdel… abdelal01 1995 984 506 -3.85 -1.27
## 6 Kareem Abd… abdulka01 1977 7674 3483 4.54 3.10
## # … with 8 more variables: raptor_total <dbl>, war_total <dbl>,
## # war_reg_season <dbl>, war_playoffs <dbl>, predator_offense <dbl>,
## # predator_defense <dbl>, predator_total <dbl>, pace_impact <dbl>
Objective
Here’s what the original 538 table looks like (note: we’ll add an extra column not shown in this picture.) This table features 538’s most updated NBA statistic, RAPTOR, which stands for Robust Algorithm (using) Player Tracking (and) On/Off Ratings.
It attempts to rank indiviual (player’s) seasons, rather than individual players themselves, because a player’s career, like any career, has ebbs and flows.
Our objective is to re-create this table and give it a fresh makeover.
Data Wrangling
We will wrangle the data to be close to what we need before using the reactable
package. We’ll first select the columns we’re interested in. Then we’ll filter for players who played for more than 1000 minutes (mp), as done in the original. We’ll arrange the data in descending order by WAR - wins above replacement.
Next, we’ll rename the columns to match the names used by 538. Then we’ll format all columns with decimal numbers to be rounded to one decimal place. Finally, we’ll choose the top 100 rows (after filtering) to keep our data manageable.
We’ll save this to a variable called raptor_table
.
raptor_table <- df %>%
select(player_name, season, mp, raptor_offense, raptor_defense, raptor_total, war_total, war_playoffs) %>%
filter(mp > 1000) %>%
arrange(desc(war_total)) %>%
rename(
NAME = player_name,
SEASON = season,
MIN_PLAYED = mp,
OFF = raptor_offense,
DEF = raptor_defense,
TOTAL = raptor_total,
WAR = war_total,
PLAYOFF_WAR = war_playoffs
) %>%
mutate_at(4:8, funs(round(., 1))) %>%
head(100)
## Warning: `funs()` is deprecated as of dplyr 0.8.0.
## Please use a list of either functions or lambdas:
##
## # Simple named list:
## list(mean = mean, median = median)
##
## # Auto named with `tibble::lst()`:
## tibble::lst(mean, median)
##
## # Using lambdas
## list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
Introduction to {reactable}
The reactable
package has a set of major functions for creating beautiful tables.
The primary function is reactable()
which we’ll use the wrap our raptor_table
variable. This function immediately provides base features for the table. In addition, we’ll use several parameters that come with this function to create our table. For example, we’ll define the table height
, minRows
for number of rows in one view and pagination
behavior for longer tables that do not fit on one view, as well as several other parameters.
Next, we’ll use a set of functions to customize columns including: colDef()
for individual columns and colGroup()
for grouping columns together.
Finally, we’ll use reactableTheme()
for overall table styling. That’s all there is to creating beautiful tables with reactable
.
Creating a Basic Table
First, we’ll wrap our raptor_table
within the reactable()
function to create a basic table. Just doing this gives us individual column sorting, which is neat. You’ll note in the original table that OFF, DEF and TOTAL are grouped under RAPTOR; this is achieved using colGroup()
.
reactable(
raptor_table,
columns = list(
OFF = colDef(name = "OFF."),
DEF = colDef(name = "DEF."),
TOTAL = colDef(name = "TOTAL")
),
columnGroups = list(
colGroup(name = "RAPTOR", columns = c("OFF", "DEF", "TOTAL"))
)
)
Add Basic Table Features
Next, we’ll add basic table features including table height, mininum rows (10) and making it compact. In addition, we’ll enable the sort icon, showSortIcon
, when any column name is clicked.
We won’t disable pagination because that will over write our column grouping
(i.e., RAPTOR).
reactable(
raptor_table,
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
columns = list(
OFF = colDef(name = "OFF."),
DEF = colDef(name = "DEF."),
TOTAL = colDef(name = "TOTAL")
),
columnGroups = list(
colGroup(name = "RAPTOR", columns = c("OFF", "DEF", "TOTAL"))
)
)
Add Search Box
Next, we’ll add a search box with the searchable
parameter, as well as placeholder text for readability (langauge
). You can type in a fake name in the search box and if there are no matches, the text will render “No matches”.
reactable(
raptor_table,
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...", noData = "No matches"),
columns = list(
OFF = colDef(name = "OFF."),
DEF = colDef(name = "DEF."),
TOTAL = colDef(name = "TOTAL")
),
columnGroups = list(
colGroup(name = "RAPTOR", columns = c("OFF", "DEF", "TOTAL"))
)
)
Format Individual Columns
Next, we’ll add some formatting and styling to each of the individual columns. This will include column width and font used within the columns. The bulk of this process will be within the columns
parameter. In addition to the OFF
, DEF
and TOTAL
columns, we’ll add formating for the rest of the columns.
This includes customizing name, customizing digits (i.e., making sure commas are placed for minutes played), font and font sizes.
We’ll also add a “+” prefix for the TOTAL
column. Althought we had previously formatted our data to one decimal place, we’ll need to re-do this with format = colFormat(digits = 1)
to make sure all digits line up properly. You’ll note that the style
for PLAYOFF_WAR
has a whiteSpace
parameter set to pre - this will be apparent why when we add visuals to this particular column below.
We will style OFF
, DEF
and TOTAL
separately in the next section.
reactable(
raptor_table,
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...", noData = "No matches"),
columns = list(
NAME = colDef(minWidth = 120, style = list(fontFamily = "liberation mono", fontSize = 14)),
SEASON = colDef(minWidth = 60, style = list(fontFamily = "liberation mono", fontSize = 14), align = "left"),
MIN_PLAYED = colDef(name = 'MIN. PLAYED', format = colFormat(separators = TRUE), minWidth = 60, style = list(fontFamily = "liberation mono", fontSize = 14), align = 'right'),
OFF = colDef(name = "OFF.", format = colFormat(digits = 1), minWidth = 60, align = 'right'),
DEF = colDef(name = "DEF.", format = colFormat(digits = 1), minWidth = 60, align = 'right'),
TOTAL = colDef(name = "TOTAL", format = colFormat(prefix = "+", digits = 1), minWidth = 60, align = 'right'),
WAR = colDef(format = colFormat(digits = 1), minWidth = 60, style = list(fontFamily = "liberation mono", fontSize = 14), align = "right"),
PLAYOFF_WAR = colDef(name = "P/O WAR", format = colFormat(digits = 1), minWidth = 100, align = 'center', style = list(fontFamily = "liberation mono", whiteSpace = "pre", fontSize = 14))
),
columnGroups = list(
colGroup(name = "RAPTOR", columns = c("OFF", "DEF", "TOTAL"))
)
)
Code Edit
Let’s pause to make the code more readable by refactoring and adding comments to better structure the code.
reactable(
# data
raptor_table,
# styling for entire table
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...",
noData = "No matches"),
# styling individual columns
# columns start
columns = list(
NAME = colDef(minWidth = 120,
style = list(fontFamily = "liberation mono",
fontSize = 14)),
SEASON = colDef(minWidth = 60,
align = "left",
style = list(fontFamily = "liberation mono",
fontSize = 14)),
MIN_PLAYED = colDef(name = 'MIN. PLAYED',
minWidth = 60,
align = 'right',
format = colFormat(separators = TRUE),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
OFF = colDef(name = "OFF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1)),
DEF = colDef(name = "DEF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1)),
TOTAL = colDef(name = "TOTAL",
minWidth = 60,
align = 'right',
format = colFormat(prefix = "+",
digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
WAR = colDef(minWidth = 60,
align = "right",
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
PLAYOFF_WAR = colDef(name = "P/O WAR",
minWidth = 100, align = 'center',
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
whiteSpace = "pre",
fontSize = 14))
), #columns end
# grouping OFF,DEF,TOTAL under RAPTOR
columnGroups = list(
colGroup(name = "RAPTOR",
columns = c("OFF", "DEF", "TOTAL"))
)
)
Add Color for OFF, DEF & TOTAL
We’ll visualize differences in offensive (OFF) and defensive (DEF) ratings for each NBA player for the regular season and playoffs by creating a color_palette
, then inserting it in the style for specific columns (i.e., OFF, DEF).
# note: For contrast, we use two colors shade (blue & red).
# this function
color_palette <- function(x) rgb(colorRamp(c("#edfeff", "#ff2c0f"))(x), maxColorValue = 255)
# Now we'll add color to the OFF and DEF columns
reactable(
# data
raptor_table,
# styling for entire table
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...",
noData = "No matches"),
# styling individual columns
# columns start
columns = list(
NAME = colDef(minWidth = 120,
style = list(fontFamily = "liberation mono",
fontSize = 14)),
SEASON = colDef(minWidth = 60,
align = "left",
style = list(fontFamily = "liberation mono",
fontSize = 14)),
MIN_PLAYED = colDef(name = 'MIN. PLAYED',
minWidth = 60,
align = 'right',
format = colFormat(separators = TRUE),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
OFF = colDef(name = "OFF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$OFF)) / (max(raptor_table$OFF) - min(raptor_table$OFF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
DEF = colDef(name = "DEF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$DEF)) / (max(raptor_table$DEF) - min(raptor_table$DEF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
TOTAL = colDef(name = "TOTAL",
minWidth = 60,
align = 'right',
format = colFormat(prefix = "+",
digits = 1),
style = list(backgroundColor = '#F5F5F5',
fontFamily = "liberation mono",
fontSize = 14)),
WAR = colDef(minWidth = 60,
align = "right",
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
PLAYOFF_WAR = colDef(name = "P/O WAR",
minWidth = 100, align = 'center',
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
whiteSpace = "pre",
fontSize = 14))
), #columns end
# grouping OFF,DEF,TOTAL under RAPTOR
columnGroups = list(
colGroup(name = "RAPTOR",
columns = c("OFF", "DEF", "TOTAL"))
)
)
Add Bars to P/O WAR
Now we’ll style the P/O WAR
column to contrast with WAR
, which includes regular season and playoffs. Basketball is a completely different game between the regular season and playoffs, so it’s worth knowing wins above replacement from the playoffs, if only to see which NBA star takes their game up another level during the playoffs.
# define a function to define the attributes of bar charts
bar_chart <- function(label, width = "100%", height = "14px", fill = "#00bfc4", background = NULL) {
bar <- div(style = list(background = fill, width = width, height = height))
chart <- div(style = list(flexGrow = 1, marginLeft = "6px", background = background), bar)
div(style = list(display = "flex", alignItems = "center"), label, chart)
}
reactable(
# data
raptor_table,
# styling for entire table
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...",
noData = "No matches"),
# styling individual columns
# columns start
columns = list(
NAME = colDef(minWidth = 120,
style = list(fontFamily = "liberation mono",
fontSize = 14)),
SEASON = colDef(minWidth = 60,
align = "left",
style = list(fontFamily = "liberation mono",
fontSize = 14)),
MIN_PLAYED = colDef(name = 'MIN. PLAYED',
minWidth = 60,
align = 'right',
format = colFormat(separators = TRUE),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
OFF = colDef(name = "OFF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$OFF)) / (max(raptor_table$OFF) - min(raptor_table$OFF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
DEF = colDef(name = "DEF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$DEF)) / (max(raptor_table$DEF) - min(raptor_table$DEF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
TOTAL = colDef(name = "TOTAL",
minWidth = 60,
align = 'right',
format = colFormat(prefix = "+",
digits = 1),
style = list(backgroundColor = '#F5F5F5',
fontFamily = "liberation mono",
fontSize = 14)),
WAR = colDef(minWidth = 60,
align = "right",
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
PLAYOFF_WAR = colDef(name = "P/O WAR",
minWidth = 100,
align = 'center',
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
whiteSpace = "pre",
fontSize = 14),
# render bar chart
cell = function(value){
width <- paste0(value * 100 / max(raptor_table$PLAYOFF_WAR), "%")
value <- format(value, width = 9, justify = "right")
bar_chart(value, width = width, fill = "#3fc1c9")
}
)
), #columns end
# grouping OFF,DEF,TOTAL under RAPTOR
columnGroups = list(
colGroup(name = "RAPTOR",
columns = c("OFF", "DEF", "TOTAL"))
)
)
Column Name Styling
We’re putting the finishing touches on this table. We’ll change all column header font to “liberation mono” and we’ll set the
defaultPageSize
to 15.
reactable(
# data
raptor_table,
# header styling
defaultColDef = colDef(headerStyle = list(fontFamily = "liberation mono", fontSize = 16)),
# styling for entire table
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...",
noData = "No matches"),
# styling individual columns
# columns start
columns = list(
NAME = colDef(minWidth = 120,
style = list(fontFamily = "liberation mono",
fontSize = 14)),
SEASON = colDef(minWidth = 60,
align = "left",
style = list(fontFamily = "liberation mono",
fontSize = 14)),
MIN_PLAYED = colDef(name = 'MIN. PLAYED',
minWidth = 60,
align = 'right',
format = colFormat(separators = TRUE),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
OFF = colDef(name = "OFF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$OFF)) / (max(raptor_table$OFF) - min(raptor_table$OFF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
DEF = colDef(name = "DEF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$DEF)) / (max(raptor_table$DEF) - min(raptor_table$DEF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
TOTAL = colDef(name = "TOTAL",
minWidth = 60,
align = 'right',
format = colFormat(prefix = "+",
digits = 1),
style = list(backgroundColor = '#F5F5F5',
fontFamily = "liberation mono",
fontSize = 14)),
WAR = colDef(minWidth = 60,
align = "right",
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
PLAYOFF_WAR = colDef(name = "P/O WAR",
minWidth = 100,
align = 'center',
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
whiteSpace = "pre",
fontSize = 14),
# render bar chart
cell = function(value){
width <- paste0(value * 100 / max(raptor_table$PLAYOFF_WAR), "%")
value <- format(value, width = 9, justify = "right")
bar_chart(value, width = width, fill = "#3fc1c9")
}
)
), #columns end
# grouping OFF,DEF,TOTAL under RAPTOR
columnGroups = list(
colGroup(name = "RAPTOR",
columns = c("OFF", "DEF", "TOTAL"),
headerStyle = list(fontFamily = "liberation mono", fontSize = 16))
),
defaultPageSize = 15
)
Table Title and Subtitle
To add a title/subtitle, we’ll save our table to a variable tbl
, then wrap it around a html
div
. This allows us to add title
and subtitle
to the table that is technically external to the table itself.
We’ll also change the background color of the table itself using the reactableTheme()
function.
options(reactable.theme = reactableTheme(
backgroundColor = "#fffef0"
))
tbl <- reactable(
# data
raptor_table,
# header styling
defaultColDef = colDef(headerStyle = list(fontFamily = "liberation mono", fontSize = 16)),
# styling for entire table
height = 600,
minRows = 10,
showSortIcon = TRUE,
compact = TRUE,
pagination = TRUE,
showPageInfo = TRUE,
searchable = TRUE,
language = reactableLang(searchPlaceholder = "Search...",
noData = "No matches"),
# styling individual columns
# columns start
columns = list(
NAME = colDef(minWidth = 120,
style = list(fontFamily = "liberation mono",
fontSize = 14)),
SEASON = colDef(minWidth = 60,
align = "left",
style = list(fontFamily = "liberation mono",
fontSize = 14)),
MIN_PLAYED = colDef(name = 'MIN. PLAYED',
minWidth = 60,
align = 'right',
format = colFormat(separators = TRUE),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
OFF = colDef(name = "OFF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$OFF)) / (max(raptor_table$OFF) - min(raptor_table$OFF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
DEF = colDef(name = "DEF.",
minWidth = 60,
align = 'right',
format = colFormat(digits = 1),
style = function(value){
# normalization is based on a cell's value in relation to the range of values in the column
normalized <- (value - min(raptor_table$DEF)) / (max(raptor_table$DEF) - min(raptor_table$DEF))
color <- color_palette(normalized)
# with color set based on the color_palette function, we can feed it to the background parameter
list(background = color, fontWeight = "bold",
fontFamily = "liberation mono",
fontSize = 14)
}
),
TOTAL = colDef(name = "TOTAL",
minWidth = 60,
align = 'right',
format = colFormat(prefix = "+",
digits = 1),
style = list(backgroundColor = '#F5F5F5',
fontFamily = "liberation mono",
fontSize = 14)),
WAR = colDef(minWidth = 60,
align = "right",
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
fontSize = 14)),
PLAYOFF_WAR = colDef(name = "P/O WAR",
minWidth = 100,
align = 'center',
format = colFormat(digits = 1),
style = list(fontFamily = "liberation mono",
whiteSpace = "pre",
fontSize = 14),
# render bar chart
cell = function(value){
width <- paste0(value * 100 / max(raptor_table$PLAYOFF_WAR), "%")
value <- format(value, width = 9, justify = "right")
bar_chart(value, width = width, fill = "#3fc1c9")
}
)
), #columns end
# grouping OFF,DEF,TOTAL under RAPTOR
columnGroups = list(
colGroup(name = "RAPTOR",
columns = c("OFF", "DEF", "TOTAL"),
headerStyle = list(fontFamily = "liberation mono", fontSize = 16))
),
defaultPageSize = 15
)
After we’ve saved our work with the reactable
function into the tbl
variable, we can wrap that in a div
and create the title and subtitle.
div(class = "table-wrap",
div(class = "table-header",
div(class = "table-title", "Top 100 Vintage NBA Seasons"),
"This table highlights 538's new NBA statistic, RAPTOR, in addition to the more established Wins Above Replacement (WAR). An extra column, Playoff (P/O) War, is provided to highlight stars performers in the post-season, when the stakes are higher. The table is limited to the top-100 players who have played at least 1,000 minutes."),
tbl
)
The cool thing is that we can style the table with css
directly in Rmarkdown (just make sure your code chunk has css
instead of r
just for the css parts). We’ll use css
to style our title
and subtitle
to make it consistent with the table itself.
.table-title {
font-size: 20px;
font-weight: 600;
font-family: "liberation mono";
padding: 10px 10px 10px 3px;
}
.table-header {
font-size: 14px;
font-weight: 300;
font-family: "liberation mono";
padding: 10px 10px 10px 10px;
}
.table-wrap {
box-shadow: 2px 3px 20px black;
background-color: #fffef0;
}