CSS 揭秘

coderljw 2024-10-13 大约 15 分钟

# 1. 尽量减少代码重复

  • 代码易维护 vs 代码量少
/* 代码易维护 */
border-width: 10px;
border-left-width: 0;

/* 代码量少 */
border-width: 10px 10px 10px 0;
1
2
3
4
5
6
  • currentColor: currentColor 是很多 CSS 颜色属性的初始值,比如 border-color 和 outline-color,以及 text-shadow 和 box-shadow 的颜色值,等等。

  • 继承: inherit 可以用在任何 CSS 属性中,而且它总是绑定到父元素的计算值(对伪元素来说,则会取生成该伪元素的宿主元素)。

.callout {
  position: relative;
}
.callout::before {
  content: '';
  position: absolute;
  top: -0.4em;
  left: 1em;
  padding: 0.35em;
  background: inherit;
  border: inherit;
  border-right: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2. 合理使用简写

  • 不要害怕使用简写属性。合理使用简写是一种良好的防卫性编码方式,可以抵御未来的风险。当然,如果我们要明确地去覆盖某个具体的展开式属性并保留其他相关样式,那就需要用展开式属性,就像我们在 “尽量减少代码重复” 一节中为了得到按钮的其他颜色版本所做的那样。

  • 如果只为某个属性提供一个值,那它就会扩散并应用到列表中的每一项。

background: url(tr.png) no-repeat top right / 2em 2em,
            url(br.png) no-repeat bottom right / 2em 2em,
            url(bl.png) no-repeat bottom left / 2em 2em;

/* 展开式属性与简写属性的配合 */
background: url(tr.png) top right,
            url(br.png) bottom right,
            url(bl.png) bottom left;
background-size: 2em 2em;
background-repeat: no-repeat;
1
2
3
4
5
6
7
8
9
10
  • 怪异的简写语法:
    • 在 background 简写属性中指定 background-size 时,需要同时提供一个 background-position 值(哪怕它的值就是其初始值也需要写出来),而且还要使用一个斜杠( / )作为分隔。
    • 这通常都是为了消除歧义。在这个例子中, top right 显然是 background-position ,而 2em 2em 是 background-size,不管它们的顺序如何。但是,请设想一下 50% 50% 这样的值,它到底是 background-size 还是 background-position 呢?当你在使用展开式属性时,CSS 解析器明白你的意图。而当你使用简写属性时,解析器需要在没有属性名提示的情况下弄清楚 50% 50% 到底指什么,这就是需要引入斜杠的原因。

# 3. 半透明边框(demo (opens new window)

  • 默认情况下,背景会延伸到边框所在的区域下层。
/* 背景延伸,边框为白色 */
border: 10px solid hsla(0, 0%, 100%, 0.5);
background: white;

/* 裁切后,边框半透明 */
border: 10px solid hsla(0, 0%, 100%, 0.5);
background: white;
background-clip: padding-box;
1
2
3
4
5
6
7
8

# 4. 多重边框

background: yellowgreen;
box-shadow: 0 0 0 10px #655,
            0 0 0 15px deeppink,
            0 2px 5px 15px rgba(0,0,0,.6);
1
2
3
4
  • outline 方案:两层边框。

    • 边框不一定会贴合 border-radius 属性产生的圆角,因此如果元素是圆角的,它的描边可能还是直角的。请注意,这种行为被 CSS 工作组认为是一个 bug,因此未来可能会改为贴合 border-radius 圆角。
background: yellowgreen;
border: 10px solid #655;
outline: 5px solid deeppink;
1
2
3

# 5. 灵活的背景定位

background: url(code-pirate.svg) no-repeat #58a;
background-position: right 20px bottom 10px;

/* 回退方案 */
background: url(code-pirate.svg) no-repeat bottom right #58a;
background-position: right 20px bottom 10px;
1
2
3
4
5
6
/* 每次改动内边距的值时,我们都需要在三个地方更新这个值 */
padding: 10px;
background: url(code-pirate.svg) no-repeat #58a;
background-position: right 10px bottom 10px;

/* 使用 background-origin后,背景图片距离边角的偏移量就跟内边距保持一致了 */
padding: 10px;
background: url('code-pirate.svg') no-repeat #58a bottom right; /* 或 100% 100% */
background-origin: content-box;
1
2
3
4
5
6
7
8
9
background: url('code-pirate.svg') no-repeat;
background-position: calc(100% - 20px) calc(100% - 10px);
1
2

# 6. 边框内圆角

  • 一个元素实现(demo (opens new window))。

    • 请注意,我们为 box-shadow 属性指定的扩张值并不一定等于描边的宽度,我们只需要指定一个足够填补 “空隙” 的扩张值就可以了。
    • 扩张值计算:border-radius * (√2 - 1)。
    • 请注意,该计算过程揭示了这个方法的另一个限制:为了让这个效果得以达成,扩张半径需要比描边的宽度值小,但它同时又要比 (√2 - 1)r 大(这里的 r 表示 border-radius )。这意味着,如果描边的宽度比 (√2 - 1)r 小,那我们是不可能用这个方法达成该效果的。
background: tan;
border-radius: 0.8em;
padding: 1em;
box-shadow: 0 0 0 0.6em #655;
outline: 0.6em solid #655;
1
2
3
4
5

# 7. 条纹背景

  • 水平条纹(demo (opens new window))。

    • CSS3:如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值。
    • 这意味着,如果我们把第二个色标的位置值设置为 0,那它的位置就总是会被浏览器调整为前一个色标的位置值。
background: linear-gradient(#fb3 33.3%, #58a 0, #58a 66.6%, yellowgreen 0);
background-size: 100% 45px;
1
2
background: linear-gradient(to right, /* 或 90deg */ #fb3 50%, #58a 0);
background-size: 30px 100%;
1
2
background: linear-gradient(
  45deg,
  #fb3 25%,
  #58a 0,
  #58a 50%,
  #fb3 0,
  #fb3 75%,
  #58a 0
);
background-size: 42px 42px;
1
2
3
4
5
6
7
8
9
10
background: repeating-linear-gradient(
  60deg,
  #fb3,
  #fb3 15px,
  #58a 0,
  #58a 30px
);
1
2
3
4
5
6
7
background: #58a;
background-image: repeating-linear-gradient(
  30deg,
  hsla(0, 0%, 100%, 0.1),
  hsla(0, 0%, 100%, 0.1) 15px,
  transparent 0,
  transparent 30px
);
1
2
3
4
5
6
7
8

# 8. 复杂的背景图案

background: #58a;
background-image: linear-gradient(white 2px, transparent 0), linear-gradient(
    90deg,
    white 2px,
    transparent 0
  ), linear-gradient(hsla(0, 0%, 100%, 0.3) 1px, transparent 0), linear-gradient(90deg, hsla(
        0,
        0%,
        100%,
        0.3
      ) 1px, transparent 0);
background-size: 75px 75px, 75px 75px, 15px 15px, 15px 15px;
1
2
3
4
5
6
7
8
9
10
11
12
background: #655;
background-image: radial-gradient(tan 30%, transparent 0), radial-gradient(tan
      30%, transparent 0);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;

/* SCSS */
@mixin polka($size, $dot, $base, $accent) {
  background: $base;
  background-image: radial-gradient($accent $dot, transparent 0),
    radial-gradient($accent $dot, transparent 0);
  background-size: $size $size;
  background-position: 0 0, $size/2 $size/2;
}
/* 使用 */
@include polka(30px, 30%, #655, tan);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
background: #eee;
background-image: linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.25) 25%,
    transparent 0,
    transparent 75%,
    rgba(0, 0, 0, 0.25) 0
  ), linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 0, transparent
      75%, rgba(0, 0, 0, 0.25) 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
1
2
3
4
5
6
7
8
9
10
11
background: repeating-conic-gradient(#bbb 0, #bbb 25%, #eee 0, #eee 50%);
background-size: 30px 30px;
1
2

# 9. 伪随机背景(demo (opens new window)

  • 为了进一步增加随机性,我们甚至可以用质数来指定各组条纹的宽度。
background: hsl(20, 40%, 90%);
background-image: linear-gradient(90deg, #fb3 11px, transparent 0),
  linear-gradient(90deg, #ab4 23px, transparent 0), linear-gradient(90deg, #655
      41px, transparent 0);
background-size: 41px 100%, 61px 100%, 83px 100%;
1
2
3
4
5

# 10. 连续的图像边框

padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white) padding-box, url(stone-art.jpg)
    border-box 0 / cover;
1
2
3
4
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
@keyframes ants {
  to {
    background-position: 100%;
  }
}
.marching-ants {
  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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
border-top: 0.2em solid transparent;
border-image: 100% 0 0 linear-gradient(90deg, currentColor 4em, transparent 0);
padding-top: 1em;
1
2
3

# 11. 自适应的椭圆

border-radius: 50% / 100% 100% 0 0;
1
border-radius: 100% 0 0 0;
1

# 12. 平行四边形

  • 基本的 CSS 变形:文字也会倾斜。
transform: skewX(-45deg);
1
  • 嵌套元素方案:对内容再应用一次反向的 skew() 变形,从而抵消容器的形效果(demo (opens new window))。
<a href="#yolo" class="button">
  <div>Click me</div>
</a>
1
2
3
.button {
  transform: skewX(-45deg);
}
.button > div {
  transform: skewX(45deg);
}
1
2
3
4
5
6
  • 伪元素方案:把所有样式(背景、边框等)应用到伪元素上,然后再对 伪元素进行变形(demo (opens new window))。
.button {
  position: relative;`
  /* 其他的文字颜色、内边距等样式…… */
}
.button::before {
  content: ''; /* 用伪元素来生成一个矩形 */
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: #58a;
  transform: skew(45deg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 13. 菱形图片

<div class="picture">
  <img src="adam-catlace.jpg" alt="..." />
</div>
1
2
3
.picture {
  width: 400px;
  transform: rotate(45deg);
  overflow: hidden;
}
.picture > img {
  max-width: 100%;
  /* transform: rotate(-45deg);  */ /* 等边八角形 */
  transform: rotate(-45deg) scale(1.42); /* 1.42 为等边三角形对角线比例 √2 向上取整值 */
}
1
2
3
4
5
6
7
8
9
10
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
1
/* 鼠标悬停时平滑地扩展为完整的面积 */
img {
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  transition: 1s clip-path;
}
img:hover {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
1
2
3
4
5
6
7
8

# 14. 切角效果

  • 线性渐变切一个角。
background: #58a; /* 回退样式 */
background: linear-gradient(-45deg, transparent 15px, #58a 0);
1
2
background: #58a; /* 回退样式 */
background: linear-gradient(135deg, transparent 15px, #58a 0) top left, linear-gradient(
      -135deg,
      transparent 15px,
      #58a 0
    ) top right, linear-gradient(-45deg, transparent 15px, #58a 0) bottom right,
  linear-gradient(45deg, transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;

/* SCSS */
@mixin beveled-corners($bg, $tl: 0, $tr: $tl, $br: $tl, $bl: $tr) {
  background: $bg; /* 回退样式 */
  background: linear-gradient(135deg, transparent $tl, $bg 0) top left, linear-gradient(
        225deg,
        transparent $tr,
        $bg 0
      ) top right, linear-gradient(-45deg, transparent $br, $bg 0) bottom right,
    linear-gradient(45deg, transparent $bl, $bg 0) bottom left;
  background-size: 50% 50%;
  background-repeat: no-repeat;
}
/* 使用:传入 2~5 个参数 */
@include beveled-corners(#58a, 15px, 5px);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
background: #58a; /* 回退样式 */
background: radial-gradient(circle at top left, transparent 15px, #58a 0) top left,
  radial-gradient(circle at top right, transparent 15px, #58a 0) top right,
  radial-gradient(circle at bottom right, transparent 15px, #58a 0) bottom right,
  radial-gradient(circle at bottom left, transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
1
2
3
4
5
6
7
border: 20px solid #58a;
border-image: 1
  url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a"><polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/></svg>');
background: #58a; /* 回退样式 */
background-clip: padding-box;
1
2
3
4
5
background: #58a;
clip-path: polygon(
  20px 0,
  calc(100% - 20px) 0,
  100% 20px,
  100% calc(100% - 20px),
  calc(100% - 20px) 100%,
  20px 100%,
  0 calc(100% - 20px),
  0 20px
);
1
2
3
4
5
6
7
8
9
10
11
border-radius: 15px;
corner-shape: bevel;
1
2

# 15. 梯形标签页

  • 对元素使用了 3D 变形之后,其内部的变形效应是 “不可逆转“。在 2D 变形的体系之下,内部的逆向变形可以抵消外部的变形效应。

  • 缺点:斜边的角度依赖于元素的宽度(demo (opens new window))。

nav > a {
  position: relative;
  display: inline-block;
  padding: 0.3em 1em 0;
}
nav > a::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: #ccc;
  background-image: linear-gradient(
    hsla(0, 0%, 100%, 0.6),
    hsla(0, 0%, 100%, 0)
  );
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-bottom: none;
  border-radius: 0.5em 0.5em 0 0;
  box-shadow: 0 0.15em white inset;
  transform: perspective(0.5em) rotateX(5deg);
  transform-origin: bottom;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 16. 简单的饼图

  • 还是用 canvas 吧! ┭┮﹏┭┮。

# 17. 单侧投影

  • 使用 4px 的模糊半径意味着投影的尺寸会比元素本身的尺寸大约 8px。

  • 一个 -5px 的扩张半径会把投影的宽度和高度各减少 10px (即每边各 5px )。

  • 单侧投影(demo (opens new window))。

box-shadow: 0 5px 4px -4px black;
1
box-shadow: 3px 3px 6px -3px black;
1
box-shadow: 5px 0 5px -5px black, -5px 0 5px -5px black;
1

# 18. 不规则投影

filter: drop-shadow(2px 2px 10px rgba(0, 0, 0, 0.5));
1

# 19. 染色效果

/* 降饱和度的橙黄色染色效果 */
filter: sepia(1) saturate(4) hue-rotate(295deg);
1
2
<a href="#something">
  <img src="tiger.jpg" alt="Rawrrr!" />
</a>
1
2
3
/* 保留上层元素的 HSL 亮度信息,并从它的下层吸取色相和饱和度信息 */
a {
  background: hsl(335, 100%, 50%);
}
img {
  mix-blend-mode: luminosity;
}
1
2
3
4
5
6
7

# 20. 毛玻璃效果(demo (opens new window)

  • backdrop-filter 是让当前元素所在区域后面的内容模糊灰度或高亮之类,要想看到效果,需要元素本身半透明或者完全透明。而 filter 是让当前元素自身模糊灰度或高亮之类(鑫神 (opens new window))。
/* 2021无效 */
body,
main::before {
  background: url('tiger.jpg') 0 / cover fixed;
}
main {
  position: relative;
  background: hsla(0, 0%, 100%, 0.3);
  overflow: hidden;
}
main::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  filter: blur(20px);
  margin: -30px;
}

/* 自身元素使用 backdrop-filter */
backdrop-filter: blur(20px);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 21. 折角效果

background: #58a; /* 回退样式 */
background: linear-gradient(
      to left bottom,
      transparent 50%,
      rgba(0, 0, 0, 0.4) 0
    ) no-repeat 100% 0 / 2em 2em, linear-gradient(
    -135deg,
    transparent 1.5em,
    #58a 0
  );
1
2
3
4
5
6
7
8
9
10
.note {
  position: relative;
  background: #58a; /* 回退样式 */
  background: linear-gradient(-150deg, transparent 1.5em, #58a 0);
  border-radius: 0.5em;
}
.note::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;
  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);
}

/* SCSS */
@mixin folded-corner($background, $size, $angle: 30deg) {
  position: relative;
  background: $background; /* 回退样式 */
  background: linear-gradient(
    $angle - 180deg,
    transparent $size,
    $background 0
  );
  border-radius: 0.5em;
  $x: $size / sin($angle);
  $y: $size / cos($angle);
  &::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: $y;
    height: $x;
    transform: translateY($y - $x) rotate(2 * $angle - 90deg);
    transform-origin: bottom right;
    border-bottom-left-radius: inherit;
    box-shadow: -0.2em 0.2em 0.3em -0.1em rgba(0, 0, 0, 0.2);
  }
}
/* 使用 */
.note {
  @include folded-corner(#58a, 2em, 40deg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 22. 连字符断行

/* text-align: justify;  */ /* 搭配两端对齐 */
hyphens: auto;
1
2

# 23. 插入换行(demo (opens new window)

<dl>
  <dt>Name:</dt>
  <dd>Lea Verou</dd>
  <dt>Email:</dt>
  <dd>lea@verou.me</dd>
  <dd>leaverou@mit.edu</dd>
  <dt>Location:</dt>
  <dd>Earth</dd>
</dl>
1
2
3
4
5
6
7
8
9
dt,
dd {
  display: inline;
}
dd {
  margin: 0;
  font-weight: bold;
}
dd + dt::before {
  content: '\A';
  white-space: pre;
}
dd + dd::before {
  content: ', ';
  margin-left: -0.25em;
  font-weight: normal;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 24. 文本行的斑马条纹(demo (opens new window)

padding: 0.5em;
line-height: 1.5;
background: beige;
background-size: auto 3em;
background-origin: content-box;
background-image: linear-gradient(rgba(0, 0, 0, 0.2) 50%, transparent 0);
1
2
3
4
5
6

# 25. 调整 tab 的宽度

pre {
  tab-size: 2;
}
1
2
3

# 26. 连字(demo (opens new window)

font-variant-ligatures: common-ligatures
                        discretionary-ligatures
                        historical-ligatures;
1
2
3

# 27. 华丽的 & 符号(demo (opens new window)

@font-face {
  font-family: Ampersand;
  src: local('Baskerville-Italic'),
       local('GoudyOldStyleT-Italic'),
       local('Palatino-Italic'),
       local('BookAntiqua-Italic');
  unicode-range: U+26;
}
h1 {
  font-family: Ampersand, Helvetica, sans-serif;
}
1
2
3
4
5
6
7
8
9
10
11

# 28. 现实中的文字效果

background: hsl(210, 13%, 40%);
color: hsl(210, 13%, 75%);
text-shadow: 0 -1px 1px black;
1
2
3
background: deeppink;
color: white;
text-shadow: 0 0 1px black,
             0 0 1px black,
             0 0 1px black,
             0 0 1px black,
             0 0 1px black,
             0 0 1px black;
1
2
3
4
5
6
7
8
/* text-shadow */
a {
  background: #203;
  color: white;
  transition: 1s;
}
a:hover {
  color: transparent;
  text-shadow: 0 0 0.1em white, 0 0 0.3em white;
}

/* filter */
a {
  background: #203;
  color: white;
  transition: 1s;
}
a:hover {
  filter: blur(0.1em);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
background: #58a;
color: white;
text-shadow: 0 1px hsl(0,0%,85%),
             0 2px hsl(0,0%,80%),
             0 3px hsl(0,0%,75%),
             0 4px hsl(0,0%,70%),
             0 5px hsl(0,0%,65%),
             0 5px 10px black;
1
2
3
4
5
6
7
8

# 29. 环形文字(demo (opens new window)

<div class="circular">
  <svg viewBox="0 0 100 100">
    <path d="M 0,50 a 50,50 0 1,1 0,1 z" id="circle" />
    <text>
      <textPath xlink:href="#circle">
        circular reasoning works because
      </textPath>
    </text>
  </svg>
</div>
1
2
3
4
5
6
7
8
9
10
.circular path {
  fill: none;
}
.circular {
  width: 15em;
  height: 15em;
  margin: 3em auto 0;
}
.circular svg {
  display: block;
  overflow: visible;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 30. 扩大可点击区域

/* 方案一 */
border: 10px solid transparent;
background-clip: padding-box;

/* 方案二 */
border: 10px solid transparent;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3) inset;
background-clip: padding-box;

/* 方案三 */
button {
  position: relative;
  /* [其余样式] */
}
button::before {
  content: '';
  position: absolute;
  top: -10px;
  right: -10px;
  bottom: -10px;
  left: -10px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 31. 紧贴底部的页脚(demo (opens new window)

body {
  display: flex;
  flex-flow: column;
  min-height: 100vh;
}
main {
  flex: 1;
}
1
2
3
4
5
6
7
8

# 32. 缓动效果

@keyframes bounce {
  60%,
  80%,
  to {
    transform: translateY(400px);
    animation-timing-function: ease;
  }
  70% {
    transform: translateY(300px);
  }
  90% {
    transform: translateY(360px);
  }
}

.ball {
  width: 0;
  height: 0;
  padding: 1.5em;
  border-radius: 50%;
  margin: auto;
  background: red radial-gradient(at 30% 30%, #fdd, red);
  animation: bounce 2s cubic-bezier(0.1, 0.25, 1, 0.25) forwards;
}

body {
  background: linear-gradient(skyblue, white 450px, yellowgreen 0);
  min-height: 100vh;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<label>
  Your username: <input id="username" />
  <span class="callout"
    >Only letters, numbers, underscores (_) and hyphens (-) allowed!</span
  >
</label>
1
2
3
4
5
6
input:not(:focus) + .callout:not(:hover) {
  transform: scale(0);
  transition: 0.25s transform;
}

.callout {
  transition: 0.5s cubic-bezier(0.25, 0.1, 0.3, 1.5) transform;
  transform-origin: 1.4em -0.4em;
}

/* Styling */
body {
  padding: 1.5em;
  font: 200%/1.6 Baskerville;
}

input {
  display: block;
  padding: 0 0.4em;
  font: inherit;
}

.callout {
  position: absolute;
  max-width: 14em;
  padding: 0.6em 0.8em;
  border-radius: 0.3em;
  margin: 0.3em 0 0 -0.2em;
  background: #fed;
  border: 1px solid rgba(0, 0, 0, 0.3);
  box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2);
  font-size: 75%;
}

.callout:before {
  content: '';
  position: absolute;
  top: -0.4em;
  left: 1em;
  padding: 0.35em;
  background: inherit;
  border: inherit;
  border-right: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 33. 闪烁效果(demo (opens new window)

@keyframes blink {
  50% {
    color: transparent;
  }
}
.box {
  /* animation: 1s blink 3; */
  animation: 1s blink 3 steps(1); /* 或用step-end */
}
1
2
3
4
5
6
7
8
9

# 34. 打字动画(demo (opens new window)

<h1>请给我一个面子,做我儿子吧!</h1>
1
@keyframes typing {
  from {
    width: 0;
  }
}
@keyframes caret {
  50% {
    border-color: transparent;
  }
}
h1 {
  width: 14em; /* 文本的宽度 */
  overflow: hidden;
  white-space: nowrap;
  border-right: 0.05em solid;
  animation: typing 3s steps(14), caret 1s steps(1) infinite;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
以父之名
周杰伦.mp3