Table of Contents generated with DocToc
Thanks for your interest in improving cfbplotR, the SportsDataverse ggplot2 extension for college-football logos, wordmarks, and player headshots!
By participating you agree to abide by the Code of Conduct.
Ways to Contribute
- Report a bug – open an issue using the Bug Report template.
- Request a feature – open an issue using the Feature Request template.
- Improve docs – typo fixes, clearer examples, and updated screenshots are always welcome.
- Add or fix a geom/element/scale/gt helper – see the conventions below.
-
Update team data – logo and wordmark URLs go stale; PRs that refresh
data-raw/are valuable.
Getting Set Up
Fork and clone the repository.
-
Install development dependencies (pak recommended):
# recommended: install.packages("pak") pak::local_install_dev_deps() # or with devtools: install.packages("devtools") devtools::install_dev_deps() -
Load the package for interactive development:
devtools::load_all()
Development Workflow
devtools::load_all() # load the package
devtools::document() # regenerate man/ + NAMESPACE after changing roxygen2
devtools::test() # run the full test suite
devtools::check() # full R CMD check -- run before opening a PR
devtools::build_readme() # re-render README.md from README.Rmd
pkgdown::build_site() # preview the pkgdown site locallyCoding Conventions
cfbplotR follows the conventions documented in CLAUDE.md. The essentials:
Architecture
cfbplotR is a thin domain layer on top of ggpath. ggpath owns image fetching, caching, colorizing, and rendering. cfbplotR:
- Resolves a CFB team name or ESPN player ID to an image URL/path (via
logo_from_school(),wordmark_from_school(),headshot_from_id()inR/utils.R). - Delegates rendering to ggpath (
ggpath::GeomFromPath$draw_panel()for geoms;ggplot2::element_grob(ggpath::element_path(...))for theme elements).
Never re-implement ggpath rendering inside cfbplotR.
ggpath S7 note
ggpath::element_path is an S7 object. The internal helper .cfb_element_to_path_grob() must construct it via ggpath::element_path(alpha=..., colour=..., hjust=..., vjust=..., size=...). Do NOT structure() a plain list and re-class it – S7 property access will break.
Naming
| Family | Pattern | Example |
|---|---|---|
| Geoms | geom_cfb_* |
geom_cfb_logos() |
| Theme elements | element_cfb_* |
element_cfb_logo() |
| Color/fill scales |
scale_color_cfb, scale_fill_cfb
|
|
| Position scales |
scale_x_cfb, scale_y_cfb
|
|
| gt formatters |
gt_fmt_cfb_*, gt_cfb_*
|
gt_fmt_cfb_logo() |
| Team utilities | cfb_* |
cfb_team_tiers() |
| Name cleaners | clean_* |
clean_school_names() |
Internal resolvers
Use the three resolver helpers – do not inline URL construction:
-
logo_from_school(team)– team name -> logo URL (falls back to NCAA logo) -
wordmark_from_school(team)– team name -> wordmark URL (falls back to NCAA wordmark) -
headshot_from_id(player_id)– ESPN player ID -> ESPN CDN headshot URL
Re-exporting ggpath
Add new ggpath re-exports to R/reexports.R using the standard roxygen2 pattern:
#' @importFrom ggpath new_symbol
#' @export
ggpath::new_symbolTeam data
R/sysdata.rda is generated from data-raw/. After updating team data, re-source the relevant script and run devtools::document(). Never edit sysdata.rda by hand.
Messaging
Use cli::cli_warn() for user-facing warnings, cli::cli_abort() for errors, and cli::cli_inform() for informational messages. Do not use bare warning() or message().
Testing
Test types
-
Snapshot tests: visual geoms and theme elements use
vdiffr::expect_doppelganger(). After intentional visual changes, runvdiffr::manage_cases()to review and accept new snapshots. Do not delete.svgfiles manually. -
Data/resolver tests: cleaning helpers and resolver functions use plain
expect_*. -
Network tests: guard with
skip_if_offline()andskip_on_cran().
Test pattern
test_that("geom_cfb_logos renders a known team", {
skip_on_cran()
skip_if_not_installed("vdiffr")
p <- ggplot2::ggplot(
data.frame(x = 1, y = 1, team = "Alabama"),
ggplot2::aes(x = x, y = y, team = team)
) +
geom_cfb_logos(width = 0.1)
vdiffr::expect_doppelganger("geom-cfb-logos-alabama", p)
})Documentation
Never hand-edit
NAMESPACEor anything inman/– regenerate withdevtools::document().Re-render
README.mdwithdevtools::build_readme()and commitREADME.Rmd+README.mdtogether. Never hand-editREADME.md.Add new exported functions to the
reference:section of_pkgdown.ymlif they are not already covered by the existingstarts_with()selectors.For user-visible changes, update
NEWS.md.-
Regenerate doctoc TOCs after editing this file,
CLAUDE.md,.github/copilot-instructions.md, or.github/pull_request_template.md:
Commit & PR Conventions
- Use Conventional Commits:
feat(geom):,fix(elements):,docs:,test:,refactor:,chore:,ci:. Mark breaking changes withtype!:or aBREAKING CHANGE:footer. - Keep unrelated changes in separate commits.
- Do not add AI tools (Claude, Copilot, etc.) as commit co-authors.
- Open your PR against
main, fill in the PR template, and confirmdevtools::check()anddevtools::test()pass before requesting review.
Thanks again for contributing!