a11yShiny

Accessible wrappers for popular R Shiny UI components, enforcing ARIA
attributes and structural requirements in line with BITV
2.0 and WCAG 2.1 AA.
Installation
Install the package directly from GitLab:
install.packages("devtools")
devtools::install_git("https://gitlab.opencode.de/bmbf/datenlabor/barrierefrei-r.git")
Alternatively, clone the repository and use renv for reproducible
dependency management:
# Set your working directory to the package root
setwd("path/to/barrierefrei-r")
renv::init()
renv::install(".")
renv::snapshot()
Getting Started
Load the package and replace standard Shiny UI functions with their
accessible counterparts:
library(a11yShiny)
ui <- a11y_fluidPage(
lang = "de",
title = "Accessible Demo",
a11y_fluidRow(
a11y_column(6, a11y_actionButton("go", label = "Go!")),
a11y_column(6, a11y_selectInput("sel", label = "Choose", choices = 1:3))
)
)
For accessible DataTables and basic line/bar charts:
output$tbl <- a11y_renderDataTable({ head(iris, 10) }, lang = "de")
output$plt <- renderPlotly({
p <- a11y_ggplot2_line(data = iris, x = ~Species, y = ~Sepal.Length)
plotly::ggplotly(p)
})
A full demo app is included in the package:
shiny::runApp(system.file("examples", "demo", "app.R", package = "a11yShiny"))
For detailed documentation on each function, see the package
reference (?a11y_fluidPage,
?a11y_actionButton, etc.) and the examples in
inst/examples/.
Core Functions &
Accessibility Features
Layout & Structure
a11y_fluidPage,
a11y_fluidRow, a11y_column:
Wrappers for fluidPage(), fluidRow() and column().
- Sets and validates
<title> title,
<lang> language, and unique <main>
landmark.
- Ensures flexible layout with classes
.a11y-flow,
.a11y-row, .a11y-col and optional
header/nav/footer.
- Ensures that only a11y_columns are used within a11y_fluidRow() (not
the standard shiny
column() function).
- Validates sum of column widths and offset (12) and single column
width and offset (1–12) within a11y_fluidRow() (only for direct children
columns).
- Warnings if nested fluidRows are detected because it can potentially
create problems for keyboard users.
- Optional aria-labels can be added.
- BITV 2.0 Criteria: 9.2.4.2, 9.3.1.1, 9.2.4.1,
9.1.4.10, 9.1.3.4, 9.1.4.4
- Limitations: Heading levels/order
(
<h1>, <h2>, …) is not enforced.
No validation of meanigful (ARIA) text label (9.2.4.6). If
nested fluidRows are used seamless keyboard navigation is not guaranteed
(9.2.1.1)
Wrapper for actionButton().
- Sets and validates visible label or non-visible aria-label.
- Sets and validates aria-label for icon-only buttons for screenreader
users.
- Ensures clear contrast and focus styling with class
.a11y-btn.
- BITV 2.0 Criteria: 9.1.1.1a, 9.1.4.1, 9.1.4.11,
9.1.4.3, 9.2.1.1, 9.2.4.7, 9.2.5.3, 9.4.1.2
- Limitations: No validation of meanigful text label
(9.2.4.6).
a11y_selectInput(),
a11y_numericInput(), a11y_textInput, a11y_dateInput,
a11y_radioButtons
Wrapper for selectInput(), numericInput(), textInput(), dateInput()
and radioButtons().
- Sets and validates visible label.
- Optional headline marking (aria-level) can be added (1-6 for
<h1>-<h6>).
- Optional helping description (aria-describedby) can be added -
either reference an existing text (via ID) or add a non-visible text
which only Screenreaders can read (via class
.a11y-sr-only).
- Ensures clear contrast and focus styling with classes
.a11y-select, .a11y-numeric,
.a11y-text, .a11y-date and
.a11y-radio.
- BITV 2.0 Criteria: 9.3.3.2, 9.1.3.1a, 9.1.3.1h,
9.1.4.1, 9.1.3.2, 9.1.4.3, 9.1.4.11, 9.1.4.13, 9.2.1.1, 9.2.4.7,
9.2.5.3, 9.4.1.2
- Limitations: No validation of correct headline
order (9.1.3.1a), meanigful text label (9.2.4.6).
Semantic groupings of combined input elements is not covered.
a11y_textButtonGroup()
Composite wrapper combining a labeled text input and an action button
(e. g. for a search bar with button).
- Internally uses
a11y_textInput() and
a11y_actionButton() with their validations and CSS focus
styling.
- Adds
aria-controls to the button, by default pointing
to the text input ID (configurable), so assistive technologies
understand which control is affected by the button.
- Optional helping description (aria-describedby) can be added -
either reference an existing text (via ID) or add a non-visible text
which only Screenreaders can read (via class
.a11y-sr-only).
- Supports different layout modes (e.g. inline vs. stacked) via
.a11y-text-action-group and modifier classes.
- Each inner element has its own ID and can be addressed separately in
server logic and UI.
- BITV 2.0 Criteria: 9.1.1.1a, 9.1.3.1a,
9.1.3.1h, 9.1.3.2, 9.1.4.1, 9.1.4.3, 9.1.4.11, 9.2.1.1, 9.2.4.7,
9.2.5.3, 9.3.3.2, 9.4.1.2
- Limitations: No automatic validation of semantic
button text or the precise semantics of aria-controls
(9.2.4.6). Correct wiring between button and controlled element
remains responsibility of the user.
a11y_textInputsGroup()
Wrapper for a group of related textInput elements inside a
<fieldset> with <legend>
(e.g. address fields, date components, grouped inputs).
- Renders a
<fieldset> with a visible
<legend> as group label and marks it with
role="group" and aria-labelledby to the
legend.
- Each inner text input:
- Sets and validates visible label.
- Optional headline marking (aria-level) can be added (1-6 for
<h1>-<h6>).
- Optional helping description (aria-describedby) can be added -
either reference an existing text (via ID) or add a non-visible text
which only Screenreaders can read (via class
.a11y-sr-only).
- Ensures clear contrast and focus styling with class
.a11y-text.
- BITV 2.0 Criteria: 9.1.3.1a, 9.1.3.1h, 9.1.3.2,
9.1.4.1, 9.1.4.3, 9.2.1.1, 9.2.4.7, 9.2.5.3, 9.3.3.2, 9.4.1.2
- Limitations: No validation of the semantic
correctness of group labels or titles; it only enforces that an
accessible name is present. Logical grouping and content semantics must
be chosen by the user.
Tables
a11y_renderDataTable()
Wrapper for renderDataTable().
- Sets and validates language (supports German and English by
default).
- Optional language can be set (other than de/en) via dt_language file
internationalization.
- Warnings if non-accessible “Copy”, “Pdf” and “Print” export
functions are used.
- Warnings if non-accessible dataTable filter in numeric columns are
used.
- Sets KeyTab keyboard navigation as default.
- Optional clear contrast and focus styling with the class
.a11y-dt: To enable CSS style, wrap dataTableOutput() in a
div with a custom class, e.g.:
div(class = "a11y-dt", DT::dataTableOutput("mytable"))
- BITV 2.0 Criteria: 9.3.1.2, 9.1.3.1e, 9.2.4.7,
9.1.4.3, 9.1.3.4, 9.1.4.4
- Limitations: DataTable can adapt to different
screen sizes (smaller displays, mobile) only via horizontal scrolling
(9.1.4.10). Setting a language attribute (lang) or other ARIA
attributes around the DataTable is not reliably possible using a wrapper
because after rendering the JavaScript/widget binding replaces or
overwrites some attributes and may reset or remove classes/IDs/ARIA
attributes - it must be set individually by the user if it differs from
the language in the body (9.3.1.2).
Charts
A generic wrapper for ggplot2/ggplotly charts is not
recommended due to low flexibility and high risk of errors.
Several approaches for accessible diagram wrappers were evaluated, but
due to limitations, only the first option was implemented in this
package as a minimal example:
- Minimal ggplot2 Wrapper per Chart Type which can be
manipulated afterwards (see
a11y_ggplot2_bar/line).
Limitation: Must be created for every chart type; user
modifications after the wrapper can impair accessibility.
- Full Chart Wrapper with Internal ggplotly
Conversion which create a complete interactive chart.
Limitation: Most accessible, but least flexible – only
practical for fixed chart formats.
- Generic ggplotly Wrapper which patches
accessibility features into a ggplot2 object after creation.
Limitation: Messy and error-prone; post-hoc changes
often fail as ggplotly may not reliably map all properties.
- A wrapper for plotlyOutput which adds landmarks and
ARIA attributes (aria-describedby). Limitation:
plotlyOutput first renders a simple DIV placeholder in the UI, but after
rendering the JavaScript/widget binding replaces or overwrites some
attributes and may reset or remove classes/IDs/ARIA attributes; the
attribute is not always guaranteed to be visible in the finished DOM
tree. It must be set individually by the user.
Support for pattern fills, certain ARIA attributes, or
colorblind-friendly mapping is always limited by ggplot2/plotly’s
underlying capabilities and design. Instead users should follow
accessibility guidelines for charts. Best practice:
Users should
- use high-contrast palettes, distinct markers for different
groups/lines, outlines for bars, clear labeling,
- add additional labels, descriptions, and helper texts accessible for
keyboards and screenreaders.
a11y_ggplot2_line()
/ a11y_ggplot2_bar()
Minimal wrapper for ggplot2() for a multi-line and simple bar
chart.
- Pre-set accessible color palette (3:1 contrast to white) and warning
if user overwrites color palette.
- Pre-set markers (line) and black outline (bar).
- Optional setting of legend title (which prevents legend title
duplication due to marker and color legend).
- Wrapper can be flexibly adjusted with ggplot2 (see example in
inst/examples/demo/app.R).
- BITV 2.0 Criteria: 9.1.4.1, 9.1.4.11
- Limitations:
- ARIA/landmark features for charts must be set via JS or on container
level.
- Minimal wrapper for each chart type needed; modifying chart after
wrapper may impair accessibility.
- No wrapper can guarantee full accessibility once plots are heavily
adapted post-hoc.
High-contrast mode
Wrapper for a global high-contrast mode toggle button.
- Renders a clearly emphasized high-contrast button with its
own strong contrast style, independent of whether
high-contrast mode is active.
- On click it toggles the
high-contrast class on the
<body> element: All standard Shiny buttons, form
controls and dataTable controls switch to a high-contrast style
(inverted colors, clear borders and focus outline)
- Sets and updates
aria-pressed="true"/"false" on the
button so screen readers can understand the toggle state.
- Internally uses
a11y_actionButton() and inherits its
validation.
- Receives an additional class
.a11y-high-contrast-toggle
that the JS and CSS high-contrast styling are attached to.
- BITV 2.0 Criteria: 9.1.4.1, 9.1.4.3, 9.1.4.11,
9.2.4.7
- Limitations: High-contrast mode is applied globally
via a CSS class on
. Components with heavily customized styles may need additional
adjustments to ensure sufficient contrast and visible focus states in
high-contrast mode.
Utility Functions
- add_aria, add_aria_inside, find_html_tags,
find_direct_children_without_class, is_nonblank_string
- Utility functions to add ARIA attributes and search tags for custom
accessibility patterns.
Known Limitations
Limitations are mainly library-dependent. Some specific capabilities
(modal copy dialogs, responsive DT layouts, numeric DT filters, chart
tooltips) cannot yet be made fully accessible through wrappers alone.
Always test your outputs with accessibility auditing tools (see
below).
Getting Help
- For bug reports, please use the issue
tracker.
- For accessibility testing, the following tools are recommended:
Contributing
Contributions to a11yShiny are welcome. Please open
an issue or submit a merge request on the GitLab
repository.
License
a11yShiny is licensed under the European Union Public Licence (EUPL)
v1.2.
R Version Support
a11yShiny requires R >= 4.1.