Howdy! Have you ever built a site (primarily on mobile) and gotten frustrated by the white space peeking out when you scroll past the top or bottom of the page? It's like the browser's way of saying, Hey, nothing here. Wanna see my boring default white background?
It’s a detail I can’t help but notice, and I made it my mission to obliterate it when possible from Frontend apps. So without further ado, I’d like to share a CSS technique that locks your background in place while letting the content scroll smoothly.
I initially cooked this up for this very blog, but it's pure HTML/CSS magic and (should) work anywhere. The secret? Use a fixed pseudo-element on the body for the background, so it stays glued to the viewport. Your actual content lives in the body, which scrolls as usual.
Let’s walk through a basic HTML/CSS tutorial you can spin up in your IDE. I like hands-on learning, you do too right?!
🤖 Disclaimer: As with all code techniques, I have been using this
::beforew/o any issues in my projects. But if you’re reading this and know of a better way and/or have reasons why you should avoid::before(perhaps due to acessibility issues?), please let me know! Always looking to learn and you can email me via the Contact form (in the Menu dropdown)
The Core Idea
- Set
htmlandbodyto full viewport size (100% width/height, no margins/padding). - Make
bodya flex container withoverflow: autoso it handles scrolling. - Add a
::beforepseudo-element tobodythat'sposition: fixed, covering the whole screen, and slap your background (color, image, gradient—whatever) on it with a lowz-index. - Your
maincontent grows to fill the space and centers everything nicely.
This way, the background is pinned
and doesn't budge, even on overscroll. Perfect for dark modes, gradients, or fixed images.
A Basic HTML/CSS Example
Here's a stripped-down version. Drop this into an HTML file. I've used a simple black-to-gray gradient, but swap it if you like.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Locked Background Demo</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
min-height: 100vh;
box-sizing: border-box;
color: #FFF; /* White text for contrast */
}
body {
display: flex;
padding: 1rem;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
overflow: auto;
background: none;
min-height: 100vh;
}
body::before {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, #000 0%, #121212 100%); /* Your favorite gradient here! */
z-index: -1;
}
main {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
box-sizing: border-box;
}
/* Just some dummy content to force scrolling */
section {
margin: 2rem 0;
text-align: center;
}
</style>
</head>
<body>
<main>
<section>
<h1>Welcome to the Demo!</h1>
<p>Scroll up and down—notice no white space? Magic!</p>
</section>
<section>
<p>Here's some more content to make it scrollable. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</section>
<section>
<p>Even more! Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</section>
<!-- Add more to test scrolling! -->
</main>
</body>
</html>
Open this in your browser, switch to mobile view, and try pulling the page up or down. The gradient stays put, no white flashes. If you're using variables (e.g., --Black-Gradient), just define them in :root for easy theming.
Goodbye Overscroll
So there ya go! It's lightweight, no JS needed, and it plays nicely with frameworks like Rails, React, etc. Plus, it's great for immersive designs like this blog or portfolios, apps, and landing pages where you want that polished feel.
Try it out in your next project, and let me know if you have any questions or feedback! Hit me up via the Contact form in the menu! I’d love to hear from you.