---
title: "Check accessibility with rendered facts"
url: https://recursiveai.org/onerender/cookbook/accessibility-facts/
description: "Lay a page out with OneRender and hand the layout to OneHtml’s accessibility checker, so rules that need rendering, such as color contrast and target…"
version: "1.0.0"
---

# Check accessibility with rendered facts

Lay a page out with OneRender and hand the layout to OneHtml’s accessibility checker, so rules that need rendering, such as color contrast and target size, get a verdict without a browser.

```shell
dotnet add package RecursiveAI.OneRender.Accessibility --version 1.0.0
dotnet add package RecursiveAI.OneRender.Image --version 1.0.0
dotnet add package RecursiveAI.OneHtml.Accessibility.Validation --version 1.2.0
```

`.Accessibility` produces the facts, `.Image` provides the bundled fonts (`FontProviders`) the layout measures text with, and OneHtml’s `.Validation` package is the checker.

```csharp
using OneRender;
using OneRender.Accessibility;
using OneRender.Image;
using RecursiveAI.OneHtml.Accessibility.Validation;

string html = """
<!doctype html>
<html lang="en"><title>Editor</title>
<style>
  body { font: 16px Arial, sans-serif }
  .icon { width: 20px; height: 20px; padding: 0 }
  .hint { color: #999; background: #fff }
  .banner { background: #1d4ed8; color: #fff; padding: 8px }
</style>
<body>
  <div role="toolbar" aria-label="Formatting">
    <button aria-label="Bold" class="icon">B</button><button aria-label="Italic" class="icon">I</button>
    <button aria-label="Insert link">Link</button>
  </div>
  <p class="hint">Drafts are saved every minute.</p>
  <p class="banner">You are offline.</p>
</body></html>
""";

// Lay the page out with OneRender: boxes, paint order, text boxes and the colors behind text.
RenderedPage rendered = AccessibilityFacts.Render(html,
    new HtmlPageOptions(FontProviders.CreateDefault()) { ViewportWidth = 1280, ViewportHeight = 800 });

var options = new CheckOptions { RuleIds = ["target-size", "color-contrast"], Facts = rendered.Facts };
CheckReport report = new Checker().Check(rendered.Document, options);
foreach (RuleResult r in report.Results.Where(r => r.Outcome is Outcome.Failed))
    Console.WriteLine($"{r.RuleId,-14} {r.Locator}: {r.Message}");
Console.WriteLine($"failed {report.Count(Outcome.Failed)}, passed {report.Count(Outcome.Passed)}, needs render {report.Count(Outcome.NeedsRender)}");

CheckReport without = new Checker().Check(rendered.Document, new CheckOptions { RuleIds = ["target-size", "color-contrast"] });
Console.WriteLine($"without facts: failed {without.Count(Outcome.Failed)}, needs render {without.Count(Outcome.NeedsRender)}");
```

```text
color-contrast body > p:nth-of-type(1): The text has a contrast ratio of 2.84:1; 4.50:1 is needed.
target-size    [aria-label="Bold"]: The target is 20 by 20 CSS pixels at 8, 8.5, under 24 by 24, and another target (20 by 20 at 28, 8.5) is too close.
target-size    [aria-label="Italic"]: The target is 20 by 20 CSS pixels at 28, 8.5, under 24 by 24, and another target (20 by 20 at 8, 8.5) is too close.
failed 3, passed 5, needs render 0
without facts: failed 1, needs render 3
```

Without facts, the checker can judge the gray hint’s contrast from the cascade alone, but it won’t guess the size of a button: three results need render. With OneRender’s facts, nothing is left undecided. The two 20-pixel icon buttons fail WCAG 2.5.8, and the white-on-blue banner passes contrast.

## What the facts hold

`rendered.Facts` is OneHtml’s `RenderedFacts`, the same shape a Chrome `DOMSnapshot` capture gives, so a check reads the same either way:

- Every element’s boxes: border box, client, offset and scroll rectangles, and visibility.
- Paint order and stacking contexts.
- Text boxes in layout order, and the color behind each run of text, blended over white, with the text’s opacity.
- Computed styles in the browser’s serialization, and form state.

The color behind text is left out where one color can’t describe it, such as text over an image or under a filter.

## Other ways in

- `AccessibilityFacts.FromInteractive(page)` reads an [`InteractivePage`](https://recursiveai.org/onerender/cookbook/hit-testing-events/) as it is now, so hover and focus styles show in the facts.
- `AccessibilityFacts.FromPage` and `FromLayout` work from a page you already laid out and painted.
- `RenderedFactsLimits` and `FactsOptions.BackgroundScanLimit` bound the work on hostile pages.

What the facts contain, and where they differ from Chrome’s, is in [Rendered page facts](https://recursiveai.org/onerender/docs/accessibility-facts/). The checker and its rules are in onehtml’s [checker docs](https://recursiveai.org/onehtml/docs/checker/).
