Skip to content

Commit 3d80a37

Browse files
dylanbCopilotGarbee
authored andcommitted
chore: add CLAUDE.md and pull request checklist (#5035)
Adds a CLAUDE.md file for contributors --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jonathan Garbee <jonathan.garbee@deque.com>
1 parent a09204f commit 3d80a37

File tree

6 files changed

+571
-0
lines changed

6 files changed

+571
-0
lines changed

CLAUDE.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# axe-core — Claude Code Context
2+
3+
**Last updated:** 2026-03-19
4+
5+
## 0. Fundamental Standards
6+
7+
- **Formatting:** Run `npm run fmt` (Prettier) and `npm run eslint` before every commit. No exceptions.
8+
- **Zero-Exception Testing:** 100% coverage goal. Run `npm test` before completion. `feat`/`fix` and other behavior-changing PRs require unit + integration tests where applicable; see `doc/code-submission-guidelines.md` for exceptions (e.g., some `chore`/docs changes).
9+
- **Import Rule:** Directory-level import restrictions are strictly enforced. See `eslint.config.js`.
10+
- **Commits:** Angular commit convention is mandatory. PRs with non-conforming commits will be rejected. See `doc/code-submission-guidelines.md`.
11+
- **Issues:** All unresolved issues tracked in [GitHub Issues](https://github.com/dequelabs/axe-core/issues).
12+
13+
## 1. Technical Guidelines
14+
15+
### Code Structure
16+
17+
- **Return Early:** Keep the happy path left-aligned. Handle errors/edge cases first with early returns — never nest when you can exit.
18+
- **Exports:** Default export at the top of the file, immediately after imports.
19+
- **JSDoc:** Add JSDoc/DocBlock comments where appropriate, especially for exported APIs and complex logic. Document parameters and return types. See `doc/code-submission-guidelines.md` (source of truth) and `doc/developer-guide.md` for additional guidance.
20+
- **Naming:** Files and rule/check IDs in kebab-case. Functions in camelCase. Booleans prefixed `is`/`has`/`should`. Constants in `UPPER_SNAKE_CASE`.
21+
- **Variables:** Declare at point of use, not at the top of the function.
22+
23+
### Virtual Nodes vs. HTMLElement
24+
25+
- **Prefer Virtual Nodes** for attribute access and property reads (`virtualNode.attr()`, `virtualNode.props`).
26+
- **Use real DOM** only when you need DOM APIs (e.g., `getBoundingClientRect`, `getRootNode`).
27+
- **`core/utils/` functions** should default to real DOM inputs/outputs, except utilities that are explicitly documented as operating on `VirtualNode` (for example, tree or selector helpers). Avoid introducing new `VirtualNode`-dependent utilities unless there is a clear performance or API benefit.
28+
- **Conversion:** Use `getNodeFromTree()` from `core/utils` when you receive an ambiguous input (such as a `VirtualNode`, selector, or mixed type) and need to resolve it to a real DOM `Node`.
29+
30+
### Import Restrictions (Hard Rules)
31+
32+
- `standards/` → nothing (pure data, no imports).
33+
- `core/utils/` → other `core/utils`, `core/base`, `standards` via **direct file paths only** (no index).
34+
- `core/imports/` → node modules **only** (the only place npm imports are allowed).
35+
- `commons/` → other `commons` (direct paths), `core/utils` (index OK).
36+
- `checks/` and `rules/` → any directory (index OK).
37+
- **Never** import `commons` from `core/utils`. This is the most commonly rejected violation.
38+
39+
### Checks & Rules
40+
41+
- **Check evaluate functions:** Return `true` (pass), `false` (fail), or `undefined` (incomplete). Use `this.data()` to pass values to message templates and to provide incomplete-result detail.
42+
- **Rule JSON:** Use `all`, `any`, `none` check arrays. `selector` + optional `matches` to scope candidates. Valid `impact` values: `"minor"`, `"moderate"`, `"serious"`, `"critical"`.
43+
- **Standards data:** Never hardcode ARIA/HTML lists in checks. Query from `standards/` via `commons/standards` functions.
44+
- **Messages:** All user-facing strings live in check/rule JSON `metadata.messages`. Use `${data.property}` templates. Support singular/plural variants. Update `locales/_template.json` whenever messages change (auto-synced on `npm run build`).
45+
46+
### High-Risk Areas (Extra Scrutiny Required)
47+
48+
- **Color contrast:** Handles all CSS color formats, opacity, blend modes, stacking contexts, text-shadow. Return `undefined` when background cannot be determined. See `doc/developer-guide.md`.
49+
- **ARIA validation:** Must stay current with spec. Query roles/attrs from `standards/aria-roles.js` and `standards/aria-attrs.js`. Handle implicit vs. explicit roles. See `doc/rule-development.md`.
50+
- **Hidden elements:** Use `isVisibleToScreenReaders()`, not CSS visibility alone. Account for `aria-hidden="true"` and Shadow DOM boundaries.
51+
- **i18n:** Update `locales/_template.json` on every message change and commit the generated file alongside source.
52+
53+
## 2. Testing
54+
55+
- **Structure:** Mirror `lib/` exactly under `test/`. File `lib/commons/text/sanitize.js``test/commons/text/sanitize.js`.
56+
- **Checks:** Use `axe.testUtils.MockCheckContext()`. Only reset `checkContext` in `afterEach``fixture` and `axe._tree` are auto-cleared.
57+
- **Integration tests:** All rule changes require an HTML + JSON pair. Use `test/integration/rules/<rule-name>/` for mocha-hosted tests or `test/integration/full/<rule-name>/` when the rule requires a full HTML page. JSON selectors must use axe array format (`["#id"]`; iframes: `["iframe", "#id"]`). Also update or create virtual-rules tests where appropriate.
58+
- **Shadow DOM:** Every relevant check/rule must include an open Shadow DOM test case using `queryShadowFixture`.
59+
- **Logging:** Do not commit `console.log` statements.
60+
61+
## 3. Build & Commits
62+
63+
- **Bundles (`axe.js`, `axe.min.js`)** are auto-generated for releases/publishing and are not committed to the repo (they are gitignored).
64+
- **Locales template (`locales/_template.json`)** is auto-generated. When message strings change, regenerate this file and commit it in the same commit as the source changes — never in a separate commit.
65+
- **One change per PR.** Do not mix refactoring with feature work.
66+
- **Commit format:** `<type>(<scope>): <subject>` — imperative, lowercase, no period, ≤100 chars total. Body explains motivation. Footer: `Closes issue #123` or full URL. See `doc/code-submission-guidelines.md` for the full type list.
67+
68+
**Example:**
69+
70+
```
71+
fix(aria-valid-attr-value): handle multiple aria-errormessage IDs
72+
73+
When aria-errormessage contains multiple space-separated IDs, verify
74+
all IDs exist in aria-describedby instead of matching the full string.
75+
76+
Closes issue #4957
77+
```
78+
79+
## 4. Documentation & API Changes
80+
81+
- **New rules:** Update `CHANGELOG.md`. `doc/rule-descriptions.md` is auto-generated by `npm run build`.
82+
- **API changes:** Update `doc/API.md` and `axe.d.ts` TypeScript definitions.
83+
- **Breaking changes:** Avoid breaking changes — prefer supporting both old and new formats simultaneously. If unavoidable, add `BREAKING CHANGE: description` to commit footer, include migration guide in `CHANGELOG.md`, and tag deprecated code with `@deprecated` JSDoc.
84+
85+
## 5. Examples (Copy-Paste Reference)
86+
87+
- **Code patterns:** `doc/examples/code-patterns.md` — return early, default export, imports, JSDoc, Virtual Node usage
88+
- **Test patterns:** `doc/examples/test-patterns.md` — unit tests, check tests, Shadow DOM tests, integration test HTML+JSON
89+
- **Rule & check templates:** `doc/examples/rule-check-templates.md` — JSON templates for rules and checks, evaluate function pattern
90+
- **PR review patterns:** `doc/examples/pr-review-patterns.md` — common reviewer feedback, anti-patterns, what reviewers look for
91+
92+
## 6. Reference Docs & Help
93+
94+
- **Contributing guide:** `CONTRIBUTING.md`
95+
- **Import rules detail:** `eslint.config.js`
96+
- **Code submission standards:** `doc/code-submission-guidelines.md`
97+
- **Developer guide:** `doc/developer-guide.md`
98+
- **Rule development:** `doc/rule-development.md`
99+
- **API reference:** `doc/API.md`
100+
- **Pull Request Checklist:** `doc/pull-request-checklist.md`
101+
- **Slack:** [axe-community](https://accessibility.deque.com/axe-community)
102+
- **Issues:** [GitHub Issues](https://github.com/dequelabs/axe-core/issues)

doc/examples/code-patterns.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Code Pattern Examples
2+
3+
Quick-reference examples for axe-core coding conventions.
4+
5+
## Default Export at Top
6+
7+
```javascript
8+
// GOOD: Default export right after imports
9+
import { getRole } from '../../commons/aria';
10+
import { isVisible } from '../../commons/dom';
11+
12+
export default function myFunction(node, options) {
13+
// function body
14+
}
15+
16+
// BAD: Export buried at bottom of file
17+
import { getRole } from '../../commons/aria';
18+
19+
function myFunction(node, options) {
20+
// body
21+
}
22+
23+
// ... more code ...
24+
25+
export default myFunction; // Too far from top
26+
```
27+
28+
## Return Early Pattern
29+
30+
```javascript
31+
// GOOD: Main path left-aligned, edge cases exit early
32+
export default function processValue(value) {
33+
if (!value) {
34+
return null;
35+
}
36+
37+
if (value.length < 3) {
38+
throw new Error('Value too short');
39+
}
40+
41+
const normalized = normalize(value);
42+
const result = transform(normalized);
43+
return result;
44+
}
45+
46+
// BAD: Nested conditionals
47+
export default function processValue(value) {
48+
let result;
49+
if (value) {
50+
if (value.length >= 3) {
51+
const normalized = normalize(value);
52+
result = transform(normalized);
53+
} else {
54+
throw new Error('Value too short');
55+
}
56+
} else {
57+
result = null;
58+
}
59+
return result;
60+
}
61+
```
62+
63+
## Import Restrictions
64+
65+
```javascript
66+
// GOOD: commons importing from core/utils via index
67+
import { getNodeFromTree } from '../../core/utils';
68+
69+
// GOOD: commons importing other commons directly
70+
import getExplicitRole from '../aria/get-explicit-role';
71+
72+
// BAD: core/utils importing from commons — NEVER DO THIS
73+
import { isDisabled } from '../../commons/forms';
74+
75+
// BAD: importing from index in core/utils — use direct path
76+
import { someUtil } from './index'; // Use: import someUtil from './some-util';
77+
```
78+
79+
## JSDoc Comments
80+
81+
### Standard function
82+
83+
```javascript
84+
/**
85+
* Determines if an element is a native select element
86+
* @method isNativeSelect
87+
* @memberof axe.commons.forms
88+
* @param {VirtualNode|Element} node Node to determine if select
89+
* @returns {Boolean}
90+
*/
91+
import nodeLookup from '../../core/utils/node-lookup';
92+
93+
function isNativeSelect(node) {
94+
const { vNode } = nodeLookup(node);
95+
const nodeName = vNode.props.nodeName;
96+
return nodeName === 'select';
97+
}
98+
```
99+
100+
### Check evaluate function
101+
102+
```javascript
103+
/**
104+
* Check if an element's `role` attribute uses any abstract role values.
105+
*
106+
* Abstract roles are taken from the `ariaRoles` standards object from the roles `type` property.
107+
*
108+
* ##### Data:
109+
* <table class="props">
110+
* <thead>
111+
* <tr>
112+
* <th>Type</th>
113+
* <th>Description</th>
114+
* </tr>
115+
* </thead>
116+
* <tbody>
117+
* <tr>
118+
* <td><code>String[]</code></td>
119+
* <td>List of all abstract roles</td>
120+
* </tr>
121+
* </tbody>
122+
* </table>
123+
*
124+
* @memberof checks
125+
* @return {Boolean} True if the element uses an `abstract` role. False otherwise.
126+
*/
127+
function abstractroleEvaluate(node, options, virtualNode) {
128+
// implementation
129+
}
130+
```
131+
132+
## Virtual Node vs HTMLElement
133+
134+
```javascript
135+
// Use Virtual Node for attribute access and property reads
136+
function myCheck(node, options, virtualNode) {
137+
const role = virtualNode.attr('role'); // Cached attribute access
138+
const nodeName = virtualNode.props.nodeName;
139+
140+
// Only access real node when you need DOM APIs
141+
const rect = node.getBoundingClientRect();
142+
const rootNode = node.getRootNode();
143+
}
144+
145+
// Convert ambiguous input using nodeLookup
146+
import nodeLookup from '../../core/utils/node-lookup';
147+
148+
function myFunction(nodeOrVirtual) {
149+
const { vNode, domNode } = nodeLookup(nodeOrVirtual);
150+
// vNode = VirtualNode, domNode = real DOM node
151+
}
152+
```

doc/examples/pr-review-patterns.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# PR Review Patterns
2+
3+
Common feedback and anti-patterns observed in axe-core code reviews.
4+
5+
## What Gets Called Out
6+
7+
### 1. Missing Tests
8+
9+
- Every behavior-changing code change needs unit tests
10+
- Rule changes need integration tests (HTML + JSON pair)
11+
- Shadow DOM test coverage required for relevant checks/rules
12+
13+
### 2. Commit Message Format
14+
15+
- Wrong type/scope
16+
- Not imperative present tense
17+
- Subject too long or capitalized
18+
- Missing issue reference in footer
19+
20+
### 3. Import Violations
21+
22+
- `core/utils` importing from `commons` (forbidden)
23+
- Using index imports where direct file paths are required
24+
- Importing node modules outside `core/imports`
25+
26+
### 4. Code Style Issues
27+
28+
- Not using return early pattern
29+
- Default export not at top of file
30+
- Nested conditionals when early return would work
31+
- Missing JSDoc comments
32+
33+
### 5. Performance Concerns
34+
35+
- Unnecessary DOM queries in loops
36+
- Not caching Virtual Node properties
37+
- Computing same value repeatedly instead of storing it
38+
39+
### 6. Incomplete Results
40+
41+
- Not returning `undefined` when a check can't determine the result
42+
- Missing `incomplete` message variants in check JSON
43+
- Not setting appropriate `this.data()` for incomplete cases
44+
45+
### 7. Accessibility Edge Cases
46+
47+
- Not considering all ARIA states
48+
- Missing edge cases for hidden elements
49+
- Not handling Shadow DOM properly
50+
51+
## What Reviewers Love
52+
53+
1. Comprehensive test coverage including edge cases
54+
2. Clear, detailed commit messages with motivation
55+
3. JSDoc comments that explain "why" not just "what"
56+
4. Performance-conscious code that caches appropriately
57+
5. Following existing patterns in similar files
58+
6. Integration tests that cover both pass and fail cases
59+
60+
## Common PR Mistakes
61+
62+
1. Not running `npm test` locally before pushing
63+
2. Not committing auto-generated `locales/_template.json` in the same commit as message source changes
64+
3. Changing multiple unrelated things in one PR — split refactoring from feature work
65+
4. Not updating integration tests when changing rule behavior
66+
5. `console.log` statements should not be committed
67+
6. Not handling `null` or `undefined` gracefully
68+
7. Hardcoding strings that should come from `standards/` data
69+
8. Changing public APIs without `BREAKING CHANGE` in commit footer

0 commit comments

Comments
 (0)