|
| 1 | +--- |
| 2 | +title: "Exploring badge accessibility" |
| 3 | +--- |
| 4 | + |
| 5 | +```{r, include = FALSE} |
| 6 | +knitr::opts_chunk$set( |
| 7 | + collapse = TRUE, |
| 8 | + comment = "#>" |
| 9 | +) |
| 10 | +``` |
| 11 | + |
| 12 | +```{r setup} |
| 13 | +library(usethis) |
| 14 | +``` |
| 15 | + |
| 16 | +Various functions in usethis insert a badge into a package's README file. |
| 17 | +A badge consists of an image with information about the package, which is usually hyperlinked to a location with further details. |
| 18 | +Here is a typical badge: |
| 19 | + |
| 20 | +<!-- badges: start --> |
| 21 | +[](https://shields.io/) |
| 22 | +<!-- badges: end --> |
| 23 | + |
| 24 | +For example, the badge message might be "passing" or "failing", reflecting the `R CMD check` status of its current development state, and then link to the actual log file. |
| 25 | +Or the badge could display the current test coverage, as a percentage of lines, and then link to a site where one can explore test coverage line-by-line. |
| 26 | + |
| 27 | +<!-- badges: start --> |
| 28 | +[](https://github.com/r-lib/usethis/actions/workflows/R-CMD-check.yaml) |
| 29 | +[](https://app.codecov.io/gh/r-lib/usethis?branch=main) |
| 30 | +<!-- badges: end --> |
| 31 | + |
| 32 | +A key point is that the badge often presents *dynamic* information. |
| 33 | + |
| 34 | +We'd like to improve the user experience for those consuming the README using assistive technology, such as a screen reader. |
| 35 | + |
| 36 | +There are at least two ways that usethis can influence this: |
| 37 | + |
| 38 | +* The construction of the hyperlinked badge. For example, we control the alt |
| 39 | + text. |
| 40 | +* The image itself. We can encourage the badge providers to build accessibility |
| 41 | + into their badge SVGs. If the "native" badge is not accessible, usethis can |
| 42 | + substitute an accessible badge from a 3rd party, such as |
| 43 | + [shields.io](https://shields.io/). |
| 44 | + |
| 45 | +This article is meant to gather badge facts in one place and facilitate a dialogue with those using assistive technology. |
| 46 | + |
| 47 | +## Markdown image link |
| 48 | + |
| 49 | +usethis currently inserts badges in a special "badge block" in the README, fenced with HTML comment tags. |
| 50 | +Within the badge block, each badge is a hyperlinked image, with alt text. |
| 51 | +Here is the basic form: |
| 52 | + |
| 53 | +``` |
| 54 | +[](http://path/to/more/details) |
| 55 | +``` |
| 56 | + |
| 57 | +Note that the `alt_text` should be a static label explaining the badge's purpose, whereas the dynamic information is baked into the externally-served image. |
| 58 | + |
| 59 | +usethis definitely controls this `alt_text`, so that is something I'd like feedback on. |
| 60 | + |
| 61 | +## Badge image |
| 62 | + |
| 63 | +Most badges these days tend to be SVG files, which is fortunate, since SVGs can carry attributes to support accessibility. |
| 64 | +We demonstrate this with a custom badge generated via the [shields.io](https://shields.io/) web service. |
| 65 | + |
| 66 | +I will request a badge with this URL: |
| 67 | + |
| 68 | +``` |
| 69 | +https://img.shields.io/badge/my__label-my__message-orange |
| 70 | +``` |
| 71 | + |
| 72 | +Here it is as a badge, with the hyperlink set to <https://shields.io/>. |
| 73 | +The badge is inside an HTML comment block, like usethis does for README badges. |
| 74 | +I've prefixed every field I can influence with "my"; there are 3 such fields: |
| 75 | + |
| 76 | +1. The alt text. |
| 77 | +2. The label. This static text appears on the left side of the badge, on a grey |
| 78 | + background, and describes the badge. |
| 79 | +3. The message. This text appears on the right side of the badge, on a colored |
| 80 | + background. This is often the dynamic part, where both the color and the text |
| 81 | + convey a dynamic fact, e.g. "passing" on a green background or "failing" on a |
| 82 | + red background. |
| 83 | + |
| 84 | +<!-- badges: start --> |
| 85 | +[](https://shields.io/) |
| 86 | +<!-- badges: end --> |
| 87 | + |
| 88 | +Now we inspect the badge SVG. |
| 89 | +I apologize in advance for the amount of data displayed here, but it seems good to show one badge in full gory detail. |
| 90 | +Later, we will display badge information very selectively. |
| 91 | + |
| 92 | +```{r R.options = list(tidyverse.quiet = TRUE)} |
| 93 | +library(xml2) |
| 94 | +library(purrr) |
| 95 | +
|
| 96 | +inspect_badge <- function(badge) { |
| 97 | + badge %>% |
| 98 | + read_xml() %>% |
| 99 | + as_list() %>% |
| 100 | + pluck("svg") |
| 101 | +} |
| 102 | +
|
| 103 | +inspect_badge("https://img.shields.io/badge/my__label-my__message-orange") |
| 104 | +``` |
| 105 | + |
| 106 | +It is my understanding that the two main pieces of metadata are the `title` and the `aria-label` attribute. |
| 107 | +Here I reveal just those two items from the badge: |
| 108 | + |
| 109 | +```{r} |
| 110 | +inspect_badge <- function(badge) { |
| 111 | + x <- badge %>% |
| 112 | + read_xml() %>% |
| 113 | + as_list() %>% |
| 114 | + pluck("svg") |
| 115 | + list( |
| 116 | + title = pluck(x, "title", 1), |
| 117 | + `aria-label` = pluck(x, attr_getter("aria-label")) |
| 118 | + ) |
| 119 | +} |
| 120 | +
|
| 121 | +inspect_badge("https://img.shields.io/badge/my__label-my__message-orange") |
| 122 | +``` |
| 123 | + |
| 124 | +This badge carries the same information in the `title` and in `aria-label`, which is "my_label: my_message". |
| 125 | +I would be interested to learn more about why the same information is included twice and if that is a good or bad thing for the screen reader experience. |
| 126 | + |
| 127 | +### shields.io badges are accessible today |
| 128 | + |
| 129 | +One of the reasons I inspected a shields.io badge is that this may provide a usable alternative for any service whose official badge is not (yet?) screen-reader-friendly. |
| 130 | + |
| 131 | +The custom badge above is completely static. |
| 132 | +But shields.io also supports custom dynamic badges, when the necessary information (label, message, color) is available from a suitable JSON endpoint. |
| 133 | +Finally, and most relevant to usethis, shields.io offers pre-configured dynamic badges for the most commonly "badged" services, including GitHub Actions and Codecov. |
| 134 | + |
| 135 | +Here is a shields.io badge for usethis's `R CMD check` workflow on GitHub Actions: |
| 136 | + |
| 137 | +<!-- badges: start --> |
| 138 | +[](https://github.com/r-lib/usethis/actions/workflows/R-CMD-check.yaml) |
| 139 | +<!-- badges: end --> |
| 140 | + |
| 141 | +Again, I am indicating fields I control with `my_alt_text` and `my_label`, so it's easier to get feedback on what usethis should do. |
| 142 | + |
| 143 | +Here is the `title` and `aria-label` of the badge above: |
| 144 | + |
| 145 | +```{r} |
| 146 | +inspect_badge("https://img.shields.io/github/workflow/status/r-lib/usethis/R-CMD-check?label=my_label-R-CMD-check") |
| 147 | +``` |
| 148 | + |
| 149 | +Now we will take inventory of the main badges inserted by usethis and where things stand re: accessibility. |
| 150 | + |
| 151 | +## `R CMD check` status |
| 152 | + |
| 153 | +We generally obtain the current `R CMD check` status from a GitHub Actions workflow. |
| 154 | + |
| 155 | +Here is the official badge provided by GitHub: |
| 156 | + |
| 157 | +<!-- badges: start --> |
| 158 | +[](https://github.com/r-lib/usethis/actions/workflows/R-CMD-check.yaml) |
| 159 | +<!-- badges: end --> |
| 160 | + |
| 161 | +Here is the `title` and `aria-label` of the badge above: |
| 162 | + |
| 163 | +```{r} |
| 164 | +inspect_badge("https://github.com/r-lib/usethis/actions/workflows/R-CMD-check.yaml/badge.svg") |
| 165 | +``` |
| 166 | + |
| 167 | +At the time of writing (late December 2021), the badge does not include such information. |
| 168 | + |
| 169 | +I have requested this in GitHub's Actions and Packages Feedback forum and the response from GitHub is encouraging. |
| 170 | +Hopefully the native badge will gain improved accessibility early in 2022. |
| 171 | + |
| 172 | +<https://github.com/github/feedback/discussions/8974> |
| 173 | + |
| 174 | +In the meantime, one could use a shields.io badge to report `R CMD check` status, as demonstrated in the previous section. |
| 175 | +A maintainer could do this as a one-off or, if the GitHub badge upgrade is slow in coming, we could implement it in `usethis::use_github_actions_badge()`. |
| 176 | + |
| 177 | +## Package lifecycle |
| 178 | + |
| 179 | +`usethis::use_lifecycle_badge()` declares the developmental stage of a package, using the framework from <https://lifecycle.r-lib.org/articles/stages.html>. |
| 180 | +This function already inserts a shields.io badge: |
| 181 | + |
| 182 | +<!-- badges: start --> |
| 183 | +[](https://lifecycle.r-lib.org/articles/stages.html#stable) |
| 184 | +<!-- badges: end --> |
| 185 | + |
| 186 | +Here is the `title` and `aria-label` of the badge above: |
| 187 | + |
| 188 | +```{r} |
| 189 | +inspect_badge("https://img.shields.io/badge/lifecycle-stable-brightgreen.svg") |
| 190 | +``` |
| 191 | + |
| 192 | +It's possible that usethis should be using a badge "owned" by the lifecycle package. |
| 193 | +I think we don't do so now, because declaring the lifecycle stage of a whole package does not necessarily imply the need to take a formal dependency on the lifecycle package, which is usually what causes badges to be copied into the `man/figures/` directory. |
| 194 | + |
| 195 | +Also, the badges shipped by the lifecycle package are _not_ currently accessible, which is another reason not to use them. |
| 196 | +But I have opened an issue about this (<https://github.com/r-lib/lifecycle/issues/117>). |
| 197 | +This should be a relatively easy fix, since these are static badges. |
| 198 | +Once done, package maintainers would need to update the SVGs that are stored in `man/figures/`. |
| 199 | + |
| 200 | +## CRAN status |
| 201 | + |
| 202 | +`usethis::use_cran_badge()` places a badge that indicates what package version is available on CRAN. |
| 203 | +This badge is served by METACRAN (<https://www.r-pkg.org>) and maintainer Gábor Csárdi has already incorporated `aria-label` into the badges (in [this commit](https://github.com/metacran/metacranweb/commit/8287a21e6dc2bc50a2d8a7b5a5a56904ae1d04ff)). |
| 204 | +All available badges are listed [here](https://www.r-pkg.org/services). |
| 205 | + |
| 206 | +Here's the badge placed by `usethis::use_cran_badge()`: |
| 207 | + |
| 208 | +<!-- badges: start --> |
| 209 | +[](https://CRAN.R-project.org/package=usethis) |
| 210 | +<!-- badges: end --> |
| 211 | + |
| 212 | +Here is the `title` and `aria-label` of the badge above: |
| 213 | + |
| 214 | +```{r} |
| 215 | +inspect_badge("https://www.r-pkg.org/badges/version/usethis") |
| 216 | +``` |
| 217 | + |
| 218 | +At the time of writing (late December 2021), `aria-label` is present, but `title` is not. |
| 219 | +I would be interested to know if this is a good situation for those using a screen reader. |
| 220 | + |
| 221 | +## Code coverage |
| 222 | + |
| 223 | +`usethis::use_coverage(type = c("codecov", "coveralls", ...)` calls the internal helper `usethis:::use_codecov_badge()` to insert a badge for either the Codecov or Coveralls service. |
| 224 | + |
| 225 | +Here are examples of those two badges (note that usethis does not use Coveralls, so I'm using a different package to demo): |
| 226 | + |
| 227 | +<!-- badges: start --> |
| 228 | +[](https://app.codecov.io/gh/r-lib/usethis?branch=main) |
| 229 | +[](https://coveralls.io/github/trinker/sentimentr?branch=master) |
| 230 | +<!-- badges: end --> |
| 231 | + |
| 232 | +Here are the `title` and `aria-label` of those badges:: |
| 233 | + |
| 234 | +```{r} |
| 235 | +inspect_badge("https://codecov.io/gh/r-lib/usethis/branch/main/graph/badge.svg") |
| 236 | +
|
| 237 | +inspect_badge("https://coveralls.io/repos/github/trinker/sentimentr/badge.svg?branch=master") |
| 238 | +``` |
| 239 | + |
| 240 | +At the time of writing (late December 2021), neither badge offers a `title` or `aria-label`. |
| 241 | + |
| 242 | +Here are badges from shields.io for Codecov and Coveralls: |
| 243 | + |
| 244 | +<!-- badges: start --> |
| 245 | +[](https://app.codecov.io/gh/r-lib/usethis?branch=main) |
| 246 | +[](https://img.shields.io/coveralls/github/trinker/sentimentr?logo=coveralls) |
| 247 | +<!-- badges: end --> |
| 248 | + |
| 249 | +Here are the `title` and `aria-label` of the shields.io badges:: |
| 250 | + |
| 251 | +```{r} |
| 252 | +inspect_badge("https://img.shields.io/codecov/c/github/r-lib/usethis?label=test%20coverage&logo=codecov") |
| 253 | +
|
| 254 | +inspect_badge("https://img.shields.io/coveralls/github/trinker/sentimentr?logo=coveralls") |
| 255 | +``` |
| 256 | + |
| 257 | +## Bioconductor |
| 258 | + |
| 259 | +If a package is on Bioconductor, `usethis::use_bioc_badge()` can be used to insert a badge for its Bioconductor build status. |
| 260 | + |
| 261 | +Here is such a badge: |
| 262 | + |
| 263 | +<!-- badges: start --> |
| 264 | +[](https://bioconductor.org/checkResults/release/bioc-LATEST/biocthis) |
| 265 | +<!-- badges: end --> |
| 266 | + |
| 267 | +Here is the `title` and `aria-label` of that badge: |
| 268 | + |
| 269 | +```{r} |
| 270 | +inspect_badge("http://www.bioconductor.org/shields/build/release/bioc/biocthis.svg") |
| 271 | +``` |
| 272 | + |
| 273 | +At the time of writing (late December 2021), the badge does not have a `title` or `aria-label`. |
| 274 | + |
| 275 | +It's not immediately clear how to resolve this, but I suspect that Bioconductor would be receptive to a request to create more accessible badges. |
0 commit comments