Error:
List items (
<li>) are not contained within<ul>or<ol>parent elements.

This error does not appear. The <li>s in question are definitely contained inside a <ul> element.

Thanks very much for filing @elijah-schow! Did you get a chance to try out the bug report instructions regarding accessibility issues?
Before creating an Accessibility issue please test that it is reproducible upstream with axe (https://www.deque.com/axe/) first and file the issue there if necessary.
My guess is that this audit is flagging it because the ul now has the role tablist and the li now need some other corresponding role but the axe folks would know better.
@patrickhulce It looks like Axe reports the same issue. I think your hypothesis might be right. Setting their role to "tab" fixes the issue, but I think the anchor tags are supposed to have that role instead of the list items.
I can also work around the issue by using div's instead of list items:
Replacing the <ul> and <li> elements with <div>s fixes the issue:
<div role="tablist" class="nav nav-tabs card-header-tabs">
<div class="nav-item">
<a href="#" class="nav-link active" role="tab">Upcoming</a>
</div>
<div class="nav-item">
<a href="#" class="nav-link" role="tab">Previous</a>
</div>
</div>
This doesn't match Bootstrap 4's suggested markup, but maybe their guide is wrong? Is there is disadvantage to using divs instead of list items?
maybe their guide is wrong?
It does appear their guide is incorrect according to axe. If the role="tab" were on the li elements instead of the a elements it would pass.
Is there is disadvantage to using divs instead of list items?
The primary disadvantage of divs is that there is no semantic meaning attached to them by default. By manually annotating role=X we are providing that semantic meaning for screen readers, so there should not be a disadvantage compared to ul/li AFAIK. (and in fact this is the problem here. That example has rewritten the "meaning" of ul to be a tablist not a regular unordered list, so the li elements no longer have a semantic ul parent)
cc @robdodson who is more knowledgeable here.
It does appear their guide is incorrect according to axe. If the role="tab" were on the li elements instead of the a elements it would pass.
What happens if you put role=presentation on the li element? That should essentially remove it from the accessibility tree.
By manually annotating role=X we are providing that semantic meaning for screen readers, so there should not be a disadvantage compared to ul/li AFAIK.
Yep, that's correct. It's also worth noting that, in Safari at least, if you change the display of a list then they throw out its semantics anyway (see the last comment in this ticket).
I think over the years it became a common practice to put navigation into ul/li elements, but that practice probably predates ARIA and has just stuck around. Since you're redefining the semantics with ARIA it doesn't matter much what the underlying HTML element is in this case.
Thanks @robdodson! Marking this one as closed then but feel free to re-open if you come across something that isn't reflecting axe's rules.
It appears that the main problem is role="tablist" attribute on the <ul> which prevents it from being announced as an actual list by assistive technologies. Consequentially then of course list items must also have role="tab" attribute not to be announced as list items, or simply role="tablist" attribute must be removed.
Most helpful comment
What happens if you put
role=presentationon thelielement? That should essentially remove it from the accessibility tree.Yep, that's correct. It's also worth noting that, in Safari at least, if you change the display of a list then they throw out its semantics anyway (see the last comment in this ticket).
I think over the years it became a common practice to put navigation into ul/li elements, but that practice probably predates ARIA and has just stuck around. Since you're redefining the semantics with ARIA it doesn't matter much what the underlying HTML element is in this case.