CSS Skill : Automatic nav highlight on Scroll

As it is close to Christmas and you may find you have a little time on your hands during the holiday why not push your CSS Skills a little further and attempt something out of your comfort zone (or at least try and work out how something is accomplished).

With that in mind the other day I heard the usual comment that “CSS can’t do that you will need to script it”. Indeed I have said that myself a few times either to be proven wrong or prove myself wrong. In this case the question was about highlighting nav items while the page scrolls to that section. It’s quite a common design pattern and usually requires JS to watch the scroll event and update the menu items accordingly.

With my interest piqued I decided to see if CSS could indeed manage to do this and after a couple of hours managed to find a couple of similar ways to do it.

Have a look at my 2 codepen demos and scroll the page up and down to see the effect (I suggest just testing in Chrome as I am not looking for full cross browser compatibility here). If you have time why not see if you can come up with something similar without looking at my code and post your result here or on codepen.

Alternatively if you have no idea how to even attempt this then look through the code and try and work out why it works the way it does,

It’s just a bit of fun and attempts to push the boundaries of CSS mainly in attempt to understand how things work. The end result relies a lot on magic numbers so is not something I would recommend using except for very rigid situations. The main emphasis is on learning and having a bit of fun attempting something that at first doesn’t seem possible.

Over the next few days I will break down the techniques used and explain how it all works.:slight_smile:


The demo above basically uses two techniques to achieve its effect but also relies on some magic numbers as already mentioned.

Without going into explicit code detail the menu is highlighted by making use of position:sticky. Position:sticky allows us to make an element become fixed into the viewport (for the duration of its current context). We can also specify at what specific position the element will become sticky, For example we can say top: 2rem and then the element will scroll until it is 2rem from the top of the viewport and then will become sticky,

Therefore all we need to do is to ensure that the element becomes sticky at the exact same point that the side menu shows the entry for that item (hence the magic numbers to slide the element to the left with a negative margin and then the top position to match the menu position also).

That achieves the first part of the technique but there is a problem. We can see the sticky element as it slides up and down the page when in fact we only want to see a highlight on the nav!

The problem to overcome is that we want a background to the sticky element but we want that background hidden until the sticky element is over the corresponding nav item. That seems like an insurmountable problem at first but with a little bit of lateral thinking a suitable method can be found.

To answer this conundrum we can make use of background-attachment:fixed. A background applied to an element in this way is only visible when the element is physically over that element. A fixed background is always relative to the viewport so we can size our background to be the size of the nav item and also position it xxpx from the top so that it sits under the relevant nav item. When the sticky element scrolls up and becomes fixed it then sits over the fixed background and you can see it only at that point. That means the sticky element only gets a background when it is in the right place for us. We have to adjust the top position for each item but that’s a minor task.

A linear gradient needs to be used because you can’t size just a background-color but indeed a linear gradient is more useful for the effect and could also be a solid colour if required.

Those are the main ingredients for this technique and the rest is just window dressing and tweaking to fit.

The 2nd version uses much the same technique but places everything along the top instead.

I mentioned that Safari is buggy with this and its not a problem with the code but a problem with Safari as Safari has always had problems with redrawing things properly in complicated situations (Safari seems to be the new IE in terms of bugs). Therefore I added a little hack for Safari to work but won’t be as nice as other browsers.

In the end it was all just a bit of fun and hope it helps with your understanding of CSS. If you don’t push the boundaries then you never know where they are.


This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.