Preface
The book "CSS Revealed" was written by the author Lea Verou. The book contains many CSS techniques, such as our commonly used gradient colors. The author can use gradient colors to achieve many interesting CSS effects. I really admire the author's CSS skills. Below I put the effects that I think can be used in general here.
effect
1. Translucent border
If you directly set a translucent background for the border, the border box presents the background of the content box. You need to set the background-clip (the default value is border-box) to padding-box, so that the background of the content area is cut from the padding box, and the border box will present its original translucent background.
.box {
width: 15em;
height: 8em;
border: 1em solid hsla(0, 0%, 100%, .5);
background:white;
background-clip: padding-box;
}
2. Multiple borders
With box-shadow, any number of projections can be created to achieve multiple borders. The setting value of each layer of the outer frame is the sum of the fourth parameter of all border-shadows inside the outer frame and the value of the outer frame to be set.
.box {
width: 20em;
height: 10em;
background: hsl(200deg, 56%, 55%, 0.9);
border: 1em solid hsl(200deg, 56%, 55%, 0.7);
box-shadow: 0 0 0 1em hsl(200deg, 56%, 55%, 0.5),
0 0 0 2em hsl(200deg, 56%, 55%, 0.2),
0 0.5em 2em 2.5em #ccc;
}
3. Background positioning
.first {
background: url('./img//logo.png') no-repeat #233A48;
background-position: right 20px bottom 10px;
}
.second {
box-sizing: border-box;
padding: 10px 20px 10px 10px;
background: url('./img//logo.png') no-repeat bottom right #436E67;
background-origin: content-box;
}
.third {
background: url('./img//logo.png') no-repeat #6C9585;
background-position: calc(100% - 20px) calc(100% - 10px);
}
4. Round inner corners of the frame
If the color of the border is only a solid color, you can use one element to achieve; if the color of the border is not a solid color, you can use two elements to achieve.
.box {
width: 20em;
height: 10em;
background: hsl(200deg, 56%, 55%, 0.5);
border-radius: 0.8em;
box-shadow: 0 0 0 0.6em hsl(200deg, 56%, 55%);
/* outline的宽度值为: border-radius(√2 - 1),再向上取整*/
outline: 1em solid hsl(200deg, 56%, 55%);
}
5. Striped background
Introduce the horizontal stripes. The vertical stripes are almost the same as the horizontal stripes. You only need to set the to right/left parameter at the first position of linear-gredient and swap the value of background-size left and right.
Constant width stripe gradient
.box {
width: 15em;
height: 10em;
/* 使用#fb3颜色在50%的位置开始渐变,使用#58a颜色在50%的位置停止渐变 */
background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 2em;
}
Unequal width stripe gradient
.box {
width: 15em;
height: 10em;
/* 如果第二个色标的位置值设置为0,那它的位置就总是会被浏览器调整为前一个色标的位置值 */
background: linear-gradient(#fb3 30%, #58a 0);
background-size: 100% 2em;
}
Diagonal stripes
.box {
width: 15em;
height: 10em;
background: repeating-linear-gradient(45deg,#79b 0, #79b 15px, #58a 0, #58a 30px);
}
6. Continuous image frame
- Vintage envelope border
.box {
width: 15em;
height: 6em;
padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
red 0, red 12.5%,
transparent 0, transparent 25%,
#58a 0, #58a 37.5%,
transparent 0, transparent 50%)
0 / 5em 5em;
}
- Ants marching border
@keyframes ants {
to {
background-position: 100%;
}
}
.box {
width: 15em;
height: 6em;
padding: 1em;
border: 1px solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
black 0, black 25%,
white 0, white 50%)
0 / 0.6em 0.6em;
animation: ants 12s linear infinite;
}
7. Parallelogram
If the element is deformed directly, the content of the element will also change accordingly, so we need two elements, one to store the content and one to apply the deformation style. This is achieved by using pseudo elements, applying all the styles to the pseudo elements, and then deforming the pseudo elements.
.box {
position: relative;
width: 10em;
height: 4em;
}
.box::before {
position: absolute;
content: '';
top: 0; bottom: 0; left: 0; right: 0;
z-index: -1;
background: hsl(200deg, 56%, 55%);
transform: skew(-45deg);
}
8. Diamond picture
@keyframes diamond {
to {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
}
.pic {
width: 10%;
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
transition: 1s clip-path;
}
.pic:hover {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
9. Cut corner effect
- Gradient scheme
.box {
width: 10em;
height: 8em;
/* 当浏览器不支持渐变时的回退方案 */
background: hsl(200deg, 56%, 55%);
background: linear-gradient(135deg, transparent 10px,hsl(200deg, 56%, 55%) 0) top left,
linear-gradient(-135deg, transparent 10px, hsl(200deg, 56%, 55%, 0.5) 0) top right,
linear-gradient(-45deg, transparent 10px,hsl(200deg, 56%, 55%) 0) bottom right,
linear-gradient(45deg, transparent 10px, hsl(200deg, 56%, 55%, 0.5) 0) bottom left;
/* 以上两层渐变是一层一层覆盖的,所以设置background-size两层渐变左右各占一半*/
background-size: 50% 50%;
background-repeat: no-repeat;
}
- SVG + border-image solution
.box {
width: 10em;
height: 8em;
/* 当浏览器不支持渐变时的回退方案 */
background: #4ca2cd;
background: radial-gradient(circle at top left, transparent 10px, #4ca2cd 0) top left,
radial-gradient(circle at top right, transparent 10px, #4ca2cd 0) top right,
radial-gradient(circle at bottom right, transparent 10px, #4ca2cd 0) bottom right,
radial-gradient(circle at bottom left, transparent 10px, #4ca2cd 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
}
10. Pie chart
@keyframes fillup {
to {
stroke-dasharray: 158 158;
}
}
svg {
width: 100px;
height: 100px;
transform: rotate(-90deg);
background: yellowgreen;
border-radius: 50%;
margin: 10px;
}
circle {
/* 填充色 */
fill: yellowgreen;
/* 描边色 */
stroke: #655;
/* 描边宽度 */
stroke-width: 32;
stroke-dasharray: 38 100;
animation: fillup 5s linear infinite;
}
<svg viewBox="0 0 32 32">
<circle r="16" cx="16" cy="16"></circle>
</svg>
A functional function can be encapsulated to create a pie chart of a specified sector size, and then <div class="pie">20%</div>
can be used to create a pie chart
createPie() {
let pieNodeList = document.getElementsByClassName('pie');
pieNodeList = Array.prototype.slice.call(pieNodeList);
pieNodeList.forEach(pie => {
const p = parseFloat(pie.textContent);
const NS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(NS, "svg");
const circle = document.createElementNS(NS, "circle");
const title = document.createElementNS(NS, "title");
circle.setAttribute("r", 16);
circle.setAttribute("cx", 16);
circle.setAttribute("cy", 16);
circle.setAttribute("stroke-dasharray", p + " 100");
svg.setAttribute("viewBox", "0 0 32 32");
title.textContent = pie.textContent;
pie.textContent = pie.textContent;
pie.textContent = '';
svg.appendChild(title);
svg.appendChild(circle);
pie.appendChild(svg);
})
}
11. Projection
It can be achieved by using box-shadow (offset to the right and downward to offset the expansion radius of the blur radius). The expansion radius will expand or shrink (when a negative value is specified) the size of the projection according to the value you specify.
- One-sided projection: set the expansion radius to the opposite value of the blur radius
.unilateral {
/* box-shadow(左右偏移 上下偏移 模糊半径 扩张半径) */
box-shadow: 0 5px 4px -4px rgba(0, 0, 0, 0.5);
}
- Neighbor projection: set the expansion radius to half the opposite value of the blur radius
.adjoin {
/* box-shadow(左右偏移 上下偏移 模糊半径 扩张半径) */
box-shadow: 4px 4px 6px -3px rgba(0, 0, 0, 0.5);
}
- Double-sided projection: Use single-sided projection twice to set up two projections
.bilateral {
box-shadow: 5px 0 4px -4px rgba(0, 0, 0, 0.5),
-5px 0 4px -4px rgba(0, 0, 0, 0.5);
}
12. Irregular projection
Common code of the two examples
.sppech_bubble, .cutout_corners {
position: relative;
width: 8em;
height: 6em;
background: #4ca2cd;
color: #f8f8f8;
text-align: center;
line-height: 6em;
}
.sppech_bubble {
border-radius: 0.5em;
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
}
.sppech_bubble::before {
position: absolute;
content: '';
top: 50%;
right: -2em;
transform: translateY(-50%);
border-width: 1em;
border-style: solid;
border-color: transparent transparent transparent #4ca2cd;
}
.cutout_corners {
background: linear-gradient(135deg, transparent 10px, #4ca2cd 0) top left,
linear-gradient(-135deg, transparent 10px, #4ca2cd 0) top right,
linear-gradient(45deg, transparent 10px, #4ca2cd 0) bottom left,
linear-gradient(-45deg, transparent 10px, #4ca2cd 0) bottom right;
background-size: 50% 50%;
background-repeat: no-repeat;
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
}
13. Frosted glass effect
.box, .content::before {
background: url('./img//strawberry.jpg') 0 / cover fixed;
}
.content {
position: relative;
background: hsla(0, 0%, 100%, 0.3);
border-radius: 8px;
/* 解决模糊效果超出容器的问题 */
overflow: hidden;
}
.content::before {
content: '';
position: absolute;
top: 0; right: 0; left: 0; bottom: 0;
filter: blur(20px);
z-index: -1;
/* filter的模糊效果会存在边缘模糊消退的问题,所以需要让伪元素相对于宿主元素的尺寸再向外扩大至少20px */
margin: -30px;
}
14. Knuckle effect
.box {
position: relative;
width: 15em;
height: 8em;
background: #4ca2cd; /* 回退样式 */
/* background-size的值为 sin30=1.5em/x cos30=1.5em/y x=3em y=1.732em*/
background: linear-gradient(-150deg, transparent 1.5em, #4ca2cd 0);
border-radius: 0.5em;
}
.box::before {
content: '';
position: absolute;
top: 0; right: 0;
background: linear-gradient(to left bottom,
transparent 50%, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.4))
100% 0 no-repeat;
width: 1.73em;
height: 3em;
/* 平移的偏移量 x - y = 3 - √3 = 1.3em */
transform: translateY(-1.3em) rotate(-30deg);
transform-origin: bottom right;
border-bottom-left-radius: inherit;
box-shadow: -0.2em 0.2em 0.3em -0.1em rgba(0, 0, 0, 0.15);
}
15, insert a newline
dt, dd {
display: inline;
margin: 0;
}
dd {
font-weight: bold;
}
dd + dt::before {
content: "\A";
white-space: pre;
}
dd + dd::before {
content: ', ';
font-weight: normal;
margin-left: -.25em;
}
16. Zebra stripes on the text line
pre {
width: 20em;
line-height: 1.5;
padding: 0.5em;
background: hsl(20, 50%, 95%);
background-image: linear-gradient(rgba(120, 0, 0, 0.1) 50%, transparent 0);
/* background-size的位置值是line-height的两倍 */
background-size: auto 3em;
/* 渐变背景会从盒子的顶部开始,代码行和条纹会有错位的问题,设置background-origin让渐变背景以content box的外沿作为基准 */
background-origin: content-box;
font-family: Consolas, Monaco, monospace;
color: #666;
}
code {
font: inherit
}
17, custom underline
a {
background: linear-gradient(#79b, #79b) no-repeat;
background-size: 100% 1px;
background-position: 0 1em;
/* 解决下划线穿过字母的降部 */
text-shadow: 0.05em 0 white, -0.05em 0 white;
}
18. Expand the clickable area
.border_one {
padding: 0.3em 0.5em;
color: #fff;
/* 扩大按钮的可点击区域 */
border: 10px solid transparent;
border-radius: 50%;
background: #4ca2cd;
/* 背景色只填充padding-box内部区域,默认值是border-box */
background-clip: padding-box;
/* 利用box-shadow内嵌投影(inset)模拟边框 */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset;
font: bold 150%/1 sans-serif;
cursor: pointer;
margin: 20px;
}
The above scheme still has the shortcomings, that is, after adding external projection to the element, a weird effect will be obtained, so use pseudo-elements instead.
.border_two {
position: relative;
border: none;
padding: 0.3em 0.5em;
color: #fff;
border-radius: 50%;
background: #4ca2cd;
/* 利用box-shadow内嵌投影(inset)模拟边框 */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset;
font: bold 150%/1 sans-serif;
cursor: pointer;
/* 添加外部投影 */
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset,
0 0.1em 0.2em -0.05em rgba(0, 0, 0, 0.5);
}
.border_two::before {
content: '';
position: absolute;
top: -10px; right: -10px;
bottom: -10px; left: -10px;
}
19. Custom check box
In most browsers, we basically cannot control the styles of checkboxes and radio boxes, but we can use the <label>
tag to simulate the checkboxes, allowing us to customize the checkbox styles.
<span>
<input type="checkbox" id="awesome" disabled />
<label for="awesome">Awesome!</label>
</span>
<span>
<input type="checkbox" id="awesome2" checked />
<label for="awesome2">Awesome!</label>
</span>
/* 隐藏原来的复选框 */
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
}
input[type="checkbox"] + label::before {
content: '\a0';
display: inline-block;
vertical-align: 0.2em;
width: 0.8em;
height: 0.8em;
margin-right: 0.2em;
border-radius: 0.2em;
background: silver;
text-indent: 0.15em;
line-height: 0.65;
}
input[type="checkbox"]:checked + label::before {
content: '\2713';
background: #4ca2cd;
}
input[type="checkbox"]:focus + label::before {
box-shadow: 0 0 0.1em 0.1em #58a;
}
input[type="checkbox"]:disabled + label::before {
background: #dcdfe6;
box-shadow: none;
cursor: not-allowed;
}
span:nth-child(2) {
margin-left: 10px;
}
20. Use blur to weaken the background
<div class="box">
<main class="de_emphasized"></main>
<doalog class="doalog">11.11</doalog>
</div>
.box {
position: relative;
width: 45%;
height: 45%;
color: #fff;
font: 200%/1.6 Baskerville, Palatino, serif;
}
main {
width: 100%;
height: 100%;
background: url('./img/bg.jpg') no-repeat;
}
main.de_emphasized {
/* 滤镜的模糊 + 阴影 */
filter: blur(3px) contrast(0.8) brightness(0.8);
}
.doalog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 150px;
height: 200px;
text-align: center;
line-height: 150px;
border-top: 3px solid #AB2D31;
/* 空心字效果 */
text-shadow: 1px 1px #AB2D31, -1px -1px #AB2D31,
1px -1px #AB2D31, -1px 1px #AB2D31;
background: #fff;
}
21. Interactive picture comparison control
<div class="image-slider">
<img src="./img/cat-before.jpg" alt="Before" />
<img src="./img/cat.jpg" alt="After" />
</div>
.image-slider {
position:relative;
display: inline-block;
}
.image-slider div {
position: absolute;
top: 0; bottom: 0; left: 0;
width: 50%;
overflow: hidden;
}
.image-slider img {
display: block;
user-select: none;
}
.image-slider input {
position: absolute;
left: 0;
bottom: 10px;
width: 100%;
filter: contrast(0.5);
margin: 0;
}
imageSlider() {
let imageSliderList = document.getElementsByClassName('image-slider');
imageSliderList = Array.prototype.slice.call(imageSliderList);
imageSliderList.forEach(slider => {
console.log('slid', slider)
// 创建一个div元素,并用它包裹第一个图片元素
const divNode = document.createElement('div');
const imgNode = slider.querySelector('img');
slider.insertBefore(divNode, imgNode);
divNode.appendChild(imgNode);
// 创建滑块
const range = document.createElement('input');
range.type = 'range';
range.oninput = function() {
console.log('dqw', this.value)
divNode.style.width = this.value + '%';
}
slider.appendChild(range);
})
}
22, adaptive internal elements
figure {
/* 回退样式 */
max-width: 300px;
/* 这个容器内部最大的不可断行元素的宽度(即最宽的单词、图片或具有固定宽度的盒元素) */
max-width: min-content;
/* 水平居中 */
margin: auto;
border: 1px solid silver;
padding: 10px;
}
figure > img {
max-width: inherit;
}
23、Fluid Fixed
header, section, footer {
/* 回退样式 */
padding: 1em;
padding: 1em calc(50% - 350px);
}
24、Sticky Footer
body {
display: flex;
flex-flow: column;
/* 1vh = 视口高度的1% */
min-height: 100vh;
}
main {
/* flex-grow flex-shrink flex-basis的简写语法 */
flex: 1;
}
25, bouncing animation
@keyframes bounce {
60%, 80%, to {
transform: translateY(200px);
animation-timing-function: ease;
}
70% {
transform: translateY(100px);
}
90% {
transform: translateY(160px);
}
}
.ball {
width: 5em;
height: 5em;
background: #4ca2cd;
border-radius: 50%;
animation: bounce 3s cubic-bezier(0.1, 0.25, 0.1, 0.25);
}
26. Flexible transition
<label>
Your username: <input type="text" id="username" />
<span class="callout">Only letters, numbers, underscores (_) and hyphens (-) allowed!</span>
</label>
input {
display: block;
padding: 0 .4em;
font: inherit;
}
.callout {
position: absolute;
max-width: 14em;
padding: .6em .8em;
border-radius: .3em;
margin: .3em 0 0 -.2em;
background: #fed;
border: 1px solid rgba(0,0,0,.3);
box-shadow: .05em .2em .6em rgba(0,0,0,.2);
font-size: 75%;
}
.callout:before {
content: "";
position: absolute;
top: -.4em;
left: 1em;
padding: .35em;
background: inherit;
border: inherit;
border-right: 0;
border-bottom: 0;
transform: rotate(45deg);
}
input:not(:focus) + .callout {
/* 失去焦点时缩放到0 */
transform: scale(0);
transition: 0.25s transform;
}
.callout {
transform-origin: 1.4em -0.4em;
transition: 0.5s cubic-bezier(0.25, 0.1, 0.3, 1.5) transform;
}
27, frame-by-frame animation
Preparation: Need to stitch each frame of pictures horizontally, as shown in the figure below. Finally, the animation is used to move each frame of the picture to the left.
Final rendering:
@keyframes loader {
to {
background-position: -800px 0;
}
}
.loader {
width: 100px;
height: 100px;
background: url('./img/loader.png') 0 0;
animation: loader 1s infinite steps(8);
}
28. Typing animation
@keyframes typing {
from {
width: 0;
}
}
@keyframes caret {
50% {
/* 回退样式 */
border-color: currentColor;
border-color: transparent;
}
}
h1 {
font: bold 200% Consolas, Monaco, monospace;
/* ch: 0字形的宽度。在等宽字体中,“0”字形的宽度与其他所有字形的宽度是一样的 */
white-space: nowrap;
overflow: hidden;
color: gray;
/* 回退样式 */
border-right: 0.05em solid transparent;
border-right: 0.05em solid;
}
typing() {
const typingNode = document.querySelectorAll('h1');
typingNode.forEach(h1 => {
let length = h1.textContent.length, s = h1.style;
s.width = length + 'ch';
s.animation = `typing 6s steps(${length}), caret 1s steps(1) infinite`;
})
}
29. Smooth animation
@keyframes panoramic {
to {
/* 左右移动背景图片 */
background-position: 100% 0;
}
}
.box {
width: 250px;
height: 150px;
background: url('./img/strawberry.jpg');
background-size: auto 100%;
animation: panoramic 10s linear infinite alternate;
/* 动画暂停 */
animation-play-state: paused;
}
.box:hover, box:focus {
/* 动画继续 */
animation-play-state: running;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。