Excessibility
Accessibility snapshot testing for Phoenix LiveView - capture HTML during tests, run Pa11y for WCAG compliance, debug with AI-friendly timeline analysis
Install / Use
/learn @lessthanseventy/ExcessibilityQuality Score
Category
Development & EngineeringSupported Platforms
README
Excessibility
Accessibility Snapshot Testing for Elixir + Phoenix
Excessibility helps you test your Phoenix apps for accessibility (WCAG compliance) by taking HTML snapshots during tests and running them through Pa11y.
Why Excessibility?
- Keep accessibility in your existing test feedback loop. Snapshots are captured inside ExUnit, Wallaby, and LiveView tests, so regressions surface together with your functional failures.
- Ship safer refactors. Explicit baseline locking and comparison lets reviewers see exactly what changed and approve intentionally.
- Debug CI-only failures quickly. Pa11y output points to the failing snapshot, and the saved artifacts make it easy to reproduce locally.
How It Works
- During tests, call
html_snapshot(conn)to capture HTML from your Phoenix responses, LiveViews, or Wallaby sessions - After tests, run
mix excessibilityto check all snapshots with Pa11y for WCAG violations - Lock baselines with
mix excessibility.baselinewhen snapshots represent a known-good state - Compare changes with
mix excessibility.compareto review what changed and approve/reject - In CI, Pa11y reports accessibility violations alongside your test failures
Features
- Snapshot HTML from
Plug.Conn,Wallaby.Session,Phoenix.LiveViewTest.View, andPhoenix.LiveViewTest.Element - Explicit baseline locking and comparison workflow
- Interactive good/bad approval when comparing snapshots
- Optional PNG screenshots via ChromicPDF
- Mockable system/browser calls for CI
- Pa11y configuration with sensible LiveView defaults
LLM Development Features
Excessibility includes powerful features for debugging Phoenix apps with AI assistance (Claude, Cursor, etc.).
Telemetry-Based Auto-Capture (Zero Code Changes!)
Debug any existing LiveView test with automatic snapshot capture - no test changes required:
# Your test - completely vanilla, zero Excessibility code
test "user interaction flow", %{conn: conn} do
{:ok, view, _html} = live(conn, "/dashboard")
view |> element("#button") |> render_click()
view |> element("#form") |> render_submit(%{name: "Alice"})
assert render(view) =~ "Welcome Alice"
end
Debug it:
mix excessibility.debug test/my_test.exs
🚀 Rich Timeline Capture
mix excessibility.debugautomatically enables telemetry capture, dramatically increasing event visibility:
- Without telemetry: ~4 events (mount, handle_params only)
- With telemetry: 10-20x more events including all render cycles
Example from real test:
- Basic test: 4 events → 11 events (added 7 render events)
- Complex test: Limited snapshots → 236 timeline events with rich analyzer insights
Render events enable powerful pattern detection:
- 🔴 Memory leak detected (2.3x growth over render cycles)
- ⚠️ 7 consecutive renders without user interaction
- 🔴 Performance bottleneck (15ms render blocking)
- ⚠️ Rapid state changes (potential infinite loop)
This happens automatically - no test changes needed!
Automatically captures:
- LiveView mount events
- All handle_event calls (clicks, submits, etc.)
- All render cycles (form updates, state changes triggered by
render_change,render_click,render_submit) - Real LiveView assigns at each step
- Complete state timeline with memory tracking and performance metrics
Example captured snapshot:
<!--
Excessibility Telemetry Snapshot
Test: test user interaction flow
Sequence: 2
Event: handle_event:submit_form
Timestamp: 2026-01-25T10:30:12.345Z
View Module: MyAppWeb.DashboardLive
Assigns: %{
current_user: %User{name: "Alice"},
form_data: %{name: "Alice"},
submitted: true
}
-->
Debug Command
The debug command outputs a comprehensive markdown report with:
- Test results and error output
- All captured snapshots with inline HTML
- Event timeline showing state changes
- Real LiveView assigns at each snapshot
- Metadata (timestamps, event sequence, view modules)
The report is both human-readable and AI-parseable, perfect for pasting into Claude.
Available formats (all args pass through to mix test):
mix excessibility.debug test/my_test.exs # Markdown report (default)
mix excessibility.debug test/my_test.exs:42 # Run specific test
mix excessibility.debug --only live_view # Run tests with tag
mix excessibility.debug test/my_test.exs --format=json # Structured JSON
mix excessibility.debug test/my_test.exs --format=package # Directory with MANIFEST
mix excessibility.latest # Re-display last report
🔍 Telemetry Timeline Analysis
Automatically captures LiveView state throughout test execution and generates scannable timeline reports:
- Smart Filtering - Removes Ecto metadata, Phoenix internals, and other noise
- Diff Detection - Shows what changed between events
- Multiple Formats - JSON for automation, Markdown for humans/AI
- CLI Control - Override filtering with flags for deep debugging
mix excessibility.debug test/my_test.exs
See CLAUDE.md for detailed usage.
Telemetry Implementation
Excessibility hooks into Phoenix LiveView's built-in telemetry events:
[:phoenix, :live_view, :mount, :stop][:phoenix, :live_view, :handle_event, :stop][:phoenix, :live_view, :handle_params, :stop][:phoenix, :live_view, :render, :stop]- Captures all render cycles (form updates, state changes)
When you run mix excessibility.debug, it:
- Enables telemetry capture via environment variable
- Attaches telemetry handlers to LiveView events
- Runs your test
- Captures snapshots with real assigns from the LiveView process
- Generates a complete debug report
No test changes needed - it works with vanilla Phoenix LiveView tests!
Manual Capture Mode
For fine-grained control, you can also manually capture snapshots:
use Excessibility
@tag capture_snapshots: true
test "manual capture", %{conn: conn} do
{:ok, view, _} = live(conn, "/")
html_snapshot(view) # Manual snapshot with auto-tracked metadata
view |> element("#btn") |> render_click()
html_snapshot(view) # Another snapshot
end
Claude Documentation
Create .claude_docs/excessibility.md to teach Claude how to use these debugging features:
mix excessibility.setup_claude_docs
MCP Server & Claude Code Skills
Excessibility includes an MCP (Model Context Protocol) server and Claude Code skills plugin for AI-assisted development.
MCP Server
The MCP server provides tools for AI assistants to run accessibility checks and debug LiveView state.
Available tools:
| Tool | Speed | Description |
|------|-------|-------------|
| check_route | Fast | Run Pa11y on a live route (requires app running) |
| explain_issue | Fast | Get explanation and fix suggestions for a WCAG violation code |
| suggest_fixes | Fast | Get Phoenix-specific code fixes for accessibility issues |
| generate_test | Fast | Generate test code with html_snapshot() calls for a route |
| list_analyzers | Fast | List available timeline analyzers |
| get_timeline | Fast | Read captured timeline showing LiveView state evolution |
| get_snapshots | Fast | List or read HTML snapshots captured during tests |
| analyze_timeline | Fast | Run analyzers on captured timeline data |
| list_violations | Fast | List recent Pa11y violations from snapshots |
| e11y_check | Slow | Run tests and/or Pa11y accessibility checks on HTML snapshots |
| e11y_debug | Slow | Run tests with telemetry capture - returns timeline for analysis |
Recommended workflow:
check_route- Quick accessibility check on running appexplain_issue- Understand what violations meangenerate_test- Create test withhtml_snapshot()callse11y_debug- Run test to capture timelineanalyze_timeline- Find performance issues
Automatic Setup:
MCP server support is configured automatically when you run the installer:
mix excessibility.install
mix deps.get
This creates .claude/mcp_servers.json with the excessibility MCP server configuration.
Use --no-mcp to skip MCP setup if you don't need AI assistant integration.
Manual Setup:
Configure Claude Code's mcp_servers.json:
{
"excessibility": {
"command": "mix",
"args": ["run", "--no-halt", "-e", "Excessibility.MCP.Server.start()"],
"cwd": "/path/to/your/project"
}
}
The MCP server is now available in Claude Code.
Claude Code Skills Plugin
Install the skills plugin for structured accessibility workflows:
claude plugins add /path/to/excessibility/priv/claude-plugin
Available skills:
| Skill | Description |
|-------|-------------|
| /e11y-tdd | TDD workflow with html_snapshot and Pa11y - sprinkle snapshots to see what's rendered, delete when done |
| /e11y-debug | Debug workflow with timeline analysis - inspect state at each event, correlate with Pa11y failures |
| /e11y-fix | Reference guide for fixing Pa11y/WCAG errors with Phoenix-specific patterns |
Example workflow:
/e11y-tdd
# Claude will guide you through:
# 1. EXPLORE - Add html_snapshot() calls to see what's rendered
# 2. RED - Write test with snapshot at key moment
# 3. GREEN - Implement featur
Related Skills
gh-issues
334.9kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
node-connect
334.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
82.3kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
