Skip to content

Fix TaskListOptInNotification Toast positioning and extend Tailwind content paths#22960

Merged
vraja-pro merged 13 commits intotrunkfrom
jordi-pv/use-the-dialog-toast-in-wordpress
Feb 27, 2026
Merged

Fix TaskListOptInNotification Toast positioning and extend Tailwind content paths#22960
vraja-pro merged 13 commits intotrunkfrom
jordi-pv/use-the-dialog-toast-in-wordpress

Conversation

@JorPV
Copy link
Contributor

@JorPV JorPV commented Feb 9, 2026

Context

The TaskListOptInNotification component uses a Toast which lacks focus trapping and proper modal accessibility. This PR replaces it with the ModalNotification component (HeadlessUI Dialog) for improved accessibility, focus management, and keyboard navigation. The notification is also correctly positioned to offset it from the WordPress admin sidebar.

Summary

This PR can be summarized in the following changelog entry:

  • Replaces the Toast with a ModalNotification dialog in the task list opt-in notification for improved accessibility, focus trapping, and keyboard navigation.
  • [@yoast/ui-library 0.0.1 enhancement] Updates position classes yst-left-0/yst-right-0 with logical properties yst-start-0/yst-end-0 in the ModalNotification component for correct RTL positioning.

Relevant technical choices:

  • Replaced the Toast component with ModalNotification in TaskListOptInNotification for improved accessibility and focus management. ModalNotification uses HeadlessUI's Dialog, providing focus trapping, Esc key dismissal, and proper ARIA semantics out of the box.
  • The OptInContainer now always renders TaskListOptInNotification and controls its visibility via isOpen/onClose props (driven by route and notification-seen state), rather than conditionally mounting/unmounting the component.
  • Moved <OptInContainer /> outside the <Notifications> wrapper in app.js since ModalNotification renders via its own HeadlessUI portal with fixed positioning.
  • Uses isAdminSidebarExpanded() helper to detect whether the WP admin sidebar is expanded

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

Steps

Using a live-link site with Local by Flywheels

  1. With Yoast Free installed and active
  2. Go to your Local site database and remove the _yoast_wpseo_task_list_opt_in_notification_seen row from the wp_usermeta
  3. Navigate to the Yoast admin General page
  4. Confirm that the TaskListOptInNotification is shown within the Yoast page as a modal dialog

LTR — Expanded sidebar
5. Verify the notification appears at the bottom-left of the screen, offset from the expanded WordPress admin sidebar, with a consistent gap.

LTR — Collapsed sidebar
6. Collapse the admin sidebar (click the collapse arrow at the bottom of the sidebar).
7. Remove the _yoast_wpseo_task_list_opt_in_notification_seen row again and reload.
8. Verify the notification appears at the bottom-left, offset from the collapsed sidebar, with a consistent gap.

RTL — Expanded sidebar
9. Switch to RTL by appending ?d=rtl to the URL (or via "Switch to RTL" plugin).
10. Remove the row again and reload.
11. Verify the notification appears at the bottom-right of the screen, offset from the expanded sidebar on the right side, with a consistent gap.

RTL — Collapsed sidebar
12. Collapse the admin sidebar.
13. Remove the row again and reload.
14. Verify the notification appears at the bottom-right, offset from the collapsed sidebar on the right side, with a consistent gap.

General checks
15. Resize the screen and confirm the notification is responsive and doesn't overlap the WP admin sidebar in any of the 4 states above.
16. Verify the close (X) button is focusable when rendered (don't close it yet).
17. Navigate with the Tab key and confirm that the focus is trapped within the notification buttons.
18. Confirm that you can't scroll the Yoast General admin page behind the notification.
19. Press the Esc key and confirm that the notification is dismissed.
20. Inspect the element with the dev tools.
21. Confirm the notification is rendered as a HeadlessUI Dialog with role="dialog" and is a child of a portal element.
22. Smoke test the Task-list introduction in different browsers.

Relevant test scenarios

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

Check the console for any React warnings or errors. Test on Chrome, Firefox, and Safari to ensure focus management works correctly across browsers.

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

QA can test this PR by following these steps:

Impact check

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

  • Focus management in notifications - verify that focus behavior doesn't break existing workflows.

Other environments

  • This PR also affects Shopify. I have added a changelog entry starting with [shopify-seo], added test instructions for Shopify and attached the Shopify label to this PR.
  • This PR also affects Yoast SEO for Google Docs. I have added a changelog entry starting with [yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached the Google Docs Add-on label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities.
  • During testing, I had activated all plugins that Yoast SEO provides integrations for.
  • I have added unit tests to verify the code works as intended.
  • 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 commited the results, if my PR introduces new images or SVGs.

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label.
  • I have added my hours to the WBSO document.

Fixes #4372

@JorPV JorPV requested a review from a team as a code owner February 9, 2026 16:19
@JorPV JorPV added the changelog: enhancement Needs to be included in the 'Enhancements' category in the changelog label Feb 10, 2026
@JorPV JorPV changed the base branch from trunk to jordi-pv/use-dialog-from-headlessui-in-our-toast-for-focus-managment February 10, 2026 09:53
@JorPV JorPV force-pushed the jordi-pv/use-the-dialog-toast-in-wordpress branch from 5d34494 to 7657e6b Compare February 10, 2026 10:00
"./src/**/*.mdx",
"./src/**/*.md",
// Scan packages/js for Tailwind classes used in WordPress plugin components
"../js/src/**/*.js",
Copy link
Contributor

@FAMarfuaty FAMarfuaty Feb 10, 2026

Choose a reason for hiding this comment

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

Not sure if adding path is safe. @yoast/ui-library is a distributed packaged available on NPM. Any external consumer of the package won't have access to../js/src/**/*.js, which will be problematic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😅 Right good point. I'll remove that and add the media query to the styles sheet.

…om:Yoast/wordpress-seo into jordi-pv/use-the-dialog-toast-in-wordpress
…cus-managment' of github.com:Yoast/wordpress-seo into jordi-pv/use-the-dialog-toast-in-wordpress
id="yoast_wpseo_task_list_opt_in_notification"
isVisible={ isVisible }
className="yst-w-96"
className="yst-w-96 md:yst-left-[160px]"
Copy link
Contributor

Choose a reason for hiding this comment

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

So here we always pass left. Would this be working for RTL?

Copy link
Contributor Author

@JorPV JorPV Feb 20, 2026

Choose a reason for hiding this comment

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

You are right, good catch. We can use start-offset instead 👍🏼
image

Base automatically changed from jordi-pv/use-dialog-from-headlessui-in-our-toast-for-focus-managment to trunk February 20, 2026 09:59
@coveralls
Copy link

Pull Request Test Coverage Report for Build c77444feb137db226807f53614dde17a23e5e54c

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.5%) to 53.68%

Totals Coverage Status
Change from base Build 28ef412cd582547032bd9243808884716c6d2c6d: 0.5%
Covered Lines: 33821
Relevant Lines: 63122

💛 - Coveralls

isOpen={ isOpen }
onClose={ onClose }
className={ classNames(
isAdminSidebarExpanded() && "md:yst-start-[160px] rtl:yst-end-[160px]"
Copy link
Contributor

@vraja-pro vraja-pro Feb 25, 2026

Choose a reason for hiding this comment

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

Lets fix spacing in RTL and also try to stick to the tailwind classes when possible. For some reason the space in RTL doesn't include the 16px padding, I'm not sure why, but this is a work around.

Image
Suggested change
isAdminSidebarExpanded() && "md:yst-start-[160px] rtl:yst-end-[160px]"
isAdminSidebarExpanded() && "md:yst-left-40 rtl:md:yst-right-44"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The rtl: variant is not configured in the Tailwind preset. See the comment in the Know limitations section:

RTL positioning: The ModalNotification sidebar offset does not fully work in RTL mode. The md:yst-start-[160px] Tailwind class is picked up because it already exists in the CSS bundle, but the RTL-specific class (rtl:yst-end-[160px]) is not generated because the ui-library's Tailwind config only scans its own src/ directory — arbitrary value classes used in packages/js are not included.

Copy link
Contributor

@vraja-pro vraja-pro left a comment

Choose a reason for hiding this comment

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

CR & AC 🚧
Few comments for mobile and RTL.

return <ModalNotification
isOpen={ isOpen }
onClose={ onClose }
className={ classNames(
Copy link
Contributor

@vraja-pro vraja-pro Feb 25, 2026

Choose a reason for hiding this comment

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

Lets add custom z index to cover the help scout button on mobile. In this case we need to use custom tailwindcss class.

Image
Suggested change
className={ classNames(
className={ classNames( "yst-z-[9999]",

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! 👍🏼

Copy link
Contributor

@vraja-pro vraja-pro left a comment

Choose a reason for hiding this comment

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

AC 🚧

notificationPositionClass = "md:yst-start-[3.25rem]";
} else {
notificationPositionClass = "md:yst-start-10";
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to this if statement because the linter complains if I add nested ternary operators.

const modalPositionClass = isAdminSidebarExpanded() ? "md:yst-start-40 rtl:md:yst-start-44" : isRtl ? "md:yst-start-[3.25rem]" : "md:yst-start-10";

…lacing 'left' and 'right' with 'start' and 'end' classes in Tailwind CSS.
@JorPV JorPV removed their assignment Feb 27, 2026
Copy link
Contributor

@vraja-pro vraja-pro left a comment

Choose a reason for hiding this comment

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

CR & AC ✅

@vraja-pro vraja-pro added this to the 27.2 milestone Feb 27, 2026
Copy link
Contributor

@vraja-pro vraja-pro left a comment

Choose a reason for hiding this comment

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

CR & AC ✅

@vraja-pro vraja-pro added this to the 27.2 milestone Feb 27, 2026
@vraja-pro vraja-pro merged commit 84440e4 into trunk Feb 27, 2026
7 of 19 checks passed
@vraja-pro vraja-pro deleted the jordi-pv/use-the-dialog-toast-in-wordpress branch February 27, 2026 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog: enhancement Needs to be included in the 'Enhancements' category in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants