You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Editable tabs: add and remove buttons owned by role=tablist keep aria-required-children firing #1025
After #1024 moves role="tablist" to .rc-tabs-nav-list, aria-required-children still fires for editable tabs: the add button (src/TabNavList/AddButton.tsx, rendered inside the nav list next to the tab nodes in src/TabNavList/index.tsx) is a button owned by the tablist, and a tablist may only own tab elements. axe-core flags it critical: "Element has children which are not allowed: button".
The remove button is the same class of problem, pre-existing: it renders as a sibling of the role="tab" node inside each tab wrapper, so the tablist owns it too.
Repro: <Tabs editable={{ onChange: ... }} items={...} /> through axe-core 4.13 flags the add button node with aria-required-children (critical). The same check on a non-editable Tabs is clean with #1024 applied, which is why this is carved out of that PR's scope.
What a fix likely needs:
relocate the add button outside [role=tablist] (or restructure so the tablist owns only tab nodes) while keeping the innerAddButtonRef size measurement working, it participates in the overflow calculation
move the remove button inside each role="tab" element, or give it the same restructuring
matching CSS on the antd side for both moves
Context: remaining scope of the aria-required-children failure reported in ant-design/ant-design#49502; antd currently carries a waiting for fix exemption in the tabs a11y test (ant-design/ant-design#53584) that covers this.
I'd like to work on this once #1024 is merged, since it builds on the new tablist placement.
For the add button, my plan is to render it outside .rc-tabs-nav-list and keep its position with CSS. The remove button is harder, because it sits inside each tab wrapper. Before I start, do you prefer moving it out of the wrapper as well, or keeping the DOM shape and handling it another way? Either way I'll add axe checks for the editable case to the a11y tests.
Move it out of the tablist subtree, same treatment as the add button.
Putting it inside role="tab" is the trap. WAI-ARIA 1.2 gives the tab role Children Presentational: True, so every descendant loses its semantics. The button stops being a button for assistive tech, and axe reports a different rule instead of the one you are fixing. Here is the spec line: https://www.w3.org/TR/wai-aria-1.2/#tab
Concretely: render the remove buttons in a layer after .rc-tabs-nav-list, position them from the wrapper's data-node-key, and keep the rc-tabs-tab-remove class and styles.remove so the .rc-tabs-tab-with-remove overrides in antd keep matching.
Nothing about interaction has to move. The wrapper keeps onClick at src/TabNavList/TabNode.tsx:95, role="tab" stays where it is at line 100, and the button keeps its stopPropagation, so click and keyboard behaviour are unchanged. What changes is only which element owns the button in the accessibility tree.
On your axe checks: yes, please add them, and cover the add button and the remove button in the same test. Split across two tests, the remove case can go back to red without the add case going red with it, which is exactly the shape of this bug.
Activity
A-RYAN-1 commentedon Oct 2, 2026
I'd like to work on this once #1024 is merged, since it builds on the new tablist placement.
For the add button, my plan is to render it outside
.rc-tabs-nav-listand keep its position with CSS. The remove button is harder, because it sits inside each tab wrapper. Before I start, do you prefer moving it out of the wrapper as well, or keeping the DOM shape and handling it another way? Either way I'll add axe checks for the editable case to the a11y tests.rubenmarcus commentedon Oct 3, 2026
Move it out of the tablist subtree, same treatment as the add button.
Putting it inside
role="tab"is the trap. WAI-ARIA 1.2 gives thetabroleChildren Presentational: True, so every descendant loses its semantics. The button stops being a button for assistive tech, and axe reports a different rule instead of the one you are fixing. Here is the spec line: https://www.w3.org/TR/wai-aria-1.2/#tabConcretely: render the remove buttons in a layer after
.rc-tabs-nav-list, position them from the wrapper'sdata-node-key, and keep therc-tabs-tab-removeclass andstyles.removeso the.rc-tabs-tab-with-removeoverrides in antd keep matching.Nothing about interaction has to move. The wrapper keeps
onClickatsrc/TabNavList/TabNode.tsx:95,role="tab"stays where it is at line 100, and the button keeps itsstopPropagation, so click and keyboard behaviour are unchanged. What changes is only which element owns the button in the accessibility tree.On your axe checks: yes, please add them, and cover the add button and the remove button in the same test. Split across two tests, the remove case can go back to red without the add case going red with it, which is exactly the shape of this bug.