diff --git a/resources/skins.vector.js/stickyHeader.js b/resources/skins.vector.js/stickyHeader.js index bb2cfd37d..fdbf6490f 100644 --- a/resources/skins.vector.js/stickyHeader.js +++ b/resources/skins.vector.js/stickyHeader.js @@ -1,8 +1,43 @@ -module.exports = function () { - var header = document.getElementById( 'vector-sticky-header' ); - if ( !header ) { +var + STICKY_HEADER_ID = 'vector-sticky-header', + STICKY_HEADER_VISIBLE_CLASS = 'vector-sticky-header-visible', + FIRST_HEADING_ID = 'firstHeading'; + +/** + * Makes sticky header functional for modern Vector. + * + * @param {HTMLElement} header + * @param {HTMLElement} stickyIntersection + */ +function makeStickyHeaderFunctional( header, stickyIntersection ) { + /* eslint-disable-next-line compat/compat */ + var stickyObserver = new IntersectionObserver( function ( entries ) { + if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) { + // Viewport has crossed the bottom edge of firstHeading so show sticky header. + // eslint-disable-next-line mediawiki/class-doc + header.classList.add( STICKY_HEADER_VISIBLE_CLASS ); + } else { + // Viewport is above the bottom edge of firstHeading so hide sticky header. + // eslint-disable-next-line mediawiki/class-doc + header.classList.remove( STICKY_HEADER_VISIBLE_CLASS ); + } + } ); + + stickyObserver.observe( stickyIntersection ); +} + +module.exports = function initStickyHeader() { + var header = /** @type {HTMLElement} */ ( document.getElementById( STICKY_HEADER_ID ) ), + stickyIntersection = /** @type {HTMLElement} */ ( document.getElementById( + FIRST_HEADING_ID + ) ); + + if ( !( + stickyIntersection && + header && + 'IntersectionObserver' in window ) ) { return; } - // TODO: Use IntersectionObserver - header.classList.add( 'vector-sticky-header-visible' ); + + makeStickyHeaderFunctional( header, stickyIntersection ); }; diff --git a/resources/skins.vector.styles/components/StickyHeader.less b/resources/skins.vector.styles/components/StickyHeader.less index d7a81ac1d..bf8389b78 100644 --- a/resources/skins.vector.styles/components/StickyHeader.less +++ b/resources/skins.vector.styles/components/StickyHeader.less @@ -22,6 +22,11 @@ justify-content: space-between; box-sizing: border-box; + // If the user has expressed their preference for reduced motion, then disable animation for the sticky header. + @media ( prefers-reduced-motion: reduce ) { + transition: none; + } + @media ( min-width: @width-breakpoint-desktop ) { padding: 6px 25px; }