Add scroll padding to the root element when the sticky header is enabled

When the sticky header is visible, it has a global impact on the
scrolling UX. For example, it can undesirably overlap elements when the
user clicks on a jump link and when the user tabs through elements in
reverse order. Therefore, we need to add scroll padding to the root
element when the sticky header is enabled (when the feature flag is on
and at higher resolutions)

Known limitations:

* Scroll padding is supported by all the latest modern browsers except
for Safari [1]. This was considered an acceptable tradeoff with the
caveat that this decision may be revisited in the future as we learn
more about user interaction with the sticky header.

[1] https://caniuse.com/mdn-css_properties_scroll-padding-top

Bug: T290518
Change-Id: Ie5eb01d7eafd18ce740be620dfb5c8849386af6e
This commit is contained in:
Nicholas Ray 2021-09-28 16:43:15 -06:00
parent 253442da67
commit 1209b388e9
2 changed files with 43 additions and 0 deletions

View File

@ -66,6 +66,7 @@ class SkinVector extends SkinMustache {
'class' => 'sticky-header-icon'
];
private const SEARCH_EXPANDING_CLASS = 'vector-search-box-show-thumbnail';
private const STICKY_HEADER_ENABLED_CLASS = 'vector-sticky-header-enabled';
/**
* T243281: Code used to track clicks to opt-out link.
@ -323,6 +324,26 @@ class SkinVector extends SkinMustache {
return parent::generateHTML();
}
/**
* @inheritDoc
*/
public function getHtmlElementAttributes() {
$original = parent::getHtmlElementAttributes();
if ( VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_STICKY_HEADER ) ) {
// T290518: Add scroll padding to root element when the sticky header is
// enabled. This class needs to be server rendered instead of added from
// JS in order to correctly handle situations where the sticky header
// isn't visible yet but we still need scroll padding applied (e.g. when
// the user navigates to a page with a hash fragment in the URI). For this
// reason, we can't rely on the `vector-sticky-header-visible` class as it
// is added too late.
$original['class'] = implode( ' ', [ $original['class'] ?? '', self::STICKY_HEADER_ENABLED_CLASS ] );
}
return $original;
}
/**
* Generate data needed to generate the sticky header.
* Lack of i18n is intentional and will be done as part of follow up work.

View File

@ -1,8 +1,14 @@
@import '../../common/variables.less';
@import 'mediawiki.mixins.less';
// Set an explicit height. This is needed for scroll padding and for other
// sticky elements on the page. Setting the height in relative units enables
// the header's height to adapt to the browser's font size setting.
@height-sticky-header: unit( 60px / @font-size-browser, em );
.vector-sticky-header {
width: 100%;
height: @height-sticky-header;
position: fixed;
top: 0;
left: 0;
@ -122,3 +128,19 @@
.client-nojs .vector-sticky-header {
display: none;
}
// T290518: When the sticky header is enabled (feature flag is on, js is
// enabled, and viewport is at higher resolutions), add scroll padding to the
// root element. This is needed so that the sticky header does not overlap the
// top of an element when the URI has a hash fragment (e.g. when the user clicks
// a jump link) and when the user tabs through elements in reverse order.
//
// Please note that this class must be independent of the
// .vector-sticky-header-visible class to correctly handle situations where the
// sticky header isn't visible yet but we still need scroll padding applied
// (e.g. when the user navigates to a page with a hash fragment in the URI).
@media ( min-width: @width-breakpoint-tablet ) {
.client-js.vector-sticky-header-enabled {
scroll-padding-top: @height-sticky-header;
}
}