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:
<!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>
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());
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.Allincludes best-practice rules such asscope-attr-valid. WithWcag22AAalone, those wouldn’t run.- The two
listitemfailures have no suggestion, because the right fix depends on what the author meant. They went away anyway, because replacing the deprecatedrole="directory"withlistput the items back inside a list. SuggestedEditList()returns the suggestions as ordinaryEditobjects without duplicates, sodoc.Applyruns them with the usual lossless splicing and verification. Six one-range changes, and the rest of the file is byte-identical. To be selective, readresult.SuggestedEditsper result and apply only the ones you accept.Recheckgives the same report a fullCheckof the new tree would give, but it re-runs only what the edits reached.report.Rechecksays 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). Each edit’s meta carries the rule id, the criteria and "suggested": true. The rules and their fixes are documented in The checker.