diff --git a/mu-plugins/blocks/sidebar-container/index.php b/mu-plugins/blocks/sidebar-container/index.php
index 08edf085d..4056b6982 100644
--- a/mu-plugins/blocks/sidebar-container/index.php
+++ b/mu-plugins/blocks/sidebar-container/index.php
@@ -45,11 +45,13 @@ function render( $attributes, $content, $block ) {
esc_html__( '↑ Back to top', 'wporg' )
)
: '';
+ $inlineBreakpoint = $attributes['inlineBreakpoint'];
$wrapper_attributes = get_block_wrapper_attributes();
return sprintf(
- '
%2$s%3$s
',
+ '%3$s%4$s
',
$wrapper_attributes,
+ esc_attr( $inlineBreakpoint ),
$content,
$back_to_top
);
diff --git a/mu-plugins/blocks/sidebar-container/postcss/style.pcss b/mu-plugins/blocks/sidebar-container/postcss/style.pcss
index fe8bcdd4e..de49d5de5 100644
--- a/mu-plugins/blocks/sidebar-container/postcss/style.pcss
+++ b/mu-plugins/blocks/sidebar-container/postcss/style.pcss
@@ -1,21 +1,34 @@
-.wp-block-wporg-sidebar-container .is-link-to-top {
- display: none;
+.wp-block-wporg-sidebar-container {
+ --local--offset-top: var(--wp-admin--admin-bar--height, 0);
- & a {
- text-decoration-line: none;
+ /* These vars are used in JS calcs */
+ --local--nav--offset: var(--wp--custom--local-navigation-bar--spacing--height, 60px);
+ --local--padding-top: var(--wp--preset--spacing--20);
- &:hover {
- text-decoration-line: underline;
+ background-color: aliceblue;
+
+ /* Account for local nav height on larger screens where it becomes fixed. */
+ @media (min-width: 890px) {
+ /* stylelint-disable-next-line length-zero-no-unit */
+ --local--nav--offset: 0px;
+ --local--offset-top: calc(var(--wp-admin--admin-bar--height, 0px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
+ }
+
+ & .is-link-to-top {
+ display: none;
+
+ & a {
+ text-decoration-line: none;
+
+ &:hover {
+ text-decoration-line: underline;
+ }
}
}
-}
-/* Slot the search & table of contents into a floating sidebar on large screens. */
-@media (min-width: 1200px) {
- .wp-block-wporg-sidebar-container {
+ /* Slot the search & table of contents into a floating sidebar on large screens. */
+ &.is-floating-sidebar {
--local--block-end-sidebar--width: 340px;
- --local--offset-top: calc(var(--wp-admin--admin-bar--height, 0px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
- --local--padding-top: var(--wp--preset--spacing--20);
width: var(--local--block-end-sidebar--width);
@@ -60,15 +73,16 @@
border-top: 1px solid var(--wp--preset--color--light-grey-1);
}
}
+}
- main .wp-block-wporg-sidebar-container {
- position: absolute;
- top: calc(var(--wp-global-header-offset, 90px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
- margin-top: var(--wp--custom--wporg-sidebar-container--spacing--margin--top);
+main .wp-block-wporg-sidebar-container.is-floating-sidebar {
+ position: absolute;
+ top: calc(var(--wp-global-header-offset, 90px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
+ margin-top: var(--wp--custom--wporg-sidebar-container--spacing--margin--top);
+
+ /* Right offset should be "edge spacing" at minimum, otherwise calculate it to be centered. */
+ right: max(var(--wp--preset--spacing--edge-space), calc((100% - var(--wp--style--global--wide-size)) / 2));
- /* Right offset should be "edge spacing" at minimum, otherwise calculate it to be centered. */
- right: max(var(--wp--preset--spacing--edge-space), calc((100% - var(--wp--style--global--wide-size)) / 2));
- }
}
/* Set up the custom properties. These can be overridden by settings in theme.json. */
diff --git a/mu-plugins/blocks/sidebar-container/src/block.json b/mu-plugins/blocks/sidebar-container/src/block.json
index c07a7574d..557d1652f 100644
--- a/mu-plugins/blocks/sidebar-container/src/block.json
+++ b/mu-plugins/blocks/sidebar-container/src/block.json
@@ -11,6 +11,10 @@
"hasBackToTop": {
"type": "boolean",
"default": true
+ },
+ "inlineBreakpoint": {
+ "type": "string",
+ "default": "1200px"
}
},
"supports": {
diff --git a/mu-plugins/blocks/sidebar-container/src/view.js b/mu-plugins/blocks/sidebar-container/src/view.js
index 3d5b724a3..e97b39701 100644
--- a/mu-plugins/blocks/sidebar-container/src/view.js
+++ b/mu-plugins/blocks/sidebar-container/src/view.js
@@ -2,16 +2,13 @@
* Fallback values for custom properties match CSS defaults.
*/
-const GLOBAL_NAV_HEIGHT = getCustomPropValue( '--wp-global-header-height' ) || 90;
-const ADMIN_BAR_HEIGHT = parseInt(
- window.getComputedStyle( document.documentElement ).getPropertyValue( 'margin-top' ),
- 10
-);
const SPACE_TO_TOP = getCustomPropValue( '--wp--custom--wporg-sidebar-container--spacing--margin--top' ) || 80;
-const SCROLL_POSITION_TO_FIX = GLOBAL_NAV_HEIGHT + SPACE_TO_TOP - ADMIN_BAR_HEIGHT;
let containers;
let mainEl;
+let adminBarHeight = getCustomPropValue( '--wp-admin--admin-bar--height' ) || 32;
+let globalNavHeight = getCustomPropValue( '--wp-global-header-height' ) || 90;
+const scrollHandlers = [];
/**
* Get the value of a CSS custom property.
@@ -33,36 +30,37 @@ function getCustomPropValue( name, element = document.body ) {
* Check the position of each sidebar relative to the scroll position,
* and toggle the "fixed" class at a certain point.
* Reduce the height of each sidebar to stop them overlapping the footer.
+ *
+ * @param {HTMLElement} container
*/
-function onScroll() {
- // Only run the scroll code if the sidebar is floating on a wide screen.
- if ( ! window.matchMedia( '(min-width: 1200px)' ).matches ) {
- return;
- }
+function createScrollHandler( container ) {
+ return function onWindowChange() {
+ // Only run the scroll code if the sidebar is floating.
+ if ( ! container.classList.contains( 'is-floating-sidebar' ) ) {
+ return false;
+ }
- const { scrollY, innerHeight: windowHeight } = window;
- const scrollPosition = scrollY - ADMIN_BAR_HEIGHT;
+ const { scrollY, innerHeight: windowHeight } = window;
+ const scrollPosition = scrollY - adminBarHeight;
+ const localNavOffset = getCustomPropValue( '--local--nav--offset', container );
+ const paddingTop = getCustomPropValue( '--local--padding-top', container );
- // Toggle the fixed position based on whether the scrollPosition is greater than the initial gap from the top.
- containers.forEach( ( container ) => {
+ // Toggle the fixed position based on whether the scrollPosition is greater than the
+ // initial gap from the top minus the padding applied when fixed.
container.classList.toggle(
'is-fixed-sidebar',
- scrollPosition > SCROLL_POSITION_TO_FIX - getCustomPropValue( '--local--padding-top', container )
+ scrollPosition > SPACE_TO_TOP + globalNavHeight + localNavOffset - adminBarHeight - paddingTop
);
- } );
- const footerStart = mainEl.offsetTop + mainEl.offsetHeight;
+ const footerStart = mainEl.offsetTop + mainEl.offsetHeight;
- // Is the footer visible in the viewport?
- if ( footerStart < scrollPosition + windowHeight ) {
- containers.forEach( ( container ) => {
+ // Is the footer visible in the viewport?
+ if ( footerStart < scrollPosition + windowHeight ) {
container.style.setProperty( 'height', `${ footerStart - scrollPosition - container.offsetTop }px` );
- } );
- } else {
- containers.forEach( ( container ) => {
+ } else {
container.style.removeProperty( 'height' );
- } );
- }
+ }
+ };
}
function init() {
@@ -70,10 +68,31 @@ function init() {
mainEl = document.getElementById( 'wp--skip-link--target' );
if ( mainEl && containers.length ) {
- onScroll(); // Run once to avoid footer collisions on load (ex, when linked to #reply-title).
- window.addEventListener( 'scroll', onScroll );
+ containers.forEach( ( container ) => {
+ const scrollHandler = createScrollHandler( container );
+ scrollHandlers.push( scrollHandler );
+ scrollHandler(); // Run once to avoid footer collisions on load (ex, when linked to #reply-title).
+ window.addEventListener( 'scroll', scrollHandler );
+ } );
+ }
+
+ function onResize() {
+ adminBarHeight = getCustomPropValue( '--wp-admin--admin-bar--height' ) || 32;
+ globalNavHeight = getCustomPropValue( '--wp-global-header-height' ) || 90;
+
+ containers.forEach( ( container ) => {
+ const breakpoint = container.dataset.breakpoint;
+ const shouldFloat = window.matchMedia( `(min-width: ${ breakpoint })` ).matches;
+ // Toggle the floating class if the window is wider than the configured breakpoint.
+ container.classList.toggle( 'is-floating-sidebar', shouldFloat );
+ } );
+
+ scrollHandlers.forEach( ( handler ) => handler() );
}
+ onResize();
+ window.addEventListener( 'resize', onResize() );
+
// If there is no table of contents, hide the heading.
if ( ! document.querySelector( '.wp-block-wporg-table-of-contents' ) ) {
const heading = document.querySelector( '.wp-block-wporg-sidebar-container h2' );