Skip to content

fix(modal): height flickering issue#22190

Merged
tay1orjones merged 9 commits into
carbon-design-system:mainfrom
gDINESH13:issue_22153
Jun 1, 2026
Merged

fix(modal): height flickering issue#22190
tay1orjones merged 9 commits into
carbon-design-system:mainfrom
gDINESH13:issue_22153

Conversation

@gDINESH13

@gDINESH13 gDINESH13 commented May 6, 2026

Copy link
Copy Markdown
Contributor

Closes #22153

The modal height was flickering when user interactions caused dynamic content changes. This occurred because the isScrollable calculation created a feedback loop: when scrollHeight was close to clientHeight, adding the cds--modal-scroll-content class would apply a 2px border, changing the clientHeight, which would remove the class, which would remove the border, causing rapid oscillation.

Changelog

New

  • None

Changed

  • Added hysteresis to isScrollable calculation in Modal component to prevent rapid class toggling when content height is near the scrollable threshold

Removed

  • None

Testing / Reviewing

I have tested with writing a playwright e2e test. I haven't committed it because it requires a new story to be added in storybook.
But from storybook point of view it may look like duplication as many variants of modal is already present.
Writing a unit test case for this fix is not capturing the bug. I would like to get some suggestions on how should I show my testing.

PR Checklist

As the author of this PR, before marking ready for review, confirm you:

  • Reviewed every line of the diff
  • Updated documentation and storybook examples
  • Wrote passing tests that cover this change
  • Addressed any impact on accessibility (a11y)
  • Tested for cross-browser consistency
  • Validated that this code is ready for review and status checks should pass

More details can be found in the pull request guide

Signed-off-by: gDINESH13 <dinesh13g@gmail.com>
@gDINESH13 gDINESH13 requested a review from a team as a code owner May 6, 2026 06:05
@netlify

netlify Bot commented May 6, 2026

Copy link
Copy Markdown

Deploy Preview for v11-carbon-react ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 19517b4
🔍 Latest deploy log https://app.netlify.com/projects/v11-carbon-react/deploys/6a049acc24aeff0008c25b58
😎 Deploy Preview https://deploy-preview-22190--v11-carbon-react.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented May 6, 2026

Copy link
Copy Markdown

Deploy Preview for v11-carbon-web-components ready!

Name Link
🔨 Latest commit 19517b4
🔍 Latest deploy log https://app.netlify.com/projects/v11-carbon-web-components/deploys/6a049acc08e69d00082c7458
😎 Deploy Preview https://deploy-preview-22190--v11-carbon-web-components.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented May 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.49%. Comparing base (a0df9d4) to head (19517b4).
⚠️ Report is 63 commits behind head on main.

Files with missing lines Patch % Lines
packages/react/src/components/Modal/Modal.tsx 85.71% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #22190      +/-   ##
==========================================
- Coverage   95.51%   95.49%   -0.03%     
==========================================
  Files         582      582              
  Lines       50320    50332      +12     
  Branches     6769     6717      -52     
==========================================
  Hits        48063    48063              
- Misses       2125     2137      +12     
  Partials      132      132              
Flag Coverage Δ
main-packages 89.18% <85.71%> (+<0.01%) ⬆️
web-components 98.07% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@netlify

netlify Bot commented May 6, 2026

Copy link
Copy Markdown

Deploy Preview for carbon-elements ready!

Name Link
🔨 Latest commit 19517b4
🔍 Latest deploy log https://app.netlify.com/projects/carbon-elements/deploys/6a049accbfb0d800082ad270
😎 Deploy Preview https://deploy-preview-22190--carbon-elements.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@heloiselui

Copy link
Copy Markdown
Contributor

Hey @gDINESH13, could you add more tests to cover this changes?

image

Signed-off-by: gDINESH13 <dinesh13g@gmail.com>
@gDINESH13

Copy link
Copy Markdown
Contributor Author

Hello, @heloiselui Thanks for checking. I'd added some TC to improve coverage but those can't be considered to claim the functionality works. Mentioned this in PR desc, please lmk what you think.

@heloiselui heloiselui left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM! I tested your changes locally by creating a Storybook similar to the StackBlitz from the issue. I ran several stress tests trying to reproduce the problem again. It seems like your changes are good!

});

describe('Modal scroll content hysteresis', () => {
it('should handle missing contentRef gracefully', () => {

@heloiselui heloiselui May 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test name says ‘missing contentRef’, but contentRef.current is not actually null here. This case stubs scrollHeight on an existing node instead. Could we either rename the test to match behavior or add a case that truly exercises !contentRef.current? -not a blocker

if (diff > 5) {
// Clearly scrollable - turn on
setIsScrollable(true);
} else if (diff < -5) {

@tay1orjones tay1orjones May 20, 2026

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.

In normal layout wouldn't scrollHeight generally bottom out at clientHeight for non-overflowing content?

I ask cause thinking about the case of if modal content shrinks, diff usually becomes 0, stays in the dead zone, and keeps cds--modal-scroll-content forever.

That would leave the fade/mask, extra last-child margin, tabIndex, and region semantics on content that is no longer scrollable.

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.

Hi @tay1orjones I understand your concern, I emulated a test case on what you mentioned to have
scrollHeight === clientHeight and it retained the cds--modal-scroll-content at that point.

The class cds--modal-scroll-content adds this 2px border block every time.
So every time this class is applied on modal the clientHeight gets reduced by 2px and when class is removed the clientHeight is increased by 2px.
And that's one base reason why modal flickering happens, this change in clientHeight.

border-block-end: 2px solid transparent;

I can reduce the -5 diff to -2px but if I add diff<=0 the flicker happens again, clearly. Can you give some ideas on how to tackle this?

Also, this retaining will happen for of a range of 2px difference right, is that a serious issue?

Comment on lines +776 to +784
// Now make it clearly not scrollable (diff < -5)
Object.defineProperty(modalContent, 'scrollHeight', {
get: () => 300,
configurable: true,
});
Object.defineProperty(modalContent, 'clientHeight', {
get: () => 400, // diff = -100, which is < -5
configurable: true,
});

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.

This technically covers it but a better test would start scrollable, shrink content until scrollHeight === clientHeight, and assert the scroll class is removed.

This might be something to test in a real browser in e2e/components/Modal/Modal-test.avt.e2e.js. The .avt.e2e.js are generally a11y-focused files but can be used for one-off stuff like this.

@gDINESH13 gDINESH13 requested a review from tay1orjones May 25, 2026 15:48
@tay1orjones tay1orjones added this pull request to the merge queue Jun 1, 2026
Merged via the queue into carbon-design-system:main with commit bceaa16 Jun 1, 2026
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Modal height jumping unrelated to children rerenders

3 participants