About This HTML Issue
An element with a tabindex attribute is nested inside an element whose ARIA role, such as button, tab or checkbox, describes a single control that should take focus as one unit.
ARIA in HTML gives an element with one of these roles the same content rules as the native control it imitates. A <button> can hold text and other phrasing content, but no interactive elements and no elements with a tabindex attribute. A <div role="button"> follows the same rule.
The value of tabindex doesn't matter. With tabindex="0", the inner element becomes a second tab stop inside the control. With tabindex="-1", it stays out of the tab order, but a script or a mouse click can still focus it, so focus lands on part of the control instead of the control itself. Keyboard and screen reader users expect to reach the control once and activate it with Enter or Space. Most of these roles also have presentational children in WAI-ARIA, which means browsers shouldn't expose their descendants to assistive technologies as separate elements.
The validator checks descendants at any depth inside elements with role set to button, checkbox, img, link, math, menuitem, menuitemcheckbox, menuitemradio, option, progressbar, radio, separator, slider, switch or tab. The element with the role can have tabindex itself; the validator only flags its descendants.
It's a warning because the ARIA in HTML rule behind it is non-normative. Older validator builds reported it as an error, with "must not appear" in place of "should not appear". Inside a native <button> or <a>, a descendant with tabindex is still an error.
Remove tabindex from the descendant, and keep tabindex="0" on the element with the role. A native <button> is simpler, because it's focusable and responds to Enter and Space without extra attributes or scripts. Some carousel scripts give each slide role="button" and an inner tabindex="-1" element as a focus target. A slide with its own content, like an image and a caption, isn't a button, so remove role="button" from the slide and put a real <button> inside it for the action.
Invalid example
<div role="button" tabindex="0">
<span class="label" tabindex="-1">Add to cart</span>
</div>
Valid example
<button type="button">
<span class="label">Add to cart</span>
</button>
Valid example keeping the role
<div role="button" tabindex="0">
<span class="label">Add to cart</span>
</div>
Find issues like this automatically
Rocket Validator scans thousands of pages in seconds, detecting HTML issues across your entire site.