问题
I found this infamous article from 2012. It details how to create scrolling shadows and still works beautifully but I really want to understand the solution and I can't seem to find the necessary information online.
Here is the minified code originally created (blog-post) by @kizmarh and improved by @leaverou:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
/* Shadow covers */
linear-gradient(white 30%, rgba(255, 255, 255, 0)),
linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%,
/* Shadows */
radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)),
radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%;
background-repeat: no-repeat;
background-color: white;
background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
background-attachment: local, local, scroll, scroll;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
If someone could just explain how this effect is achieved? I think I got the general gist (there are white shadows which are covering the black ones if no further scrolling is possible which is achieved with background-attachments) but I am really confused by a number of things like:
- How can the white shadows cover the black ones while the content behind them stays visible?
- How are gradients placed by putting percentages after the declaration (
linear-gradient(...) n% n%
)? - Why isn't the code working when you use the background shorthand?
- What exactly is
farthest-side at 50% 0
doing? - Why doesn't it work without
background-color: white;
?
回答1:
How can the white shadows cover the black ones while the content behind them stays visible?
The content isn't behind them, the content is above which is logical since the content is always above the background. The use of black coloration on the shadow which is the same as text coloratiion make you think that the shadow is above but it's not.
How are gradients placed by putting percentages after the declaration (linear-gradient(...) n% n%)?
0% 100%
means left 0% top 100%
which is the same as left bottom
and since the background is having a width equal to 100%
(set with background-size
) it's also the same as bottom
(related for full detail: Using percentage values with background-position on a linear gradient)
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
/* Shadow covers */
linear-gradient(white 30%, transparent),
linear-gradient(transparent, white 70%) bottom,
/* Shadows */
radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)),
radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) bottom;
background-repeat: no-repeat;
background-color: white;
background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
background-attachment: local, local, scroll, scroll;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Why isn't the code working when you use the background shorthand?
You simply need to correctly write it like below:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
/*Gradient position / size repeat attachment*/
/* Shadow covers */
linear-gradient(white 30%, transparent) top /100% 40px no-repeat local,
linear-gradient(transparent, white 70%) bottom/100% 40px no-repeat local,
/* Shadows */
radial-gradient(farthest-side at 50% 0 , rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) top /100% 14px no-repeat,
radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) bottom/100% 14px no-repeat,
#fff;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Note how I removed scroll
because it's the default value and you need to specify a position for all the gradient because it's mandatory with background-size
in the shorthand (related Issues with "background-position" in "background" shorthand property).
What exactly is farthest-side at 50% 0 doing?
it's creating an ending shape where the center is at 50% 0
(left 50% top 0
or center top
) and it should touch the edge of its background area defined by the background-size
. For 50% 100%
it's center bottom
Here is a basic example to illustrate:
.box {
width:200px;
height:100px;
background:
radial-gradient(farthest-side at center top,red 100%,transparent 100%) top/100% 50px no-repeat;
border:1px solid;
}
<div class="box"></div>
Our background size is 100% 50px
and the red curvature is touching the edge since the color stop is 100%
creating our half ellipse.
Another trivial example where we keep the center of the shape at the center:
.box {
width:200px;
height:100px;
background:
radial-gradient(farthest-side,red 100%,transparent 100%) top/100% 50px no-repeat;
border:1px solid;
}
<div class="box"></div>
Using our code with different values to better see:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
/* Shadow covers */
linear-gradient(white 30%, transparent) top /100% 40px no-repeat local,
linear-gradient(transparent, white 70%) bottom/100% 40px no-repeat local,
/* Shadows */
radial-gradient(farthest-side at top , red 100%, rgba(0, 0, 0, 0)) top /100% 14px no-repeat,
radial-gradient(farthest-side at bottom , red 100%, rgba(0, 0, 0, 0)) bottom/100% 14px no-repeat,
#fff;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Note how I simplified center top
(50% 0
) to only top
and the same for center bottom
Some related question to get more details around radial-gradient
:
How to animate a radial-gradient using CSS?
How to control height of ellipse in radial gradient
Why doesn't it work without background-color: white;?
It works fine without:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
/* Shadow covers */
linear-gradient(white 30%, transparent) top /100% 40px no-repeat local,
linear-gradient(transparent, white 70%) bottom/100% 40px no-repeat local,
/* Shadows */
radial-gradient(farthest-side at top , rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) top/100% 14px no-repeat,
radial-gradient(farthest-side at bottom , rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) bottom/100% 14px no-repeat;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Here is the code using different coloration and values to better understand each gradient and what is happening. You can also clearly notice that the text is above and the white background isn't needed.
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
font-weight:bold;
font-size:25px;
background:
/* Shadow covers */
linear-gradient(red 30%, white) top /100% 40px no-repeat local,
linear-gradient(white, red 70%) bottom/100% 40px no-repeat local,
/* Shadows */
radial-gradient(farthest-side at top , yellow 100%, green 100%) top/100% 30px no-repeat,
radial-gradient(farthest-side at bottom , yellow 100%, green 100%) bottom/100% 30px no-repeat;
}
body {
background:pink;
}
ul {
margin:0;
padding:0;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
And here is an optimized version of your initial code:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
background:
linear-gradient(white 30%, transparent),
radial-gradient(farthest-side at top, rgba(0, 0, 0, .2), transparent),
linear-gradient(transparent, white 70%) bottom,
radial-gradient(farthest-side at bottom, rgba(0, 0, 0, .2), transparent) bottom;
background-repeat: no-repeat;
background-size: 100% 40px,100% 14px;
background-attachment: local, scroll;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Another version:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
--rad:radial-gradient(farthest-side, rgba(0, 0, 0, .2), transparent);
background:
linear-gradient(white 30%, transparent),
var(--rad) 0 -14px,
linear-gradient(transparent, white 70%) bottom,
rvar(--rad) 0 calc(100% + 14px);
background-size: 100% 40px,100% 28px;
background-attachment: local, scroll;
background-repeat: no-repeat;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
Still another one with less gradient:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
--rad:radial-gradient(50% 50%, rgba(0, 0, 0, .2), transparent) no-repeat;
background:
linear-gradient(white 12px, transparent 40px calc(100% - 40px),white calc(100% - 12px)) local,
var(--rad) left 0 top -14px / 100% 28px,
var(--rad) left 0 bottom -14px / 100% 28px;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
A final one (yes the last one ..) with less of code:
.scrollbox {
overflow: auto;
width: 200px;
max-height: 150px;
--rad:radial-gradient(50% 14px, rgba(0, 0, 0, .2), transparent);
background:
linear-gradient(white 12px, transparent 40px calc(100% - 40px),white calc(100% - 12px)) local,
var(--rad) top /100% 200%,
var(--rad) bottom/100% 200%;
}
<div class="scrollbox">
<ul>
<li>Ah! Scroll below!</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>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>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>The end!</li>
<li>No shadow there.</li>
</ul>
</div>
来源:https://stackoverflow.com/questions/59429383/how-does-this-scrolling-shadows-css-magic-work