Every package, in full
The full API of every npm package, one block each. Every function is kept together with the types it takes and hands back.
@out-of-order/core
npm i -D @out-of-order/coreThe analyzer. It computes the tab sequence, grades it against the rules, and reshapes the findings. Pure and framework-free: every other package builds on it.
audit()function
audit(root?, options?) → AuditResult
Computes the tab sequence for root and grades it against the enabled rules.
It runs in the browser only, because tabbable uses real CSS layout for
visibility and several rules read bounding rects.
The result's violations hold live Elements. For a printable or
serializable view, pass the result to
formatViolations().
Parameters
| Parameter | Type | Description |
|---|---|---|
root |
Document | Element |
Optional, defaults to document. The subtree to analyze. A
Document analyzes the whole page, exactly like tabbing through it.
|
options |
AuditOptions |
Optional. Per-rule severity overrides and custom rules. See Options & severity. |
Returns → AuditResulttype
Holds the computed tab sequence, every violation found, and the overall verdict.
| Property | Type | Description |
|---|---|---|
valid |
boolean |
True when no enabled rule produced an error. Warnings are advisory
and never flip it, so read violations when you want to surface those
too.
|
sequence |
SequenceEntry[] |
Every tab stop, in the exact order the Tab key reaches it. |
violations |
Violation[] |
One entry per offending element, each carrying the rules it failed, across all enabled rules and both severities. |
SequenceEntrytype
One entry per tab stop in AuditResult.sequence.
| Property | Type | Description |
|---|---|---|
element |
Element |
The focusable node at this stop. |
selector |
string |
A short, CSS-ish path to the element, for messages and logs. |
orderIndex |
number |
Its zero-based position in the tab sequence. |
tabIndex |
number |
The resolved tabindex, 0 when the element is focusable without one.
|
rect |
DOMRect |
The element's bounding rect at analysis time, from real browser layout. This is why several rules need a real browser. |
Violationtype
One entry per offending element in AuditResult.violations,
carrying every rule it failed.
| Property | Type | Description |
|---|---|---|
element |
Element |
The offending element. |
selector |
string |
A CSS-ish path to that element, for messages and logs. |
orderIndex? |
number |
Its position in the tab sequence, present only when it is a stop. |
issues |
Issue[] |
The rules this element failed, error-severity first. |
Issuetype
A single rule failure on one element.
| Property | Type | Description |
|---|---|---|
rule |
string |
The id of the rule that fired, a built-in id or a custom rule's own. Stable, so you can key or filter on it. |
severity |
"error" | "warning" |
How serious the finding is, after any per-call override. |
message |
string |
A plain-language account of what is wrong. |
fix? |
string |
A suggested remediation, when the rule has one. |
docs? |
string |
A spec link (WCAG, WAI-ARIA, or ARIA APG) for the rule. |
relatedElements? |
Element[] |
Other elements that share this issue's root cause. Ringed alongside the
violation's element but not reported as separate violations, so one
missing fix does not become N findings.
|
ignored? |
boolean |
True when the element
approves this rule via
data-ooo-ignore. An ignored error never flips valid.
|
Options & severity
The severity values and the options object taken by
audit() and, through TraceOptions.audit, by
trace().
Severity & RuleOverridetypes
The grades a rule reports at, and what an override can set it to.
| Type | Definition | Description |
|---|---|---|
Severity |
"error" | "warning" |
The two grades a rule can report at. |
RuleOverride |
Severity | "off" |
What you set a rule to in an override. The value
"off" disables the rule.
|
AuditOptionstype
The options object passed to audit().
| Property | Type | Description |
|---|---|---|
rules? |
Partial<Record<RuleId, RuleOverride>>
|
Per-rule overrides. Omit a rule to keep its default. Set
"error" or "warning" to enable it at that grade, or
"off" to disable it.
|
customRules? |
Rule[] |
Extra rules that run alongside the built-ins, overridable through
rules like any built-in. See custom rules.
|
RuleId is the union of the built-in rule ids, so overrides autocomplete.
The full list is on the rules page, and the ids with their
default grades are also exported as data, see
Helpers & exports.
formatViolations()function
formatViolations(result, format) → string | ByElement[] | ByViolation[] | FlatIssue[]
Reshapes a result's violations into the named view and leaves the result
itself untouched. "text" renders a human-readable block. The other views
are serializable arrays with every element reference flattened to its selector string,
so they survive JSON.stringify. The overloads narrow the return type when
format is a literal.
AuditFormattype
The four views, and what each returns.
| Format | Returns | Description |
|---|---|---|
"text" |
string |
A ready-to-print block: one paragraph per element, its issues indented. |
"by-element" |
ByElement[] |
One entry per offending element with its issues nested, plus an
issueCount. The serializable twin of Violation.
|
"by-violation" |
ByViolation[] |
One entry per failed rule with the offending elements nested, plus an
elementCount. Errors sort before warnings.
|
"flat" |
FlatIssue[] |
One entry per element-issue pair, nothing nested: a
SerializedIssue plus the element's selector and
orderIndex?.
|
SerializedIssuetype
The issue shape shared by the formatted views: an
Issue with its element references flattened to selector strings.
| Property | Type | Description |
|---|---|---|
rule |
string |
The id of the rule that fired. |
severity |
Severity |
How serious the finding is. |
message |
string |
What is wrong. |
fix? |
string |
A suggested remediation, when the rule has one. |
docs? |
string |
The rule's spec link. |
related? |
string[] |
Selectors of the elements sharing this issue's root cause. |
ignored? |
boolean |
Approved via data-ooo-ignore. |
ByElementtype
One "by-element" entry: an offending element with its failures nested.
| Property | Type | Description |
|---|---|---|
selector |
string |
A CSS-ish path to the offending element. |
orderIndex? |
number |
Its position in the tab sequence, present only when it is a stop. |
issueCount |
number |
How many rules the element failed (issues.length). |
issues |
SerializedIssue[] |
The failures themselves, error-severity first. |
ByViolationtype
One "by-violation" entry: a failed rule with every element that tripped it
nested.
| Property | Type | Description |
|---|---|---|
rule |
string |
The id of the failed rule. |
severity |
Severity |
The rule's grade, after any per-call override. |
docs? |
string |
The rule's spec link. |
elementCount |
number |
How many elements failed it (elements.length). |
elements |
{ selector, orderIndex?, message, fix?, related?, ignored? }[]
|
The offending elements, each with its own message and, when present, the related
selectors and its
data-ooo-ignore approval.
|
FlatIssuetype
One "flat" entry. Extends SerializedIssue with where the issue
sits, so a single row carries everything.
| Property | Type | Description |
|---|---|---|
selector |
string |
A CSS-ish path to the offending element. |
orderIndex? |
number |
Its position in the tab sequence, present only when it is a stop. |
| … | SerializedIssue |
Plus every SerializedIssue field, flattened in. |
Custom rules
A rule is a pure function over the computed sequence. Pass your own through
AuditOptions.customRules and they flow into the result and the overlay like
any other.
Ruletype
One custom rule.
| Property | Type | Description |
|---|---|---|
id |
string |
Surfaced as rule on every Issue the rule produces.
|
docs? |
string |
The spec link the rule is grounded in. |
severity |
Severity |
The grade its findings get unless the caller overrides it through
AuditOptions.rules.
|
run |
(sequence: SequenceEntry[], ctx: RuleContext) => Finding[]
|
The rule itself, run over the sequence. |
RuleContexttype
Handed to a rule's run as its second argument.
| Property | Type | Description |
|---|---|---|
container |
Element |
The analyzed root, so a rule can look beyond the tab sequence. |
inSequence |
Set<Element> |
A fast "is this element a tab stop?" test. |
Findingtype
Returned by a rule's run, one per issue.
| Property | Type | Description |
|---|---|---|
message |
string |
What is wrong. |
fix? |
string |
A suggested remediation. Shown separately from the message (the overlay renders it as a "Possible fix" line). |
target |
SequenceEntry | Element |
The element the finding is about, a SequenceEntry
when it is a tab stop.
|
relatedElements? |
Element[] |
Other elements sharing the same root cause. |
For a worked example, see adding your own rule.
Helpers & exports
The DOM helpers the built-in rules lean on, exported so a custom rule can reuse them.
| Export | Signature | Description |
|---|---|---|
isInteractive |
(el: Element) => boolean |
Returns whether an element is interactive, meaning a native control or an element with an interactive ARIA role. |
selectorFor |
(el: Element) => string |
Returns a short, CSS-ish path to an element, useful for messages and logs. |
isScreenReaderOnly |
(el: Element, rect?: DOMRect) => boolean
|
Returns whether an element is an intentional
.sr-only-style utility (tiny and clipped), the standard way to expose
text to screen readers, which a rule must not flag as invisible.
|
isRuleIgnored |
(el: Element, ruleId: string) => boolean
|
Returns whether the element
approves the rule via
data-ooo-ignore. Element-scoped, never inherited by descendants.
|
composedDescendants |
(root: ParentNode) => Generator<Element>
|
Yields every element under root in tree order, descending into open
shadow roots. The composed-tree stand-in for querySelectorAll("*"),
which never enters a shadow root.
|
The rule set as data
The built-in rule set, exported for building tooling around it: option pickers, docs, config validation.
| Export | Type | Description |
|---|---|---|
RuleIdtype |
"no-positive-tabindex" | …
|
The union of the built-in rule ids, what
AuditOptions.rules keys on. The full list, each with its clause, is
on the rules page.
|
DEFAULT_SEVERITYconst |
Record<RuleId, Severity>
|
Every built-in rule's default grade, keyed by id. What the overrides are applied against. |
@out-of-order/vitest
npm i -D @out-of-order/vitestThe audit as a one-line assertion for Vitest Browser Mode. Released, but the API is still settling.
toHaveValidTabOrder()matcher
expect(root).toHaveValidTabOrder(options?)
Import the package once in a setup file and it registers the matcher and its types:
import "@out-of-order/vitest";.
The assertion target must be an Element or a Document. It runs
audit(root, options) and passes on AuditResult.valid, so
warnings never fail a test but errors do. A failure prints the "text" view
of the violations. Works with .not, which expects at least one error.
It refuses to run outside a real browser. With no DOM at all, or under a layout-less
simulated DOM like jsdom or happy-dom, it throws and tells you
to enable Vitest Browser Mode instead of silently passing on meaningless layout.
Parameters
| Parameter | Type | Description |
|---|---|---|
options |
AuditOptions |
Optional. The same per-rule overrides and custom rules
audit() takes. See Options & severity.
|
Setup and configuration live in the vitest README.
@out-of-order/trace
npm i -D @out-of-order/traceThe visual overlay, built on the analyzer: numbered tab stops, the path between them, and every finding ringed in place on the live page.
trace()function
trace(options?) → TraceHandle
Mounts the overlay layer, draws the first analysis, and starts observing. It re-analyzes itself on DOM mutation, so most pages need nothing past the call.
The audit-shaped fields here (AuditOptions,
AuditResult) are core's types. The overlay depends on
core but does not re-export it, so for the headless audit() path install
@out-of-order/core and import from there.
Parameters → TraceOptionstype
Every field is optional.
| Property | Type | Description |
|---|---|---|
root? |
Document | Element |
The subtree to analyze. Defaults to document. |
audit? |
AuditOptions |
Rule overrides and custom rules, forwarded to
audit() on every analysis.
|
motion? |
MotionMode
|
Animation behaviour. Defaults to "auto", which follows the user's
prefers-reduced-motion setting, live.
|
peekKey? |
ModifierKey
|
Tap it to peek through the overlay at the page beneath without hiding it, tap
again to restore. Defaults to
"Alt".
|
onResult? |
(result: AuditResult) => void
|
Called after every re-analysis with the fresh result. The first call is
synchronous, before
trace() returns.
|
Returns → TraceHandletype
For driving the overlay after the mount.
| Member | Type | Description |
|---|---|---|
visible |
boolean |
Read-only. Reports whether the overlay is currently visible. |
setVisible |
(visible: boolean) => void |
Shows or hides the whole overlay, including its badges, lines, and rings. |
toggle |
() => void |
Flips the overlay between shown and hidden. |
destroy |
() => void |
Removes the overlay layer, observers, and listeners. |
result |
AuditResult | null |
The latest analysis result, or null before the first draw. |
MotionMode & ModifierKeytypes
The two option unions above, exported by name.
| Type | Definition | Description |
|---|---|---|
MotionMode |
"auto" | "on" | "off" |
Whether the overlay animates. "auto" follows
prefers-reduced-motion, the other two force it.
|
ModifierKey |
"Alt" | "Control" | "Shift" | "Meta"
|
The modifier that drives the peek toggle. |
@out-of-order/cli
npx @out-of-order/cli <url>
The audit from the terminal, against any URL. No JS API: it launches headless Chromium,
runs the same audit(), and prints the findings.
out-of-ordercommand
out-of-order <url> [options]
Point it at a URL, a bare domain (https:// is assumed,
http:// for localhost), or a local HTML file. Pages that are still an empty
shell at load, common for SPAs, are re-audited up to --tries times until
something tabbable renders.
| Option | Value | Description |
|---|---|---|
--format |
text | by-element | by-violation | flat | json
|
Output shape, default text. The middle three are the
formatViolations() views. json is
CLI-only: the full result with valid, the tab sequence, and the
violations.
|
--rule |
<id>=<level> |
Set a rule to off, error, or warning.
Repeatable.
|
--wait |
<selector> |
Wait for a selector before auditing, for pages that render after load. |
--tries |
<n> |
Re-audit up to n times, a second apart, while the page has no
tabbable elements. Default 5.
|
--timeout |
<ms> |
Navigation and selector timeout, default 30000. |
--viewport |
<WxH> |
Viewport size, for example 1440x900. |
--auth |
<file> |
Storage-state file to load, saved by
login. Default: the per-host file under ~/.config/out-of-order/auth.
|
--overlay |
boolean |
Open a headed browser with the visual overlay drawn on the live page instead of
printing. Excludes --format.
|
--version |
boolean |
Print the version. |
--help |
boolean |
Show the usage message. |
Exit codes
| Code | Meaning | Description |
|---|---|---|
0 |
valid |
No error-severity violations. Warnings may still have printed. |
1 |
violations |
At least one error-severity violation, so a CI step fails on it. |
2 |
failed run |
The run itself failed: a usage error, a crash, a non-OK HTTP status, or a page with no tabbable elements on any try. |
The full option list is also in the CLI README.
out-of-order logincommand
out-of-order login <url> [options]
For pages behind a login. Every later audit of that host picks it up automatically.
Needs an http(s) <url>, and takes only the three options below. The
session is stored per host under ~/.config/out-of-order/auth unless
--auth names a file.
| Option | Value | Description |
|---|---|---|
--auth |
<file> |
Where to save the storage state. Pass the same file to later audits with their own
--auth.
|
--timeout |
<ms> |
Navigation timeout, default 30000. |
--viewport |
<WxH> |
Viewport size, for example 1440x900. |