a11yShiny: Barrierefreiheit für R Shiny Apps

Das a11yShiny R‑Paket stellt Wrapper‑Funktionen für gängige R‑Shiny‑UI‑Elemente bereit, wodurch diese für sehbehinderte Nutzende besser zugänglich werden.
Autor:in

Jan Liebnitzky

Veröffentlichungsdatum

16. März 2026

Der Stand der Barrierefreiheit in Web-Apps

Im Jahr 2016 hat die Europäische Union die Richtlinie zur Barrierefreiheit von Websites und mobilen Anwendungen angenommen, welche sich an Organisationen des öffentlichen Sektors richtet. Ihr Ziel ist es, Websites und Apps für Menschen mit Behinderungen zugänglicher zu machen. Leider bleiben die meisten Websites in Deutschland nach wie vor voller Barrieren, siehe diese SZ Analyse (Achtung Paywall).

R Shiny und Barrierefreiheit

Als R Shiny Entwickler ist Barrierefreiheit nicht meine oberste Priorität. Ich entwickle einen Prototyp mit Fokus auf Funktionen mit dem größten Business Value. Aber oft genug, wenn man im öffentlichen Sektor arbeitet oder wenn ein Shiny-Dashboard viele Nutzende hat, wird Barrierefreiheit wichtiger. Das Erste, woran ich bei Barrierefreiheit dachte, waren farbenblindfreundliche Diagramme und hohe Kontraste. Aber als ich tiefer in die Web Content Accessibility Guidelines (WCAG 2.2) eintauchte, lernte ich über Bildschirmleser, responsive Layouts, Tab-Fokus, ARIA-Labels und vieles mehr. Ich habe auch gelernt, dass ‘A11y’ eine Kurzform für Accessibility ist (11 Buchstaben zwischen A und y) und dass Barrierefreiheit kein Funktionsparameter ist, den man ein- und ausschalten kann, sondern dass es viel Arbeit an R-, HTML- und CSS-Code erfordert… Aber, sollte es nicht einfacher sein?

Barrierefreiheit vereinfachen

Mit dem a11yShiny Paket, das nun auf CRAN verfügbar ist, wird Barrierefreiheit einfacher. Das a11yShiny-Paket ist ein erster Schritt, um Barrierefreiheit in R Shiny so einfach wie einen Funktionsaufruf zu machen. Es folgt dabei der BITV-2.0, der deutschen Umsetzung der Europäischen Richtlinie. Das README beschreibt detailliert, welche der BITV-2.0-Kriterien erfüllt sind, wenn die a11yShiny-Wrapper-Funktionen verwendet werden.

Installation

Die neueste Version kann direkt von CRAN installiert und anschließend die Bibliothek aufgerufen werden.

install.packages("a11yShiny")

library(a11yShiny)

a11y_ Shiny-Ersetzungen für reaktionsfähige Layouts, ARIA-Labels und Tab-Fokus

Anstatt die üblichen Shiny-Funktionen fluidPage, fluidRow, column und verschiedene Eingabefunktionen (selectInput, radioButtons, usw.) aufzurufen, verwendet man die a11y_-Wrapper-Funktionen.

a11y_fluidPage, a11y_fluidRow und a11y_column stellen responsive Layouts sicher. Dies ist für den Fall, dass Nutzende die Größe des Zooms und der Schrift vergrößern möchten, ohne das Layout zu beeinträchtigen.

Code
library(shiny)

a11yShiny::a11y_fluidPage(
    lang = "en-US",
    title = "Demo",
      tags$hr(),
    a11yShiny::a11y_fluidRow(
      a11yShiny::a11y_column(4, 
                             # this aria label you can see with your browser's developer tools
                             aria_label = "More accessible inputs",
                             offset = 8,
                             # Contrast mode button for all widgets
                             a11yShiny::a11y_highContrastButton())
    ),
  tags$hr(),
  a11yShiny::a11y_fluidRow(
      a11yShiny::a11y_column(
        4,
          shiny::radioButtons("radio_choice", "I am not accessible:",
            choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3),
            selected = 1
          )
        ),
      a11yShiny::a11y_column(
        4,
        offset = 4,
          a11yShiny::a11y_radioButtons("radio_choice_acc",
                                       "I am accessible (hover me):",
            choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3),
            selected = 1
          )
      )
  ),
    tags$hr(),
  a11yShiny::a11y_fluidRow(
    a11yShiny::a11y_column(
        6,
        div(
          id = "search-row",
          style = "display: flex; align-items: center; gap: 12px;",
          textInput("searchbox", label = NULL, placeholder = "Enter your question:", width = "100%"),
          actionButton(
            "do_search",
            label = NULL,
            icon = icon("search")
          )
        )
        ),
    #Composite Inputs for text and button
            a11yShiny::a11y_column(
6,
        div(
          a11yShiny::a11y_textButtonGroup(
            textId = "text-acc",
            buttonId = "btn-acc",
            label = "Enter your question (accessible):",
            value = "",
            placeholder = NULL,
            button_label = NULL,
            button_icon = icon("search"),
            button_aria_label = "Search",
            controls = NULL,
            layout = c("inline", "stack"),
            # aria label describing the grouped input widgets
            text_describedby = "Enter your question and send it with click on search button",
            text_heading_level = NULL
          )
        )
    )
      )
)



Alle a11y-Funktionen ermöglichen es Ihnen, Accessible Rich Internet Applications (ARIA) Labels hinzuzufügen. ARIA-Labels sind HTML-Attribute, die visuelle Elemente, die keinen sichtbaren Text aufweisen (z. B. Symbole, Schaltflächen ohne Beschriftung), für Benutzer mit Sehbehinderungen durch einen Bildschirmleser verständlich machen. Und wenn man keine Label-Beschreibung hinzufügt, erinnert eine Fehlermeldung daran, dies zu tun.

Oft genug hat man mehrere Eingabewidgets, die zusammengehören. Zum Beispiel ein Chat-TextInput und eine Senden-ActionButton. Mit a11yShiny kann man diese Eingaben thematisch gruppieren, indem zusätzliche ARIA-Labels angewendet werden.

HTML von Standard‑R‑Shiny‑ und a11yShiny‑Gruppen‑Input‑Widgets
Vergleich der HTML‑Struktur von Standard‑R‑Shiny‑ und a11yShiny‑Gruppen‑Input‑Widgets: Hervorhebung der Attribute aria‑label und aria‑describedby im a11yShiny‑Widget.

Eine weitere nützliche Funktion, die mit den a11y_-Funktionen kommt, ist der Tab-Fokus, der die Tastaturnavigation erleichtert, indem die aktuelle Auswahl mit einem fett schwarzen Rahmen hervorgehoben wird.

Zu guter Letzt gibt es einen Hochkontrast-Button, der einen hohen Kontrast auf alle Eingabe-Widgets anwendet.

Tabellen und Diagramme

Und was ist mit all den verschiedenen Diagrammen und Tabellen? a11y_renderDataTable macht Ihre DT-Tabelle barrierearm, indem Sprachattribute und Tab-Fokus standardmäßig gesetzt werden. Es ermöglicht auch bessere Kontraste und Tab-Fokus.

Für Diagramme (ggplot2) können Barrierefreiheitsmerkmale jedoch nicht zuverlässig mit Wrapper-Funktionen angewendet werden, da sie wenig flexibel sind und fehleranfällig. Stattdessen wird empfohlen, Best Practices mit Hochkontrast-Paletten, unterschiedlichen Markierungen für verschiedene Gruppen/Linien, Umrissen für Balken und klarer Beschriftung zu folgen.

What’s next?

Heißt das, dass jede Shiny-App, die a11yShiny verwendet, automatisch BITV-2.0-konform ist? Leider nein. Die Komponenten müssen korrekt verwendet werden: Beschriftungen müssen sinnvoll sein, Eingaben sollten sinnvoll gruppiert werden, die Tastaturnavigation muss einer logischen Reihenfolge folgen und Benutzer von assistiven Technologien benötigen genügend Kontext. Auch barrierefreie Komponenten allein garantieren nicht, dass die gesamte Website oder Anwendung barrierefrei ist. Die BITV-2.0-Konformität hängt von dem gesamten User Journey ab und sollte immer mit den richtigen Tools und idealerweise von ExpertInnen überprüft werden.

Das a11yShiny-Paket ist unter der Europäischen Union Public Licence lizenziert und auf openCode veröffentlicht. Es ist ein erster Schritt zur Barrierefreiheit aus der Box für R Shiny-Webanwendungen. Ich reue mich auf euer Feedback, Ideen und Pull Requests (Email, LinkedIn)!

Vielen Dank an Annabel Malle und dem Team von d‑fine GmbH für ihr besonderes Engagement, das dieses Projekt ermöglicht hat!