CSS-only scrollable navigation bar with dynamic "more" arrow

I recently redesigned the Team Salvato website, moving it away from WordPress (thank goodness!) and into a static site generator, which I feel is appropriate for the content it serves.

(I reused a lot of it for this website, because I'm too lazy to come up with something different!)

When thinking about a mobile layout, I decided that I didn't want the typical "hamburger menu" icon, because I was never a fan of those myself—I'm not sure why, but I always feel a bit bothered for site navigation to be hidden from me until I expand the menu (which inevitably takes up nearly the entire viewport).

So, I decided it would be nice to have the same navigation bar as on desktop, but with the ability to scroll it left and right.

The challenge is that it's not obviously scrollable, unless maybe the user's screen size causes a menu item to be cut off at the edge, which would help them figure it out. Not very graceful.

My solution was to add a "more" arrow that would appear at the right side of the menu, hinting that it's scrollable. But I want the arrow to disappear once the user has scrolled all the way to the right, because that's how UIs typically behave.

Can we do this without any JavaScript? Sort of—it's a little bit janky, and I don't know if it's extremely future-proof. But as of this writing, it works in all major browsers.

The code

First, let's define the basic HTML and CSS we'll be using for the menu bar. (This is slightly simplified for brevity.)


<nav id="navbar">
    <div id="nav-list">
            <li>IP Guidelines</li>


#navbar {
    margin: 0;
    padding: 1.5rem 0;
    position: sticky;
    width: 100%;
    height: 2.5rem;
    top: 0;
    background: #fff;
    z-index: 2;
    max-width: 100vw;
#nav-list {
    height: 100%;
    overflow-x: scroll;
    scrollbar-width: none; /* Firefox */
#navbar ul {
    padding-inline-start: 0;
    padding: 0 2.5rem 0 1rem;
    height: 100%;
    width: max-content;
    display: flex;
    gap: 0.75rem;
    white-space: nowrap;
    margin-block-start: 0;
    margin-block-end: 0;

Looking okay so far!

Next, let's add a "more" arrow to the right side of the bar, to help indicate that it's scrollable. I'll use :after to draw a Unicode arrow and give it absolute positioning on the right side of the element.

Since it's overlapping the navbar text, it also needs a background. I'm giving it a subtle gradient, which I think helps indicate that the content is "hidden" behind the overflow.


#navbar ul:after {
    position: absolute;
    right: 0;
    top: 0;
    height: 95%;
    width: 2rem;
    padding-left: 0.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.75rem;
    content: '\0203A';
    background: linear-gradient(
        to left,
        #fff 1.75rem,

Now, the tricky part—getting the arrow to disappear when we've finished scrolling.

What we can do is mask out the right side of #navbar ul, so that elements behind the mask don't get drawn. The cut-out part of the mask scrolls onscreen as we reach the end of the menu, covering up the "more" arrow and making it invisible.

#navbar ul {
    -webkit-mask-image: linear-gradient(
        to left,
        transparent 1.5rem,
        #000 1.5rem

We'll also add that gradient effect to the left side of the menu bar. I think there are a few ways to do this, but this is the simplest I found, without having to add more HTML elements. It's just another mask gradient, which starts transparent and graduates to a solid color.

#nav-list {
    -webkit-mask-image: linear-gradient(
        to right,
        #000 1rem

The final result:


I can think of a couple downsides to this approach.

The first is that scrollable menu bars are not nearly as common as hamburger menus, so the user may not have an immediate intuition that they can scroll.

Secondly, the "more" arrow could possibly be mistaken for a button. I don't think it's too likely, but it does resemble the button seen at the clickable ends of classic scrollbars, so I'd call it a nonzero chance.

Still, the way this solution fuses jank and elegance feels pretty on-brand for me, so naturally it's a good fit for the website representing my brand. In any case, I've seen much worse!