Note: This is a guest post by Mike Herchel, an official maintainer of Drupal core’s CSS subsystem as well as Drupal's default theme, Olivero. He's also a founder of Dripyard premium Drupal themes.
While working on a Drupal core bug in the Navigation module’s toolbar, I discovered the issue was related to the usage of Drupal.displace(), which is included in Core’s JavaScript and CSS APIs. Reading through the comments, it was clear that a few folks weren’t entirely sure what this does. Luckily, it’s much simpler than it seems.
What problem does Drupal.displace() solve?
Drupal.displace() outputs JavaScript and CSS variables that can let your frontend theme know if there are any Drupal admin toolbars (or anything else) fixed to the edge of your screen.
What does that mean? Below is the header of Dripyard’s Neonbyte theme. The header is fixed to the top of the page, which means when you scroll, the header stays put.
But what happens when I log in? The admin toolbar overlaps!
How do I prevent this? As a front-end developer, how do I know when to push down my fixed header, how far to push it down, and what happens if the toolbar is in its sidebar orientation?
This is where Drupal.displace() comes in!
How to use Drupal.displace()
As a frontender, the most common way to use Drupal.displace() is through the CSS variables that get attached to the root <html> element.
So, instead of writing CSS like this:
.site-header {
position: fixed;
top: 20px;
}
I take into account the CSS variables and do this:
.site-header {
position: fixed;
top: calc(20px + var(--drupal-displace-offset-top, 0px));
}
Note that we’re using a fallback value of 0px so the CSS calc() function resolves properly, allowing the top property to work even when the CSS variable isn’t present.
Keen-eyed readers might have also noticed that we’re using Drupal.displace() variables when calculating the width of the header. This is important for elements fixed to the left (like the new Navigation toolbar), or to the right (the off-canvas dialog).
Handling offsets for RTL languages
It’s important to mention that the initial JS API for displace was added back in 2013 (way before CSS logical properties). So, if we’re accommodating RTL languages such as Hebrew or Arabic (which Dripyard’s Drupal themes support), we need to be explicit.
Here’s an example of handling a sidebar that appears to the left on LTR languages (such as English) and to the right on RTL languages.
.skip-link {
position: absolute;
left: var(--drupal-displace-offset-left, 0);
&:dir(rtl) {
right: var(--drupal-displace-offset-right, 0);
}
}
How do I Drupal.displace() in JavaScript?
If you’re creating your own toolbar (or other fixed UI), you’ll need to add a data-offset-[edge] attribute to the markup that you’re injecting. Note that you’ll have to manually accommodate RTL languages by checking for the writing mode before setting it.
After the HTML is injected into the DOM, simply run Drupal.displace(), and it’ll loop through all of the elements that have those attributes, update the CSS vars, and then return an object that looks like this:
{
bottom: 0
left: 240
right: 0
top: 40
}
So, let's fix that core bug!
The core issue in question involves the new Navigation module. When the contextual top bar was present, the left toolbar had an unsightly gap at the bottom.
You can see this on the Florida DrupalCamp website (which coincidentally is happening in just a few months in sunny Orlando, FL).
The root cause was actually unnecessary use of Drupal.displace()! Because the Navigation toolbar should always be spanning 100% of the height, it doesn’t need to take into account the top offset, as it currently does.
Conclusion
While sometimes a bit overwhelming, Drupal has just about an API for everything (which is one of our greatest strengths). The Drupal.displace() API is super useful for us front-end devs, and helps keep the UI working as expected.
If you want to see a real-world example of how we handle displacement logic, check out Dripyard’s premium Drupal themes, which are designed with best practices in mind, from fixed headers to RTL support.