Repository: dequelabs/axe-core (JavaScript)

Issue:
Color-contrast does not take into account <slot> elements and the light DOM
Product: axe-core && axe Extension
Expectation: Correctly Report the Contrast on a Manual Scan
Actual: False Reports of an Issue
Motivation: This was Previously working

<pre><code>
axe-core version: 4.3.5
extension: 4.19.0
</code></pre>

To reproduce this issue you can use our test site https://main.wc.design.infor.com/ids-radio/
With Axe Latest version use the extension and Scan the Page.

I can see the page is reporting errors on the label custom element that is almost black on a white background. This previously did work ok (noticed in last week or so). I also wonder how the rules are deployed as we have seen other wierd things like some people seeing it and some not. We see the same thing in our tests which run the puppetteer axe.

![Screen Shot 2021-12-16 at 3 04 37 PM](https://user-images.githubusercontent.com/814283/146441670-9a263de8-dd1f-4ff8-83c7-dfb59a099dd9.png)

I wonder if its because the element is a customElement and the text is linked via a slot.

![Screen Shot 2021-12-16 at 3 05 42 PM](https://user-images.githubusercontent.com/814283/146441649-70035d98-31a7-4452-bed0-c3f45558d2dd.png)


Discussion: Hey @tmcconechy - thanks for the report, and thanks for including the URL! I can confirm that I'm seeing the same results you are, and that it does appear to be a bug. I will pass thing along to the correct team members to review.
The issue is being raised as a "needs review" item a.k.a. incomplete, this is expected behavior
@dylanb - what is the expected behavior here? it seems like maybe using any custom elements is not possible with the tool? I'm also pretty sure this did work?

Is there anymore info you need to make it complete? Thanks
Digging into this issue it appears that our color contrast code doesn't take into account custom elements with `<slot>` correctly. Take the following example which produces a needs review due to overlapping elements:

```html
<my-group>
  <my-paragraph>
      Let's have some different text!
  </my-paragraph>
</my-group>

<script src="/axe.js"></script>
<script>
  customElements.define('my-group',
    class extends HTMLElement {
      constructor() {
        super();

        const shadowRoot = this.attachShadow({mode: 'open'});
        shadowRoot.innerHTML = '<div style="position: absolute;"><slot></slot></div>'
      }
    }
  );

  customElements.define('my-paragraph',
    class extends HTMLElement {
      constructor() {
        super();

        const shadowRoot = this.attachShadow({mode: 'open'});
        shadowRoot.innerHTML = '<p><slot>My default text</slot></p>'
      }
    }
  );
</script>
```

Our code thinks the `<div>` child of the `<my-group>` custom element is detached from the `<my-paragraph>` element, where in reality the `<slot>` moves the element to be a child of the `<div>`. Our code misses this so assumes the `<div>` is childless and the `position: absolute` on it makes it the highest element in the stack.



Fix (patch):
diff --git a/lib/commons/dom/create-grid.js b/lib/commons/dom/create-grid.js
index f4c0278502..509fbba5f9 100644
--- a/lib/commons/dom/create-grid.js
+++ b/lib/commons/dom/create-grid.js
@@ -54,10 +54,17 @@ export default function createGrid(
   while (node) {
     let vNode = getNodeFromTree(node);
 
+    if (vNode && vNode.parent) {
+      parentVNode = vNode.parent;
+    }
+    // elements with an assigned slot need to be a child of the slot element
+    else if (node.assignedSlot) {
+      parentVNode = getNodeFromTree(node.assignedSlot);
+    }
     // an svg in IE11 does not have a parentElement but instead has a
     // parentNode. but parentNode could be a shadow root so we need to
     // verify it's in the tree first
-    if (node.parentElement) {
+    else if (node.parentElement) {
       parentVNode = getNodeFromTree(node.parentElement);
     } else if (node.parentNode && getNodeFromTree(node.parentNode)) {
       parentVNode = getNodeFromTree(node.parentNode);