---
title: "Apply suggested accessibility fixes"
url: https://recursiveai.org/onehtml/cookbook/apply-suggested-fixes/
description: "Take the edits the checker suggests for rules with exactly one correct fix, apply the ones you accept as lossless edits, and re-check only what changed."
version: "1.0.0"
---

# Apply suggested accessibility fixes

Take the edits the checker suggests for rules with exactly one correct fix, apply the ones you accept as lossless edits, and re-check only what changed.

Most accessibility failures need a person: only the author knows what an image shows or what a button does. A few have one fix that needs no judgment. For those, the checker attaches a suggested edit. In 1.0.0 these are `aria-conditional-attr`, `aria-deprecated-role`, `aria-hidden-body`, `aria-valid-attr`, `avoid-inline-spacing`, `scope-attr-valid` and `tabindex`. The library never applies a suggestion by itself.

The input is a staff page with six failures that have a suggested fix, and two `listitem` failures that don’t:

```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Staff directory | Harbor Street Library</title>
</head>
<body>
<main>
  <h1>Staff directory</h1>
  <ul role="directory">
    <li>Ana Ruiz, branch manager</li>
    <li>Sam Okafor, children's librarian</li>
  </ul>
  <table>
    <tr><th scope="column">Desk</th><th scope="column">Hours</th></tr>
    <tr><td>Reference</td><td>9 to 6</td></tr>
  </table>
  <p style="letter-spacing: 0.1em !important" aria-describedy="note">Call ahead for appointments.</p>
  <a href="/contact/" tabindex="3">Contact us</a>
</main>
</body>
</html>
```

```csharp
using System.Text;
using RecursiveAI.OneHtml;
using RecursiveAI.OneHtml.Css;
using RecursiveAI.OneHtml.Editing;
using RecursiveAI.OneHtml.Accessibility;
using RecursiveAI.OneHtml.Accessibility.Validation;
using RecursiveAI.OneHtml.Accessibility.Validation.Catalogue;

byte[] input = File.ReadAllBytes("staff.html");
Document doc = Document.Parse(input);
DocumentStyles styles = DocumentStyles.Create(doc);
AccessibilityTree tree = AccessibilityTree.Build(doc, styles);

var checker = new Checker();
var options = new CheckOptions { Profile = ValidationProfile.All };
CheckReport report = checker.Check(tree, options);

foreach (RuleResult result in report.WithOutcome(Outcome.Failed))
{
    string fix = result.SuggestedEdits.Count > 0 ? result.SuggestedEdits[0].Description : "no suggested edit";
    Console.WriteLine($"{result.RuleId}: {fix}");
}

// The library never applies a suggestion; you decide which ones to take
IReadOnlyList<Edit> suggestions = report.SuggestedEditList();
EditResult edit = doc.Apply(suggestions);
Console.WriteLine($"Applied {suggestions.Count} edits: {edit.Succeeded}");

// Re-check only what the edits reached
RestyleResult restyle = styles.Restyle(edit);
AccessibilityTree updated = tree.Update(edit, restyle);
CheckReport next = checker.Recheck(report, edit, restyle, updated);
Console.WriteLine($"Failed before: {report.Count(Outcome.Failed)}, after: {next.Count(Outcome.Failed)}");
Console.WriteLine($"Incremental: {next.Recheck!.Incremental}, rules carried over: {next.Recheck.RulesCarried}, " +
                  $"re-run in part: {next.Recheck.RulesRerunPartly}, in full: {next.Recheck.RulesRerunInFull}");

foreach (EditedRange range in edit.Changes!.Ranges)
{
    string before = Encoding.UTF8.GetString(input, range.OldByteRange.Start, range.OldByteRange.Length);
    Console.WriteLine($"[{before}] -> [{range.NewText}]");
}
File.WriteAllBytes("staff.fixed.html", edit.Document!.ToLosslessBytes());
```

```text
aria-deprecated-role: Use the role list instead of directory.
aria-valid-attr: Remove the aria-describedy attribute.
avoid-inline-spacing: Declare letter-spacing in the style attribute without !important.
listitem: no suggested edit
listitem: no suggested edit
scope-attr-valid: Remove the scope attribute.
scope-attr-valid: Remove the scope attribute.
tabindex: Set tabindex to 0; the focus order does not change.
Applied 6 edits: True
Failed before: 8, after: 0
Incremental: True, rules carried over: 7, re-run in part: 50, in full: 73
[directory] -> [list]
[ scope="column"] -> []
[ scope="column"] -> []
[ !important] -> []
[ aria-describedy="note"] -> []
[3] -> [0]
```

What happened:

- `Profile = ValidationProfile.All` includes best-practice rules such as `scope-attr-valid`. With `Wcag22AA` alone, those wouldn’t run.
- The two `listitem` failures have no suggestion, because the right fix depends on what the author meant. They went away anyway, because replacing the deprecated `role="directory"` with `list` put the items back inside a list.
- `SuggestedEditList()` returns the suggestions as ordinary `Edit` objects without duplicates, so `doc.Apply` runs them with the usual lossless splicing and verification. Six one-range changes, and the rest of the file is byte-identical. To be selective, read `result.SuggestedEdits` per result and apply only the ones you accept.
- `Recheck` gives the same report a full `Check` of the new tree would give, but it re-runs only what the edits reached. `report.Recheck` says how much was carried over.

To review suggestions before applying them, `report.SuggestedEdits()` returns them as one JSON edit envelope you can store, show in a review tool and replay later (see [Save edits as JSON and replay them](https://recursiveai.org/onehtml/cookbook/save-and-replay-edits/)). Each edit’s `meta` carries the rule id, the criteria and `"suggested": true`. The rules and their fixes are documented in [The checker](https://recursiveai.org/onehtml/docs/checker/).
