use transition on ::-webkit-scrollbar?

25,760

Solution 1

It is fairly easy to achieve using Mari M's background-color: inherit; technique in addition with -webkit-background-clip: text;.

Live demo; https://jsfiddle.net/s10f04du/

@media screen and (-webkit-min-device-pixel-ratio:0) { 
  .container {
    overflow-y: scroll;
    overflow-x: hidden;
    background-color: rgba(0,0,0,0);
    -webkit-background-clip: text;
    transition: background-color .8s;
  }
  .container:hover {
    background-color: rgba(0,0,0,0.18);  
  }
  .container::-webkit-scrollbar-thumb {
    background-color: inherit;
  }
}

Solution 2

Here's another idea based on replies here. You can use color instead of background-color and then box-shadow to colorize the thumb. You can use -webkit-text-fill-color to color back the text:

https://codepen.io/waterplea/pen/dVMopv

*::-webkit-scrollbar-thumb {        
  box-shadow: inset 0 0 0 10px;
}

div {
  overflow: auto;
  -webkit-text-fill-color: black;
  color: rgba(0, 0, 0, 0);
  transition: color .3s ease;
}

div:hover {
  color: rgba(0, 0, 0, 0.3);
}

Solution 3

Short answer: No, it's not possible to use transition on a ::-webkit-scrollbar

Long answer: There are means to achieve a similar effect entirely in CSS.

Explanation:

  1. We need to create an outer container that will scroll. Then we want to create an inner container.
  2. The outer container will have a background-color property. This property will match the color we want to transition on for the scrollbar.
  3. The inner container will match the height of the outer container and will have a background-color that masks the outer container.
  4. The scrollbar's background-color will inherit the outer container's.
  5. The transition property will be applied to the background-color of the outer container.

A major disadvantage here is that you have to do some masking. This can be a bit of a hassle if your background isn't a solid color, since the inner container will most likely need to match. If that's not a worry, this will work for you. Here's the code to put it all together for a page with a horizontally scrolling component.

HTML

<div id="container-outer">
    <div id="container-inner">
        <!-- Content goes here -->
    </div>
</div>

CSS

    /* Scrollbar */
    ::-webkit-scrollbar {
        border: 0;
        height: 10px;
    }
    ::-webkit-scrollbar-track {
        background: rgba(0,0,0,0);
    }
    ::-webkit-scrollbar-thumb {
        background-color: inherit; /* Inherits from outer container */
        border-radius: 20px;
    }

    /* Container */
    #container-outer {
        overflow-y: hidden;
        overflow-x: scroll; /* Horizontal-only scrolling */
        height: 400px;
        background-color: white; /* Initial color of the scrollbar */
        transition: background-color 200ms;
    }
    #container-outer:hover {
        background-color: red; /* Hover state color of the scrollbar */
    }
    #container-inner {
        background-color: white; /* Masks outer container */
        font-size: 0;
        height: inherit; /* Inherits from outer container */
        width: 10000px; /* Set to see the scrolling effect */
    }

Notes:

  • It should be obvious, but this is a Webkit solution. The question was specifically about Webkit and not anything else, so this answer is only in regards to Webkit.
  • Your outer container will probably need a max-width that will match whatever your inner container's width is, otherwise you may see some oddity on extremely large displays. This is an edge case for when the browser width is larger than the horizontally scrolling content width. This is assuming you are using a horizontal scroll, as the example code does.
  • Hovering styles don't work as intended on mobile in most circumstances. This is an issue given how there is a huge market penetration of Webkit mobile browsers. Take that into consideration before using this solution.

Solution 4

Adapted from @brillout's answer, if we transition the border-color instead of background-color, we can avoid using background-clip: text, which leaves some fragments behind if you have any nested text.

Full explanation:

  1. Place your content inside some wrapper div and then add a transition to border-color on your wrapper to change the color on hover.
  2. Add an inset border to your scrollbar, and set the width large enough fill the entire element.
  3. Set border-color: inherit on your scrollbar.

Now when we hover over the wrapper, the border color will transition. The wrapper doesn't have a border, so we don't see anything happen. However, the scrollbar is inheriting that color, so the scrollbar color changes.

Here is the most important code. A full example is available in this fiddle and the snippet below.

#scroller {
  /* fill parent */
  display: block;
  width: 100%;
  height: 100%;

  /* set to some transparent color */
  border-color: rgba(0, 0, 0, 0.0);
  /* here we make the color transition */
  transition: border-color 0.75s linear;
  /* make this element do the scrolling */
  overflow: auto;
}

#scroller:hover {
  /* the color we want the scrollbar on hover */
  border-color: rgba(0, 0, 0, 0.1);
}

#scroller::-webkit-scrollbar,
#scroller::-webkit-scrollbar-thumb,
#scroller::-webkit-scrollbar-corner {
  /* add border to act as background-color */
  border-right-style: inset;
  /* sum viewport dimensions to guarantee border will fill scrollbar */
  border-right-width: calc(100vw + 100vh);
  /* inherit border-color to inherit transitions */
  border-color: inherit;
}
<div id="scroller">...</div>

body {
  background: whitesmoke;
}

#wrapper {
  width: 150px;
  height: 150px;
  margin: 2em auto;
  box-shadow: 0 0 15px 5px #ccc;
}

#scroller {
  /* fill parent */
  display: block;
  width: 100%;
  height: 100%;
}

#content {
  display: block;
  width: 300px;
  height: auto;
  padding: 5px;
}

#scroller {
  /* The background-color of the scrollbar cannot be transitioned.
     To work around this, we set and transition the property
     of the wrapper and just set the scrollbar to inherit its
     value. Now, when the the wrapper transitions that property,
     so does the scrollbar. However, if we set a background-color,
     this color shows up in the wrapper as well as the scrollbar.
     Solution: we transition the border-color and add a border-right
     to the scrollbar that is as large as the viewport. */
  border-color: rgba(0, 0, 0, 0.0);
  transition: border-color 0.75s linear;
  /* make this element do the scrolling */
  overflow: auto;
}

#scroller:hover {
  border-color: rgba(0, 0, 0, 0.1);
  transition: border-color 0.125s linear;
}

#scroller::-webkit-scrollbar,
#scroller::-webkit-scrollbar-thumb,
#scroller::-webkit-scrollbar-corner {
  /* add border to act as background-color */
  border-right-style: inset;
  /* sum viewport dimensions to guarantee border will fill scrollbar */
  border-right-width: calc(100vw + 100vh);
  /* inherit border-color to inherit transitions */
  border-color: inherit;
}

#scroller::-webkit-scrollbar {
  width: 0.5rem;
  height: 0.5rem;
}

#scroller::-webkit-scrollbar-thumb {
  border-color: rgba(0, 0, 0, 0.1);
  /* uncomment this to hide the thumb when not hovered */
  /* border-color: inherit; */
}

#scroller::-webkit-scrollbar-thumb:hover {
  border-color: rgba(0, 0, 0, 0.15);
}

#scroller::-webkit-scrollbar-thumb:active {
  border-color: rgba(0, 0, 0, 0.2);
}
<div id="wrapper">
  <div id="scroller">
    <div id="content">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pretium mi felis, pharetra ornare lorem pellentesque pulvinar. Donec varius condimentum nunc at mollis. Pellentesque posuere et quam eu tristique. Integer interdum enim non interdum mattis. Suspendisse gravida nibh enim, non efficitur lacus suscipit quis. Etiam pharetra libero auctor ultricies ornare. Duis dapibus semper semper. Nam sit amet lobortis arcu.

  Maecenas fermentum risus quis justo convallis, non ornare erat fringilla. Cras eleifend leo sapien, ac iaculis orci ultricies sed. Praesent ultrices accumsan risus, pharetra pharetra lorem dignissim id. Aenean laoreet fringilla eros, vel luctus eros luctus sed. Nullam fermentum massa sit amet arcu dictum, nec bibendum lectus porta. Duis pellentesque dui sed nisi ultricies, vitae feugiat dui accumsan. Nam sollicitudin, ex et viverra ultricies, justo metus porttitor quam, quis vestibulum nibh nisl eget leo.

  Integer luctus arcu et sapien accumsan fringilla. Integer mollis tellus vel imperdiet elementum. Ut consequat ac nibh ac sagittis. Duis neque purus, pellentesque nec erat id, pharetra ornare sapien. Etiam volutpat tincidunt nunc ac facilisis. Aenean sed purus pellentesque, vehicula mauris porta, fringilla nibh. Ut placerat, risus et congue rutrum, lorem arcu aliquet urna, sollicitudin venenatis lorem eros et diam. Aliquam sodales ex risus, ac vulputate ipsum porttitor vel. Pellentesque mattis nibh orci. Morbi turpis nulla, tincidunt vitae tincidunt in, sodales et arcu. Nam tincidunt orci id sapien venenatis porttitor ut eu ipsum. Curabitur turpis sapien, accumsan eget risus et, congue suscipit ligula.

  Maecenas felis quam, ultrices ac ornare nec, blandit at leo. Integer dapibus bibendum lectus. Donec pretium vehicula velit. Etiam eu cursus ligula. Nam rhoncus diam lacus, id tincidunt dui consequat id. Ut eget auctor massa, quis laoreet risus. Nunc blandit sapien non massa bibendum, ac auctor quam pellentesque. Quisque ultricies, est vitae pharetra hendrerit, elit enim interdum odio, eu malesuada nibh nulla a nisi. Ut quam tortor, feugiat sit amet malesuada eu, viverra in neque. Nullam lacinia justo sit amet porta interdum. Etiam enim orci, rutrum sit amet neque non, finibus elementum magna. Sed ac felis quis nunc fermentum suscipit.

  Ut aliquam placerat nulla eget aliquam. Phasellus sed purus mi. Morbi tincidunt est dictum, faucibus orci in, lobortis eros. Etiam sed viverra metus, non vehicula ex. Sed consectetur sodales felis, vel ultrices risus laoreet eget. Morbi ut eleifend lacus, ac accumsan risus. Donec iaculis ex nec ante efficitur vestibulum.
    </div>
  </div>
</div>

Solution 5

I was inspired by @waterplea's answer.

The box-shadow inherits the text color by default. Using this feature is very good to achieve my desired effect.

.a {
  width: 100px;
  height: 200px;
  overflow-y: scroll;
  border: 1px solid #a3a3a3;

  transition: color 800ms;
  /* Initial color of scroll bar */
  color: rgba(8, 114, 252, 0.452);
}
.a span {
  /* Prevent content text from inheriting colors. */
  color: #000;
}

.a:hover {
  color: rgb(8, 114, 252);
}

.a::-webkit-scrollbar {
  appearance: none;
  width: 10px;
}

.a::-webkit-scrollbar-thumb {
  /* The box-shadow inherits the text color by default. */
  box-shadow: inset 0 0 0 20px;
  border-radius: 50px;
}
<div class="a">
  <span>
    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ratione, odio. Lorem ipsum dolor sit, amet consectetur
    adipisicing elit. Ratione, odio. Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ratione, odio. Lorem ipsum
    dolor sit, amet consectetur adipisicing elit. Ratione, odio.
  </span>
</div>
Share:
25,760
Busti
Author by

Busti

Updated on December 11, 2021

Comments

  • Busti
    Busti over 2 years

    is it possible to use transitions on webkit scrollbars? I tried:

    div#main::-webkit-scrollbar-thumb {
        background: rgba(255,204,102,0.25);
        -webkit-transition: background 1s;
        transition: background 1s;
    }
    
    div#main:hover::-webkit-scrollbar-thumb {
        background: rgba(255,204,102,1);
    }
    

    but it isn't working. Or is it possible to create a similar effect (without javascript)?

    Here is a jsfiddle showing the rgba transition problem