accented 0.0.0-20250124142030
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.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/accented.d.ts +27 -0
- package/dist/accented.d.ts.map +1 -0
- package/dist/accented.js +85 -0
- package/dist/accented.js.map +1 -0
- package/dist/dom-updater.d.ts +2 -0
- package/dist/dom-updater.d.ts.map +1 -0
- package/dist/dom-updater.js +96 -0
- package/dist/dom-updater.js.map +1 -0
- package/dist/elements/accented-container.d.ts +350 -0
- package/dist/elements/accented-container.d.ts.map +1 -0
- package/dist/elements/accented-container.js +131 -0
- package/dist/elements/accented-container.js.map +1 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +20 -0
- package/dist/logger.js.map +1 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +56 -0
- package/dist/scanner.js.map +1 -0
- package/dist/state.d.ts +5 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +8 -0
- package/dist/state.js.map +1 -0
- package/dist/task-queue.d.ts +10 -0
- package/dist/task-queue.d.ts.map +1 -0
- package/dist/task-queue.js +44 -0
- package/dist/task-queue.js.map +1 -0
- package/dist/types.d.ts +84 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/are-issue-sets-equal.d.ts +3 -0
- package/dist/utils/are-issue-sets-equal.d.ts.map +1 -0
- package/dist/utils/are-issue-sets-equal.js +6 -0
- package/dist/utils/are-issue-sets-equal.js.map +1 -0
- package/dist/utils/deep-merge.d.ts +4 -0
- package/dist/utils/deep-merge.d.ts.map +1 -0
- package/dist/utils/deep-merge.js +18 -0
- package/dist/utils/deep-merge.js.map +1 -0
- package/dist/utils/transform-violations.d.ts +4 -0
- package/dist/utils/transform-violations.d.ts.map +1 -0
- package/dist/utils/transform-violations.js +39 -0
- package/dist/utils/transform-violations.js.map +1 -0
- package/dist/utils/update-elements-with-issues.d.ts +5 -0
- package/dist/utils/update-elements-with-issues.d.ts.map +1 -0
- package/dist/utils/update-elements-with-issues.js +46 -0
- package/dist/utils/update-elements-with-issues.js.map +1 -0
- package/package.json +38 -0
- package/src/accented.test.ts +24 -0
- package/src/accented.ts +99 -0
- package/src/dom-updater.ts +104 -0
- package/src/elements/accented-container.ts +147 -0
- package/src/logger.ts +21 -0
- package/src/scanner.ts +68 -0
- package/src/state.ts +12 -0
- package/src/task-queue.test.ts +135 -0
- package/src/task-queue.ts +59 -0
- package/src/types.ts +97 -0
- package/src/utils/are-issue-sets-equal.test.ts +49 -0
- package/src/utils/are-issue-sets-equal.ts +10 -0
- package/src/utils/deep-merge.test.ts +27 -0
- package/src/utils/deep-merge.ts +18 -0
- package/src/utils/transform-violations.test.ts +124 -0
- package/src/utils/transform-violations.ts +45 -0
- package/src/utils/update-elements-with-issues.test.ts +209 -0
- package/src/utils/update-elements-with-issues.ts +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Pavel Pomerantsev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Accented
|
|
2
|
+
|
|
3
|
+
Accented is a library that helps visually identify accessibility issues in a website or webapp under development.
|
|
4
|
+
|
|
5
|
+
It can be set up in only a few lines of code
|
|
6
|
+
|
|
7
|
+
It complements approaches to other means of continuous automated accessibility testing such as static code analysis and inclusion of accessibility tests in test suites. Think of it as a form of linting, but for a rendered web page rather than for source code.
|
|
8
|
+
|
|
9
|
+
Accented uses the [axe-core](https://github.com/dequelabs/axe-core) testing engine.
|
|
10
|
+
|
|
11
|
+
TODO: example screenshots, without Accented / with Accented.
|
|
12
|
+
|
|
13
|
+
## Basic usage
|
|
14
|
+
|
|
15
|
+
* The library can be used in three ways:
|
|
16
|
+
* NPM (with a bundler)
|
|
17
|
+
* `import accented from 'https://esm.sh/accented';`.
|
|
18
|
+
* `import('https://esm.sh/accented').then(({default: accented}) => { accented(); });` (this version will work in the console, unless it violates the content security policy, which shouldn't be the case locally).
|
|
19
|
+
* For example, this works on medium.com
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
### Exports
|
|
24
|
+
|
|
25
|
+
* `accented`: the default library export. It’s the function that enables the continuous scanning and highlighting
|
|
26
|
+
on the page in whose context in was called. Example: `const disable = accented(options)`.
|
|
27
|
+
* Parameters: the only parameter is `options`. See [Options](#options).
|
|
28
|
+
* Returns: a `disable` function that takes no parameters. When called, disables the scanning and highlighting,
|
|
29
|
+
and cleans up any changes that Accented has made to the page.
|
|
30
|
+
|
|
31
|
+
#### Type exports
|
|
32
|
+
|
|
33
|
+
The following types are exported for TypeScript consumers:
|
|
34
|
+
|
|
35
|
+
* `AccentedOptions`: the `options` parameter (see [Options](#options)).
|
|
36
|
+
* `DisableAccented`: the type of the function returned by `accented`.
|
|
37
|
+
|
|
38
|
+
### Options
|
|
39
|
+
|
|
40
|
+
#### `outputToConsole`
|
|
41
|
+
|
|
42
|
+
**Type:** boolean.
|
|
43
|
+
|
|
44
|
+
**Default:** `true`.
|
|
45
|
+
|
|
46
|
+
Whether the list of elements with issues should be printed to the browser console whenever issues are added, removed, or changed.
|
|
47
|
+
|
|
48
|
+
#### `callback`
|
|
49
|
+
|
|
50
|
+
**Type:** function.
|
|
51
|
+
|
|
52
|
+
**Default:** no-op (`() => {}`).
|
|
53
|
+
|
|
54
|
+
A function that Accented will call after every scan.
|
|
55
|
+
It accepts a single `params` object with the following properties:
|
|
56
|
+
|
|
57
|
+
* `elementsWithIssues`: the most up-to-date array of all elements with accessibility issues.
|
|
58
|
+
* `scanDuration`: how long the last scan took, in milliseconds (may be useful for performance tracking).
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
accented({
|
|
64
|
+
callback: ({ elementsWithIssues, scanDuration }) => {
|
|
65
|
+
console.log('Elements with issues:', elementsWithIssues);
|
|
66
|
+
console.log('Scan duration:', scanDuration);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### `name`
|
|
72
|
+
|
|
73
|
+
**Type:** string.
|
|
74
|
+
|
|
75
|
+
**Default:** `"accented"`.
|
|
76
|
+
|
|
77
|
+
The character sequence that’s used in various elements, attributes and stylesheets that Accented adds to the page.
|
|
78
|
+
|
|
79
|
+
You shouldn’t have to use this attribute unless some of the names on your page conflict with what Accented provides by default.
|
|
80
|
+
|
|
81
|
+
* The data attribute that’s added to elements with issues (default: `data-accented`).
|
|
82
|
+
* The custom element that encapsulates the button and dialog attached to each element with issues (default: `accented-container`).
|
|
83
|
+
* The CSS cascade layer containing page-wide Accented-specific styles (default: `accented`).
|
|
84
|
+
* The prefix for some of the CSS custom properties used by Accented (default: `--accented-`).
|
|
85
|
+
|
|
86
|
+
**Example:**
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
accented({name: 'my-name'});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
With the above option provided, the attribute set on elements with issues will be `data-my-name`,
|
|
93
|
+
the custom element will be called `my-name-container`, and so on.
|
|
94
|
+
|
|
95
|
+
#### `throttle`
|
|
96
|
+
|
|
97
|
+
An object controlling when Accented will run its scans.
|
|
98
|
+
|
|
99
|
+
#### `throttle.wait`
|
|
100
|
+
|
|
101
|
+
**Type:** number.
|
|
102
|
+
|
|
103
|
+
**Default:** 1000.
|
|
104
|
+
|
|
105
|
+
The delay (in milliseconds) after a mutation or after the last Accented scan.
|
|
106
|
+
|
|
107
|
+
If the page you’re scanning has a lot of nodes,
|
|
108
|
+
scanning may take a noticeable time (~ a few hundred milliseconds),
|
|
109
|
+
during which time the main thread will be blocked most of the time.
|
|
110
|
+
|
|
111
|
+
You may want to experiment with this value if your page contents change frequently
|
|
112
|
+
or if it has JavaScript-based animations running on the main thread.
|
|
113
|
+
|
|
114
|
+
#### `throttle.leading`
|
|
115
|
+
|
|
116
|
+
**Type:** boolean.
|
|
117
|
+
|
|
118
|
+
**Default:** `true`.
|
|
119
|
+
|
|
120
|
+
If set to true, the scan runs immediately after a mutation.
|
|
121
|
+
In this case, `wait` only applies to subsequent scans,
|
|
122
|
+
giving the page at least `wait` milliseconds between the end of the previous scan
|
|
123
|
+
and the beginning of the next one.
|
|
124
|
+
|
|
125
|
+
If set to false, the wait applies to mutations as well,
|
|
126
|
+
delaying the output.
|
|
127
|
+
This may be useful if you’re expecting bursts of mutations on your page.
|
|
128
|
+
|
|
129
|
+
### Styling
|
|
130
|
+
|
|
131
|
+
TODO: Create a separate doc with info on using `:root` and CSS layers to control some aspects of styling.
|
|
132
|
+
|
|
133
|
+
## Miscellaneous
|
|
134
|
+
|
|
135
|
+
### Shadow DOM
|
|
136
|
+
|
|
137
|
+
Highlighting elements inside shadow DOM is not supported yet, see [#25](https://github.com/pomerantsev/accented/issues/25).
|
|
138
|
+
|
|
139
|
+
### Iframes
|
|
140
|
+
|
|
141
|
+
Although axe-core is capable of scanning iframes, Accented doesn’t provide that as a special capability.
|
|
142
|
+
|
|
143
|
+
Instead, if you wish to scan the document in an iframe, initialize Accented inside the iframed document.
|
|
144
|
+
There should be no interference between the instances of Accented running in the parent and child documents.
|
|
145
|
+
|
|
146
|
+
TODO: expand this section and better explain the concepts.
|
|
147
|
+
|
|
148
|
+
## Frequently asked questions
|
|
149
|
+
|
|
150
|
+
<!-- TODO: how can this section be better formatted? This probably should be regular sections rather than a Q&A. -->
|
|
151
|
+
|
|
152
|
+
**Q:** can Accented be used in a CI (continuous integration) environment?
|
|
153
|
+
|
|
154
|
+
**A:** no, it’s only meant for local development. Accented runs accessibility tests on every state of the page that’s currently in the developer’s browser. However, if you additionally need something for CI, consider using [axe-core](https://www.npmjs.com/package/axe-core) in your automated test suite, either directly, or through wrappers such as [jest-axe](https://www.npmjs.com/package/jest-axe) or [axe-playwright](https://www.npmjs.com/package/axe-playwright).
|
|
155
|
+
|
|
156
|
+
**Q:** does Accented affect performance?
|
|
157
|
+
|
|
158
|
+
**A:** TODO: it might (it’s inevitable because it’s on the main thread), but we’ve taken X, Y, and Z measures to make it less noticeable. You can also take A, B, and C steps yourself.
|
|
159
|
+
* Only re-running on the changed part of the page.
|
|
160
|
+
* Throttling calls and giving the ability to tweak it.
|
|
161
|
+
* Providing the ability to select which rules to run, and which elements to run them on.
|
|
162
|
+
* TODO: explore axe-core’s internals. Can I make it yield periodically?
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AccentedOptions, DisableAccented } from './types';
|
|
2
|
+
export type { AccentedOptions, DisableAccented };
|
|
3
|
+
/**
|
|
4
|
+
* Enables highlighting of elements with accessibility issues.
|
|
5
|
+
*
|
|
6
|
+
* @param {AccentedOptions} options - The options object.
|
|
7
|
+
*
|
|
8
|
+
* @returns A `disable` function that can be called to stop the scanning and highlighting.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* accented();
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const disableAccented = accented({
|
|
15
|
+
* outputToConsole: false,
|
|
16
|
+
* throttle: {
|
|
17
|
+
* wait: 500,
|
|
18
|
+
* leading: false
|
|
19
|
+
* },
|
|
20
|
+
* callback: ({ elementsWithIssues, scanDuration }) => {
|
|
21
|
+
* console.log('Elements with issues:', elementsWithIssues);
|
|
22
|
+
* console.log('Scan duration:', scanDuration);
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
export default function accented(options?: AccentedOptions): DisableAccented;
|
|
27
|
+
//# sourceMappingURL=accented.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accented.d.ts","sourceRoot":"","sources":["../src/accented.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAgB,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE9E,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,CAAC;AAiBjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,eAAe,CAiD/E"}
|
package/dist/accented.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import createDomUpdater from './dom-updater.js';
|
|
2
|
+
import createLogger from './logger.js';
|
|
3
|
+
import createScanner from './scanner.js';
|
|
4
|
+
import { enabled, extendedElementsWithIssues } from './state.js';
|
|
5
|
+
import deepMerge from './utils/deep-merge.js';
|
|
6
|
+
import getAccentedContainer from './elements/accented-container.js';
|
|
7
|
+
// IMPORTANT: when changing any of the properties or values, also do the following:
|
|
8
|
+
// * update the default value in the type documentation accordingly;
|
|
9
|
+
// * update validations and validation tests if necessary;
|
|
10
|
+
// * update examples in the accented() function JSDoc;
|
|
11
|
+
// * update examples in the Readme.
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
name: 'accented',
|
|
14
|
+
outputToConsole: true,
|
|
15
|
+
throttle: {
|
|
16
|
+
wait: 1000,
|
|
17
|
+
leading: true
|
|
18
|
+
},
|
|
19
|
+
callback: () => { }
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Enables highlighting of elements with accessibility issues.
|
|
23
|
+
*
|
|
24
|
+
* @param {AccentedOptions} options - The options object.
|
|
25
|
+
*
|
|
26
|
+
* @returns A `disable` function that can be called to stop the scanning and highlighting.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* accented();
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const disableAccented = accented({
|
|
33
|
+
* outputToConsole: false,
|
|
34
|
+
* throttle: {
|
|
35
|
+
* wait: 500,
|
|
36
|
+
* leading: false
|
|
37
|
+
* },
|
|
38
|
+
* callback: ({ elementsWithIssues, scanDuration }) => {
|
|
39
|
+
* console.log('Elements with issues:', elementsWithIssues);
|
|
40
|
+
* console.log('Scan duration:', scanDuration);
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
*/
|
|
44
|
+
export default function accented(options = {}) {
|
|
45
|
+
// Argument validation
|
|
46
|
+
if (options.throttle !== undefined) {
|
|
47
|
+
if (typeof options.throttle !== 'object' || options.throttle === null) {
|
|
48
|
+
throw new TypeError(`Invalid argument: \`throttle\` option must be an object if provided. It’s currently set to ${options.throttle}.`);
|
|
49
|
+
}
|
|
50
|
+
if (options.throttle.wait !== undefined && (typeof options.throttle.wait !== 'number' || options.throttle.wait < 0)) {
|
|
51
|
+
throw new TypeError(`Invalid argument: \`throttle.wait\` option must be a non-negative number if provided. It’s currently set to ${options.throttle.wait}.`);
|
|
52
|
+
}
|
|
53
|
+
if (options.callback !== undefined && typeof options.callback !== 'function') {
|
|
54
|
+
throw new TypeError(`Invalid argument: \`callback\` option must be a function if provided. It’s currently set to ${options.callback}.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
58
|
+
console.warn('Accented: this script can only run in the browser, and it’s likely running on the server now. Exiting.');
|
|
59
|
+
console.trace();
|
|
60
|
+
return () => { };
|
|
61
|
+
}
|
|
62
|
+
const { name, outputToConsole, throttle, callback } = deepMerge(defaultOptions, options);
|
|
63
|
+
const AccentedContainer = getAccentedContainer(name);
|
|
64
|
+
if (!customElements.get(`${name}-container`)) {
|
|
65
|
+
customElements.define(`${name}-container`, AccentedContainer);
|
|
66
|
+
}
|
|
67
|
+
if (enabled.value) {
|
|
68
|
+
// TODO: add link to relevant docs
|
|
69
|
+
console.warn('You are trying to run the Accented library more than once. ' +
|
|
70
|
+
'This will likely lead to errors.');
|
|
71
|
+
console.trace();
|
|
72
|
+
}
|
|
73
|
+
enabled.value = true;
|
|
74
|
+
const cleanupScanner = createScanner(name, throttle, callback);
|
|
75
|
+
const cleanupDomUpdater = createDomUpdater(name);
|
|
76
|
+
const cleanupLogger = outputToConsole ? createLogger() : () => { };
|
|
77
|
+
return () => {
|
|
78
|
+
enabled.value = false;
|
|
79
|
+
extendedElementsWithIssues.value = [];
|
|
80
|
+
cleanupScanner();
|
|
81
|
+
cleanupDomUpdater();
|
|
82
|
+
cleanupLogger();
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=accented.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accented.js","sourceRoot":"","sources":["../src/accented.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-updater.d.ts","sourceRoot":"","sources":["../src/dom-updater.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,IAAI,EAAE,MAAM,cAmGpD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { effect } from '@preact/signals-core';
|
|
2
|
+
import { extendedElementsWithIssues } from './state.js';
|
|
3
|
+
export default function createDomUpdater(name) {
|
|
4
|
+
const attrName = `data-${name}`;
|
|
5
|
+
function supportsAnchorPositioning() {
|
|
6
|
+
return CSS.supports('anchor-name: --foo') && CSS.supports('position-anchor: --foo');
|
|
7
|
+
}
|
|
8
|
+
function setAnchorName(element, id) {
|
|
9
|
+
const anchorNameValue = element.style.getPropertyValue('anchor-name') || window.getComputedStyle(element).getPropertyValue('anchor-name');
|
|
10
|
+
const anchorNames = anchorNameValue
|
|
11
|
+
.split(/,\s*/)
|
|
12
|
+
.filter(anchorName => anchorName.startsWith('--'));
|
|
13
|
+
if (anchorNames.length > 0) {
|
|
14
|
+
element.style.setProperty('anchor-name', `${anchorNameValue}, --${name}-anchor-${id}`);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
element.style.setProperty('anchor-name', `--${name}-anchor-${id}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function removeAnchorName(element, id) {
|
|
21
|
+
const anchorNameValue = element.style.getPropertyValue('anchor-name');
|
|
22
|
+
const anchorNames = anchorNameValue
|
|
23
|
+
.split(/,\s*/)
|
|
24
|
+
.filter(anchorName => anchorName.startsWith('--'));
|
|
25
|
+
const index = anchorNames.indexOf(`--${name}-anchor-${id}`);
|
|
26
|
+
if (anchorNames.length === 1 && index === 0) {
|
|
27
|
+
element.style.removeProperty('anchor-name');
|
|
28
|
+
}
|
|
29
|
+
else if (anchorNames.length > 1 && index > -1) {
|
|
30
|
+
element.style.setProperty('anchor-name', anchorNames.filter((_, i) => i !== index).join(', '));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function setIssues(extendedElementsWithIssues) {
|
|
34
|
+
const displayAccentedContainers = supportsAnchorPositioning();
|
|
35
|
+
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
36
|
+
elementWithIssues.element.setAttribute(attrName, elementWithIssues.id.toString());
|
|
37
|
+
if (displayAccentedContainers) {
|
|
38
|
+
setAnchorName(elementWithIssues.element, elementWithIssues.id);
|
|
39
|
+
if (elementWithIssues.element.parentElement) {
|
|
40
|
+
elementWithIssues.element.insertAdjacentElement('afterend', elementWithIssues.accentedContainer);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
elementWithIssues.element.insertAdjacentElement('beforeend', elementWithIssues.accentedContainer);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function removeIssues(extendedElementsWithIssues) {
|
|
49
|
+
for (const elementWithIssues of extendedElementsWithIssues) {
|
|
50
|
+
elementWithIssues.element.removeAttribute(attrName);
|
|
51
|
+
removeAnchorName(elementWithIssues.element, elementWithIssues.id);
|
|
52
|
+
elementWithIssues.accentedContainer.remove();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const stylesheet = new CSSStyleSheet();
|
|
56
|
+
stylesheet.replaceSync(`
|
|
57
|
+
@layer ${name} {
|
|
58
|
+
:root {
|
|
59
|
+
--${name}-primary-color: red;
|
|
60
|
+
--${name}-secondary-color: white;
|
|
61
|
+
--${name}-outline-width: 2px;
|
|
62
|
+
--${name}-outline-style: solid;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
[${attrName}]:not(:focus-visible) {
|
|
66
|
+
outline-width: var(--${name}-outline-width) !important;
|
|
67
|
+
outline-offset: calc(-1 * var(--${name}-outline-width)) !important;
|
|
68
|
+
outline-color: var(--${name}-primary-color) !important;
|
|
69
|
+
outline-style: var(--${name}-outline-style) !important;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
`);
|
|
73
|
+
let previousExtendedElementsWithIssues = [];
|
|
74
|
+
document.adoptedStyleSheets.push(stylesheet);
|
|
75
|
+
const removeStylesheet = () => {
|
|
76
|
+
if (document.adoptedStyleSheets.includes(stylesheet)) {
|
|
77
|
+
document.adoptedStyleSheets.splice(document.adoptedStyleSheets.indexOf(stylesheet), 1);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const disposeOfElementsEffect = effect(() => {
|
|
81
|
+
const added = extendedElementsWithIssues.value.filter(elementWithIssues => {
|
|
82
|
+
return !previousExtendedElementsWithIssues.some(previousElementWithIssues => previousElementWithIssues.element === elementWithIssues.element);
|
|
83
|
+
});
|
|
84
|
+
const removed = previousExtendedElementsWithIssues.filter(previousElementWithIssues => {
|
|
85
|
+
return !extendedElementsWithIssues.value.some(elementWithIssues => elementWithIssues.element === previousElementWithIssues.element);
|
|
86
|
+
});
|
|
87
|
+
removeIssues(removed);
|
|
88
|
+
setIssues(added);
|
|
89
|
+
previousExtendedElementsWithIssues = [...extendedElementsWithIssues.value];
|
|
90
|
+
});
|
|
91
|
+
return () => {
|
|
92
|
+
removeStylesheet();
|
|
93
|
+
disposeOfElementsEffect();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=dom-updater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-updater.js","sourceRoot":"","sources":["../src/dom-updater.ts"],"names":[],"mappings":"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"}
|