Skip to content

Tabs: Pre-stabilization API cleanup and refactoring#79337

Merged
t-hamano merged 14 commits into
trunkfrom
polish-tabs
Jun 24, 2026
Merged

Tabs: Pre-stabilization API cleanup and refactoring#79337
t-hamano merged 14 commits into
trunkfrom
polish-tabs

Conversation

@t-hamano

@t-hamano t-hamano commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Related to #73230

What?

Pre-stabilization cleanup for the Tabs block family.

Why?

This block is currently experimental, but once it is stabilized, we must maintain backward compatibility. At this stage, we need to remove redundant APIs and dead code to make it robust.

How?

The following is a list of the changes included in this PR.

  • Bug fix:
    • I noticed that the gap support for the List block in the editor is not working. This was caused by Try: Remove Tab (Tab list item) block  #77439, which excluded child blocks. The reason is that layout support does not work with useBlockProps. To fix this, I used __unstableLayoutClassNames. (ref)
  • API surface:
    • Drop the public core/tabs store and collapse everything into a single locked core/tabs store (closes the third-party extension point) (ref)
    • Reduce the interactivity state to a flat array of tab IDs, shrinking the SSR payload (ref)
  • Dead code & duplication
    • Remove the focusRef/cancelAnimationFrame cleanup that never actually ran (ref)
    • Remove a pointless useMemo and an empty attributes definition (ref1, ref2)
    • Remove redundant interactivity directives on the panel (inherited from the ancestor)
    • Move static role/tabindex from render-time output into the saved markup (ref1, ref2)
  • Styles & consistency
    • Delete tab-list's duplicate editor.scss and unify the active state on aria-selected (ref1, ref2)
    • Add init.js to tab-list / tab-panels so all four blocks are structured the same way (ref1, ref2)
    • Correct the @since tags to 7.1.0

Testing Instructions

Insert a Tab block and perform a smoke test. It should work correctly as before.

  • The reordering of tabs and tab panels should be synchronized.
  • Change the active tab. Both in the editor and the frontend, that tab should be active on page load.
  • Set custom anchors for the tab panels. In the frontend, those anchors should be respected instead of sequential numbers.
  • In the frontend, it should be fully keyboard-operable. Left and right arrow keys should allow navigation between tab buttons, and Enter or Space keys should activate that tab.

Use of AI Tools

This PR was authored with the assistance of Claude Code (Anthropic). All changes have been reviewed by the author.

t-hamano and others added 11 commits June 19, 2026 11:46
The focusRef was never assigned a requestAnimationFrame id, so the
unmount cleanup was always a no-op. Remove the unused ref, the
window.cancelAnimationFrame destructuring, and the empty effect.

Co-Authored-By: Claude <[email protected]>
The .is-active::before active indicator in editor.scss duplicated the
rule already defined in style.scss, which loads in the editor too. Drop
editor.scss and its editorStyle reference in block.json.

Co-Authored-By: Claude <[email protected]>
The editor buttons used an is-active class while the front end used
aria-selected, requiring two selectors for the same indicator. Give the
editor buttons role="tab" and aria-selected, and drop the is-active
class so style.scss matches a single [aria-selected="true"] selector.

Co-Authored-By: Claude <[email protected]>
The tabs and tab-panel blocks ship an init.js individual-block entry,
but tab-list and tab-panels were missing one, leaving the four-block
set inconsistent. Add matching init.js so each block has a standalone
build entry ahead of stabilization.

Co-Authored-By: Claude <[email protected]>
The block defines no attributes, so the empty attributes object was
redundant. Remove it to match blocks that simply omit the key.

Co-Authored-By: Claude <[email protected]>
Tabs was the only block-library block exposing an unlocked public
Interactivity store (core/tabs) alongside a locked core/tabs/private
one, requiring a createReadOnlyProxy wrapper to guard internal state.
No other block offers such a third-party extension point, and keeping
it would commit us to a public API contract at stabilization.

Collapse the two stores into a single locked core/tabs store, matching
the pattern used by accordion, navigation, image, etc. Remove the now
unused createReadOnlyProxy and rename privateState/privateActions to
state/actions. Update the data-wp-interactive and wp_interactivity_state
namespaces accordingly.

Co-Authored-By: Claude <[email protected]>
The server-side tabs list carried `label` and `index` on every entry,
but the front end only ever reads each tab's `id` (in view.js lookups
and in the tab-list render callback). Those extra fields only inflated
the serialized `wp_interactivity_state` payload and the `core/tabs-list`
context.

Emit the list as a plain array of ID strings and update both consumers
accordingly, shrinking the SSR state and simplifying the lookups.

Co-Authored-By: Claude <[email protected]>
`effectiveActiveIndex` was wrapped in useMemo despite being a plain `??`
fallback between two primitives. Memoizing it costs more (dependency
comparison plus hook overhead) than recomputing the value, so inline it
and drop the now-unused import.

Co-Authored-By: Claude <[email protected]>
The tabs block family has not shipped yet, so its server-side functions
are introduced in 7.1.0 rather than 7.0.0. Correct the @SInCE tags to
match the actual release.

Co-Authored-By: Claude <[email protected]>
The render callback re-set `role="tabpanel"` (already produced by save.js)
and a constant `tabindex="0"`, injecting static markup at render time for
no reason. Move `tabindex` into the saved output and drop both static
set_attribute() calls, leaving the callback to handle only the attributes
that depend on the server-generated tab ID or interactivity state.

Co-Authored-By: Claude <[email protected]>
The render callback set `data-wp-interactive` and a `data-wp-context`
carrying `tab.id` on every panel. The namespace and the active-tab
context are already inherited from the ancestor `wp-block-tabs` element,
and nothing ever read `tab.id` (panel visibility resolves via the panel's
own `id` attribute and the inherited `activeTabIndex`). Remove both so the
callback only sets the panel-specific `aria-labelledby` and hidden binding.

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions Bot added the [Package] Block library /packages/block-library label Jun 19, 2026
Comment on lines -217 to -218
// Public store for third-party extensibility.
store( 'core/tabs', {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, there are no blocks that expose the interactivity API store. Whether or not to expose it should be carefully considered in the future based on user feedback.

Removing tab-list/editor.scss left a `@use` import in the block-library
editor.scss aggregate, breaking the build. Remove the stale import.

Co-Authored-By: Claude <[email protected]>
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

Size Change: -1.28 kB (-0.02%)

Total Size: 7.5 MB

📦 View Changed
Filename Size Change
build/modules/block-library/tabs/view.min.js 700 B -211 B (-23.16%) 🎉
build/scripts/block-library/index.min.js 324 kB -65 B (-0.02%)
build/styles/block-library/editor-rtl.css 12.4 kB -51 B (-0.41%)
build/styles/block-library/editor-rtl.min.css 10.3 kB -45 B (-0.44%)
build/styles/block-library/editor.css 12.5 kB -51 B (-0.41%)
build/styles/block-library/editor.min.css 10.3 kB -45 B (-0.44%)
build/styles/block-library/style-rtl.css 21.6 kB -12 B (-0.06%)
build/styles/block-library/style-rtl.min.css 18.2 kB -12 B (-0.07%)
build/styles/block-library/style.css 21.8 kB -11 B (-0.05%)
build/styles/block-library/style.min.css 18.1 kB -13 B (-0.07%)
build/styles/block-library/tab-list/editor-rtl.css 0 B -196 B (removed) 🏆
build/styles/block-library/tab-list/editor-rtl.min.css 0 B -164 B (removed) 🏆
build/styles/block-library/tab-list/editor.css 0 B -196 B (removed) 🏆
build/styles/block-library/tab-list/editor.min.css 0 B -163 B (removed) 🏆
build/styles/block-library/tab-list/style-rtl.css 411 B -13 B (-3.07%)
build/styles/block-library/tab-list/style-rtl.min.css 369 B -11 B (-2.89%)
build/styles/block-library/tab-list/style.css 413 B -12 B (-2.82%)
build/styles/block-library/tab-list/style.min.css 369 B -12 B (-3.15%)

compressed-size-action

@t-hamano t-hamano added [Type] Code Quality Issues or PRs that relate to code quality [Block] Tabs Affects the Tabs Block labels Jun 19, 2026
@t-hamano t-hamano self-assigned this Jun 19, 2026
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

Flaky tests detected in d8173a8.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/28079631833
📝 Reported issues:

The Tab List block renders its buttons from the `tabs` attribute instead of
inner blocks, so the layout block support never applied its container classes
(`is-layout-flex`, `wp-container-*`) to the wrapper in the editor — those are
only added via `useInnerBlocksProps`. As a result flex layout and blockGap had
no effect in the editor, which previously required hardcoded styles.

Apply `__unstableLayoutClassNames` to the wrapper manually so the layout
support's flex and blockGap styles take effect in the editor, matching the
front end. Set the default layout `flexWrap` to `nowrap` so the no-wrap
behavior is driven by the layout support in both contexts.

Co-Authored-By: Claude <[email protected]>
@t-hamano t-hamano marked this pull request as ready for review June 19, 2026 04:21
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: t-hamano <[email protected]>
Co-authored-by: Mamaduka <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@Mamaduka

Copy link
Copy Markdown
Member

Let's also audit effects that update attributes and see if those values can be derived. While this has always been a bad pattern, it has become even flakier with RTC. See #78989.

@t-hamano

Copy link
Copy Markdown
Contributor Author

Let's also audit effects that update attributes and see if those values can be derived.

That's right. It's possible that useTabListItemsSync might be causing some issues in RTC. Since refactoring it would likely involve a significant amount of work, we are considering addressing it in a follow-up.

P.S. If there are any other blockers that should be addressed to stabilize the Tabs block, please comment on #73230 🙇‍♂️

@Mamaduka Mamaduka mentioned this pull request Jun 24, 2026
14 tasks

@Mamaduka Mamaduka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me ✅

Noticed a couple of issues, but those aren't related to this PR.

@t-hamano

Copy link
Copy Markdown
Contributor Author

Thank you, @Mamaduka!

@t-hamano t-hamano merged commit e2076bc into trunk Jun 24, 2026
42 checks passed
@t-hamano t-hamano deleted the polish-tabs branch June 24, 2026 07:04
@github-project-automation github-project-automation Bot moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1 Editor Tasks Jun 24, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.5 milestone Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Block] Tabs Affects the Tabs Block [Package] Block library /packages/block-library [Type] Code Quality Issues or PRs that relate to code quality

Projects

Development

Successfully merging this pull request may close these issues.

2 participants