flyx
flyx

Reputation: 39728

CSS box-shadow on scrolled content

I want to have a div with an inset box-shadow that has scrolled content in it. Unfortunately, the box-shadow doesn't get casted on the elements within the content, but rather on the background, but I want it to cover the content elements as well.

I stumbled upon this solution: http://jsfiddle.net/HPkd3/ (via). The problem is, I can't get it to work with my scrolled setup; if I position the mask inside the scrolling div, the shadow scrolls away - and if I position it outside of the div, the scrollbar has the shadow cast upon it, which looks weird.

Any ideas how to get this right?

Edit: Some example code: http://jsfiddle.net/ZSpSS/2/

I want the red squares in this example covered with the shadow, while the shadow should be persistent when I scroll through the content.

Upvotes: 17

Views: 35114

Answers (5)

vsync
vsync

Reputation: 130351

CSS-only solution:

animation-timeline allows to infer a scrollable element and style it accordingly:

ul {
 --shadow-size: 20px;
 --shadow-color:  #00000055;

 font: 22px Arial;
 border: 1px solid black;
 height: 100px;
 padding: 1em 2em;
 overflow: auto;
 resize: vertical;
}

.scrollShadow {
 --shadow-size: 30px;

 background-image: linear-gradient(
   var(--shadow-color) 0%,
   transparent calc((var(--scroll)) * var(--shadow-size)),

   /* The below "transparent" color-stop starts at the bottom with an offset 
    * of the size of the shadow which is multiplied by a bumber from 0 to 1, 
    * which decreases the more is scrolled down, meanning the bottom's 
    * shadow size decreases the more is scrolled down. 
    */
   transparent calc(100% - var(--shadow-size) * (1 - var(--scroll))),
   var(--shadow-color) 100%
 );

 background-position-y: var(--shadow-offset);
 background-size: 100% calc(100% - var(--shadow-offset));
 
 animation: scroll-shadows cubic-bezier(.25,.85,.8,.22);;
 animation-timeline: scroll(self);
 animation-range: 0 100%;
}

/* Aniatable custom property */
@property --scroll {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;  
}

@keyframes scroll-shadows {
  /* Sets the shadow-color only if the element is scrollable */
  from, to { --shadow-color: #0003; }
  to {
    --scroll: 1;
  }
}
<ul contentEditable class="scrollShadow">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    <li>11</li>
    <li>12</li>
    <li>13</li>
    <li>14</li>
    <li>15</li>
    <li>16</li>
    <li>17</li>
    <li>18</li>
    <li>19</li>
    <li>20</li>
</ul>

See my Codepen examples using variations of this method:

  1. Demo 1
  2. Demo 2
  3. Demo 3 - Best of all

Relevant Links:

📖 Learn everything about scroll-driven animations


👉 See support table (spoilers - no Firefox)

While this answer might not be helpful to most now, it will certainly become more and more relevant as time passes and support grows.

Upvotes: 3

Richard
Richard

Reputation: 909

Have you tried something like this:

CSS:

#test{
    width:500px;
    height:200px;
    overflow:auto;
    -moz-box-shadow: inset 1px 1px 20px 4px black;
    -webkit-box-shadow: inset 1px 1px 20px 4px black;
    box-shadow: inset 1px 1px 20px 4px black;
}

HTML:

<div id="test"><p>
sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd sadasd 
<br /></p></div>

If you can give more details I can help with a more specific answer

Upvotes: -1

Karthik Raj
Karthik Raj

Reputation: 189

try this

box-shadow:1px 1px 1px 1px #000000 inset; pointer-events: none;

Upvotes: 1

Yobert
Yobert

Reputation: 485

I totally have this working! Check out:

http://jsfiddle.net/yobert/6Ff4u/

Note the red background blocks correctly are "under" the shadows.

Caveats: Requires you to guess the size of the scrollbar in pixels. I bet there is a safe way to measure this with javascript though. If you only have a vertical scrollbar, this ends up being much simpler since you don't need to adjust the margin-bottom.

Upvotes: 8

breezy
breezy

Reputation: 1918

Here is one possible solution.

I commented my CSS styles and you should easily understand my html markup. Here is what I did.

  1. Created two divs
  2. One is acting as the container for the one that has the inset box shadow .outer
  3. The other one contains the inset box shadow .inner-content
  4. I added overflow:scroll to the .inner-content div to apply your scroll bar. ( you can also change overflow:scroll to overflow:auto which will also give you a scroll bar

Upvotes: 0

Related Questions