Skip to content

Editable tabs: add and remove buttons owned by role=tablist keep aria-required-children firing #1025

Description

@rubenmarcus

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.

Activity

  1. A-RYAN-1 commented on Oct 2, 2026

    @A-RYAN-1

    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.

  2. rubenmarcus commented on Oct 3, 2026

    @rubenmarcus
    Author

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions