Problem:
I’m designing a website where both the main content and the sidebar scroll together using a single scrollbar. Since the sidebar is shorter, I’ve used position: sticky with a negative top value to keep its bottom visible and prevent showing any empty space beneath it. This works when scrolling down. However, when scrolling back up, the sidebar doesn’t scroll until the sticky position is reached, causing a delay in its upward movement.
I want the sidebar to behave like the one on X.com (Twitter) — scrolling smoothly in sync with the main content, both up and down, without relying on a sticky offset. The sidebar should always stay aligned and scroll naturally, regardless of scroll position.(for more detail go to x homepage and see how main and right side bar is scrolled and see my attached code images)
context:
I’m building an X.com (Twitter) clone with two scrollable areas: the main section and a shorter right sidebar. Since they share a single scrollbar, the sidebar finishes scrolling first, revealing undesigned space below it. I want to replicate how X.com handles this: once the sidebar reaches its max scroll, it behaves like it’s fixed in place. But when the user scrolls back up, the sidebar scrolls upward smoothly with the main content—unlike position: sticky, which keeps it stuck until a certain scroll position is reached. (See attached images for reference.)
Minimum reproducible example:
- Go near bottom
- try going up
- observe sidebar how in this case it is stuck when scrolling back up, the sidebar doesn’t scroll until the sticky position is reached,
- repeat same on x.com homepage to see how it works
for (i = 0; i < 5; i++) {
document.querySelector(".right").insertAdjacentHTML("afterbegin", `<div class="box">${5-i}</div>`)
}
for (i = 0; i < 15; i++) {
document.querySelector(".main").insertAdjacentHTML("afterbegin", `<div class="box">${15-i}</div>`)
}
const small = document.querySelector(".right")
const smallbot = small.getBoundingClientRect()
small.style.top = `${window.innerHeight - smallbot.height}px`;
.box {
height: 200px;
width: 300px;
border: 2px solid red;
margin: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: larger;
}
.cont {
display: flex;
}
.right {
position: sticky;
}
<div class="cont">
<div class="main">
</div>
<div>
<div class="right" style="top: 0px;"></div>
</div>
</div>
Previous Approaches that did not worked:
-
I’ve tried several approaches, including dynamically applying position: absolute when scrolling up. However, this either positions the sidebar relative to the body (misaligned to the left), or, if a left value is set, it stays fixed in one spot—unlike X.com, where the sidebar appears to stick from the bottom and scrolls up smoothly with the main content once fully scrolled.
-
When scrolling up, I tried dynamically removing the top value to unstick the sidebar, which works—but it shifts the entire div to the top of the screen, positioning it incorrectly.
Expectation:
I’m aiming for a UI like X.com, where the main content and sidebar scroll together. The shorter sidebar stops scrolling once fully scrolled—appearing fixed to avoid showing blank space—and when scrolling back up, it scrolls upward smoothly in sync with the main content—appearing fixed from bottom to avoid showing blank space above (See X’s homepage for reference.)
Image 1 (when user have scrolled to near bottom):

Image 2 (user is going up but side bar is stuck due to sticky) ;

X image (user near bottom):

X image (user try to go up and are able to go up):

code for reference:
For reference, this is the full code I’m currently using:
for (i = 0; i < 5; i++) {
document.querySelector(".middle").insertAdjacentHTML("beforeend", `<div class="flex text-amber-50 gap-[10px] mt-auto mb-[20px] p-[13px] ">
<div class="bg-emerald-700 rounded-full h-[47px] w-[47px] flex flex-none box-border items-center justify-center ">V</div>
<div>
<div class="flex justify-between w-full">
<div class="flex">
<div class="font-semibold">Vatsal</div>
<img src="svg/blue.svg" alt="bluetk" class="h-[20px] mr-[5px] ml-[2px]">
<div class="text-gray-500"> @vatsal1010 . 21h </div>
</div>
<div class="flex">
<div class="flex">
<img src="svg/grok.svg" alt="grok" class="w-[26.25px] h-[30px] mr-[3px]">
<img src="svg/3dot.svg" alt="grok" class="w-[26.25px] h-[30px]">
</div>
</div>
</div>
<div class="mt-[5px]">Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eius,
amet odit alias
accusantium ea, natus architecto quibusdam molestias consequuntur expedita recusandae officiis
perferendis accusamus? Quae animi omnis explicabo iure!</div>
<img src="space.gif" class="w-full mt-[28px]"></img>
</div>
</div>
<div class="bg-gray-600 h-[1px] w-full"></div>`)
document.querySelector(".yo").insertAdjacentHTML("beforeend", `<div class="flex justify-between items-center">
<div>
<div class="text-gray-600">Trending in india</div>
<div class="text-[15px] font-semibold">BIG BREAKING</div>
<div class="text-gray-600">433K posts</div>
</div>
<img src="svg/more.svg" alt="more" class="h-[26px] w-[26px]">
</div>`)
}
document.querySelector(".yo").insertAdjacentHTML("beforeend", `<div class="font-extrabold text-[15px] text-blue-400">Show more</div>`)
const side = document.querySelector(".cont")
const sidebot = side.getBoundingClientRect()
side.style.top = `${window.innerHeight - sidebot.height}px`;
.box {
height: fit-content;
align-self: flex-start;
}
.size {
height: 62px;
width: 243px;
gap: 20px;
font-weight: 600;
align-items: center;
font-size: 20px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body class="bg-black flex justify-center h-full">
<div class="w-[90vw] flex ">
<div class="w-[16vw] h-[100vh] relative">
<div class="flex flex-col w-[16vw] h-[100vh] fixed">
<div class="text-amber-50 ">
<div class="flex size">
<img src="svg/x.svg" alt="x" class="w-[26.25px] h-[30px]">
</div>
<div class="flex size">
<img src="svg/ome.svg" alt="home" class="w-[26.25px] h-[30px]">
<div>Home</div>
</div>
<div class="flex size">
<img src="svg/search.svg" alt="search" class="w-[26.25px] h-[30px]">
<div>Explore</div>
</div>
<div class="flex size">
<img src="svg/bell.svg" alt="bell" class="w-[26.25px] h-[30px]">
<div>Notification</div>
</div>
<div class="flex size">
<img src="svg/message.svg" alt="message" class="w-[26.25px] h-[30px]">
<div>Message</div>
</div>
<div class="flex size">
<img src="svg/grok.svg" alt="grok" class="w-[26.25px] h-[30px]">
<div>Grok</div>
</div>
<div class="flex size">
<img src="svg/profiile.svg" alt="profile" class="w-[26.25px] h-[30px]">
<div>Profile</div>
</div>
<div class="flex size">
<img src="svg/more.svg" alt="more" class="w-[26.25px] h-[30px]">
<div>More</div>
</div>
<div>
<button
class="bg-amber-50 text-black p-[3px] rounded-[30px] w-[219px] h-[52px] font-bold mt-[7px] text-[24px]">Post
</button>
</div>
</div>
<div class="flex text-amber-50 gap-[10px] mt-auto mb-[20px] ">
<div class="bg-emerald-700 rounded-full h-[47px] w-[47px] flex items-center justify-center p-[15px]">
V
</div>
<div>
<div>Vatsal Sharma</div>
<div class="text-gray-500">@vatsal101011456765</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-600 h-full w-[2px]"></div>
<!-- <div class="overflow-auto flex"> -->
<div class="w-[45vw] h-full middle ">
<div class="flex h-[53px] text-amber-50 justify-around">
<div class="flex justify-center items-center ">For you</div>
<div class="flex justify-center items-center">Following</div>
</div>
<div class="bg-gray-600 h-[1px] w-full"></div>
<div class="flex text-amber-50 gap-[10px] pt-[13px] pl-[13px] items-center ">
<div class="bg-emerald-700 rounded-full h-[47px] w-[47px] flex items-center justify-center p-[15px]">V
</div>
<div class="text-gray-500 text-[20px]">What's happening?</div>
</div>
<div class="flex justify-end mr-[15px] mb-[10px]">
<button
class="bg-amber-50 text-black p-[3px] rounded-[30px] w-[108px] h-[40px] font-bold mt-[7px] text-[19px]">Post</button>
</div>
<div class="bg-gray-600 h-[1px] w-full"></div>
<div class="flex justify-center items-center text-blue-400 text-[15px] m-[7px]">Show 1,120 posts</div>
<div class="bg-gray-600 h-[1px] w-full"></div>
</div>
<div class="bg-gray-600 h-h-full w-[2px]"></div>
<div class="cont sticky h-fit">
<div class="text-amber-50 w-[27vw] flex flex-col items-center gap-[20px] ">
<input type="text" class="text-amber-50 bg-black rounded-2xl border-gray-400 border-2 w-[22vw] p-[4px]"
placeholder="Search">
<div class="flex flex-col pt-[12px] pb-[12px] pl-[16px] pr-[16px] gap-[7px] w-[22vw] border-gray-400 border-2">
<div class="font-extrabold text-[20px]">Subscribe to premium</div>
<div>Subscribe to unlock new features and if eligible, receive a share of revenue.</div>
<button class=" bg-blue-400 rounded-[50px] w-[100px] p-[5px]">Subscribe</button>
</div>
<div class="text-amber-50 border-gray-400 border-2 pt-[12px] pb-[12px] pl-[16px] pr-[16px] flex flex-col gap-[20px] yo">
<div class="font-extrabold text-[20px]">What's happening</div>
<div class="flex gap-[15px]">
<div>
<img src="rand.jpg" alt="picture" class="w-[85px] h-[85px] object-none rounded-2xl">
</div>
<div>
<div>FC barcelona vs Inter milano</div>
<div class="text-gray-600">starts at 7:00pm</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
</script>
</body>
</html>
```