Skip to content

fix(components): guard styled button rgba() against React 19 defaultProps removal#23319

Merged
enricobattocchi merged 1 commit into
feature/fix-gutenberg-23.3from
react19-components-button-rgba
May 29, 2026
Merged

fix(components): guard styled button rgba() against React 19 defaultProps removal#23319
enricobattocchi merged 1 commit into
feature/fix-gutenberg-23.3from
react19-components-button-rgba

Conversation

@enricobattocchi
Copy link
Copy Markdown
Member

@enricobattocchi enricobattocchi commented May 29, 2026

Context

React 19 (Gutenberg 23.3 / WordPress 7.1) stops applying defaultProps to function and forwardRef components when the element is created through the automatic JSX runtime (jsx()); the classic createElement runtime still applies them. Several @yoast/components styled buttons (Button/BaseButton, LinkButton, and the IconButtonToggle eye/highlight toggle) interpolate rgba( props.<color> ), and those colours are supplied only by defaultProps. When an automatic-runtime consumer (Free's packages/js) renders one of these buttons, the default colours drop to undefined, rgba(undefined) throws in polished's parseToRgb, and the surrounding subtree crashes.

Verified live on a React 19.2.4 site (Gutenberg 23.3-RC1) with all three React-19 readiness PRs applied: the block-editor content-analysis "Highlight this result in the text" eye toggles (IconButtonToggle, rendered via packages/js' Results.js) render fully styled, with the box-shadow rgba() resolved to a real colour and no parseToRgb throw, and the console is clean. BaseButton / LinkButton surfaces (Workouts, Free admin screens) likewise render cleanly.

Part of the React 19 readiness work tracked in Yoast/reserved-tasks#235.

Summary

This PR can be summarized in the following changelog entry:

  • [@yoast/components] Fixes a bug where buttons could crash the surrounding UI on React 19 because their default colours were no longer applied.

Relevant technical choices:

  • BaseButton and LinkButton apply their own defaults through a styled-components .attrs callback, routed through the outermost addBaseStyle layer so the defaults reach every inner hover/focus/active interpolation on both JSX runtimes. This keeps each button's own defaults correct on React 19 (e.g. LinkButton keeps its own activeBorderColor and BaseButton keeps type="button"), which a single shared fallback could not.
  • IconButtonToggle merges its colour defaults in-component (a plain { ...defaults, ...props } spread) so they survive the automatic runtime and its rendered styling stays intact, rather than relying on defaultProps.
  • defaultProps is kept on each component for classic-runtime consumers; the .attrs/in-component merge is what makes them safe on the automatic runtime.
  • IconButtonBase is intentionally left untouched: it has no defaults of its own and its colours are always passed by its only consumer, IconButtonToggle, which now supplies defined values.
  • Behaviour is unchanged on React 18 and under the classic runtime: the existing component snapshot tests are untouched.

Test instructions

Test instructions for the acceptance test before the PR gets merged

Requires a React-19 environment (the Gutenberg 23.3+ plugin active, or WordPress 7.1) with Yoast SEO active. Test with the browser console open.

  1. Open a post in the block editor, add a paragraph of text and set a focus keyphrase so the SEO and readability analyses run. Each assessment result shows a small eye "Highlight this result in the text" toggle. The toggles should render styled (light-grey background, a subtle bottom box-shadow, a 1px border) and the console should show no hex notation / Cannot read properties of undefined errors. Before this fix they threw in parseToRgb and crashed the analysis subtree.
  2. Open Yoast SEO → Workouts and other Free admin screens. BaseButton / LinkButton should render and the console should stay clean.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

The bug is a render/console crash, only observable on a React 19 build (Gutenberg 23.3+), so test with the console open in the block editor.

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA tests on the Gutenberg 23.3-RC1 (React 19) build, following the acceptance test steps above.

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

  • Any surface that renders @yoast/components Button / LinkButton / IconButtonToggle through the automatic JSX runtime (Free packages/js: the content-analysis result toggles and the Workouts screens). No effect on React 18 or classic-runtime consumers (the other add-ons).

Other environments

  • This PR also affects Shopify.
  • This PR also affects Yoast SEO for Google Docs.

Documentation

  • I have written documentation for this change. Code comments explain the .attrs default mechanism on BaseButton/LinkButton and the in-component default merge on IconButtonToggle.

Quality assurance

  • I have tested this code to the best of my abilities. Verified live on a React 19.2.4 (Gutenberg 23.3-RC1) site: the editor content-analysis eye toggles render fully styled and the console is clean.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for. Tested with Free and Premium active.
  • I have added unit tests to verify the code works as intended. tests/buttonReact19Test.js renders BaseButton / LinkButton / IconButtonToggle with defaultProps neutralised (mirroring the automatic runtime) and asserts they still render, that BaseButton keeps type="button", and that IconButtonToggle produces identical styled output.
  • If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
  • I have written this PR in accordance with my team's definition of done.
  • I have checked that the base branch is correctly set.
  • I have run grunt build:images and committed the results, if my PR introduces or edits images or SVGs.

Innovation

  • No innovation project is applicable for this PR.

Refs Yoast/reserved-tasks#235, Yoast/reserved-tasks#231
Fixes Yoast/reserved-tasks#1253

🤖 Generated with Claude Code

@enricobattocchi enricobattocchi added the changelog: bugfix Needs to be included in the 'Bugfixes' category in the changelog label May 29, 2026
@github-actions
Copy link
Copy Markdown

@enricobattocchi Please be aware that following packages have been abandoned and are not actively maintained anymore:

Package name Path
@yoast/babel-preset packages/babel-preset
@yoast/components packages/components
@yoast/e2e-tests packages/e2e-tests
@yoast/helpers packages/helpers
@yoast/jest-preset packages/jest-preset
@yoast/style-guide packages/style-guide
eslint-config-yoast packages/esling-config-yoast

Please consider using the other packages instead.

@coveralls
Copy link
Copy Markdown

coveralls commented May 29, 2026

Coverage Report for CI Build 0

Warning

No base build found for commit 9e083b7 on feature/fix-gutenberg-23.3.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 34.873%

Details

  • Patch coverage: 17 of 17 lines across 3 files are fully covered (100%).

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 16840
Covered Lines: 6245
Line Coverage: 37.08%
Relevant Branches: 10588
Covered Branches: 3320
Branch Coverage: 31.36%
Branches in Coverage %: Yes
Coverage Strength: 9.52 hits per line

💛 - Coveralls

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates @yoast/components button styling to prevent React 19’s automatic JSX runtime (which no longer applies defaultProps for function/forwardRef components) from passing undefined into rgba(), which can throw and crash rendering.

Changes:

  • Added || colors.$color_* fallbacks inside rgba() interpolations for BaseButton focus/active/base shadows.
  • Added a similar rgba() fallback for LinkButton’s box-shadow color.
  • Added rgba() fallbacks for IconButtonBase pressed/unpressed box-shadow colors, plus explanatory inline comments.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/components/src/buttons/Button.js Adds rgba() fallbacks (and rationale comment) to avoid crashes when default color props become undefined under React 19 automatic runtime.
packages/components/src/buttons/LinkButton.js Adds a rgba() fallback for boxShadowColor to avoid rgba(undefined) crashes.
packages/components/src/IconButtonBase.js Adds rgba() fallbacks for pressed/unpressed box-shadow colors to prevent crashes when upstream defaultProps are not applied.

Comment thread packages/components/src/buttons/Button.js
@enricobattocchi enricobattocchi force-pushed the react19-components-button-rgba branch from b427e34 to 9ec60f9 Compare May 29, 2026 19:35
@github-actions
Copy link
Copy Markdown

@enricobattocchi Please be aware that following packages have been abandoned and are not actively maintained anymore:

Package name Path
@yoast/babel-preset packages/babel-preset
@yoast/components packages/components
@yoast/e2e-tests packages/e2e-tests
@yoast/helpers packages/helpers
@yoast/jest-preset packages/jest-preset
@yoast/style-guide packages/style-guide
eslint-config-yoast packages/esling-config-yoast

Please consider using the other packages instead.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread packages/components/src/buttons/Button.js Outdated
Comment thread packages/components/src/IconButtonBase.js Outdated
@enricobattocchi enricobattocchi force-pushed the react19-components-button-rgba branch from 9ec60f9 to a7e3bbe Compare May 29, 2026 20:23
@github-actions
Copy link
Copy Markdown

@enricobattocchi Please be aware that following packages have been abandoned and are not actively maintained anymore:

Package name Path
@yoast/babel-preset packages/babel-preset
@yoast/components packages/components
@yoast/e2e-tests packages/e2e-tests
@yoast/helpers packages/helpers
@yoast/jest-preset packages/jest-preset
@yoast/style-guide packages/style-guide
eslint-config-yoast packages/esling-config-yoast

Please consider using the other packages instead.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@enricobattocchi enricobattocchi force-pushed the react19-components-button-rgba branch from a7e3bbe to 58248cf Compare May 29, 2026 21:27
@github-actions
Copy link
Copy Markdown

@enricobattocchi Please be aware that following packages have been abandoned and are not actively maintained anymore:

Package name Path
@yoast/babel-preset packages/babel-preset
@yoast/components packages/components
@yoast/e2e-tests packages/e2e-tests
@yoast/helpers packages/helpers
@yoast/jest-preset packages/jest-preset
@yoast/style-guide packages/style-guide
eslint-config-yoast packages/esling-config-yoast

Please consider using the other packages instead.

@enricobattocchi enricobattocchi requested a review from Copilot May 29, 2026 21:28
@enricobattocchi enricobattocchi changed the base branch from release/27.8 to feature/fix-gutenberg-23.3 May 29, 2026 21:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread packages/components/src/IconButtonToggle.js
Comment thread packages/components/tests/buttonReact19Test.js
Comment thread packages/components/tests/buttonReact19Test.js
…ct 19

React 19's automatic JSX runtime no longer applies defaultProps to function/forwardRef
components (styled components included). The @yoast/components styled buttons relied on
defaultProps for their colours (interpolated into rgba(), which throws on undefined) and for
BaseButton's type (a typeless <button> defaults to submit and can submit surrounding forms).

BaseButton and LinkButton apply their defaults through a styled-components .attrs callback on the
outermost style layer, so they reach every inner hover/focus/active interpolation and stay correct
per component (e.g. LinkButton keeps its own activeBorderColor) on both JSX runtimes.
IconButtonToggle merges its colour defaults in-component (a plain object spread) so they survive
the automatic runtime and its rendered styling stays intact. defaultProps is kept on each for
classic consumers. IconButtonBase is left untouched: its colours come from IconButtonToggle, its
only consumer, which now always passes defined values.

Adds a regression test that neutralises each component's defaultProps (mirroring the automatic
runtime): BaseButton/LinkButton must still render and keep type=button, and IconButtonToggle must
produce identical styled output, guarding its merged default colours against regressions.

Verified on a React 19.2.4 site (Gutenberg 23.3-RC1) with all three React-19 PRs applied: the
editor content-analysis eye toggles render fully styled (box-shadow rgba resolved, no parseToRgb
throw) and the console is clean.

Relates to Yoast/reserved-tasks#1252

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@enricobattocchi enricobattocchi force-pushed the react19-components-button-rgba branch from 58248cf to aed72d1 Compare May 29, 2026 21:45
@github-actions
Copy link
Copy Markdown

@enricobattocchi Please be aware that following packages have been abandoned and are not actively maintained anymore:

Package name Path
@yoast/babel-preset packages/babel-preset
@yoast/components packages/components
@yoast/e2e-tests packages/e2e-tests
@yoast/helpers packages/helpers
@yoast/jest-preset packages/jest-preset
@yoast/style-guide packages/style-guide
eslint-config-yoast packages/esling-config-yoast

Please consider using the other packages instead.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread packages/components/src/IconButtonToggle.js
@enricobattocchi enricobattocchi merged commit 31cc456 into feature/fix-gutenberg-23.3 May 29, 2026
26 of 27 checks passed
@enricobattocchi enricobattocchi deleted the react19-components-button-rgba branch May 29, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog: bugfix Needs to be included in the 'Bugfixes' category in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants