The same chart rendered with matplotlib defaults versus the package’s accessible theme, palette, and alt text. Toggle WCAG level to see contrast and font thresholds shift; the audit table reports per-criterion status for each version.
#| standalone: true
#| viewerHeight: 720
from shiny import App, ui, render
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
from itables import to_html_datatable
import a11yviz
_iris = load_iris(as_frame=True)
iris = _iris.frame
iris["species"] = _iris.target_names[iris["target"]]
markers = ["o", "s", "^"]
dt_options = dict(
connected=True,
buttons=["copy", "csv", "excel", "pdf"],
pageLength=10,
scrollX=True,
autoWidth=True,
classes="compact stripe hover",
)
app_ui = ui.page_sidebar(
ui.sidebar(
ui.input_radio_buttons("level", "WCAG level:",
choices=["AA", "AAA"], selected="AA",
inline=True),
width=240,
),
ui.navset_card_underline(
ui.nav_panel("Baseline",
ui.output_plot("plot_before", height="320px"),
ui.h3("Audit", class_="h6 mt-3"),
ui.output_ui("audit_before"),
),
ui.nav_panel("Improved",
ui.output_plot("plot_after", height="320px"),
ui.h3("Audit", class_="h6 mt-3"),
ui.output_ui("audit_after"),
),
),
)
def server(input, output, session):
def make_baseline():
fig, ax = plt.subplots(figsize=(7, 4))
for sp, sub in iris.groupby("species"):
ax.scatter(sub["sepal length (cm)"], sub["sepal width (cm)"],
label=sp)
ax.set_title("Iris (default matplotlib)")
ax.legend()
return fig
def make_improved():
with a11yviz.theme(level=input.level()):
fig, ax = plt.subplots(figsize=(7, 4))
cols = a11yviz.palette("dark2_8")
for i, (sp, sub) in enumerate(iris.groupby("species")):
ax.scatter(sub["sepal length (cm)"], sub["sepal width (cm)"],
label=sp, color=cols[i], marker=markers[i])
ax.set_xlabel("Sepal length (cm)")
ax.set_ylabel("Sepal width (cm)")
ax.set_title("Iris (theme + dark2_8 palette)")
ax.legend(title="Species")
return a11yviz.alt_text(fig,
"Iris sepal length vs width by species, AA accessible.")
@render.plot
def plot_before():
return make_baseline()
@render.plot
def plot_after():
return make_improved()
@render.ui
def audit_before():
df = pd.DataFrame(a11yviz.audit(make_baseline(), level=input.level()))
return ui.HTML(to_html_datatable(df, table_id="audit-baseline", **dt_options))
@render.ui
def audit_after():
df = pd.DataFrame(a11yviz.audit(make_improved(), level=input.level()))
return ui.HTML(to_html_datatable(df, table_id="audit-improved", **dt_options))
app = App(app_ui, server)