accented 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +40 -26
  2. package/dist/accented.d.ts.map +1 -1
  3. package/dist/accented.js +4 -3
  4. package/dist/accented.js.map +1 -1
  5. package/dist/common/strings.d.ts +2 -0
  6. package/dist/common/strings.d.ts.map +1 -0
  7. package/dist/common/strings.js +2 -0
  8. package/dist/common/strings.js.map +1 -0
  9. package/dist/common/tokens.d.ts +9 -0
  10. package/dist/common/tokens.d.ts.map +1 -1
  11. package/dist/common/tokens.js +28 -0
  12. package/dist/common/tokens.js.map +1 -1
  13. package/dist/constants.d.ts +3 -1
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/constants.js +2 -1
  16. package/dist/constants.js.map +1 -1
  17. package/dist/elements/accented-dialog.d.ts +25 -8
  18. package/dist/elements/accented-dialog.d.ts.map +1 -1
  19. package/dist/elements/accented-dialog.js +12 -24
  20. package/dist/elements/accented-dialog.js.map +1 -1
  21. package/dist/elements/accented-trigger.d.ts +24 -12
  22. package/dist/elements/accented-trigger.d.ts.map +1 -1
  23. package/dist/elements/accented-trigger.js +8 -4
  24. package/dist/elements/accented-trigger.js.map +1 -1
  25. package/dist/logger.d.ts +4 -1
  26. package/dist/logger.d.ts.map +1 -1
  27. package/dist/logger.js +196 -17
  28. package/dist/logger.js.map +1 -1
  29. package/dist/scanner.js +1 -1
  30. package/dist/scanner.js.map +1 -1
  31. package/dist/types.d.ts +31 -1
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/types.js.map +1 -1
  34. package/dist/utils/are-issue-sets-equal.d.ts.map +1 -1
  35. package/dist/utils/are-issue-sets-equal.js +2 -2
  36. package/dist/utils/are-issue-sets-equal.js.map +1 -1
  37. package/dist/utils/are-issues-equal.d.ts +3 -0
  38. package/dist/utils/are-issues-equal.d.ts.map +1 -0
  39. package/dist/utils/are-issues-equal.js +5 -0
  40. package/dist/utils/are-issues-equal.js.map +1 -0
  41. package/dist/utils/shadow-dom-aware-mutation-observer.d.ts +4 -4
  42. package/dist/utils/shadow-dom-aware-mutation-observer.d.ts.map +1 -1
  43. package/dist/utils/supports-anchor-positioning.d.ts.map +1 -1
  44. package/dist/utils/supports-anchor-positioning.js +14 -1
  45. package/dist/utils/supports-anchor-positioning.js.map +1 -1
  46. package/dist/utils/transform-violations.d.ts.map +1 -1
  47. package/dist/utils/transform-violations.js +8 -3
  48. package/dist/utils/transform-violations.js.map +1 -1
  49. package/dist/validate-options.d.ts.map +1 -1
  50. package/dist/validate-options.js +3 -0
  51. package/dist/validate-options.js.map +1 -1
  52. package/package.json +7 -7
  53. package/src/accented.test.ts +1 -1
  54. package/src/accented.ts +4 -3
  55. package/src/common/strings.ts +2 -0
  56. package/src/common/tokens.ts +36 -0
  57. package/src/constants.ts +5 -1
  58. package/src/elements/accented-dialog.ts +22 -24
  59. package/src/elements/accented-trigger.ts +11 -5
  60. package/src/logger.ts +276 -22
  61. package/src/scanner.ts +1 -1
  62. package/src/types.ts +32 -1
  63. package/src/utils/are-issue-sets-equal.test.ts +1 -6
  64. package/src/utils/are-issue-sets-equal.ts +2 -5
  65. package/src/utils/are-issues-equal.ts +7 -0
  66. package/src/utils/supports-anchor-positioning.ts +17 -1
  67. package/src/utils/transform-violations.test.ts +1 -1
  68. package/src/utils/transform-violations.ts +13 -5
  69. package/src/utils/update-elements-with-issues.test.ts +6 -2
  70. package/src/validate-options.ts +5 -0
@@ -1 +1 @@
1
- {"version":3,"file":"validate-options.js","sourceRoot":"","sources":["../src/validate-options.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate-options.js","sourceRoot":"","sources":["../src/validate-options.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "accented",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A frontend library for continuous accessibility testing and issue highlighting",
5
5
  "type": "module",
6
6
  "main": "dist/accented.js",
@@ -25,20 +25,20 @@
25
25
  "bugs": {
26
26
  "url": "https://github.com/pomerantsev/accented/issues"
27
27
  },
28
- "homepage": "https://www.accented.dev",
28
+ "homepage": "https://accented.dev",
29
29
  "dependencies": {
30
- "@preact/signals-core": "^1.8.0",
30
+ "@preact/signals-core": "^1.11.0",
31
31
  "axe-core": "^4.10.3"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/jsdom": "^21.1.7",
35
- "jsdom": "^26.0.0"
35
+ "jsdom": "^26.1.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "pnpm copyCommon && tsc",
39
- "copyCommon": "node --import tsx ./scripts/copy-common.ts",
40
- "watchCommon": "node --import tsx ./scripts/watch-common.ts",
41
- "checkBuiltFiles": "node --import tsx ./scripts/check-built-files.ts",
39
+ "copyCommon": "node ./scripts/copy-common.ts",
40
+ "watchCommon": "node ./scripts/watch-common.ts",
41
+ "checkBuiltFiles": "node ./scripts/check-built-files.ts",
42
42
  "checkImportsInBuiltFiles": "node ./dist/accented.js",
43
43
  "typecheckTests": "tsc -p ./tsconfig.test.json",
44
44
  "watch": "pnpm copyCommon && (pnpm watchCommon & tsc --watch)",
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
- import { suite, test } from 'node:test';
3
2
  import type { Mock } from 'node:test';
3
+ import { suite, test } from 'node:test';
4
4
 
5
5
  import { accented } from './accented';
6
6
 
package/src/accented.ts CHANGED
@@ -55,6 +55,7 @@ export function accented(options: AccentedOptions = {}): DisableAccented {
55
55
 
56
56
  const defaultOutput: Required<AccentedOptions['output']> = {
57
57
  console: true,
58
+ page: true,
58
59
  };
59
60
 
60
61
  const defaultThrottle: Required<AccentedOptions['throttle']> = {
@@ -83,7 +84,7 @@ export function accented(options: AccentedOptions = {}): DisableAccented {
83
84
 
84
85
  if (enabled.value) {
85
86
  console.warn(
86
- 'You are trying to run the Accented library more than once, which may lead to errors. See https://www.accented.dev/getting-started#run-once-per-page',
87
+ 'You are trying to run the Accented library more than once, which may lead to errors. See https://accented.dev/getting-started#run-once-per-page',
87
88
  );
88
89
  console.trace();
89
90
  }
@@ -96,9 +97,9 @@ export function accented(options: AccentedOptions = {}): DisableAccented {
96
97
  const { disconnect: cleanupIntersectionObserver, intersectionObserver } =
97
98
  setupIntersectionObserver();
98
99
  const cleanupScanner = createScanner(name, context, axeOptions, throttle, callback);
99
- const cleanupDomUpdater = createDomUpdater(name, intersectionObserver);
100
+ const cleanupDomUpdater = output.page ? createDomUpdater(name, intersectionObserver) : () => {};
100
101
  const cleanupLogger = output.console ? createLogger() : () => {};
101
- const cleanupScrollListeners = setupScrollListeners();
102
+ const cleanupScrollListeners = output.page ? setupScrollListeners() : () => {};
102
103
  const cleanupResizeListener = supportsAnchorPositioning(window)
103
104
  ? () => {}
104
105
  : setupResizeListener();
@@ -0,0 +1,2 @@
1
+ export const description =
2
+ 'A frontend library for continuous accessibility testing and issue highlighting';
@@ -3,6 +3,42 @@ export const primaryColorLight = 'oklch(0.8 0.2 0)';
3
3
  export const colorLight = 'oklch(0.98 0 0)';
4
4
  export const colorDark = 'oklch(0.22 0 0)';
5
5
 
6
+ const hueGray = 0;
7
+ const hueBlue = 230;
8
+ const hueGold = 90;
9
+ const hueRed = 0;
10
+
11
+ const focusLightnessLight = 0.45;
12
+ const focusLightnessDark = 0.8;
13
+ const chromaFocus = 0.25;
14
+
15
+ export const colorFocus = `light-dark(oklch(${focusLightnessLight} ${chromaFocus} ${hueBlue}), oklch(${focusLightnessDark} ${chromaFocus} ${hueBlue}))`;
16
+
17
+ const impactLightnessLight = 0.8;
18
+ const impactLightnessDark = 0.45;
19
+ const chromaImpactGray = 0;
20
+ const chromaImpactNonGray = 0.16;
21
+
22
+ const colorImpactMinorLight = `oklch(${impactLightnessLight} ${chromaImpactGray} ${hueGray})`;
23
+ const colorImpactModerateLight = `oklch(${impactLightnessLight} ${chromaImpactNonGray} ${hueBlue})`;
24
+ const colorImpactSeriousLight = `oklch(${impactLightnessLight} ${chromaImpactNonGray} ${hueGold})`;
25
+ const colorImpactCriticalLight = `oklch(${impactLightnessLight} ${chromaImpactNonGray} ${hueRed})`;
26
+
27
+ const colorImpactMinorDark = `oklch(${impactLightnessDark} ${chromaImpactGray} ${hueGray})`;
28
+ const colorImpactModerateDark = `oklch(${impactLightnessDark} ${chromaImpactNonGray} ${hueBlue})`;
29
+ const colorImpactSeriousDark = `oklch(${impactLightnessDark} ${chromaImpactNonGray} ${hueGold})`;
30
+ const colorImpactCriticalDark = `oklch(${impactLightnessDark} ${chromaImpactNonGray} ${hueRed})`;
31
+
32
+ export const colorImpactMinor = `light-dark(${colorImpactMinorLight}, ${colorImpactMinorDark})`;
33
+ export const colorImpactModerate = `light-dark(${colorImpactModerateLight}, ${colorImpactModerateDark})`;
34
+ export const colorImpactSerious = `light-dark(${colorImpactSeriousLight}, ${colorImpactSeriousDark})`;
35
+ export const colorImpactCritical = `light-dark(${colorImpactCriticalLight}, ${colorImpactCriticalDark})`;
36
+
37
+ export const consoleColorImpactMinor = `light-dark(${colorImpactMinorDark}, ${colorImpactMinorLight})`;
38
+ export const consoleColorImpactModerate = `light-dark(${colorImpactModerateDark}, ${colorImpactModerateLight})`;
39
+ export const consoleColorImpactSerious = `light-dark(${colorImpactSeriousDark}, ${colorImpactSeriousLight})`;
40
+ export const consoleColorImpactCritical = `light-dark(${colorImpactCriticalDark}, ${colorImpactCriticalLight})`;
41
+
6
42
  /* https://systemfontstack.com/ */
7
43
  export const fontSystemSans =
8
44
  '-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, Adwaita Sans, Cantarell, Ubuntu, roboto, noto, helvetica, arial, sans-serif';
package/src/constants.ts CHANGED
@@ -1,3 +1,7 @@
1
- export const accentedUrl = 'https://www.accented.dev';
1
+ import type { Issue } from './types';
2
+
3
+ export const accentedUrl = 'https://accented.dev';
2
4
  export const issuesUrl = 'https://github.com/pomerantsev/accented/issues';
3
5
  export const getAccentedElementNames = (name: string) => [`${name}-trigger`, `${name}-dialog`];
6
+
7
+ export const orderedImpacts: Array<Issue['impact']> = ['minor', 'moderate', 'serious', 'critical'];
@@ -1,5 +1,15 @@
1
1
  import type { Signal } from '@preact/signals-core';
2
- import { colorDark, colorLight, fontSystemMono, fontSystemSans } from '../common/tokens.js';
2
+ import {
3
+ colorDark,
4
+ colorFocus,
5
+ colorImpactCritical,
6
+ colorImpactMinor,
7
+ colorImpactModerate,
8
+ colorImpactSerious,
9
+ colorLight,
10
+ fontSystemMono,
11
+ fontSystemSans,
12
+ } from '../common/tokens.js';
3
13
  import { accentedUrl } from '../constants.js';
4
14
  import { logAndRethrow } from '../log-and-rethrow.js';
5
15
  import type { Issue } from '../types.ts';
@@ -18,7 +28,7 @@ export interface AccentedDialog extends HTMLElement {
18
28
  export const getAccentedDialog = () => {
19
29
  const dialogTemplate = document.createElement('template');
20
30
  dialogTemplate.innerHTML = `
21
- <dialog dir="ltr" lang="en" aria-labelledby="title">
31
+ <dialog dir="ltr" lang="en" aria-labelledby="title" closedby="any">
22
32
  <div id="button-container">
23
33
  <button id="close" aria-label="Close">✕</button>
24
34
  </div>
@@ -65,30 +75,13 @@ export const getAccentedDialog = () => {
65
75
  --background-color: light-dark(var(--light-color), var(--dark-color));
66
76
  --text-color: light-dark(var(--dark-color), var(--light-color));
67
77
 
68
- --impact-lightness: 0.80;
69
- --focus-lightness: 0.45;
70
- @media (prefers-color-scheme: dark) {
71
- --impact-lightness: 0.45;
72
- --focus-lightness: 0.80;
73
- }
74
-
75
- --blue-hue: 230;
76
- --gold-hue: 90;
77
- --red-hue: 0;
78
-
79
78
  /* Contrasts with background. */
80
- --focus-color: oklch(var(--focus-lightness) 0.25 var(--blue-hue));
81
-
82
- --impact-chroma: 0.16;
83
-
84
- --impact-moderate-hue: var(--blue-hue);
85
- --impact-serious-hue: var(--gold-hue);
86
- --impact-critical-hue: var(--red-hue);
79
+ --focus-color: ${colorFocus};
87
80
 
88
- --impact-minor-color: oklch(var(--impact-lightness) 0 0);
89
- --impact-moderate-color: oklch(var(--impact-lightness) var(--impact-chroma) var(--impact-moderate-hue));
90
- --impact-serious-color: oklch(var(--impact-lightness) var(--impact-chroma) var(--impact-serious-hue));
91
- --impact-critical-color: oklch(var(--impact-lightness) var(--impact-chroma) var(--impact-critical-hue));
81
+ --impact-minor-color: ${colorImpactMinor};
82
+ --impact-moderate-color: ${colorImpactModerate};
83
+ --impact-serious-color: ${colorImpactSerious};
84
+ --impact-critical-color: ${colorImpactCritical};
92
85
 
93
86
  --base-size: max(1rem, 16px);
94
87
 
@@ -396,6 +389,11 @@ export const getAccentedDialog = () => {
396
389
  }
397
390
  }
398
391
 
392
+ /**
393
+ * This can be removed once the closedBy attribute
394
+ * is available in all supported browsers:
395
+ * https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy
396
+ */
399
397
  #onDialogClick(event: MouseEvent) {
400
398
  const dialog = event.currentTarget as HTMLDialogElement;
401
399
  if (
@@ -1,5 +1,5 @@
1
- import { effect } from '@preact/signals-core';
2
1
  import type { Signal } from '@preact/signals-core';
2
+ import { effect } from '@preact/signals-core';
3
3
  import { fontSystemSans } from '../common/tokens.js';
4
4
  import { logAndRethrow } from '../log-and-rethrow.js';
5
5
  import type { Position } from '../types.ts';
@@ -29,10 +29,16 @@ export const getAccentedTrigger = (name: string) => {
29
29
  --ratio: 1.2;
30
30
  --base-size: max(1rem, 16px);
31
31
  position: fixed !important;
32
- inset-inline-start: anchor(self-start) !important;
33
- inset-inline-end: anchor(self-end) !important;
34
- inset-block-start: anchor(self-start) !important;
35
- inset-block-end: anchor(self-end) !important;
32
+ ${
33
+ supportsAnchorPositioning(window)
34
+ ? `
35
+ inset-inline-start: anchor(self-start) !important;
36
+ inset-inline-end: anchor(self-end) !important;
37
+ inset-block-start: anchor(self-start) !important;
38
+ inset-block-end: anchor(self-end) !important;
39
+ `
40
+ : ''
41
+ }
36
42
 
37
43
  /* Revert potential effects of white-space: pre; set on a trigger's ancestor. */
38
44
  white-space: normal !important;
package/src/logger.ts CHANGED
@@ -1,36 +1,290 @@
1
1
  import { effect } from '@preact/signals-core';
2
- import { accentedUrl } from './constants.js';
2
+ import {
3
+ consoleColorImpactCritical,
4
+ consoleColorImpactMinor,
5
+ consoleColorImpactModerate,
6
+ consoleColorImpactSerious,
7
+ } from './common/tokens.js';
8
+ import { accentedUrl, orderedImpacts } from './constants.js';
3
9
  import { elementsWithIssues, enabled } from './state.js';
4
- import type { ElementWithIssues } from './types.ts';
10
+ import type { ElementWithIssues, Issue } from './types.ts';
11
+ import { areElementsWithIssuesEqual } from './utils/are-elements-with-issues-equal.js';
12
+ import { areIssueSetsEqual } from './utils/are-issue-sets-equal.js';
13
+ import { areIssuesEqual } from './utils/are-issues-equal.js';
5
14
 
6
- function filterPropsForOutput(elements: Array<ElementWithIssues>) {
7
- return elements.map(({ element, issues }) => ({ element, issues }));
15
+ // For user friendliness, we want to balance two things:
16
+ // * the user shouldn't have to click in the console too many times to get to the info they need;
17
+ // * the output should be concise and not overwhelm the user with too much information at once.
18
+ // This number is chosen as a compromise between these two factors.
19
+ const MAX_ISSUES_BEFORE_OUTPUT_COLLAPSE = 5;
20
+
21
+ // Groups have bold color by default in the console.
22
+ // This doesn't seem appropriate for our purposes, so we have to explicitly set the normal font weight.
23
+ const defaultStyle = 'font-weight: normal;';
24
+
25
+ // We'll use the same colors in the console as in the dialog UI
26
+ // (except the theme will be reversed since in the dialog, the color needs to have enough contrast against text color,
27
+ // and in the console, the color needs to have enough contrast against the background color).
28
+ const colors = {
29
+ minor: consoleColorImpactMinor,
30
+ moderate: consoleColorImpactModerate,
31
+ serious: consoleColorImpactSerious,
32
+ critical: consoleColorImpactCritical,
33
+ };
34
+
35
+ const uppercasedImpactText = (impact: Issue['impact']) =>
36
+ impact.charAt(0).toUpperCase() + impact.slice(1);
37
+
38
+ const titleAndUrl = (issue: { title: Issue['title']; url: Issue['url'] }) =>
39
+ `${issue.title} ${issue.url}`;
40
+
41
+ // This sorting is not ideal since it doesn't work very well with shadow DOM.
42
+ const sortByElementPositions = (
43
+ a: ElementWithIssues['element'],
44
+ b: ElementWithIssues['element'],
45
+ ) => (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_PRECEDING ? 1 : -1);
46
+
47
+ type IssueType = {
48
+ title: Issue['title'];
49
+ url: Issue['url'];
50
+ impact: Issue['impact'];
51
+ elements: Array<{ element: ElementWithIssues['element']; description: Issue['description'] }>;
52
+ };
53
+
54
+ type GroupedByIssueType = Record<string, IssueType>;
55
+
56
+ const getIssueTypeGroups = (elementsWithIssues: Array<ElementWithIssues>) => {
57
+ const groupedByIssueType = elementsWithIssues.reduce((acc, { element, issues }) => {
58
+ for (const issue of issues) {
59
+ if (!acc[issue.id]) {
60
+ acc[issue.id] = {
61
+ title: issue.title,
62
+ url: issue.url,
63
+ impact: issue.impact,
64
+ elements: [],
65
+ };
66
+ }
67
+ acc[issue.id]?.elements.push({ element, description: issue.description });
68
+ }
69
+ return acc;
70
+ }, {} as GroupedByIssueType);
71
+
72
+ const sorted = Object.values(groupedByIssueType).sort((a, b) => {
73
+ const impactComparison = orderedImpacts.indexOf(b.impact) - orderedImpacts.indexOf(a.impact);
74
+ if (impactComparison !== 0) {
75
+ return impactComparison;
76
+ }
77
+ return b.elements.length - a.elements.length;
78
+ });
79
+
80
+ return sorted;
81
+ };
82
+
83
+ function logIssuesByElement(elementsWithIssues: Array<ElementWithIssues>) {
84
+ // Elements with more severe issues (or with a higher number of issues of the same severity)
85
+ // will appear higher in the output.
86
+ // This way, issues with a higher severity will be prioritized.
87
+ const sortedElementsWithIssues = elementsWithIssues.toSorted((a, b) => {
88
+ const impacts = orderedImpacts.toReversed();
89
+ const impactWithDifferentIssueCount = impacts.find((impact) => {
90
+ const aCount = a.issues.filter((issue) => issue.impact === impact).length;
91
+ const bCount = b.issues.filter((issue) => issue.impact === impact).length;
92
+ return aCount !== bCount;
93
+ });
94
+ if (impactWithDifferentIssueCount) {
95
+ const aCount = a.issues.filter(
96
+ (issue) => issue.impact === impactWithDifferentIssueCount,
97
+ ).length;
98
+ const bCount = b.issues.filter(
99
+ (issue) => issue.impact === impactWithDifferentIssueCount,
100
+ ).length;
101
+ return bCount - aCount; // Sort by count of issues with the same impact
102
+ }
103
+ return sortByElementPositions(a.element, b.element);
104
+ });
105
+
106
+ for (const { element, issues } of sortedElementsWithIssues) {
107
+ const sortedAndFilteredImpacts = orderedImpacts
108
+ .toReversed()
109
+ .filter((impact) => issues.some((issue) => issue.impact === impact));
110
+
111
+ const issuesWithImpacts = sortedAndFilteredImpacts
112
+ .map((impact) => `%c${issues.filter((issue) => issue.impact === impact).length} ${impact}`)
113
+ .join(', ');
114
+
115
+ const baseOutput = `${issuesWithImpacts}%c`;
116
+ const output =
117
+ issues.length === 1 && issues[0]
118
+ ? `${baseOutput}\n${titleAndUrl(issues[0])}\n%o`
119
+ : `${baseOutput}\n%o`;
120
+
121
+ console.groupCollapsed(
122
+ output,
123
+ ...sortedAndFilteredImpacts.map((impact) => `color: ${colors[impact]};`),
124
+ defaultStyle,
125
+ element,
126
+ );
127
+
128
+ if (issues.length === 1) {
129
+ // If an element has just one issue, output that issue inline, to reduce the number of clicks in the console for the user.
130
+ console.log(issues[0]?.description);
131
+ } else {
132
+ for (const issue of issues) {
133
+ console.groupCollapsed(
134
+ `%c${uppercasedImpactText(issue.impact)}:%c\n${titleAndUrl(issue)}`,
135
+ `color: ${colors[issue.impact]};`,
136
+ defaultStyle,
137
+ );
138
+ console.log(issue.description);
139
+ console.groupEnd();
140
+ }
141
+ }
142
+ console.groupEnd();
143
+ }
144
+ }
145
+
146
+ function logIssuesByType(issueTypeGroups: Array<IssueType>) {
147
+ for (const { title, url, impact, elements } of issueTypeGroups) {
148
+ // We'll output the element itself next to the issue description if there's just one associated element,
149
+ // to reduce the number of clicks in the console for the user.
150
+ const shouldOutputElementInline = elements.length === 1;
151
+ const baseOutput = `%c${uppercasedImpactText(impact)} (${elements.length} element${elements.length === 1 ? '' : 's'}):%c\n${titleAndUrl({ title, url })}`;
152
+ const output = shouldOutputElementInline ? `${baseOutput}\n%o` : baseOutput;
153
+ console.groupCollapsed(
154
+ output,
155
+ `color: ${colors[impact]};`,
156
+ defaultStyle,
157
+ ...(shouldOutputElementInline ? [elements[0]?.element] : []),
158
+ );
159
+ if (shouldOutputElementInline) {
160
+ console.log(elements[0]?.description);
161
+ } else {
162
+ for (const { element, description } of elements.sort((elementContainer1, elementContainer2) =>
163
+ sortByElementPositions(elementContainer1.element, elementContainer2.element),
164
+ )) {
165
+ console.groupCollapsed('%o', element);
166
+ console.log(description);
167
+ console.groupEnd();
168
+ }
169
+ }
170
+ console.groupEnd();
171
+ }
172
+ }
173
+
174
+ function logNewIssues(
175
+ elementsWithIssues: Array<ElementWithIssues>,
176
+ previousElementsWithIssues: Array<ElementWithIssues>,
177
+ ) {
178
+ // The elements with accessibility issues that didn't have any associated issues
179
+ // or that weren't in the DOM at the time of last scan.
180
+ const addedElements = elementsWithIssues.filter((elementWithIssues) => {
181
+ return !previousElementsWithIssues.some((previousElementWithIssues) =>
182
+ areElementsWithIssuesEqual(previousElementWithIssues, elementWithIssues),
183
+ );
184
+ });
185
+
186
+ // The elements that now have more issues than at the time of last scan,
187
+ // with just the new issues (previously existing issues are filtered out).
188
+ const existingElementsWithNewIssues = elementsWithIssues.reduce<Array<ElementWithIssues>>(
189
+ (acc, elementWithIssues) => {
190
+ let foundElementWithIssues: ElementWithIssues | null = null;
191
+ for (const previousElementWithIssues of previousElementsWithIssues) {
192
+ if (
193
+ areElementsWithIssuesEqual(previousElementWithIssues, elementWithIssues) &&
194
+ !areIssueSetsEqual(previousElementWithIssues.issues, elementWithIssues.issues)
195
+ ) {
196
+ const newIssues = elementWithIssues.issues.filter((issue) => {
197
+ return !previousElementWithIssues.issues.some((prevIssue) =>
198
+ areIssuesEqual(prevIssue, issue),
199
+ );
200
+ });
201
+ if (newIssues.length > 0) {
202
+ foundElementWithIssues = {
203
+ ...elementWithIssues,
204
+ issues: newIssues,
205
+ };
206
+ acc.push(foundElementWithIssues);
207
+ }
208
+ break;
209
+ }
210
+ }
211
+ return acc;
212
+ },
213
+ [],
214
+ );
215
+
216
+ const elementsWithNewIssues = [...addedElements, ...existingElementsWithNewIssues];
217
+ const newIssueCount = elementsWithNewIssues.reduce((acc, { issues }) => acc + issues.length, 0);
218
+ if (newIssueCount === 0) {
219
+ console.log('No new issues');
220
+ } else {
221
+ const newIssuesMessage = `%cNew issues (${newIssueCount} in ${elementsWithNewIssues.length} element${elementsWithNewIssues.length === 1 ? '' : 's'})`;
222
+ if (newIssueCount <= MAX_ISSUES_BEFORE_OUTPUT_COLLAPSE) {
223
+ // Don't collapse the new issues if there are not too many (this hopefully helps user avoid unnecessary clicks in the console).
224
+ console.group(newIssuesMessage, defaultStyle);
225
+ } else {
226
+ console.groupCollapsed(newIssuesMessage, defaultStyle);
227
+ }
228
+ // Output by element (no specific reason for this choice, just a preference).
229
+ logIssuesByElement(elementsWithNewIssues);
230
+ console.groupEnd();
231
+ }
232
+ }
233
+
234
+ function logIssues(
235
+ elementsWithIssues: Array<ElementWithIssues>,
236
+ previousElementsWithIssues: Array<ElementWithIssues>,
237
+ ) {
238
+ const elementCount = elementsWithIssues.length;
239
+
240
+ if (elementCount === 0) {
241
+ console.log(`No accessibility issues (Accented, ${accentedUrl}).`);
242
+ return;
243
+ }
244
+
245
+ const issueCount = elementsWithIssues.reduce((acc, { issues }) => acc + issues.length, 0);
246
+ console.group(
247
+ `%c${issueCount} accessibility issue${issueCount === 1 ? '' : 's'} in ${elementCount} element${elementCount === 1 ? '' : 's'} (Accented, ${accentedUrl}):\n`,
248
+ defaultStyle,
249
+ );
250
+
251
+ if (issueCount <= MAX_ISSUES_BEFORE_OUTPUT_COLLAPSE) {
252
+ // Don't collapse issues if there are not too many (this hopefully helps user avoid unnecessary clicks in the console).
253
+ // Output by element (no specific reason for this choice, just a preference).
254
+ logIssuesByElement(elementsWithIssues);
255
+ } else {
256
+ // When there are many issues, outputting them all would probably make the console too noisy,
257
+ // so we collapse them.
258
+ // Moreover, we output all issues twice, by element and by issue type, to give users more choice.
259
+ console.groupCollapsed(`%cAll by element (${elementsWithIssues.length})`, defaultStyle);
260
+ logIssuesByElement(elementsWithIssues);
261
+ console.groupEnd();
262
+
263
+ const issueTypeGroups = getIssueTypeGroups(elementsWithIssues);
264
+ console.groupCollapsed(`%cAll by issue type (${issueTypeGroups.length})`, defaultStyle);
265
+ logIssuesByType(issueTypeGroups);
266
+ console.groupEnd();
267
+ }
268
+
269
+ if (previousElementsWithIssues.length > 0) {
270
+ // Log new issues separately, to make it easier for the user to know what issues
271
+ // were introduced recently.
272
+ logNewIssues(elementsWithIssues, previousElementsWithIssues);
273
+ }
274
+
275
+ console.groupEnd();
8
276
  }
9
277
 
10
278
  export function createLogger() {
11
- let firstRun = true;
279
+ let previousElementsWithIssues: Array<ElementWithIssues> = [];
12
280
 
13
281
  return effect(() => {
14
282
  if (!enabled.value) {
15
283
  return;
16
284
  }
17
285
 
18
- const elementCount = elementsWithIssues.value.length;
19
- if (elementCount > 0) {
20
- const issueCount = elementsWithIssues.value.reduce(
21
- (acc, { issues }) => acc + issues.length,
22
- 0,
23
- );
24
- console.log(
25
- `${issueCount} accessibility issue${issueCount === 1 ? '' : 's'} found in ${elementCount} element${issueCount === 1 ? '' : 's'} (Accented, ${accentedUrl}):\n`,
26
- filterPropsForOutput(elementsWithIssues.value),
27
- );
28
- } else {
29
- if (firstRun) {
30
- firstRun = false;
31
- } else {
32
- console.log(`No accessibility issues found (Accented, ${accentedUrl}).`);
33
- }
34
- }
286
+ logIssues(elementsWithIssues.value, previousElementsWithIssues);
287
+
288
+ previousElementsWithIssues = elementsWithIssues.value;
35
289
  });
36
290
  }
package/src/scanner.ts CHANGED
@@ -77,7 +77,7 @@ export function createScanner(
77
77
  });
78
78
  } catch (error) {
79
79
  console.error(
80
- `Accented: axe-core (the accessibility testing engine) threw an error. Check the \`axeOptions\` property (https://www.accented.dev/api#axeoptions) that you’re passing to Accented. If you still think it’s a bug in Accented, file an issue at ${issuesUrl}.\n`,
80
+ `Accented: axe-core (the accessibility testing engine) threw an error. Check the \`axeOptions\` property (https://accented.dev/api#axeoptions) that you’re passing to Accented. If you still think it’s a bug in Accented, file an issue at ${issuesUrl}.\n`,
81
81
  error,
82
82
  );
83
83
  }
package/src/types.ts CHANGED
@@ -36,9 +36,40 @@ export type Output = {
36
36
  /**
37
37
  * Whether the list of elements with issues should be printed to the browser console whenever issues are added, removed, or changed.
38
38
  *
39
+ * **Example:**
40
+ *
41
+ * ```js
42
+ * accented({
43
+ * output: {
44
+ * console: false
45
+ * }
46
+ * });
47
+ * ```
48
+ *
49
+ * In the example, the issues will not be logged to the console, while elements on the page will still be highlighted.
50
+ *
39
51
  * @default true
40
52
  * */
41
53
  console?: boolean;
54
+
55
+ /**
56
+ * Whether Accented should highlight elements with issues on the page.
57
+ *
58
+ * **Example:**
59
+ *
60
+ * ```js
61
+ * accented({
62
+ * output: {
63
+ * page: false
64
+ * }
65
+ * });
66
+ * ```
67
+ *
68
+ * In the example, the issues will only be logged to the console, with no highlights on the page.
69
+ *
70
+ * @default true
71
+ * */
72
+ page?: boolean;
42
73
  };
43
74
 
44
75
  /**
@@ -229,7 +260,7 @@ export type Issue = {
229
260
  title: string;
230
261
  description: string;
231
262
  url: string;
232
- impact: axe.ImpactValue;
263
+ impact: Exclude<axe.ImpactValue, null>;
233
264
  };
234
265
 
235
266
  export type BaseElementWithIssues = {
@@ -19,12 +19,7 @@ const issue2: Issue = {
19
19
  impact: 'serious',
20
20
  };
21
21
 
22
- // @ts-expect-error
23
- const issue2Clone: Issue = Object.keys(issue2).reduce((obj, key) => {
24
- // @ts-expect-error
25
- obj[key] = issue2[key];
26
- return obj;
27
- }, {});
22
+ const issue2Clone: Issue = structuredClone(issue2);
28
23
 
29
24
  const issue3: Issue = {
30
25
  id: 'id3',
@@ -1,12 +1,9 @@
1
1
  import type { Issue } from '../types.ts';
2
-
3
- const issueProps: Array<keyof Issue> = ['id', 'title', 'description', 'url', 'impact'];
2
+ import { areIssuesEqual } from './are-issues-equal.js';
4
3
 
5
4
  export function areIssueSetsEqual(issues1: Array<Issue>, issues2: Array<Issue>) {
6
5
  return (
7
6
  issues1.length === issues2.length &&
8
- issues1.every((issue1) =>
9
- Boolean(issues2.find((issue2) => issueProps.every((prop) => issue2[prop] === issue1[prop]))),
10
- )
7
+ issues1.every((issue1) => Boolean(issues2.find((issue2) => areIssuesEqual(issue1, issue2))))
11
8
  );
12
9
  }