-
Notifications
You must be signed in to change notification settings - Fork 1.1k
USWDS: Header: Add fix for Safari-only header bug #5443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- This forces the browser scrollbar to prevent media query confusion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is working well for me! While the scrolling isn't ideal, I think it's a rare case someone would find themselves at that viewport size and try to scroll.
A couple potential recommendations to try as well as a documentation edit then I think we'll be golden 🥇
packages/usa-header/src/index.js
Outdated
// Detect Safari and add a body class for a Safari-only CSS bug fix. | ||
// More details in https://github.com/uswds/uswds/pull/5443 | ||
const isSafari = navigator.userAgent.indexOf("Safari") !== -1; | ||
const isNotChrome = navigator.userAgent.indexOf("Chrome") === -1; | ||
if (isSafari && isNotChrome) { | ||
body.classList.add("is-safari"); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering why we check for Chrome but not other browsers. I've found articles that explain that Chromium browsers will provide the Safari
user agent as well as the chomium one.
Could we add a note about this and potentially a source so if we forget and question this down the road it's well documented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, @mahoneycm. I've added a comment in the code and a note in 5a41838.
I also included a note in the Solution section of the PR description pointing to this MDN article that explains userAgent detection with Safari and Chrome.
@media (min-width: $safari-header-bug-min-width) { | ||
.usa-js-mobile-nav--active.is-safari { | ||
overflow: scroll; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In your JS solution, were you capturing the current scroll position then using that to lock the view to that position?
I was curious if we could set position: fixed
with the current top offset to lock vertical scrolling at these widths but it seems like your previous solution was doing something similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting idea!
In 625c7f4, I added position: fixed
to the body CSS when the overflow: scroll
rule is enacted in Safari, and it does prevent scroll while maintaining the scrollbar. (Woohoo!)
A couple of things I noticed: When the menu is opened in this state, the page content does shift down a bit so that the content aligns with the top of the viewport. In the current state, the content also shifts, but not as much and only on page close.
I will work on thinking through if there are any possible downsides to this approach. I will also experiment with iOS tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This stack overflow solution uses JavaScript to calculate the position of the viewport after the scroll and set the document body top
to match it to prevent the content shift.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. Thanks! Implemented in 0600cf0.
Now when the mobile menu is open in the 15px window range in Safari, the body element will have fixed positioning and receive the current JS scrollTop value to set the body'stop
position.
Trying to think of any potential unintended side effects and I think we are good to go. Curious if anyone else has thoughts on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that while there isn't a vertical shift when the menu opens, there is a shift to the top when the menu is then closed.
If we can get that sorted I think we'll be in a good standing! I like that this solution prevents scrolling on the affected widths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mahoneycm This is pre-existing behavior, so I am inclined to leave it as is in this PR just to keep things scoped. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@amyleadem Good point! Since the header isn't sticky and remains at the top, I think that we can leave it 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mahoneycm Great! Ready to smash that "approve" button? Are there any outstanding items?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice fix. Added some suggestions. I was able to test this in Safari 16.6 without any issues.
Consider moving the Safari check to its own function and calling that in toggleNav()
.
- Improve readability of code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestions @mejiaj. Definitely feels more readable now. You and @mahoneycm are making me better!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor suggestion to separate browser check from setting padding. Otherwise LGTM.
I tested functionality on:
- Chromium v116
- Firefox v118
- Safari v16.6
- Safari iPad mini (2022)
The class only shows in Safari and the scrollbar is gone.
packages/usa-header/src/index.js
Outdated
const setSafariScrollPosition = (body) => { | ||
const isSafari = navigator.userAgent.includes("Safari"); | ||
const isNotChrome = !navigator.userAgent.includes("Chrome"); | ||
const currentScrollPosition = `-${window.scrollY}px`; | ||
if (isSafari && isNotChrome) { | ||
body.style.setProperty("--scrolltop", currentScrollPosition); | ||
body.classList.add("is-safari"); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Co-authored-by: James Mejia <james.mejia@gsa.gov>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job, I was able to confirm:
- Safari class doesn't show on other browsers
- Scroll error is gone in Safari
- Only padding right is modified when setting/removing styles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well-scoped solution
Summary
Fixed a Safari-only bug that caused the mobile menu to unexpectedly close at a narrow range of window widths.
Breaking change
This is not a breaking change.
Related issue
Closes #5371
Related pull requests
Changelog PR
Preview link
Preview link: Documentation page
Problem statement
In Safari, the mobile header panel will unexpectedly close when opened in a narrow range of window widths. This issue happens when the mobile menu is opened in windows widths within the 15px preceding the
$theme-header-max-width
breakpoint. With the default"desktop"
value of ` 1024px, this issue happens at window widths of 1009px - 1023px.The issue is demonstrated in this GIF when testing the range right before the
"desktop"
breakpoint:Known Safari bug
This issue happens in Safari because it does not include the width of the vertical scrollbar when it calculates the width of the viewport. (See this open webkit bug report). This means that in Safari, the registered viewport width will change depending on the presence or absence of a vertical scrollbar.
In USWDS, opening the mobile menu removes the vertical scrollbar on
<body>
because its overflow is set to hidden. When this scrollbar disappears, Safari updates the value of the viewport size to be 15px (the width of the scrollbar) wider than the original value. If this new viewport width registers as a "desktop" width, the CSS will show the desktop view of the header.For example, if you trigger the mobile menu at an original viewport width of 1020px, Safari will reset value of the viewport width to 1035px once the menu is opened. Because this new width is greater than the default
$theme-header-max-width
value of 1024px, the CSS turns on desktop menu display.Solution
To resolve this, this PR forces Safari to keep the the body's vertical scrollbar when the mobile menu is open. This fix is applied only to Safari and only to the affected 15px of screen width (by default, 1009-1024px).
Forcing the scrollbar at these window widths prevents Safari from changing its width definition when the mobile menu is open, which removes confusion about which version of the header navigation to show.
Testing and review
Review:
is-safari
class is added to Safari onlyUse Storybook to test that the mobile menu opens in Safari:
Use Storybook to test that this does not affect other browsers: