Skip to content
Plotcnbeta
DocsChartsBlocksPlaygroundThemesExamples
Get started
Guide
  • Introduction
  • System Design
  • Engine Strategy
  • Installation
  • Project Setup
  • shadcn/ui Setup
  • Plotcn Registry
Fundamentals
  • Usage
  • Theming
  • Accessibility
  • Motion & Animation
  • Performance
  • TypeScript & I18n
Google Charts
  • Google Charts
  • Google GeoChart
Fundamentals / Accessibility•WCAG-Aware Engineering

Accessibility

Visualizations that communicate beyond the visual.

Plotcn treats accessibility as part of the chart contract: meaningful labels, structured summaries, keyboard interaction, non-color encoding, reduced motion, and accessible data fallbacks.

Semantic HTMLKeyboardScreen ReadersReduced MotionNon-color EncodingAccessible Data
Edit on GitHub

Multi-Modal Accessibility Model

One shared dataset rendered into multiple sensory and input paths. Every user consumes the same core insight.

Universal Access
Screen Reader UserExperience Pathway
Path 1 of 5 active

Consumes structured semantics via NVDA, VoiceOver, or JAWS. Relies on titles, descriptions, text summaries, and tables.

LAYER 01
Accessible Name

Heading connected via aria-labelledby provides concise context.

LAYER 02
Deterministic Summary

Natural-language text surfaces trend, peak, minimum, and overall delta.

LAYER 03
Data Table Fallback

Semantic <table> alternative enables cell-by-cell row navigation.

Accessibility model

A data visualization is not accessible merely because its root <svg> element has an aria-label attribute. Graphical paths, bezier curves, and canvas pixels are fundamentally invisible to screen readers, while hover-triggered overlays exclude keyboard and mobile touch users.

Plotcn treats accessibility as an architectural contract composed of layered representations:

  1. Accessible Name: A concise, visible title communicating what the chart represents.
  2. Accessible Description: Contextual metadata specifying units of measure, time horizons, and coordinate dimensions.
  3. Structured Text Summary: Deterministic, natural-language sentences surfacing the key insight, trend direction, period peak, and minimum.
  4. Accessible Interaction: Full keyboard traversal, visible focus rings, roving tab indices, and touch inspection gestures.
  5. Structured Data Alternative: A semantic data table sharing the identical underlying data source.
The visualization is not the only way to access the data. A chart may be the most efficient visual representation for sighted users, but essential business insights must remain accessible through text, semantics, keyboard interaction, or a structured tabular alternative.
++

Give every chart a meaningful name

Every chart must have a concise, human-readable name that answers "what am I looking at?" in less than five seconds.

Meaningful vs. generic names

  • Good: Monthly revenue, January through June 2026
  • Good: Server latency distribution by region (p99)
  • Bad: Chart
  • Bad: Graph 1
  • Bad: Visualization

Native heading relationships

Prefer visible headings connected via aria-labelledby over hidden label attributes. Visible labels ground the visualization for all users, including sighted users navigating with screen magnifiers:

TSX
<section  aria-labelledby="revenue-chart-title"  aria-describedby="revenue-chart-desc"  className="rounded-xl border border-border p-6">  <header className="space-y-1 mb-4">    <h3 id="revenue-chart-title" className="text-base font-semibold text-foreground">      Monthly Revenue (H1 2026)    </h3>    <p id="revenue-chart-desc" className="text-xs text-muted-foreground">      Total gross revenue across all sales channels, measured in thousands of USD.    </p>  </header>  {/* Visualization Container */}  <div role="img" aria-labelledby="revenue-chart-title">    <PlotLineChart data={monthlyRevenue} />  </div></section>
Use stable, server-safe identifiers. Avoid generating random IDs with Math.random() or timestamps during render. Use React 19's useId() or explicit, deterministic string keys to prevent React hydration mismatches.
++

Add a useful description

While the accessible name provides a title, the accessible description provides operational context. A good description answers:

  • What primary metrics are tracked
  • What units of measurement apply (e.g. US dollars, milliseconds, percentage points)
  • What time boundary or categorical domain is represented
  • What interactive mechanisms are available (e.g. "Use arrow keys to step between monthly data points")

Avoid redundant geometry narrations

Do not narrate the physical graphics. Screen-reader users need domain information, not a commentary on SVG shapes:

  • Helpful: "Line chart showing gross monthly revenue from January through June 2026, measured in thousands of US dollars. Values range from a low of $42K in January to a peak of $67.4K in May."
  • Unhelpful: "This is a chart. The chart contains a curved green path with six circular nodes and horizontal dashed grid lines."
++

Provide a text summary

For many analytical charts, a concise text summary is significantly more useful to screen-reader users than forcing them to navigate through dozens of individual data points.

Text Summary Paradigm

Screen-reader users benefit far more from concise key insights than traversing 50 isolated SVG paths.

Monthly Revenue (H1 2026)Peak: May ($67.4K)
$42K
Jan
$47K
Feb
$51K
Mar
$58K
Apr
$67.4K
May
$61K
Jun
#summary:Revenue expanded by 45.2% from $42,000 in January to $61,000 in June. The period reached a peak of $67,400 in May.

Communicate the insight, not every pixel

A high-quality summary surfaces the core analytical takeaway:

  • Direction & Trend: Upward growth, downward contraction, or stable baseline
  • Extremes: Maximum peak and minimum floor with corresponding dates/categories
  • Total Delta: Net change across the represented period
  • Selected State: Currently highlighted or pinned record
TSX
// Deterministic summary calculationfunction generateRevenueSummary(data: RevenueDatum[]): string {  if (data.length === 0) return "No revenue data available."  const min = data.reduce((a, b) => (a.revenue < b.revenue ? a : b))  const max = data.reduce((a, b) => (a.revenue > b.revenue ? a : b))  const start = data[0].revenue  const end = data[data.length - 1].revenue  const delta = Math.round(((end - start) / start) * 100)  return `Revenue grew by ${delta}% overall from January to June. Period peak was $${max.revenue.toLocaleString()} in ${max.month}, with a low of $${min.revenue.toLocaleString()} in ${min.month}.`}
Avoid subjective AI conclusions in generic charts. Only generate deterministic, mathematically verifiable summaries. State "Revenue increased by 45%" rather than subjective claims like "Revenue showed outstanding performance".
++

Provide a structured data alternative

Data-dense visualizations—such as multi-series line charts, financial candlestick charts, and heatmaps—should offer an accessible tabular alternative.

Accessible Data Alternative

Provide a semantic data table disclosure so power users and screen readers can navigate exact numerical figures.

Single source of truth rule

The data table must consume the identical JavaScript data array passed to the visualization component. Never construct independent, static copies of data for assistive technology:

TSX
export function AccessibleChartCard({ data }: { data: MonthlyData[] }) {  const [showTable, setShowTable] = useState(false)  return (    <section aria-labelledby="sales-metric-title">      <div className="flex items-center justify-between mb-4">        <h3 id="sales-metric-title" className="text-base font-semibold">          Sales Performance        </h3>        <Button          variant="outline"          size="sm"          onClick={() => setShowTable(!showTable)}          aria-expanded={showTable}          aria-controls="sales-data-table"        >          {showTable ? "Hide data table" : "View data table"}        </Button>      </div>      {/* Visual Chart */}      <PlotLineChart data={data} />      {/* Semantic Table Alternative */}      {showTable && (        <div id="sales-data-table" className="mt-4">          <table className="w-full text-left font-mono text-xs">            <caption className="sr-only">Monthly sales revenue data</caption>            <thead>              <tr>                <th scope="col">Month</th>                <th scope="col">Revenue</th>                <th scope="col">Variance</th>              </tr>            </thead>            <tbody>              {data.map((row) => (                <tr key={row.month}>                  <th scope="row">{row.month}</th>                  <td>${row.revenue.toLocaleString()}</td>                  <td>{row.variance}%</td>                </tr>              ))}            </tbody>          </table>        </div>      )}    </section>  )}

Visible disclosure vs. visually-hidden tables

  • Visible Disclosure ([View data table]): Strongly recommended for analytical and enterprise dashboards. Benefits keyboard users, cognitive accessibility users, and executives who wish to copy numbers into spreadsheets.
  • Visually-Hidden Table (sr-only): Appropriate when page layout constraints strictly forbid expanding controls, ensuring screen readers still have structured table navigation keys (T, Ctrl+Alt+Arrows).
++

Use meaningful semantic structure

Prefer semantic HTML wrapper elements (<section>, <header>, <h3>, <figure>, <figcaption>) over generic unsemantic <div> containers:

TSX
<figure  role="region"  aria-labelledby="figure-caption"  className="rounded-xl border border-border p-5">  <PlotLineChart data={chartData} />  <figcaption id="figure-caption" className="text-xs text-muted-foreground mt-3">    Figure 1.1: Gross quarterly sales across North American regions from Q1 2024 to Q2 2026.  </figcaption></figure>

Marking decorative SVG elements

Non-informational graphical artifacts must be explicitly hidden from screen readers so they do not interrupt reading flow:

TSX
{/* Decorative Cartesian Gridlines */}<line  x1="0"  y1={y}  x2={width}  y2={y}  stroke="var(--border)"  aria-hidden="true"/>{/* Decorative Drop Shadow / Glow Filters */}<filter id="decorativeGlow" aria-hidden="true">  <feGaussianBlur stdDeviation="3" /></filter>
++

Keyboard interaction

Any interactive visualization capability offered to mouse cursor users must also be fully operable via keyboard.

Keyboard Navigation Flow (Roving Tabindex)

Interactive Model
Tab
Enter Chart

Enters the chart component as a single tab stop.

←→
Step Points

Steps through data points sequentially.

Enter / Space
Lock / Select

Pins tooltip details or triggers drill-down.

Escape
Dismiss

Closes popovers and returns focus to canvas.

Click canvas or press Tab to focus • Use Arrow KeysPoint 3 of 6
#live-announcement:Mar selected. Value: $51,000 (3 of 6).

The roving tabindex pattern

When a chart renders 30 or 60 data points, do not make every point a separate tab stop (tabIndex={0}). That forces keyboard users to press Tab 60 times just to bypass the chart.

Instead, implement a roving tabindex:

  1. The outer chart container receives tabIndex={0} as a single tab stop.
  2. Once focused, arrow keys (← and →) step between data points internally.
  3. Enter or Space selects or pins the active point.
  4. Escape dismisses any open tooltip card or returns focus to the parent widget.
TSX
function handleChartKeyDown(e: React.KeyboardEvent) {  switch (e.key) {    case "ArrowRight":      e.preventDefault()      setActiveIndex((prev) => Math.min(prev + 1, data.length - 1))      break    case "ArrowLeft":      e.preventDefault()      setActiveIndex((prev) => Math.max(prev - 1, 0))      break    case "Enter":    case " ":      e.preventDefault()      setIsPinned((prev) => !prev)      break    case "Escape":      e.preventDefault()      setIsPinned(false)      break  }}
++

Focus management

Interactive chart controls must feature prominent, unambiguous visual focus indicators.

Focus ring standards

Plotcn charts inherit your application's focus styling:

CSS
/* Inherited accessible focus ring */:focus-visible {  outline: 2px solid var(--ring);  outline-offset: 2px;}

Contextual announcements on focus

When keyboard focus moves between data points, provide rich auditory feedback via a live region or accessible text string:

  • Helpful: "March 2026, revenue $51,000, 3 of 6."
  • Unhelpful: "Point."

Focus persistence across redraws

Theme changes, responsive window resizes, and live-data updates must not arbitrarily reset keyboard focus back to the <body> element. Preserve logical focus on the active data index across component renders.

++

Tooltips and hover

A tooltip must never be the exclusive mechanism for accessing metric values.

The hover-only anti-pattern

  • Flawed Workflow: Sighted mouse hover reveals exact values; keyboard users and mobile touch users see nothing.
  • Accessible Workflow: Values are revealed upon hover, upon keyboard :focus-visible, upon mobile touch tap, and are duplicated inside the accessible data summary or table.

Non-interfering tooltip timing

  • Tooltips should not dismiss prematurely when users move the cursor toward them.
  • If a tooltip contains interactive actions (e.g. "Drill into May sales"), implement it as an accessible Popover or Dialog component with keyboard focus trap, rather than a transient hover tooltip.
++

Do not rely on color alone

Up to 8% of male users and 0.5% of female users experience color vision deficiencies. When distinguishing multiple series, color hue must always be accompanied by a secondary, non-color encoding.

Non-Color Visual Encoding

Never convey information by color hue alone. Pair colors with stroke patterns, markers, and direct labels.

Actual Revenue● Circle
Stroke: solid

Solid primary line with circular geometric vertex marks.

Target Budget■ Square
Stroke: 6 4 dashed

Dashed comparison line with square geometric vertex marks.

Risk Forecast▲ Triangle
Stroke: 2 3 dotted

Dotted forecast line with triangular warning vertex marks.

Secondary encoding strategies

  1. Distinct Stroke Dash Patterns:
    • Primary: Solid line (stroke="solid")
    • Secondary: Dashed line (strokeDasharray="6 4")
    • Tertiary: Dotted line (strokeDasharray="2 3")
  2. Geometric Vertex Markers:
    • Series A: Solid circle (●)
    • Series B: Solid square (■)
    • Series C: Upward triangle (▲)
  3. Signed Numbers & Directional Glyphs:
    • Positive Gain: +$14,200 accompanied by an upward green indicator (▲)
    • Negative Loss: -$8,500 accompanied by a downward red indicator (▼)
  4. Direct Line Labels: Place text labels at the terminal end of lines rather than relying exclusively on a detached legend box.
++

Motion and reduced motion

Vestibular disorders can make animations—such as sweeping line entries, pulsing glows, and bouncing physics simulations—disorienting or nauseating.

Plotcn strictly honors the OS-level prefers-reduced-motion media query:

CSS
@media (prefers-reduced-motion: reduce) {  /* Immediately resolve transitions and line sweeps */  *, *::before, *::after {    animation-duration: 0.01ms !important;    animation-iteration-count: 1 !important;    transition-duration: 0.01ms !important;  }}

Engine-specific reduced motion implementations

  • Recharts: Pass isAnimationActive={false} when prefers-reduced-motion is detected.
  • D3.js: Render directly into the final coordinate scales. Never architect a visualization such that the animated transition is the only code path computing geometric attributes.
  • Google Charts: Map animation.duration: 0 inside the chart configuration options object.
++

Dynamic data updates

Real-time streaming dashboards, polling intervals, and user filters trigger frequent visual updates.

Discrete live announcements

Do not wrap the entire visualization inside aria-live="assertive". Announcing every coordinate redraw spams screen readers and overwhelms users.

Instead, announce only discrete, meaningful state changes:

TSX
<div aria-live="polite" className="sr-only">  {filterStatusText}</div>
  • Good: "Filtered by North America: 14 regional nodes displayed."
  • Bad: "Chart redraw complete." (repeated every 2 seconds on polling).
++

Loading, empty, and error states

All non-ready visualization states must provide semantic clarity to assistive technologies.

Truthful loading states

  • Mark the chart container with aria-busy="true" and aria-label="Loading revenue data...".
  • Never render synthetic placeholder numbers during loading. Screen-reader users may interpret placeholder digits as factual business data.

Contextual empty states

Explain what criteria produced the empty state:

  • Good: "No transactions recorded between March 1 and March 15, 2026."
  • Bad: "No data."

Actionable error states

Provide the failure reason and an accessible button to retry the fetch:

TSX
<div role="alert" className="rounded-xl border border-destructive/40 bg-destructive/10 p-6 text-center">  <p className="text-sm font-semibold text-destructive">Failed to load revenue metrics</p>  <p className="text-xs text-muted-foreground mt-1">Unable to connect to the analytics cluster.</p>  <Button variant="outline" size="sm" onClick={retryFetch} className="mt-3">    Retry connection  </Button></div>
++

Recharts accessibility

Recharts generates standard SVG DOM nodes, but does not provide an accessible experience out of the box.

Plotcn wrapping pattern for Recharts

When using Recharts:

  1. Wrap the <ResponsiveContainer> in a semantic <section> with aria-labelledby and aria-describedby.
  2. Connect a deterministic <ChartSummary /> component adjacent to the chart.
  3. Provide an <AccessibleDataDisclosure /> table.
  4. Disable entrance animations via isAnimationActive={!prefersReducedMotion}.
++

D3.js accessibility

D3 provides total control over DOM and SVG generation, which gives developers greater responsibility.

The D3 architectural rule

Use D3 for mathematics and layout scales; use React for semantic DOM and SVG rendering.

By allowing React to render the SVG elements, you maintain standard React JSX props (aria-label, tabIndex, onKeyDown, :focus-visible) rather than mutating imperative DOM trees with D3 selections.

For dense charts with over 1,000 nodes, do not render 1,000 focusable elements. Use binning, aggregated cluster summaries, and an accessible data table alternative.

++

Google Charts accessibility

Google Charts renders inside an internal sandboxed DOM or <iframe>, placing its internal structure outside of direct React prop control.

The external accessible shell strategy

Because Google Charts manages its own internal SVG tags, Plotcn provides an external accessible shell:

  1. Render a clean semantic wrapper with aria-labelledby, aria-describedby, and the text summary outside the Google container <div>.
  2. Intercept Google Charts select events and mirror the selected datum into standard React state.
  3. Provide a structured data table disclosure so assistive technologies can read data points directly from your original JavaScript array without touching Google's internal SVG.
  4. On Google GeoChart, never rely on choropleth color intensity alone; provide region name labels and numerical tables.
++

Responsive layout and zoom

Accessible visualizations must accommodate users who view dashboards on mobile devices or utilize 400% browser zoom.

  • Avoid Fixed Pixel Widths: Always use fluid responsive containers (100% width).
  • Graceful Decluttering: On small viewports or at high zoom levels, automatically reduce tick counts (e.g. display every second month rather than all twelve) to prevent label overlapping.
  • No Two-Dimensional Scrolling: Ensure charts scale vertically or provide internal card horizontal scrolling without forcing the global browser window to scroll horizontally.
++

Test accessibility

Accessibility verification requires both automated scanning and manual assistive-technology testing.

Plotcn Visualization Accessibility Verification Matrix

Engineering QA Protocol
Plotcn accessibility testing matrix across naming, keyboard, motion, and states
CategoryAccessibility CheckAudit MethodEngineering Tier
Naming & Labels
Accessible Name & Description
Inspect root SVG/section for aria-labelledby and aria-describedby pointing to valid, stable DOM IDs.
Screen Reader & DOM InspectionMandatory for All Charts
Insights
Natural-Language Data Summary
Verify concise sentence surfacing overall direction, peak month/value, minimum, and delta percentage.
Text / DOM AuditMandatory for Key Charts
Data Alternative
Semantic Table Alternative
Confirm table contains matching numeric values from the single source array with scoped <th> cells.
Screen Reader Table Keys (T / Ctrl+Alt+Arrows)Recommended for Multi-series
Keyboard
Roving Tabindex Navigation
Ensure chart enters as 1 tab stop. Left/Right arrows move through data points with visible 2px outline.
Keyboard Only (Tab, ← / →, Enter, Esc)Mandatory for Interactive
Visual Encoding
Non-Color Data Distinction
Switch viewport to grayscale. Confirm series remain distinguishable via dash patterns, markers, and direct labels.
Grayscale / Monochrome FilterMandatory for Multi-series
Motion
prefers-reduced-motion Support
Emulate reduced motion. Chart must render directly into final coordinates without sweeping or bouncing.
DevTools Emulation (Reduced Motion)Mandatory if Animated
States
Truthful Loading / Empty / Error
Verify aria-busy during loading (no fake data), helpful error descriptions, and accessible retry buttons.
Network Throttling & Mock FailureMandatory for Async Charts
Responsive Zoom
400% Browser Zoom Inspection
Confirm content does not produce two-dimensional page scrolling; labels declutter gracefully.
Browser Zoom at 400% (320px layout)Mandatory for Responsive

Practical QA testing protocol

  1. Keyboard-Only Audit: Unplug or disable the mouse. Can you reach the chart, step between points with arrow keys, inspect metrics, and dismiss popovers with Escape?
  2. Screen Reader Verification: Test using NVDA (Windows), VoiceOver (macOS/iOS), or TalkBack (Android). Does the synthesizer announce the title, description, and natural-language summary clearly?
  3. Monochrome Audit: View the chart in grayscale or with Windows High Contrast Mode. Are all series still distinct?
  4. Reduced Motion Emulation: Open DevTools, emulate prefers-reduced-motion: reduce, and confirm animations immediately settle into final positions.
  5. Edge Case Datasets: Test with empty arrays, single-item arrays, negative values, and zero baselines.
++

Common accessibility mistakes

Common Visualization Accessibility Mistakes

Diagnostic Checklist
aria-label Alone is Incomplete

PROBLEM:Setting aria-label='Revenue Chart' and assuming the visualization is fully accessible.

WHY:A screen-reader user still has zero awareness of the trend, the numbers, the peak month, or the underlying dataset.

FIX:Pair the title with an accessible description, a structured data summary, and a data table alternative.
50 SVG Nodes as 50 Tab Stops

PROBLEM:Making every path, circle, or bar element focusable with tabIndex={0}.

WHY:Tabbing through 50 data points becomes exhausting and pollutes the global document tab sequence.

FIX:Enter the chart container as 1 tab stop, then use a roving tabindex (Arrow keys) to step through internal marks.
Hover-Only Tooltip Metric Data

PROBLEM:Exact coordinate values and percentages only appear upon mouse cursor hover.

WHY:Keyboard users, voice-control users, and mobile touch users can never reach or inspect that data.

FIX:Expose identical details on keyboard focus, provide an accessible data table, or summarize directly in text.
Color as Sole Data Distinction

PROBLEM:Distinguishing three lines solely by green, blue, and orange hues.

WHY:Color-blind users (~8% of males) and users in high-glare environments cannot tell which line is which.

FIX:Add geometric vertex markers (circle, square, triangle), distinct dash patterns, or direct line labels.
Noisy Live Regions on Every Redraw

PROBLEM:Placing an entire interactive chart inside aria-live='polite' or 'assertive'.

WHY:Screen readers spam the user with continuous coordinate announcements on every cursor jitter or frame refresh.

FIX:Only announce meaningful, discrete state changes (e.g. 'Filter applied: 12 results' or 'May selected: $67,400').
Focus Dropped on Chart Redraw

PROBLEM:Re-rendering or swapping themes completely unmounts the chart and drops keyboard focus back to <body>.

WHY:Disorients keyboard users and forces them to re-navigate the entire page to find their place.

FIX:Preserve logical focus state across renders, and avoid recreating outer DOM container nodes on resize.
++

Recommended accessibility workflow

Follow this repeatable 12-step engineering protocol whenever you install or build a Plotcn visualization:

12-Step Accessible Visualization Delivery Sequence

Engineering Protocol
01
Define Core Meaning

Identify the primary insight or trend the chart is intended to communicate.

02
Add Visible Title

Attach an explicit heading (<h2> or <h3>) with a stable, server-safe ID.

03
Attach Description

Write a concise sentence specifying units, timeline, and scope via aria-describedby.

04
Craft Data Summary

Surface directional trend, peak, floor, and total change in natural language.

05
Add Table Alternative

Provide an accessible semantic table disclosure sharing the same underlying data array.

06
Establish Keyboard Path

Configure 1 Tab stop into chart; roving Arrow keys (←/→) to inspect data points.

07
Enforce Visible Focus

Style :focus-visible with a high-contrast 2px outline and 2px offset.

08
Abolish Hover-Only

Ensure all metric tooltips can be triggered via keyboard focus and mobile tap.

09
Check Non-Color Cues

Audit in grayscale: add distinct stroke dashes, geometric markers, and direct labels.

10
Respect Reduced Motion

Bypass line sweeps and spring physics under prefers-reduced-motion: reduce.

11
Validate Dynamic States

Audit truthful loading skeletons, informative empty states, and actionable retry errors.

12
Perform Screen-Reader QA

Verify live readout using VoiceOver (macOS/iOS) or NVDA (Windows) before shipping.

The Plotcn Accessibility Contract
Static Chart

Accessible Name + Description + Text Summary

Data-Rich Chart

+ Semantic Data Table Alternative

Interactive Chart

+ Roving Tabindex + Visible Focus + Touch

Animated Chart

+ Instant Reduced Motion Fallback

++

Next steps

Now that your visualizations communicate accessibly across all sensory and input modes, explore specific engine guides and pre-built components:

Installation

Prepare a React or Next.js project and install required dependencies.

shadcn/ui Setup

Configure components.json and integrate Plotcn with the shadcn Registry.

Registry Workflow

Learn how visualization components enter your local source tree.

PreviousThemingNextMotion & Animation

On this page

  • Accessibility model
  • Give every chart a meaningful name
  • Add a useful description
  • Provide a text summary
  • Provide a structured data alternative
  • Use meaningful semantic structure
  • Keyboard interaction
  • Focus management
  • Tooltips and hover
  • Do not rely on color alone
  • Motion and reduced motion
  • Dynamic data updates
  • Loading, empty, and error states
  • Recharts accessibility
  • D3.js accessibility
  • Google Charts accessibility
  • Responsive layout and zoom
  • Test accessibility
  • Common accessibility mistakes
  • Recommended accessibility workflow
  • Next steps