Search code examples
htmlangulartwitter-bootstrapaccessibilityprimeng

Tabs in Modal Aren't Tabbable - Accessibility


Please see this example https://stackblitz.com/edit/primeng-dynamicdialog-demo-quno4x?file=src/app/tabs.component.html

Click on the Show button. Then press tab.

The x gets outlined, which is correct behavior. enter image description here

Now press tab again. Notice how the x is still focused.

The expected behavior is that tab One should be focused.

No matter how many times you press tab, it's not possible to focus on the tabs One, Two, and Three

If you view source code, find the anchor href tags for the tabs, add tabindex="0" to them them, and then press tab a few times, you are able to focus on them as you can see here.

<a tabindex="0" _ngcontent-lsq-c155="" href="" ngbnavlink="" id="ngb-nav-9" role="tab" aria-controls="ngb-nav-9-panel" aria-selected="true" aria-disabled="false" class="nav-link active">One</a>

enter image description here

I don't understand why I need to add tabindex="0" in order for this to be accessibility compliant. The interesting thing is that if these tabs aren't in a modal, this bug doesn't happen.


Solution

  • To me this seems to be a bug in PrimeNG FocusTrap implementation.

    Though I have never worked on PrimeNG, I thought of digging a bit and this is what I found:

    • PrimeNG as any other library has a FocusTrap implementation for Modals (Dialog, DynamicDialog, etc)
    • They do not consider anchor <a> elements as focusable which seems quite weird. Anchor elements within modal can't be focused. The selector being used for element.querySelectorAll(selector) is as below:
        `button:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]),
        [href][clientHeight][clientWidth]:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]),
        input:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), select:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]),
        textarea:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]), [tabIndex]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]),
        [contenteditable]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden]):not(.p-disabled)`
    
    • There is a CSS selector for href above but it doesn't match anchor tag:
        [href][clientHeight][clientWidth]:not([tabindex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden])
    
    • There is another selector which checks for the presence of tabindex attribute:
        [tabIndex]:not([tabIndex = "-1"]):not([disabled]):not([style*="display:none"]):not([hidden])
    


    I don't understand why I need to add tabindex="0" in order for this to be accessibility compliant. The interesting thing is that if these tabs aren't in a modal, this bug doesn't happen.

    • When you add tabindex="0", it matches the selector specified in last point, and hence it works.
    • If tabs aren't in a modal, there is no FocusTrap and hence no issue.