# CSS 教程

介绍 CSS 样式与布局基础，覆盖常用选择器、盒模型与排版要点。适合网页开发入门，帮你把页面样子调顺。

# CSS高级功能

## 自定义属性

CSS 提供的属性（比如`font-weight`、`line-height`）都是标准给定的，但是 CSS 也允许用户自定义属性，这又称为“CSS 变量”。

自定义属性跟普通属性一样，也是定义在某个选择器里面，而且只对该选择器有效。因此自定义属性所在区块，相当于变量的作用域。

`:root`选择器之中，可以设置全局的自定义属性。

```css
:root {
  --base: #ffc600;
  --spacing: 10px;
  --blur: 10px;
}
```

变量也可以在行内定义。

```html
<html style = "--color: red;">
```

所有自定义属性都必须以两个连词线开头，并且大小写敏感。

使用时，通过`var()`函数取出变量。

```css
img {
  padding: var(--spacing);
  background: var(--base);
  -webkit-filter: blur(var(--blur));
  filter: blur(var(--blur));
}
```

`var()`函数接受第二个参数，指定默认值。如果某个自定义属性没有设置，默认值就会生效。

```css
width: var(--custom-width, 20%);
```

下面是另一个例子。

```css
foo {
  padding: var(--gutter, 10px 0 0 5px);
}
```

如果默认值包含逗号，那么`var()`会将第一个逗号后面的所有值，当作默认值。

```css
.someElement {
  font-family: var(--main-font, "lucida grande" , tahoma, Arial);
}
```

上面代码中，`--main-font`的默认值是`"lucida grande" , tahoma, Arial`。

`var()`内部还可以使用`var()`。

```css
.someElement {
  background-color: var(--first-color, var(--second-color, white));
}
```

上面代码中，如果没有设置`--first-color`，默认值`var(--second-color, white)`就会生效。如果`--second-color`也没有设置，那么`white`就会生效。

自定义属性可以是全局的，也可以是局部的。在`:root`选择器里面定义的，就是全局变量，可以在任何其他选择器里面读取。而在其他选择器里面定义，就是局部变量，只能在该选择器里面读取。

下面代码中，局部变量可以覆盖全局变量。

```css
:root { --text-color: green; }
div { --text-color: blue; }
.error { --text-color: red; }

* {
  color: var(--text-color);
}
```

上面代码中，一般文字是绿色，`div`元素的文字是蓝色，`error`类的文字是红色。

定义变量的时候，也可以读取其他变量。

```css
:root {
  --brand-color: red;
  --header-text-color: var(--brand-color);
}
```

变量也可以与`calc`函数结合使用。

```css
:root {
  --container-width: 1000px;
  max-width: calc(var(--container-width) / 2);
}
```

变量不包含单位时，不能直接添加单位。

```css
/* 无效 */
.quote {
  --t-padding: 20;
  padding-top: var(--t-padding)px;
}

/* 有效 */
.quote {
  --t-padding: 20;
  padding-top: calc(var(--t-padding) * 1px);
}
```

JavaScript 可以操作这些变量。

```javascript
element.style.setProperty('--my-color', 'rebeccapurple');
element.style.getPropertyValue('--my-color');
// "rebeccapurple"
element.style.removeProperty('--my-color');
```

下面是例子。

```javascript
// 取得变量值
var styles = window.getComputedStyle(document.documentElement);
var bgValue = String(styles.getPropertyValue('--background')).trim();

// 设置变量值
document.documentElement.style.setProperty('--background', 'black');
// 另一种写法
document.documentElement.style.setProperty('--h-color', 'var(--p-color)');
```

下面是一个监听事件的例子。

```javascript
const docStyle = document.documentElement.style;

document.addEventListener('mousemove', (e) => {
  docStyle.setProperty('--mouse-x', e.clientX);
  docStyle.setProperty('--mouse-y', e.clientY);
});
```

下面是一个使用CSS变量的例子，只要`<input>`的值发生变化，样式就会随之发生变化。

```javascript
// get the inputs
const inputs = [].slice.call(document.querySelectorAll('.controls input'));

// listen for changes
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));

function handleUpdate(e) {
  // append 'px' to the end of spacing and blur variables
  const suffix = (this.id === 'base' ? '' : 'px');
  document.documentElement.style.setProperty(`--${this.id}`, this.value + suffix);
}
```

## 参考链接

- [CSS Variables Guide](https://nearsoft.com/blog/css-variables-guide/), Tony Martinez

## supports

`supports`命令用来判断浏览器是否支持某项CSS功能。

```javascript
@supports not (display: grid) {
  // 不支持网格布局
  // 老式浏览器代码
}
@supports (display: grid) {
  // 支持网格布局
  // 新式浏览器代码
}
```

另一个例子。

```javascript
@supports (object-fit: cover) {
  img {
    object-fit: cover;
  }
  div {
    width: auto;
    background: green;
   // some other complex code for the fancy new layout
  }
}
```

## initial-letter

`initial-letter`决定首字母的下沉样式。

```css
/* 默认值 */
initial-letter: normal;

/* 占据1.5行高度 */
initial-letter: 1.5;

/* 占据3行高度 */
initial-letter: 3.0;

/* 占据3行高度，下沉2行高度 */
initial-letter: 3.0 2;
```

## 滤镜

网页的灰度显示。

```css
html {
  -webkit-filter: grayscale(100%);
  filter: grayscale(100%);
}
```

## CSS Shape

`shape-outside`属性使得行内（inline）的内容，围绕`outside`指定的曲线排列。

`shape-margin`属性指定`shape-outside`与内容之间的`margin`。

```css
.circle {
  width: 250px;
  height: 250px;
  background-color: #40a977;
  border-radius: 50%;
  float: left;
  -webkit-shape-outside: circle();
  shape-outside: circle();
}
```

`circle`函数可以使用`circle(r at x y)`这样的形式，定义半径和圆心的坐标。注意，这里的坐标是相对于原始形状，而不是相对于父容器。

其他形状的函数。

- ellipse()
- polygon()

参考链接

- [How to Use CSS Shapes in Your Web Design](https://webdesign.tutsplus.com/tutorials/how-to-use-css-shapes-in-your-web-design--cms-27498), by Louie R.

# CSS 动画

## timer

- linear：线性运行，各个时刻速度都一样。
- ease: 快速加速，然后逐渐减速，CSS 的默认值。
- ease-in：逐渐加速，结尾时变快，适用于退出页面显示的元素。
- ease-out：开始时速度最快，然后逐渐慢下来，适用于进入页面显示的元素。
- ease-in-out：主键加速然后变慢，与`ease`相似，但开始时加速较慢，适合那些在页面有着明确开始和结束的动画。

```css
#div1 {transition-timing-function：linear;} 
#div2 {transition-timing-function：ease;} 
#div3 {transition-timing-function：ease-in;} 
#div4 {transition-timing-function：ease-out ;} 
#div5 {transition-timing-function：ease-in-out;}
```

上面的代码等价于下面的代码。

```css
#div1 {transition-timing-function: cubic-bezier(0,0,1,1);}
#div2 {transition-timing-function: cubic-bezier(0.25,0.1,0.25,1);}
#div3 {transition-timing-function: cubic-bezier(0.42,0,1,1);}
#div4 {transition-timing-function: cubic-bezier(0,0,0.58,1);}
#div5 {transition-timing-function: cubic-bezier(0.42,0,0.58,1);}
```

## transition

`transition`属性定义元素状态变化的过渡。

```css
transition: [*property] [*transition-duration] [transition-timing-function] [transition-delay];
```

`transition`属性一共有四个参数。

- `*property`：元素的哪一个 CSS 属性需要过渡效果，该参数必需。
- `*transition-duration`：过渡效果的持续时间，该参数必需。
- `transition-timing-function`：定时函数，默认是`ease`。
- `transition-delay`：过渡从多少秒以后开始，默认是`0`。

```css
/*1*/ transition: background-color 2s;
/*2*/ transition: background-color 2s 0.5s;
/*3*/ transition: background-color 2s ease-in;
/*4*/ transition: background-color 2s ease-out 0.5s;
/*5*/ transition: background-color 2s, height 1s ease-in-out;
/*6*/ transition: all 1s ease-out;
```

## animation

`animation`属性用来指定元素的动画效果。

```css
animation: [*animation-name] [*animation-duration] [animation-timing-function] [animation-delay] [animation-iteration-count] [animation-direction] [animation-fill-mode] [animation-play-state];
```

- `*animation-name`：关键帧的名字，该参数必需。
- `*animation-duration`：动画持续的时间，该参数必需。
- `animation-timing-function`：定时器函数，默认是`ease`。
- `animation-delay`：动画效果多少秒后开始，默认为`0`。
- `animation-iteration-count`：动画重复的次数，可以指定为一个整数，表示多少次，默认值是`infinite`关键字，表示无限次。
- `animation-direction`：动画方向，可能的值为`forward`、`backward`或`alternating`，默认值为`normal`。
- `animation-fill-mode`：默认值为`none`。
- `animation-play-state`：动画默认是否生效，默认值为`running`。

```css
@keyframes bounce {
  0% {
    transform: scale(0.1);
    opacity: 0;
  }
  60% {
    transform: scale(1.2);
    opacity: 1;
  }
  100% {
    transform: scale(1);
  }
}
.ball {
  animation: bounce 2s infinite;
}
```

### keyframes

首先，需要使用`keyframes`定义一个动画。

```css
keyframes rotation {
  from {
    transform: rotate(90deg);
  }
  to {
    transform: rotate(450deg);
  }
}
```

上面代码中，`keyframes`关键字后面是动画名称（上例是`rotation`），大括号内部是关键帧，`from`表示起始帧，`to`表示结束帧，其他帧由浏览器自动计算出来。

另一种定义方法是使用百分比，定义关键帧，`from`就是`0%`，`to`就是`100%`。

```css
@keyframes color {
  0% {
    fill: #99002f
  }
  50% {
    fill: #ffc426
  }
  100% {
    fill: #99002f
  }
}
```

使用的时候，通过`animation`属性将动画赋给指定元素即可。（[Demo](http://demo.hongkiat.com/css3-animation-steps/)）

```css
.second {
  animation: rotation 60s steps(60) infinite;
  transform-origin: 100% 50%;
}
```

`animation`属性是一个简写形式，它包括以下一些子属性。

- animation-name
- animation-duration
- animation-timing-function
- animation-delay
- animation-iteration-count
- animation-direction
- animation-fill-mode

```css
svg path {
  animation: color 2s infinite cubic-bezier(.85,.01,.25,1);
}

/* 等同于 */
svg path {
  animation-name: color;
  animation-duration: 2s;
  animation-iteration-count: infinite;
  animation-timing-function: cubic-bezier(.85,.01,.25,1);
}
```

## 隐入（fade in）

```css
@keyframes fade-in {
  0% { opacity: 0; }
  100% { opacity: 1; }
}
.fade-in {
  animation: fade-in 1s forwards linear;
}
```

## 上浮（fade up）

```css
@keyframes fade-up {
  0% {
    opacity: 0; transform: translateY(3em);
  }
  100% {
    opacity: 1; transform: translateY(none);
  }
}
.fade-up {
  animation: fade-up 3s cubic-bezier(.05,.98,.17,.97) forwards;
}
```

## 左右摆动（wiggle）

```css
@keyframes wiggle {
0%, 7% { transform: rotateZ(0); opacity: 0; }
15% { transform: rotateZ(-15deg); opacity: 1; }
20% { transform: rotateZ(10deg); }
25% { transform: rotateZ(-10deg); }
30% { transform: rotateZ(6deg); }
35% { transform: rotateZ(-4deg); }
40%, 100% { transform: rotateZ(0); }
}
```

## 上下弹跳（bounceAround）

```css
.animation {
	…
	animation: bounceAround 1.1s ease-in-out infinite;
}

@keyframes bounceAround {
	0% {transform:translateY(0);}
	20% {transform:translateY(-60px) rotate(0deg);}
	25%{transform:translateY(20px) rotate(0deg);}
	35%, 55%{transform:translateY(0px) rotate(0deg);}
	60% {transform: translateY(-20px) rotate(0deg);}
	100%{transform: translateY(-20px) rotate(360deg);}
}
```

# 背景

如果不定义背景图片或者背景色，一个网页元素的背景就是透明的。

## opaque

`opaque`的值在0到100之间。默认值是100，表示百分之百不透明，而0表示百分之百透明。

## background-attachment

`background-attachment`属性设置背景图案的位置，是否在视口里面是固定的，也就是说，是否随着滚动条一起滚动。由于存在两种滚动条——视口的滚动条和区块的滚动条——因此`background-attachment`的行为有三种模式，对应三个属性值。

- `scroll`：这个是默认值，表示背景图片绑定区块的内容，会随着视口滚动条滚动，但不会随着区块滚动条滚动。
- `fixed`：背景图片绑定视口，不会随着视口滚动条和区块滚动条滚动。
- `local`：背景图片会随着视口滚动条和区块滚动条滚动。

## background-repeat

`background-repeat`指定当背景图片小于容器时的平铺方式。

```css
html {
  background-image: url(logo.png);
  background-repeat: repeat-x;
}
```

它可以取以下值。

- repeat：背景图片沿容器的X轴和Y轴平铺，将会平铺满整个容器，可能会造成背景图片显示不全。
- repeat-x： 背景图片沿容器的X轴平铺。
- repeat-y：背景图片沿容器的Y轴平铺。
- no-repeat：背景图片不做任何平铺。
- round：背景图片沿容器的X轴和Y轴平铺，将会铺满整个容器。如果有多余空间，会升缩背景图片适应容器大小，不会造成图片显示不全。
- space：背景图片沿容器的X轴和Y轴平铺，将会铺满整个容器。如果有多余空间，不会改变背景图片的大小，而是平均分配相邻图片之间的空间，不会造成图片显示不全。

`background-repeat`可以设置两个值，分别表示 X 轴和 Y 轴的重复方式。

```css
.element {
/* background-repeat: horizontal vertical */
   background-repeat: repeat space;
   background-repeat: repeat repeat;
   background-repeat: round space;
}
```

`background-repeat`只设置一个值的时候，其实是下面方式的简写。

- `repeat`：相当于`repeat repeat`
- `repeat-x`：相当于`repeat-x no-repeat`
- `repeat-y`：相当于`no-repeat repeat`
- `no-repeat`：相当于`no-repeat no-repeat`
- `space`：相当于`space space`
- `round`：相当于`round round`

## 参考链接

- [单聊background-repeat](http://www.w3cplus.com/css3/css3-background-repeat-space-round.html)，by 大漠

# 盒状模型

## width

`width: auto`表示父元素提供的所有宽度（100%）。

## max-width

`max-width`设为`none`，可以让元素占满父元素的整个宽度。

## overflow

`overflow`属性指定如果元素的大小超出容器时的行为。

- `visible`：默认值，表示溢出的部分可见
- `hidden`：表示溢出的部分不可见
- `scroll`：表示发生溢出时，会显示滚动条，用户可以拖动滚动条，看到溢出的部分

## clip

`clip`规则指定元素超出容器大小时，具体显示哪个部分。它只对绝对定位（absolute）和固定定位（fixed）的元素有效。

```css
clip: rect(0px, 100px, 100px, 0px);
```

`react`函数四个参数的含义如下。

- 第一个参数：剪切后的顶边距离容器的顶边的距离
- 第二个参数：剪切后的右边界距离容器的左边界的距离
- 第三个参数：剪切后的底边距离容器的顶边的距离
- 第四个参数：剪切后的左边界距离容器的左边界的距离

```css
clip: rect(auto 100px auto auto);
```

该规则已经被废除，但是浏览器还是普遍支持。

## clip-path

`clip-path`是`clip`规则的继承者，用来剪切元素的大小。它对所有定位方式都适用。

```css
clip: rect(110px, 160px, 170px, 60px);
/* 等同于 */
clip-path: inset(10px 20px 30px 40px);
```

`clip-path`支持多种路径定义方式。

```css
.clip-me {

  /* referencing path from an inline SVG <clipPath> */
  clip-path: url(#c1);

  /* referencing path from external SVG */
  clip-path: url(path.svg#c1);

  /* polygon */
  clip-path: polygon(5% 5%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%);

  /* circle */
  clip-path: circle(30px at 35px 35px);

  /* ellipse */
  clip-path: ellipse(65px 30px at 125px 40px);

  /* inset-rectangle() may replace inset() ? */
  /* rectangle() coming in SVG 2 */

  /* rounded corners... not sure if a thing anymore */
  clip-path: inset(10% 10% 10% 10% round 20%, 20%);

}
```

## box-sizing

`box-sizing`指定盒状模型的宽度范围。

- `border-box`：宽度包括padding和border
- `content-box`：默认值，只包括内容区的宽度，不包括padding和border

```css
*, *:after, *:before {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
```

## object-fit

`object-fit`命令指定一个元素的大小应该如何适应它的容器，主要用于图像。它可以取以下的值。

- fill：容器的宽度和高度就是元素的宽度和高度，会截去溢出的部分。
- contain：元素会填满容器，不会改变长宽比，可能会有溢出。
- cover：元素会填满容器，可能会改变长宽比，但不会有溢出。
- none：元素不会改变的大小，具体的展示效果由默认算法决定
- scale-down：展示效果会选择`none`或`contain`之中，对象尺寸较小的一个
- inherit：使用父元素的值
- initial：使用浏览器的默认值
- unset：这个属性是`inherit`和`initial`的结合，如果该元素继承父元素，就采用`inherit`，否则采用`initial`

# 颜色

## 颜色关键字

CSS 允许使用关键字表示颜色，CSS2.1 设置了16个基本的颜色，CSS 3 又增加了131个。

## 六位颜色表示法

六位颜色表示法又称为 RGB 表示，分别使用两位十六进制数表示红色（R）、绿色（G）、蓝色（B），组合在一起就是六位，然后加上`#`前缀，比如`#59007F`。

每种颜色使用0～255表示浓度，0表示没有，255表示浓度最深。

如果一个十六进制颜色是由三对重复的数字组成的，如`#ff3344`，可以缩写为`#f34`。

## 八位颜色表示法

Chrome 浏览器支持八位的颜色表示法`RRGGBBAA`，前六位是红、绿、蓝颜色值，后两位表示透明度（alpha transparency）。比如，`#ffffff80`相当于`rgba(255, 255, 255, .5)`。

alpha设置越接近0，颜色就越透明。如果设为0，就是完全透明的，就像没有设置任何颜色。类似地，1表示完全不透明。

## HSL 和 HSLA 表示法

HSL 代表色相（hue）、饱和度（saturation）和亮度（lightness），其中色相的取值范围为0～360，饱和度和亮度的取值均为百分数，范围为0～100%。

- 红色为hsl(0,100%,50%);
- 黄色为hsl(60,100%,50%);
- 绿色为hsl(120,100%,50%);
- 青色为hsl(180,100%,50%);
- 蓝色为hsl(240,100%,50%);
- 紫红色为hsl(300,100%,50%);

用HSLA指定alpha透明度的方法与RGBA是一致的。

## currentColor

`currentColor`是一个属性值，代表当前元素的`color`属性的值。

```css
.box {
  color: red;
  border: 1px solid currentColor;
  box-shadow: 0 0 2px 2px currentColor;
}
```

上面代码，`border`和`box-shadow`的颜色都与`color`保持一致。这种写法的好处是，如果要修改`color`，只要修改一处就可以了，不用修改三个地方。

`currentColor`的另一个用处，是让衍生类都继承基类的颜色。

```css
.box {
    color: red;
}
.box .child-1 {
    background: currentColor;
}
.box .child-2 {
    color: currentColor;
    border 1px solid currentColor;
}
```

伪元素也可以使用`currentColor`。

```css
.box {
    color: red;
}
.box:before {
    color: currentColor;
    border: 1px solid currentColor;
}
```

## 参考链接

- [使用 currentColor 属性写出更好的 CSS 代码](http://www.qcyoung.com/2016/09/28/%E3%80%90%E8%AF%91%E3%80%91%E4%BD%BF%E7%94%A8%20currentColor%20%E5%B1%9E%E6%80%A7%E5%86%99%E5%87%BA%E6%9B%B4%E5%A5%BD%E7%9A%84%20CSS%20%E4%BB%A3%E7%A0%81/)

# 常用组件

## 对话框

显示屏幕居中的对话框。

```html
<div id="dialog">
  <p>I am the dialog's content</p>
  <button type="button">close dialog</button>
</div>
```

```css
#dialog {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 400px;
  height: 200px;
  transform: translate3d(-50%,-50%,0);
  background: white;
  border: 100% solid rgba(0, 0, 0, 2.5);
}

#dialog:before {
  content: "";
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.25);
  z-index: 999;

  /* fade in */
  transition: opacity 0.2s ease-in-out;
}

#dialog[hidden] {
  /*
    [hidden] usually sets display:none, which we
    need to revert in order to allow animations
  */
  display: block;
  /*
    actually hide the element,
    making its contents unaccessible
  */
  visibility: hidden;
  /*
    make sure the element is out of viewport
  */
  transform: translate3d(0px, -1px, 0px) scale(0);
  /*
    delay transform until animations are done
  */
  transition:
    visibility 0s linear 0.2s,
    transform 0s linear 0.2s;
}
```

## 参考链接

- [Accessible dialog tutorial](https://allyjs.io/tutorials/accessible-dialog.html)

# display 属性

`display: none;`会从文档流移除该元素及其子元素，仿佛它们是不存在的。它们占据的空间会释放出来。

`display: inline;`产生行内元素，没有自己的高度和宽度，由容器决定，前后不会产生换行。

`display: block;`产生块级元素，会占据一行，占满容器的宽度。

`display: list-item;`将元素渲染为一个列表项，行首产生一个列表标记，可以用`list-style`定制样式。

`display: inline-block;`产生行内的块级元素，有自己的高和宽，但是前后不会产生换行。

## 表格相关的设置

- `table` 对应`<table>`元素
- `table-header-group` 对应`<thead>`
- `table-row`	对应`<tr>`
- `table-cell` 对应`<td>`
- `table-row-group` 对应`<tbody>`
- `table-footer-group` 对应`<tfoot>`
- `table-column-group` 对应`<colgroup>`
- `table-column` 对应`<col>`
- `table-caption` 对应`<caption>`
- `inline-table` 将一个表格渲染具有`inline-block`的形式

## 表格显示

```css
div {
  display: table;
  display: table-cell;
  display: table-column;
  display: table-colgroup;
  display: table-header-group;
  display: table-row-group;
  display: table-footer-group;
  display: table-row;
  display: table-caption;
}
```

## flexbox

`display: flexbox;`是一维布局，定义沿着一根直线的布局。这根直线可以折行。

## grid

`display: grid;`是二维布局。

## direction

`direction`命令指定元素的排列方向，默认是从左到右`ltr`，可以设置为从右到左`rtl`。

```css
direction: rtl;
```

## 参考链接

- [How well do you know CSS display?](https://www.chenhuijing.com/blog/how-well-do-you-know-display/), by Chen Hui Jing

# 常用功能

## 置中

`transform`方法。

容器设置相对定位。

```css
.container {
  position: relative;
  width: 200px;
  height: 200px;
  overflow: hidden;
}
```

元素的左上角先定位在容器中央，然后向西北方向移动自身的`50%`。

```css
.container div {
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%,-50%);
      -ms-transform: translate(-50%,-50%);
          transform: translate(-50%,-50%);
}
```

## 隐藏元素

彻底隐藏一个元素。

```css
[hidden] {
  display: none;
}
```

在视觉上隐藏一个元素。

```css
.visuallyhidden {
  position: absolute;

  width: 1px;
  height: 1px;
  margin: -1px;
  border: 0;
  padding: 0;

  clip-path: inset(100%);
  clip: rect(0 0 0 0);
  overflow: hidden;
}
```

## 参考链接

- [Hiding DOM elements](https://allyjs.io/tutorials/hiding-elements.html)

# 字体

## 字体文件类型

内嵌OpenType（Embedded OpenType，.eot）。在使用@font-face时，Internet Explorer 8及之前的版本仅支持内嵌OpenType。内嵌OpenType是Microsoft的一项专有格式，它使用数字版权管理技术防止在未经许可的情况下使用字体。

TrueType（.ttf）和OpenType（.otf），台式机使用的标准字体文件类型。TrueType和OpenType得到了Mozilla Firefox（3.5及之后的版本）、Opera（10及之后的版本）、Safari（3.1及之后的版本）、Mobile Safari（iOS 4.2及之后的版本）、Google Chrome（4.0及之后的版本）及Internet Explorer（9及之后的版本）的广泛支持。这些格式不使用数字版权管理。

Web开放字体格式（Web Open Font Format，.woff）。这种较新的标准是专为Web字体设计的。Web开放字体格式的字体是经压缩的TrueType字体或OpenType字体。WOFF格式还允许在文件上附加额外的元数据。字体设计人员或厂商可以利用这些元数据，在原字体信息的基础上，添加额外的许可证或其他信息。这些元数据不会以任何方式影响字体的表现，但经用户请求，这些元数据可以呈现出来。Mozilla Firefox（3.6及之后的版本）、Opera（11.1及之后的版本）、Safari（5.1及之后的版本）、Google Chrome（6.0及之后的版本）及Internet Explorer（9及之后的版本）均支持Web开放字体格式。

可缩放矢量图形（Scalable Vector Graphics，.svg）。简言之，应避免对Web字体文件使用SVG。它更多地用于早期Web字体，因为它是iOS 4.1上移动Safari唯一支持的格式（还有可能引起一些崩溃的情况）。从iOS 4.2（于2011年初即被广泛使用）起，移动Safari开始支持TrueType。

## font-size

`font-size`属性用于设置网页的字体大小。

下面是设置网页根字体大小的例子。

```css
:root {
  font-size: 16px;
}

/* 也可以针对 html 元素设置
html {
  font-size: 16px;
}
```

然后，每个一级区块的`font-size`采用`rem`单位，内部属性使用`em`单位。这时，`em`的大小是基于`rem`设置的。

```css
button {
    font-size: 0.875rem;
    // All the internal/external value use in 'em'
    // This value use of your "font-size" as the basic font size
    // And you will not have any problem with the font size of the container ( Example bottom )
    padding: .5em 1em;
    border: .125em solid #e3e3e3;
    @media (min-width: 48rem){ // min-width: 768px
      font-size: 1.125rem;
    }
    @media (min-width: 62rem){ // min-width: 992px
      font-size: 1.375rem;
    }
}
```

## font-smoothing

`font-smooth`属性主要用于控制浏览器对字体的渲染。比如，下面的设置可以减少字体渲染出现锯齿。

```css
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
```

## font-display

有时，样式表指定的字体需要下载（称为 web font），`font-display`属性用于控制浏览器在下载字体时的渲染行为。

```css
@font-face {
  font-family: 'my-font';
  font-display: optional;
  src: url(my-font.woff2) format('woff2');
}
```

这里需要理解，浏览器对于下载字体的处理过程。

首先，浏览器发现样式表指定的字体，是一种需要下载的字体，会去检查缓存里面是否存在这种字体，如果不存在就开始从网上下载。这时，网页上使用这种字体的文字，会显示不出来，而展现一片空白。这段时间称为”阻塞期“（block），每种浏览器的”阻塞期“时间长度不尽相同，Chrome、Firefox 和 Safari 都是3秒，即文字会有3秒钟显示不出来，而 IE 是0秒，即没有阻塞期。

然后，阻塞期结束，如果字体已经下载完成，浏览器就会采用下载的字体进行渲染，文字就会显示出来。如果下载还没有结束，浏览器就会采用替代字体进行渲染，文字也会显示出来，但是与最终状态不一样，这段时间称为”替换期“（swap）。此时，浏览器其实还在下载字体。

最后，等到字体下载成功，这时浏览器会用下载的字体，替换现有的字体，这时网页会一闪。如果字体下载失败了，这时浏览器就会继续使用现有的字体，因此网页不会发生变化。

`font-display`可以取以下值。

（1）`font-display: block`

这是浏览器的默认行为，即打开阻塞期。

（2）`font-display: swap`

这个值会关闭阻塞期，直接进入替换期，即浏览器不会出现文字显示不出来的情况。

（3）`font-display: fallback`

这个值设置阻塞期的长度是100毫秒，即文字有100毫秒显示不出来，然后立即进入替换期。等到字体下载结束，再使用下载的字体渲染。

（4）`font-display: optional`

这个值设置阻塞期也是100毫秒。然后，等到100毫秒结束，浏览器发现字体已经下载完成，就使用下载的字体渲染，否则就不再下载，永久性使用替代字体渲染。它主要用于网速较慢的环境，不让用户长时间等到字体下载。

一般来说，正常情况下都推荐使用`font-display:optional`。如果是图标字体等没有替代字体的情况下，可以使用`font-display: block`。

## 参考链接

- [Font-display](https://font-display.glitch.me/), by Monica Dinculescu

# 函数

CSS 提供一些函数，方便操作。

## minmax

`minmax`提供一个长度范围，不小于较小值，不大于较大值。

```css
minmax( min, max )
```

如果第二个参数`max`小于第一个参数`min`，那么`max`会被忽略，等同于将长度设置为`min`。

```css
minmax(400px, 50%)
minmax(30%, 300px)
```

网格布局中，`max`也允许设置为比例因子`fr`，但`min`不能设置为`fr`。

```css
minmax(200px, 1fr)
```

网格布局中，还可以使用关键字`max-content`和`min-content`，分别表示最大的和最小的可分配宽度。

```css
minmax(100px, max-content)
minmax(min-content, 400px)
```

关键字`auto`在`max`位置等同于`max-content`，在`min`位置等同于`min-content`。

```css
minmax(max-content, auto)
minmax(auto, 300px)
minmax(min-content, auto)
```

## image-set()

`image-set()`用来选取符合响应式条件的图片。

```css
background-image: image-set( "foo.png" 1x, "foo-2x.png" 2x);
```

# 图像处理

## filter

`filter`命令支持以下函数。

- url()：使用svg滤镜
- blur()：高斯模糊
- brightness()：亮度
- contrast()：对比度
- drop-shadow()：阴影
- grayscale()：灰度
- hue-rotate()：色调旋转
- invert()：色调分离
- opacity()：透明
- saturate()：饱和度
- sepia()：加入褐色色调

```css
filter: saturate(200%);
filter: sepia(100%);
filter: opacity(50%);
filter: url(resources.svg#c1);
filter: blur(5px);
filter: invert(100%);
filter: hue-rotate(90deg);
filter: brightness(0.5);
filter: contrast(200%);
filter: grayscale(50%);
filter: grayscale(0.5) blur(10px);
filter: drop-shadow(16px 16px 10px black);
```

# CSS 概述

## 简介

CSS 用于定义网页的样式，比如每个网页元素的位置、大小、颜色等等。

CSS 随着网页的诞生而诞生。1996年12月，CSS 1.0 标准问世，目前广泛使用的是2000年4月发布的 CSS 3 标准。

CSS 3 采用模块化结构，每个模块都是独立定义的，定义完成以后，再加入 CSS 标准。截止2017年，CSS 共有88个模块，下面是其中一次主要模块。

> - Display
> - Box alignment
> - Flexible box
> - CSS Grid
> - Inline Layout
> - Position Layout
> - CSS Shapes
> - CSS Transforms

CSS 样式表就是一个文本文件，定义每个网页元素的样式规则。

## 样式规则

CSS 样式表由大量样式规则组成。每条样式规则分成两个部分：选择器（selector）和声明块（declaration block）。

选择器指明本条规则对哪些网页元素生效，声明块描述样式规则的具体内容。

```css
h1 {
  color: red;
}
```

上面代码是一条样式规则，其中`h1`是选择器，大括号的部分就是声明块。这个规则的意思是，`h1`元素的颜色为红色。

声明块的内容，放在一对大括号里面。大括号之中是一个或多个键值对，每个键值对用分号结尾，给出一条样式描述。键值对的顺序并不重要。

```css
h1 {
  background-color: yellow;
  color: red;
}
```

上面代码中，声明块里面包含两条样式描述，它们的顺序不重要，完全可以对调位置。

声明块可以写成多行，也可以写成一行。缩进和换行只是为了增加可读性，CSS 引擎会忽略它们。

```css
h1 { color: red; }
```

上面代码中，声明块写成了一行。它与写成多行的效果是一样的。

CSS 允许重复声明某个样式，这时最后声明的键值对会覆盖前面的键值对。

```css
h1 {
  color: red;
  color: black;
}
```

上面代码中，颜色属性申明了两次。后面声明的黑色，会覆盖前面声明的红色。

声明块里面的键值对，由键名和键值组成。键名称为 CSS 属性（property），由 CSS 标准给出，键值是该属性允许被赋予的一个值。

学习 CSS，主要就是学习各种各样的 CSS 属性。

## CSS 注释

CSS 使用`/* ... */`表示注释，可以是单行，也可以是多行。

```css
h1 { /* 这是单行注释 */
  color: red;
}

/* 这是多行注释
h1 {
  color: red;
}
*/
```

上面代码中，多行注释包含整个`h1`的样式规则，这意味着这个样式规则不会生效。

注意：注释不能嵌套。

## CSS 的继承

某些 CSS 属性不仅影响网页元素本身，还会影响该元素的后代元素，这称为 CSS 的继承。

最典型的例子就是字体属性`font-family`，网页根元素`html`设置了字体以后，该网页的所有元素都会默认使用指定的字体。

以下是常见的可以继承的 CSS 属性。

> 文本
> - color（颜色，a元素除外）
> - direction（方向）
> - font（字体）
> - font-family（字体系列）
> - font-size（字体大小）
> - font-style（用于设置斜体）
> - font-variant（用于设置小型大写字母）
> - font-weight（用于设置粗体）
> - letter-spacing（字母间距）
> - line-height（行高）
> - text-align（用于设置对齐方式）
> - text-indent（用于设置首行缩进）
> - text-transform（用于修改大小写）
> - visibility（可见性）
> - white-space（用于指定如何处理空格）
> - word-spacing（字间距）
>
> 列表
> - list-style（列表样式）
> - list-style-image（用于为列表指定定制的标记）
> - list-style-position（用于确定列表标记的位置）
> - list-style-type（用于设置列表的标记）
>
> 表格
> - border-collapse（用于控制表格相邻单元格的边框是否合并为单一边框）
> - border-spacing（用于指定表格边框之间的空隙大小）
> - caption-side（用于设置表格标题的位置）
> - empty-cells（用于设置是否显示表格中的空单元格）
>
> 页面设置（对于印刷物）
> - orphans（用于设置当元素内部发生分页时在页面底部需要保留的最少行数）
> - page-break-inside（用于设置元素内部的分页方式）
> - widows（用于设置当元素内部发生分页时在页面顶部需要保留的最少行数）
>
> 其他
> - cursor（鼠标指针）
> - quotes（用于指定引号样式）

## 样式的优先级

如果多条规则都对同一个网页元素指定样式，只要 CSS 属性不发生冲突，那么这些规则都是有效的。

如果 CSS 属性发生冲突，那就存在一个优先级的问题：哪一条规则优先生效？

CSS 采用以下规则，决定样式规则的优先级。

- 特殊性（specificity）
- 顺序（order）
- 重要性（importance）

特殊性指的是选择器的具体程度，选择器越特殊，规则就越强。遇到冲突时，优先应用特殊性强的规则。特殊性最低的是元素名本身，然后是元素的`class`属性，特殊性最高的是`id`属性。建议在样式表中多使用`class`选择器，避免使用`id`选择器，这样的灵活性最好。

规则指的是，同样特殊性的规则，晚出现的优先级高。

重要性指的是，特别注明某条规则的重要程度要比其他所有规则高，方法是在声明的末尾加上`!important`。由于可维护性很差，一旦出现问题，很难排查，除非是殊情况，否则不推荐使用这种方法。

```html
p {
  color: orange !important;
}
```

## 样式表嵌入网页的方法

HTML 网页采用`<link>`标签将 CSS 样式表嵌入网页。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Demo</title>
    <link rel="stylesheet" href="style.css" />
</head>
<body>
  ... ...
</body>
</html>
```

上面代码中，`<link>`标签放在`<head>`内部，`rel="stylesheet"`指明这是样式表，`href`属性给出样式表文件的网址。浏览器解析到这一行代码，就会去加载样式表。

上例的样式表文件是`style.css`。样式表的后缀名通常是`.css`，但也可以是其他后缀名。

网页也可以使用`<style>`标签，直接包含样式规则。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Demo</title>
    <style>
    img {
        border: 4px solid red;
    }
    </style>
</head>
<body>
  ... ...
</body>
</html>
```

上面代码中，`<head>`内部的`<style>`标签直接包含了样式规则。

最后，针对某个网页元素的样式，也可以写在该元素的`style`属性。

```html
<img src="demo.jpg" style="border: 4px solid red;">
```

上面代码中，`style`属性指定了`img`元素的样式。

`style`属性只对当前元素生效，需要一个个指定，非常麻烦。由于多个样式表可以同时对一个元素生效，`style`属性维护起来非也常困难，所以不建议使用，一般只用于 JavaScript 脚本指定动态样式，以及某些特殊情况。

内联样式由于位于外部样式表和嵌入样式表之后，优先级高于其他所有样式，除非其他地方与之冲突的样式标记了`!important`。

# 响应式布局：media query

响应式布局（responsive）的含义是，网页会根据不同的媒介，自动采用不同的 CSS 规则。它主要通过 media 命令实现。

`media`命令用来规定 CSS 规则生效的媒介。`@media`命令后面使用关键词，指定生效的条件。

```css
@media print {
   …
}

@media screen {
   …
}
```

上面代码中，打印和显示屏分别使用不同的 CSS 规则。

媒介名称之前，还可以使用`not`和`only`关键字。

```css
@media not screen {
   …
}

@media only screen {
   …
}
```

`@media`还允许使用表达式，指定 CSS 生效的条件。表达式可以放在圆括号之中。

```css
@media (min-width: 800px) {
  p {
    font-size: 18px;
  }
}
```

上面代码中，`media`命令规定，只有在屏幕宽度大于等于`800px`时，`p`元素的大小才等于`18px`。

如果同时需要满足多个条件，可以使用`and`关键字。下面的例子是为不同的设备指定不同的背景图片。

```css
/* default is desktop image */
.someElement { background-image: url(sunset.jpg); }

@media only screen and (max-width : 1024px) {
  .someElement { background-image: url(sunset-small.jpg); }
}
```

下面是另一个例子。

```css
.component__header {
  font-size: 2rem;
  @media (min-width: 1200px) {
    font-size: 3rem
  }
}

@media only screen
  and (max-width : 603px)
  and (max-height : 966px) {
  /* Styles here */
}
```

# 基本操作

## 垂直置中

（1）方法一

原理：子元素的 top, right, bottom, left, margin, and padding属性，针对的是父元素的维度；transform针对的子元素本身的维度。

父元素、子元素需有明确高度，不能是auto。

```css

.children{
	background: #ffdb4c;
	height: 300px;
	position: relative;
	top: 50%;
	transform: translateY(-50%);
}

```

（2）方法二

```css

.parent { position: relative; }

.child {
    position: absolute;

    left: 50%;
    top: 50%;

    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

```

（3）方法三

```css
.parent { display: table; }

.child {
  display: table-cell;
  vertical-align: middle;
}
```

（5）行内置中

```css
.parent { line-height: 500px; }

.child {
    display: inline-block;
    vertical-align: middle;
}
```

（6）方法四（只对图片有效）

```css
.thumb {
  background-image: url(my-img.jpg);
  background-position: center;
  /* is this supported by IE8? I don't know */
  background-size: cover;

  height: 0;
  overflow: hidden;
  padding-bottom: 50%;
  width: 50%;
}
```

（7）方法五

```css
.children {
  margin-top: calc(50% - 12.5%);
}
```

（8）方法六

```css
.container {
  display: flex;
  justify-content: center;
  align-items: center;
}
```

（9）参考链接

- [Alignment and sizing in CSS](https://github.com/timseverien/timseverien.github.io/blob/master/_posts/2013-11-25-css-alignment-and-sizing.md)

## 清理浮动

```css

.clearfix:after{
  visibility:hidden;
  display:block;
  font-size:0;
  content:" ";
  clear:both;
  height:0;
}

.clearfix{
  zoom:1; /* for IE6 IE7 */
}

```

# position

`position`属性用来指定一个元素在网页上的位置，一共有5种定位方式，即`position`属性主要有五个值。

> - `static`
> - `relative`
> - `fixed`
> - `absolute`
> - `sticky`

下面就依次介绍这五个值。

## static 属性值

`static`是`position`属性的默认值。如果省略`position`属性，浏览器就认为该元素是`static`定位。

这时，浏览器会按照源码的顺序，决定每个元素的位置，这称为“正常的页面流”（normal flow）。每个块级元素占据自己的区块（block），元素与元素之间不产生重叠，这个位置就是元素的默认位置。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111720.jpg)

注意，`static`定位所导致的元素位置，是浏览器自主决定的，所以这时`top`、`bottom`、`left`、`right`这四个属性无效。

## relative，absolute，fixed

`relative`、`absolute`、`fixed`这三个属性值有一个共同点，都是相对于某个基点的定位，不同之处仅仅在于基点不同。所以，只要理解了它们的基点是什么，就很容易掌握这三个属性值。

这三种定位都不会对其他元素的位置产生影响，即不管有没有这个元素，其他元素的位置不变，因此元素之间可能产生重叠。

### relative 属性值

`relative`表示，相对于默认位置（即`static`时的位置）进行偏移，即定位基点是元素的默认位置。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111721.jpg)

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111722.jpg)

它必须搭配`top`、`bottom`、`left`、`right`这四个属性一起使用，用来指定偏移的方向和距离。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111723.jpg)

```css
div {
  position: relative;
  top: 20px;
}
```

上面代码中，`div`元素从默认位置向下偏移`20px`（即距离顶部`20px`）。

### absolute 属性值

`absolute`表示，相对于祖先元素（一般是父元素）进行偏移，即定位基点通常是父元素。

它有一个重要的限制条件：父元素不能是`static`定位，否则定位基点会移到最近一个非`static`定位的祖先元素。如果所有祖先元素都是`static`定位，定位基点就变成整个网页的根元素`html`。另外，`absolute`定位也必须搭配`top`、`bottom`、`left`、`right`这四个属性一起使用。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111801.jpg)

```css
/*
  HTML 代码如下
  <div id="father">
    <div id="son"></div>
  </div>
*/

#father {
  positon: relative;
}
#son {
  position: absolute;
  top: 20px;
}
```

上面代码中，父元素是`relative`定位，子元素是`absolute`定位，所以子元素的定位基点是父元素，相对于父元素的顶部向下偏移`20px`。如果父元素是`static`定位，上例的子元素就是距离网页的顶部向下偏移`20px`。

注意，`absolute`定位的元素会被“正常页面流”忽略，即在“正常页面流”中，该元素所占空间为零，周边元素不受影响。

### fixed 属性值

`fixed`表示，相对于视口（viewport，浏览器窗口）进行偏移，即定位基点是浏览器窗口。这会导致元素的位置不随页面滚动而变化，好像固定在网页上一样。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111802.jpg)

它如果搭配`top`、`bottom`、`left`、`right`这四个属性一起使用，表示元素的初始位置是基于视口计算的，否则初始位置就是元素的默认位置。

```css
div {
  position: fixed;
  top: 0;
}
```

上面代码中，`div`元素始终在视口顶部，不随网页滚动而变化。

## 四、sticky 属性值

`sticky`跟前面四个属性值都不一样，它会产生动态效果，很像`relative`和`fixed`的结合：一些时候是`relative`定位（定位基点是自身默认位置），另一些时候自动变成`fixed`定位（定位基点是视口）。

因此，它能够形成“动态固定”的效果。比如，网页的搜索工具栏，初始加载时在自己的默认位置（`relative`定位）。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111604.jpg)

页面向下滚动时，工具栏变成固定位置，始终停留在页面头部（`fixed`定位）。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111605.jpg)

等到页面重新向上滚动回到原位，工具栏也会回到默认位置。

`sticky`生效的前提是，必须搭配`top`、`bottom`、`left`、`right`这四个属性一起使用，不能省略，否则等同于`relative`定位，不产生“动态固定”的效果。原因是这四个属性用来定义“偏移距离”，浏览器把它当作`sticky`的生效门槛。

它的具体规则是，当页面滚动，父元素开始脱离视口时（即部分不可见），只要与`sticky`元素的距离达到生效门槛，`relative`定位自动切换为`fixed`定位；等到父元素完全脱离视口时（即完全不可见），`fixed`定位自动切换回`relative`定位。

请看下面的示例代码。（注意，除了已被淘汰的 IE 以外，其他浏览器目前都支持`sticky`。但是，Safari 浏览器需要加上浏览器前缀`-webkit-`。）

```css
#toolbar {
  position: -webkit-sticky; /* safari 浏览器 */
  position: sticky; /* 其他浏览器 */
  top: 20px;
}
```

上面代码中，页面向下滚动时，`#toolbar`的父元素开始脱离视口，一旦视口的顶部与`#toolbar`的距离小于`20px`（门槛值），`#toolbar`就自动变为`fixed`定位，保持与视口顶部`20px`的距离。页面继续向下滚动，父元素彻底离开视口（即整个父元素完全不可见），`#toolbar`恢复成`relative`定位。

## 五、 sticky 的应用

`sticky`定位可以实现一些很有用的效果。除了上面提到“动态固定”效果，这里再介绍两个。

## 5.1 堆叠效果

堆叠效果（stacking）指的是页面滚动时，下方的元素覆盖上方的元素。下面是一个图片堆叠的例子，下方的图片会随着页面滚动，覆盖上方的图片（查看 [demo](https://jsbin.com/fegiqoquki/edit?html,css,output)）。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111609.jpg)

HTML 代码就是几张图片。

```html
<div><img src="pic1.jpg"></div>
<div><img src="pic2.jpg"></div>
<div><img src="pic3.jpg"></div>
```

CSS 代码极其简单，只要两行。

```css
div {
  position: sticky;
  top: 0;
}
```

它的原理是页面向下滚动时，每张图片都会变成`fixed`定位，导致后一张图片重叠在前一张图片上面。详细解释可以看[这里](https://dev.to/vinceumo/slide-stacking-effect-using-position-sticky-91f)。

### 5.2 表格的表头锁定

大型表格滚动的时候，表头始终固定，也可以用`sticky`实现（查看 [demo](https://jsbin.com/decemanohe/edit?html,css,output)）。

![](https://www.wangbase.com/blogimg/asset/201911/bg2019111610.jpg)

CSS 代码也很简单。

```css
th {
  position: sticky;
  top: 0; 
}
```

需要注意的是，`sticky`必须设在`<th>`元素上面，不能设在`<thead>`和`<tr>`元素，因为这两个元素没有`relative`定位，也就无法产生`sticky`效果。详细解释可以看[这里](https://css-tricks.com/position-sticky-and-table-headers/)。

## z-index

`z-index`表示元素重叠时的层次关系。这个值越高，对应的元素就在越上层。所有元素的默认`z-index`是0。

由于静态布局的元素不会产生重叠，所以该属性对静态元素无效。只有元素是非静态布局（即`position`属性的值不是`static`），`z-index`属性才有意义。如果没有设置`z-index`，元素重叠时，HTML 代码中越后面出现的元素在越上层。

`z-index`属性的难点在于，一组设置了`z-index`属性的元素，它们的堆叠顺序取决于，它们是否处在同一个堆叠上下文（stacking context）。三种情况会创建堆叠上下文。

- 网页的根元素（即`<html>`元素）
- 网页元素是非静态布局，且`z-index`属性不等于`auto`
- 网页元素的`opacity`属性小于1

同一个堆叠上下文之中，元素的堆叠（从下层到上层）按照以下顺序决定。

- 堆叠上下文的根元素
- 设置了定位、且`z-index`为负值的元素
- 没有设置定位的元素
- 设置了定位、且`z-index`为`auto`的元素
- 设置了定位、且`z-index`为正值的元素

```css
/* HTML 代码如下
<div id="container">
  <div id="item-a">A</div>
  <div id="item-b">b</div>
</div>
<div id="item-c">C</div>
*/

div {
  width: 100px;
  height: 100px;
}

#item-a {
  background-color: red;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
}

#item-b {
  background-color: blue;
  position: absolute;
  top: 20px;
  left: 20px;
  z-index: 2;
}

#item-c {
  background-color: yellow;
  position: absolute;
  top: 40px;
  left: 40px;
  z-index: 1;
}
```

上面代码中，`item-a`在最上层，然后是`item-b`，最下层是`item-c`。

现在，对`item-a`和`item-b`父元素`container`设一下定位。

```css
#container {
  position: relative;
  z-index: 0;
}
/* 或者写成
#container {
  opacity: 0.99;
}
*/
```

结果就会变成`item-c`在最上层，然后是`item-a`，最下层是`item-b`。这是因为这时`container`形成一个独立的堆叠上下文，`item-a`和`item-b`处在这个上下文之中，与`item-c`不再是同一个层级的关系了。

## 参考链接

- [What No One Told You About Z-Index](https://philipwalton.com/articles/what-no-one-told-you-about-z-index/), by Philip Walton

# CSS 属性

## `inherit`属性值

CSS 的许多属性可以继承，即子元素默认继承父元素的属性。比如，`body`元素的字体样式，可以被页面的所有元素继承。

```css
body {
  font-family: Arial, Helvetica, sans-serif;
}
```

上面代码中，`body`设置了`font-family`了，它的后代元素就不用设置这个属性了，除非需要改变字体。

根据标准，以下 CSS 属性可以继承。

1.  `azimuth`
2.  `border-collapse`
3.  `border-spacing`
4.  `caption-side`
5.  `color`
6.  `cursor`
7.  `direction`
8.  `elevation`
9.  `empty-cells`
10.  `font-family`
11.  `font-size`
12.  `font-style`
13.  `font-variant`
14.  `font-weight`
15.  `font`
16.  `letter-spacing`
17.  `line-height`
18.  `list-style-image`
19.  `list-style-position`
20.  `list-style-type`
21.  `list-style`
22.  `orphans`
23.  `pitch-range`
24.  `pitch`
25.  `quotes`
26.  `richness`
27.  `speak-header`
28.  `speak-numeral`
29.  `speak-punctuation`
30.  `speak`
31.  `speech-rate`
32.  `stress`
33.  `text-align`
34.  `text-indent`
35.  `text-transform`
36.  `visibility`
37.  `voice-family`
38.  `volume`
39.  `white-space`
40.  `widows`
41.  `word-spacing`

其他属性默认不能继承，比如`border`属性。父元素设置了`border`以后，子元素如果要有边框，必须重新设一遍。

```css
.main-list {
  border: 1rem solid #000;
  color: red;
  font-family: Verdana
}

.sub-list {
  border: 1rem solid #000;
}
```

上面代码中，`.sum-list`是`.main-list`的子元素，两者的边框必须各自设置。

CSS 提供了`inherit`属性值，如果要让子元素继承父元素的属性，可以使用这个属性值。

```css
.main-list {
  border: 1rem solid #000;
  color: red;
  font-family: Verdana;
}

.sub-list {
  border: inherit;
}
```

上面代码中，`.sub-list`的`border`属性，就继承了`.main-list`，从而两者的边框都一样。它的好处是，如果要修改边框，只要修改一处即可。

## initial 属性值

`initial`属性值可以将 CSS 属性设回初始值。它的主要用处是，让那些默认继承父元素的 CSS 属性不再继承，回到初始值。

```css
.berries {
  border: 1rem solid #000;
  color: red;
  font-family: Verdana;
  margin-bottom: 10px;
}

.berries h1 {
  color: initial;
}
```

上面代码中，`.berries`是`h1`的父元素，而`color`属性是可以继承的，如果不设置`h1`的颜色，`h1`就会是红色的。现在`h1`的`color`设为`initial`，就不再继承父元素的颜色，而是回到浏览器给予`h1`的默认颜色，即黑色。

## unset 属性值

`unset`属性值的作用是，如果存在继承，则继承父元素的值（等同于`inherit`），如果不存在继承，则重置为初始值（等同于`initial`）。`unset`的意思，就是去除当前样式表对该元素的样式设置。

```css
h1 {
  color: blue;
}

div {
  border: 1rem solid #000;
  color: red;
  font-family: Verdana;
  margin-bottom: 10px;
}

.berries h1 {
  color: unset;
}
```

上面代码中，`div`是`h1`的父元素，如果不设置`.berries h1`的`color`属性，`h1`会显示为蓝色，设成`color: unset`以后，`h1`继承了父元素的`color`，显示为红色。

`unset`与`inherit`的区别在于，如果不设置`div`的`color`，那么`.berries h1`将显示为浏览器赋予的默认颜色（黑色），而不是红色。

```css
h1 {
  color: blue;
}

div {
  border: 1rem solid #000;
  font-family: Verdana;
  margin-bottom: 10px;
}

.berries h1 {
  color: unset;
}
```

上面代码中，父元素`div`没有设置颜色，这时子元素`.berries h1`将显示为浏览器默认颜色（黑色），而不是蓝色。

## revert 属性值

`revert`属性值用于消除当前样式表对该元素设置的样式，这也是它名字的含义（`还原`），基本等同于`unset`。具体来说，如果存在继承，该元素会显示继承的属性值，如果不存在继承，则分成以下两种情况。

- `revert`用在网站提供的样式表：则显示用户演示表设置的值。如果不存在用户样式表，则浏览器赋予的默认值。
- `revert`用在用户提供的样式表：显示浏览器赋予的默认值。

我们知道，样式表可以分成三层：用户提供的样式表，可以覆盖网站提供的样式表；网站提供的样式表，又可以覆盖浏览器的默认样式表。`revert`主要针对的就是多层样式表同时存在的情况，然后用于去除本层样式表对元素的影响。

大多数情况下，`revert`与`unset`是一样的，主要差异是 CSS 属性的初始值与浏览器的默认值可能有差异。

```html
<h3 style="font-weight: unset;">hello</h3>
<h3 style="font-weight: revert;">hello</h3>
```

上面代码中，`font-weight: unset`会回到`font-weight`的初始值，即`normal`。而`<font-weight: revert>`会回到浏览器对`h3`的`font-weight`默认值，一般是粗体。

如果想要清除当前样式表对该元素的所有设置，可以使用`all: revert`。

```html
<h3 style="all: revert;">hello</h3>
```


## background-blend-mode

background-blend-mode属性指定背景的颜色混合模式，共有16个值可取：normal（默认值，即不混合）, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color and luminosity（显示单色效果）。

可以显示多张背景图片的混合，或者背景图片与背景色的混合。

```javascript
  background-image: url(...g);
  background-color: #51B7D3;
  background-blend-mode: luminosity;
```

## background-image

`background`属性可以同时指定背景图和背景颜色。

```css
body {
  background: url(sweettexture.jpg) blue;
}
```

## background-position

`background-position`指定背景图的位置。

```css
background-position: 100px 5px;
```

位置属性可以指定关键字。

- top
- right
- bottom
- left
- center

## background-blend-mode

`background-blend-mode`用于指定两种颜色混合的方式。

```css
div {
  background: url(img/pattern.png), url(img/jellyfish.jpg), #f07e32;
  background-blend-mode: screen;
}
```

Demo：http://codepen.io/tutsplus/live/wMvoyj

教程：http://webdesign.tutsplus.com/tutorials/blending-modes-in-css-color-theory-and-practical-application--cms-25201

## border-image

在边框上显示图像。

## content

指定伪元素的内容。

```css
.myDiv:after {
  content: "I am hardcoded text from the *content* property";
}

div[data-line]:after {
  content: attr(data-line); /* 属性名没有引号 */
}

div[data-line]:after {
  content: "[line " attr(data-line) "]";
}

```

## counter

counter用来实现计数器。

```html
<ol class="list">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ol>
```

`li`元素前面添加计数器的代码如下。

```css
.list {
    counter-reset: i; //reset conunter
}
.list > li {
    counter-increment: i; //counter ID
}
.list li:after {
    content: "[" counter(i) "]"; //print the result
}
```

下面是一个高级用法的例子。

```html
<div class="numbers">  
  <input id="one" type="checkbox"><label for="one">1</label>
  <input id="two" type="checkbox"><label for="two">2</label>
  <input id="three" type="checkbox"><label for="three">3</label>
  <input id="four" type="checkbox"><label for="four">4</label>
  <input id="five" type="checkbox"><label for="five">5</label>
  <input id="one-hundred" type="checkbox"><label for="one-hundred">100</label>
</div>  
<p class="sum">  
  Sum 
</p>  
```

然后，利用计数器做出一个累加计算器。

```css
.numbers {
  counter-reset: sum;
}

#one:checked { counter-increment: sum 1; }
#two:checked { counter-increment: sum 2; }
#three:checked { counter-increment: sum 3; }
#four:checked { counter-increment: sum 4; }
#five:checked { counter-increment: sum 5; }
#one-hundred:checked { counter-increment: sum 100; }

.sum::after {
  content: '= ' counter(sum);
}
```

## cursor

`cousor`属性用来指定鼠标形状。

```css
.cursor_auto {
    cursor: auto;
}

.cursor_default {
    cursor: default;
}

.cursor_none {
    cursor: none;
}

.cursor_pointer {
    cursor: pointer;
}

.cursor_progress {
    cursor: progress;
}

.cursor_help {
    cursor: help;
}

.cursor_text {
    cursor: text;
}

.cursor_cell {
    cursor: cell;
}

.cursor_crosshair {
    cursor: crosshair;
}

.cursor_alias {
    cursor: alias;
}

.cursor_context-menu {
    cursor: context-menu;
}

.cursor_vertical-text {
    cursor: vertical-text;
}  /*not compatible with opera*/

.cursor_copy {
    cursor: copy;
}

.cursor_move {
    cursor: move;
}

.cursor_no-drop {
    cursor: no-drop;
}  /*not compatible with opera*/

.cursor_not-allowed {
    cursor: not-allowed;
}  /*not compatible with opera*/

.cursor_all-scroll {
    cursor: all-scroll;
}  /*not compatible with opera*/

.cursor_col-resize {
    cursor: col-resize;
}  /*not compatible with opera*/

.cursor_row-resize {
    cursor: row-resize;
}  /*not compatible with opera*/

.cursor_nesw-resize {
    cursor: nesw-resize;
}
.cursor_nwse-resize {
    cursor: nwse-resize;
}

.cursor_n-resize {
    cursor: n-resize;
}

.cursor_e-resize {
    cursor: e-resize;
}

.cursor_s-resize {
    cursor: s-resize;
}

.cursor_w-resize {
    cursor: w-resize;
}

.cursor_ns-resize {
    cursor: ns-resize;
}

.cursor_ew-resize {
    cursor: ew-resize;
}

.cursor_ne-resize {
    cursor: ne-resize;
}

.cursor_nw-resize {
    cursor: nw-resize;
}

.cursor_sw-resize {
    cursor: sw-resize;
}

.cursor_se-resize {
    cursor: se-resize;
}

.cursor_wait {
    cursor: wait;
}

.cursor_grab {
    cursor: grab;
}  /*only compatible with firefox and opera*/

.cursor_grabbing {
    cursor: grabbing;
}  /*only compatible with firefox and opera*/

.cursor_zoom-in {
    cursor: zoom-in;
}  /*not compatible with explorer*/

.cursor_zoom-out {
    cursor: zoom-out;
}  /*not compatible with explorer*/

.cursor_custom {
    cursor: url(image.gif), url(image.cur), auto;
}
```

## display

display属性表示如何展示元素。

```css
display: block;
display: flex;
```

### filter

filter属性表示图片滤镜，支持grayscale, blur, sepia, saturate, opacity, brightness, contrast, hue-rotate, invert效果。

灰度效果。

```css

img.bw {
  filter: grayscale(1);
}

```

动画效果。

```css

img.bw {
	filter: grayscale(0);
}

img.bw.grey {
	filter: grayscale(1);
	transition-property: filter;
	transition-duration: 1s;	
}

```

其他例子。

```css

/* 模糊 */
.myElement {
	-webkit-filter: blur(2px);
}

/* 组合效果 */
.myElement {
	-webkit-filter: blur(2px) grayscale(.5) opacity(0.8);
}

```

模糊与鼠标悬停效果相结合。

```css
.spoiler {
	-webkit-filter: blur(20px);
	-webkit-transition-property: -webkit-filter;
	-webkit-transition-duration: .4s;
}
.spoiler:hover, .spoiler:focus {
	-webkit-filter: blur(0px);
}
```

## flex

flex功能可以指定容器采用弹性布局。

```css
div{
  display: flex;
}
```

## filter

filter属性在指定元素上应用滤镜。

- blur()：模糊，参数为模糊半径
- brightness()：亮度，0%为全黑，100%为原始亮度
- contrast()：对比度，0%为全黑，100%为原始对比度
- grayscale()：灰度，0%为原始色彩，100%为完全灰度。
- hue-rotate()：色调，0为原始色调，360为色彩轮旋转一周后回到原色调。
- invert()：负片效果，0%为原始效果，100%为完全负片效果。
- opacity()：透明度，0%为完全透明，100%为完全不透明。
- saturate()：饱和度，0%为完全不饱和，100%为完全饱和。
- sepia()：作旧效果，0%为原始效果，100%为完全作旧
- drop-shadow()：阴影效果，设置同box-shadow接近
- url()：引用定义在SVG文件中的滤镜

多个滤镜可以联合使用。

```css
filter: sepia(1) brightness(150%) contrast(0.5);
```

## mix-blend-mode

mix-blend-mode属性指定前景与背景的颜色混合模式，即前景色与背景色的混合。它的取值同background-blend-mode属性一样，也是16个值。

## object-fit

定义内容如何适应容器的高和宽，比如不同大小的图片，如何放在同一个位置。

```css
img {
  height: 100px;
  width: 100px;
  object-fit: contain;
}
```

object-fit可能的值共有五个。

```css
object-fit: fill
object-fit: contain
object-fit: cover
object-fit: none
object-fit: scale-down
```

- contain：图片自动升缩，以固有的长宽比，完整显示在容器中。
- fill：图片自动填满容器，即使破坏原有的长宽比。
- cover：保留图像的长宽比，但会自动升缩以填满容器，长度或宽度中较小的一个会完全在容器中展示，较大的一个会溢出。
- none：完全忽视容器的大小，使用图片固有的长宽比。
- scale-down: none或者contain中导致图片尺寸较小的那个值。

cover表示自动将图像的中心点，放置到容器的中心点，同时根据容器的大小，截取自身的大小。

```css

img {
  object-fit: cover;
}

```

参考链接

- MDN, [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit)
- Chris Nager, [Center and crop images with a single line of CSS](https://medium.com/@chrisnager/center-and-crop-images-with-a-single-line-of-css-ad140d5b4a87)
- Chris Mills, [Exploring object-fit](https://hacks.mozilla.org/2015/02/exploring-object-fit/)

## object-position

object-position设置容器中的对象（通常是图片）的垂直和水平位置，与background-position设置背景图片的写法相同。

```css

img {
  height: 100px;
  width: 100px;
  object-fit: contain;
  object-position: top 70px;
}

```

## pointer-events

该属性定义当前图形对象会不会成为鼠标动作的目标。

```css
/* 图片将对鼠标行为无反应 */
img {
  pointer-events: none;
}

a[href="http://example.com"] {
  pointer-events: none;
}
```

一旦pointer-events设为none，就不会触发JavaScript事件。在该元素上点击，任何addEventListener添加的回调函数，都不会触发。

## text-overflow

该属性定义了文本超出容器宽度后，如何处理。如果将多余文字显示成三点的省略号，可以像下面这样设置。

```css
.ellipsis {
    overflow: hidden; 
    white-space: nowrap; 
    text-overflow: ellipsis;
}
```

上面代码第一行是隐藏溢出，第二行是防止断行，第三行是在行尾加上省略号。

## position

position属性用来确定元素的定位。

```css
position: relative;
position: sticky;
position: absolute;
position: fixed;
```

## transition

```css
/* Format */
transition: property || duration || timing-function || delay

/* Various Examples */
transition: all 300ms ease 0;
transition: all 0.5s ease-in-out 0;
transition: background 300ms cubic-bezier(.61,-0.67,0,1.45) 0;
transition: opacity 100ms ease 0, background 200ms ease-in-out 0, transform 200ms ease-out 0;

/* Cross Browser Prefixes */
-webkit-transition: all 300ms ease 0;
-moz-transition: all 300ms ease 0;
-o-transition: all 300ms ease 0;
transition: all 300ms ease 0;
```

# 响应式布局

图片自适应各种屏幕的写法。

```css
img {
  width: 100%;
  height: auto;
}
```

如果使用`<img>`或`<picture>`元素的`srcset`属性，上面这样的写法就够了。但是，对于普通的`<img>`元素，还要加上`max-width: 100%;`，这是为了防止图片大于容器宽度的情况。

# 变形

`transform`命令用于元素的变形，它有多种设置。

```css
transform: scaleX(-1);
```

上面命令设置元素的`x`坐标沿水平轴翻转，`y`坐标不变。`scaleX(-1)`会起到水平翻转的效果。

# CSS的单位

## inherit

大多数 CSS 属性默认继承它的父元素的设定，但是有一些属性默认不继承。比如，`input`元素和`textarea`元素的 CSS 属性，都是不继承父元素的。这时，可以强制它们继承。

```css
* {
  font-family: inherit;
  line-height: inherit;
  color: inherit;
}

html {
  font-size: 125%;
  font-family: sans-serif;
  line-height: 1.5;
  color: #222;
}
```

上面代码中，首先强制所有元素的`font-family`、`line-height`和`color`属性都继承父元素，然后在网页的根元素`html`上设置具体的值。

## em

`em`是一种相对单位，`1em`等于当前元素的`font-size`。一般来说，浏览器默认的字体大小是16像素，所以`1em`默认等于16像素。

```css
h1 { font-size: 20px } /* 1em = 20px */
p { font-size: 16px } /* 1em = 16px */
```

上面代码中，对于`h1`元素，`1em`相当于`20px`；对于`p`元素，`1em`相当于`16px`。

如果当前元素没有指定字体的像素大小，那么`1em`等于父元素字体的像素大小。这一点非常重要，务必牢记。

```css
html { font-size: 16px }
h1 { font-size: 2em } /* 16px * 2 = 32px */
```

上面代码中，`h1`元素的字体大小是`2em`，但是它没有指定像素字体大小，因此`2em`等于2倍的父元素像素字体大小，也就是`32px`。

`em`的一大好处是，很容易保持不同元素之间的比例关系，因此它比像素单位更合适用来设定字体大小。

```css
html { font-size: 100%; }
h1 { font-size: 2em; }
p { font-size: 1em; }
```

上面代码中，`h1`的字体大小就是`p`的2倍。

整个网页的字体大小，如果全部使用`em`单位，不使用像素单位，会引发一个问题。`em`将会基于父元素计算，从而出现累积效应。

```css
html { font-size: 100%; } /* 默认是 16px */
div { font-size: 2em; } /* 字体大小 32px */
p { font-size: 1em; } /* 字体大小 32px */
```

上面代码中，`html`是`div`的父元素，所以`div`的`1em`是基于`html`计算的，因此`2em`字体大小等于`32px`。另一方面，`div`是`p`的父元素，所以`p`的`1em`实际上也是`32px`。

除了字体大小，其他许多属性也可以使用`em`，比如`margin`和`padding`，相当于间接由`font-size`决定。

```css
.header {
  font-size: 16px;
  padding: 0.5em 0.75em; /* 相当于 8px 12px */
  background: #7F7CFF;
}
```

上面代码中，`.header`元素的字体大小是`16px`，因此`1em`等于`16px`，所以`padding`就相当于`8px 12px`。

这里比较混淆的是，如果`font-size`也使用`em`，两者的计算基点是不一样的。

```css
h1 {
  font-size: 2em; /* 1em = 16px */
  padding： 1em;  /* 1em = 32px */
}
```

上面代码中，`font-size`是基于父元素计算的，如果父元素的字体大小是`16px`，那么`font-size`就是`32px`；`padding`是基于`font-size`计算的，由于`h1`的`font-size`是`32px`，所以`padding`就是`32px`。

由于以上的累积效应和计算基点差异的原因，造成`em`不容易精确控制，实际开发中往往改用`rem`。

## rem

`rem`单位与`em`几乎完全一致，只有一个差异，它总是等于根元素`<html>`的`font-size`大小，与当前元素或父元素的设置无关，这就避免了`em`的缺陷。

```css
h1 {
  font-size: 2rem; /* 1rem = 16px */
  margin-bottom: 1rem;
}

p {
  font-size: 1rem; /* 1rem = 16px */
  margin-bottom: 1rem;
}
```

上面代码中，不管`<h1>`和`<p>`在什么位置，`1rem`的大小总是不变的。

那么，何时使用`rem`，何时使用`em`呢？一个[规则](https://zellwk.com/blog/rem-vs-em/)是字体大小`font-size`属性使用`rem`，其他必须等比例缩放的属性使用`em`。下面是一个例子。

```css
button {
  font-size: 0.875rem;
  padding: .5em 1em;
  border: .125em solid #e3e3e3;
  @media (min-width: 48rem){ /* min-width: 768px */
    font-size: 1.125rem;
  }
  @media (min-width: 62rem){ /* min-width: 992px */
    font-size: 1.375rem;
  }
}
```

上面代码中，随着屏幕宽度的变化，字体大小会跟着改变，`padding`和`border`会始终保持比例关系。

## vh，vw

`vh`表示百分之一的浏览器视口高度，`vw`表示百分之一的浏览器视口宽度。每当视口的高度和宽度发生变化，它们就会自动重新计算。

```css
html { font-size: 3vw; }
```

上面代码中，如果视口宽度增加，字体大小也会增加；视口宽度减小，字体大小也会减小。但是，如果宽度太小（比如`320px`），这样算出来的字体会太小（`3px`）；如果宽度太大（比如`1440px`），字体又会太大（`43px`）。因此，可以考虑使用`calc`函数。

```css
html { font-size: calc(18px + 0.25vw); }
```

## vmin，vmax

`vmin`表示`vh`与`vw`之中较短的那个单位，`vmax`则表示较长的那个单位。

一般来说，PC的屏幕是屏宽大于屏高，手机的屏幕是屏高大于屏宽。所以，很可能会出现，某一个区域在PC屏幕中宽度较小，在手机屏幕中宽度较大。这时，这两个单位就可以派上用处了。

```css
h1 {
  font-size: 20vmin;
}
```

注意，上面的`h1`使用`vmin`单位时，当宽屏设备的视口宽度缩小时，它的字体大小是不变的，因为视口的高度没有改变。

## ch

`ch`表示多少个字符。

```css
width: 40ch;
```

上面代码表示宽度为40个字符。

## calc()

calc方法用于计算值，常用于两种不同的单位之间的计算（比如百分比和绝对长度）。

实例1。每行放置4张图片，可以采用如下的代码。

```css
img {
  float: left;
  width: calc(25% - 20px);
  margin: 10px;
}
```

实例2。动态排列图片，可以配合media query。

```css

img {
  float: left;
  margin: 10px;
  width: calc(100% * 1 / 4 - 20px);
}

@media (max-width: 900px) {
  img {
    width: calc(100% * 1 / 3 - 20px);
  }
}

@media (max-width: 600px) {
  img {
    width: calc(100% * 1 / 2 - 20px);
  }
}

@media (max-width: 400px) {
  img {
    width: calc(100% - 20px);
  }
}

```

## attr()

attr()用于读取网页元素的属性值。

```css
div[data-line]:after { 
	content: "[line " attr(data-line) "]"; 
}
```

## 参考链接

- Zell, [REM vs EM – The Great Debate](http://zellwk.com/blog/rem-vs-em/)

# 打印样式

## 基本用法

`@media print`命令可以设置打印样式，即用户选择打印当前网页时，生效的 CSS 规则。

```css
@media print {
  h1 { font-size: 16pt; }
}
```

上面代码设置的`h1`样式，对屏幕浏览不产生效果，只有用户打印网页时才会生效。

`@media print`命令可以与正常样式规则混合使用。

```css
p { margin: 1em 0; }

@media print {
  .related-articles { display: none; }
}
```

上面代码中，`p`元素的样式对屏幕浏览和打印都有效，`.related-articles`的样式只对打印有效。

如果要设置某些规则只对屏幕浏览有效，可以像下面这样写。

```css
@media screen {
  /* 只对屏幕浏览有效 */
}

@media print {
  /* 只对打印有效 */
}
```

## 分页符

分页符属性用来设置页面的分页（即另起一页），共有三个相关属性。

- page-break-before：元素之前分页
- page-break-after：元素之后分页
- page-break-inside：元素内部分页

这三个属性的值都是两个：`always`（生效）和`avoid`（避免）。

```css
h1 {
  /* 总是在 h1 元素之前分页 */
  page-break-before: always;
}

section.city-map {
  /* 在元素之前和之后分页，即该元素单独占据一页 */
  page-break-before: always;
  page-break-after: always;
}

table {
  /* 表格尽可能不要分页 */
  page-break-inside: avoid;
}
```

## orphans 属性，widow 属性

`orphans`属性和`widow`属性设置某个元素如何跨页拆分。

`orphans`属性设置跨页前的行数少于多少行时，所有行都移到下一页打印。

```css
p {
  orphans: 3;
}
```

上面代码设置，如果某个段落出现在上一页的结尾少于3行（比如只有两行），那么该段落全部移到下一页显示。

`widow`属性设置出现在新页面的行数，最少应该有几行。

```css
p {
  widow: 2;
}
```

上面代码设置，如果某个段落出现在新页面的开头少于两行（比如只有一行），那么该段落全部移到上一页显示。

## @page 命令

`@page`命令主要用来定义页面距。

```css
@page {
  margin: 2cm;
}
```

此外，还可以用`:first`、`:last`、`:left`、`:right`和`:blank`选择器，选中特殊页面。

```css
@page:first {
  margin: 0;
}
```

上面代码设置第一页的页边距为`0`。

## 技巧

### 重复表格的表头

如果希望打印表格的时候，每一页都出现表头，只需要使用`<thead>`元素定义表头，`<tbody>`元素定义表的数据部分即可。

```html
<table>
  <thead>
    <tr>
      <th>City</th>
      <th>Population</th>
  </thead>
  <tbody>
    <tr>
      <td>Sydney</td>
      <td>4.627 million (2018)</td>
    </tr>
  </tbody>
</table>
```

上面代码中，如果表格跨页，表头的`City`和`Population`字段会在每一页都打印出来。

### 打印链接的网址

如果希望打印出链接的网址，可以使用`:after`伪元素。

```css
@media print {
  a[href]:after {
    content: "(" attr(href) ")";
  }
}
```

## 参考链接

- [Print CSS basics in 10 minutes](https://www.paperplane.app/blog/print-css-basics/), Paperplay

# CSS 计数器

## 简介

- counter-reset：新建或重置一个计数器。
- counter-increment：指定计数器的值加1。
- counter()：取出指定计数器的值。

```css
counter-reset：counterName;
counter-increment: counterName;
counter(counterName);
```

下面是一个例子。

```css
ul{
  counter-reset: section;
}
li{
  list-style-type: none;
  counter-increment: section;
}
li:before{
  content: counters(section, '-') '.';
}
```

## counter-reset

`counter-reset`属性新建或重置计数器。计数器的值默认为`0`。

可以使用第二个参数，修改计数器的默认值。

```css
counter-reset: count -1;
```

上面示例将计数器的默认值设为`-1`。

可以在一条语句里面，同时新建多个计数器。

```css
counter-reset: counter1 1 counter2 4;
counter-reset: chapter section 1 page;
```

上面示例，第一行新建了两个计数器，第二行新建了三个计数器，并且计数器`section`的默认值设为`1`。

如果这个属性的值设为`none`，则表示不重置计数器，可以用来取消上层规则的设置。

## counter-increment

`counter-increment`属性默认将计数器的值加`1`，但也可以使用第二个值，指定变化的幅度。

```css
counter-increment: counter-name -1;
```

上面示例表示将计数器的值减`1`。

可以在一条`counter-increment`语句里面，同时改变多个计数器。

```css
counter-increment: counter1 1 counter2 -4;

counter-increment: chapter section 2 page;
```

上面示例的第一行，计数器`counter1`增加`1`，计数器`counter2`减去`4`。第二行，计数器`chapter`和`page`增加`1`，计数器`section`增加2。

如果这个属性设为`none`，表示不改变计数器的值。

## counter()

`counter()`还有可选的第二个参数，设置计数器的值样式，默认是阿拉伯数字。

```css
counter(countername, upper-roman)；
```

上面示例将计数器的值样式设为大写的罗马数字。

常用的样式值如下。

- decimal-leading-zero：数字带有前导0。
- lower-roman：小写的罗马数字。
- upper-roman：大写的罗马数字。
- lower-alpha：小写的英文字母。
- upper-alpha：大写的英文字母。

也可以使用`@counter-style`定义自己的规则。

```css
@counter-style winners-list {
  system: fixed;
  symbols: url(gold-medal.svg) url(silver-medal.svg) url(bronze-medal.svg);
  suffix: " ";
}
```

上面示例是金牌、银牌、铜牌的计数符号。

```css
@counter-style thumbs {
  system: cyclic;
  symbols: "\1F44D";
  suffix: " ";
}

ul {
  list-style: thumbs;
}
```

上面示例是将列表符号变成点赞。

```css
@counter-style circled-alpha {
  system: fixed;
  symbols: Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ;
  suffix: " ";
}

.items {
  list-style: circled-alpha;
}
```

上面示例将列表符号变成带圈的英文字母。

## counters()

`counters()`用来取出计数器的值，并且指定多重计数器之间的连接字符。

```css
counters(name, strings, style);
```

它与`counter()`的不同是多了一个`strings`参数，表示子序号的连接字符串。

```css
counters(countername, '-');
counters(countername, '.', upper-roman);
```

上面示例第一行中，计数器与子计数器的连接符号为`-`。第二行为小数点。

# 表单的 CSS 设置

## textarea

文本框（textarea）用于让用户输入大量文本。它的高度通常是固定的，如果用户输入的文本多于文本框的高度，文本框就会出现滚动条，只有拖动滚动条，才能看见文本的其余部分。

`form-sizing`属性让文本框具有自适应高度，随着用户输入的文本增加，而增加文本框的高度。

```css
textarea {
  form-sizing: normal;
}
```

## 参考链接

- [Textareas with auto-increasing height using CSS](https://www.amitmerchant.com/textarea-auto-increase-height/)

# 多栏式布局

多栏式布局是 CSS 原生提供的一种内容分栏显示的解决方案。

## 基本用法

`column-count`属性指定`div`的子元素分成四栏。

```css
div {
  column-count:4;
}
```

`column-width`属性指定每一栏的宽度。

```css
div {
  column-width:100px;
}
```

上面代码中，`column-width`属性指定每一栏的宽度是100像素。如果`div`元素的宽度是800像素，那么内容就将分成8栏。

注意，`column-count`与`column-width`不应同时使用，它们之中同时只能有一个属性生效。另外，设定`position: fixed`和`position: absolute`子元素，将不纳入多栏式布局的栏计算。

## 间隔

多栏式布局对栏与栏之间的间隔，提供了如下属性。

- column-gap：间隔宽度
- column-rule-color：间隔线的颜色
- column-rule-style：间隔线的样式，比如`dashed`、`dotted`等，默认是`solid`
- column-rule-width：间隔线本身的宽度
- column-rule：`column-rule-color`、`column-rule-style`、`column-rule-width`这三个属性的快捷形式。

```css
div {
	column-gap: 20px;
	column-rule: 2px solid #33c;
}
```

上面代码指定栏与栏之间的间隔是`20px`，间隔线的样式是`2px solid #33c`。

## column-span

`column-span`属性指定某个子元素可以占据多栏的宽度，比如标题。它只能设两个值`all`和`none`。

```css
h2 {
  column-span: all;
}
```

## column-fill

`column-fill`属性指定内容如何在多栏之间分配。

默认值`balance`表示栏与栏是等高的，`auto`表示只占据内容所需的空间。

## 内容的断点

浏览器会自己决定，内容在栏与栏之间如何断点。下面的三个属性可以调整这个行为。

`break-inside`属性决定子元素内部如何断点。它可以取以下值。

- auto：默认值，表示子元素内部可以插入断点
- avoid：避免在子元素内部插入所有类型（page、column、region）的断点
- avoid-column：避免子元素内部插入栏与栏的断点

`break-before`属性决定子元素前方如何断点。它可以取以下值。

- auto：默认值，子元素前方可以插入断点
- avoid：避免在子元素前方插入所有类型（page、column、region）的断点
- avoid-column：避免在子元素前方插入栏与栏的断点
- column：子元素前方强制插入一个栏断点

`break-after`属性决定子元素后方如何断点。它可以取以下值。

- auto：默认值，子元素后方可以插入断点
- avoid：避免在子元素后方插入所有类型（page、column、region）的断点
- avoid-column：避免在子元素后方插入栏与栏的断点
- column：子元素后方强制插入一个栏断点

## 参考链接

- Molly E. Holzschlag, [CSS3 Multi-Column Layout](https://dev.opera.com/articles/css3-multi-column-layout/)

# flex布局

网页布局（layout）是CSS的一个重点应用。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071001.gif)

传统的布局有下面几种。

- display: block;
- display: inline;
- display: list-item;
- display: table;

[display](https://developer.mozilla.org/en-US/docs/Web/CSS/display)属性搭配[position](https://developer.mozilla.org/en-US/docs/Web/CSS/position)属性和[float](https://developer.mozilla.org/en-US/docs/Web/CSS/float)属性，是最常见的布局解决方案。

但是，这个方案基于[盒状模型](https://developer.mozilla.org/en-US/docs/Web/CSS/box_model)，对于那些特殊位置的布局非常不方便。比如，[垂直居中](https://css-tricks.com/centering-css-complete-guide/)就很不容易实现，代码好像一种破解，而不是正常的解决。

```css
.element {
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071002.png)

2009年，W3C提出了一种新的方案——Flex布局，用来简便地、完整地、响应式地实现各种页面布局。经过5年多的发展，Flex布局几乎得到了所有浏览器的支持。这意味着，你现在就能很安全地使用这项功能。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071003.jpg)

可以毫无疑问地说，Flex布局是未来的首选方案。本文介绍它的语法，主要是翻译和参考了下面两篇文章：[A Complete Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)和[A Visual Guide to CSS3 Flexbox Properties](https://scotch.io/tutorials/a-visual-guide-to-css3-flexbox-properties)。

## 一、Flex布局是什么？

Flex 是 flexible box 的缩写，意为弹性的盒状布局，中文可以译为“弹性布局”。它的设计目的是为盒状模型提供最大的灵活性，可以在不知道容器大小或者容器大小可变的情况下，指定布局方式。

任何一个容器都可以指定为 Flex 布局。

```css
.box {
  display: flex;
}
```

行内元素也可以使用 Flex 布局。

```css
.box {
  display: inline-flex;
}
```

Webkit内核的浏览器，必须加上-webkit前缀。

```css
.box{
  display: -webkit-flex; /* Safari */
  display: flex;
}
```

注意，设为Flex布局以后，子元素的float、clear和vertical-align属性将失效。

## 二、基本概念

采用 Flex 布局的元素，称为 Flex 容器（flex container），简称“容器”。它的所有子元素自动成为容器成员，称为 Flex 项目（flex item），简称“项目”。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071004.png)

容器默认存在两根轴：水平的主轴（main axis）和垂直的交叉轴（cross axis）。主轴的开始位置（与边框的交叉点）叫做main start，结束位置叫做main end；交叉轴的开始位置叫做cross start，结束位置叫做cross end。

项目默认沿主轴排列，在主轴占据的空间叫做 main size，在交叉轴占据的空间叫做 cross size。

## 三、容器的属性

以下 6 个属性设置在容器上。

> - flex-direction
> - flex-wrap
> - flex-flow
> - justify-content
> - align-items
> - align-content

### 3.1 flex-direction属性

`flex-direction`属性决定主轴的方向（即项目的排列方向）。

```css
.box {
  flex-direction: row | row-reverse | column | column-reverse;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071005.png)

它可能有4个值。

- `row`（默认值）：主轴为水平方向，起点在左端。
- `row-reverse`：主轴为水平方向，起点在右端。
- `column`：主轴为垂直方向，起点在上沿。
- `column-reverse`：主轴为垂直方向，起点在下沿。

### 3.2 flex-wrap属性

默认情况下，项目都排在一条线（又称“轴线”）上。`flex-wrap`属性定义，如果一条轴线排不下，如何换行。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071006.png)

```javascript
.box{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
```

它可能取三个值。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071007.png)

（1）`nowrap`（默认）：不换行。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071008.jpg)

（2）`wrap`：换行，第一行在上方。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071009.jpg)

（3）`wrap-reverse`：换行，第一行在下方。

### 3.3 flex-flow

`flex-flow`属性是`flex-direction`属性和`flex-wrap`属性的简写形式，默认值为`row nowrap`。

```css
.box {
  flex-flow: <flex-direction> || <flex-wrap>;
}
```

下面是一个例子。

```css
div {
	display: flex;
	flex-flow: row wrap;
}
```

### 3.4 justify-content属性

`justify-content`属性定义了项目在主轴上的对齐方式。

```javascript
.box {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071010.png)

它可能取5个值，具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

- `flex-start`（默认值）：左对齐
- `flex-end`：右对齐
- `center`： 居中
- `space-between`：两端对齐，项目之间的间隔都相等，项目与边框没有间隔。
- `space-around`：每个项目两侧的间隔相等。所以，项目之间的间隔比项目与边框的间隔大一倍。

### 3.5 align-items属性

`align-items`属性定义项目在交叉轴上如何对齐。

```javascript
.box {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071011.png)

它可能取5个值。具体的对齐方式与交叉轴的方向有关，下面假设交叉轴从上到下。

- `flex-start`：交叉轴的起点对齐。
- `flex-end`：交叉轴的终点对齐。
- `center`：交叉轴的中点对齐。
- `baseline`: 项目的第一行文字的基线对齐。
- `stretch`（默认值）：如果项目未设置高度或设为auto，将占满整个容器的高度。

### 3.6 align-content属性

`align-content`属性定义了多根轴线的对齐方式。如果项目只有一根轴线，该属性不起作用。

```css
.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071012.png)

该属性可能取6个值。

> - `flex-start`：与交叉轴的起点对齐。
> - `flex-end`：与交叉轴的终点对齐。
> - `center`：与交叉轴的中点对齐。
> - `space-between`：与交叉轴两端对齐，轴线之间的间隔平均分布。
> - `space-around`：每根轴线两侧的间隔都相等。所以，轴线之间的间隔比轴线与边框的间隔大一倍。
> - `stretch`（默认值）：轴线占满整个交叉轴。

## 四、项目的属性

以下6个属性设置在项目上。

- `order`
- `flex-grow`
- `flex-shrink`
- `flex-basis`
- `flex`
- `align-self`

### 4.1 order属性

项目在主轴的排列顺序，默认是其出现的顺序。`order`属性可以改变这一点，让后面出现的项目，排到前面去。

`order`属性定义项目的排列顺序。数值越小，排列越靠前，默认为`0`。

```css
.item {
  order: <integer>;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071013.png)

### 4.2 flex-grow 属性

`flex-grow`属性定义项目的放大比例，默认为0，即如果主轴存在剩余空间，也不放大。

```css
.item {
  flex-grow: <number>; /* default 0 */
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071014.png)

如果所有项目的`flex-grow`属性都为1，则它们将等分剩余空间（如果有的话）。如果一个项目的`flex-grow`属性为2，其他项目都为1，则前者占据的剩余空间将比其他项目多一倍。因此，`flex-grow`也可以理解成放大的速率，`2`的增长速率比`1`快一倍。

`flex-grow`的值是一种比例关系，所有项目都设成`1`，与都设成`2`或`0.5`，结果是一样的。

### 4.3 flex-shrink 属性

`flex-shrink`属性定义了项目的缩小比例，默认为1，即如果空间不足，该项目将缩小。

```css
.item {
  flex-shrink: <number>; /* default 1 */
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071015.jpg)

如果所有项目的`flex-shrink`属性都为1，当空间不足时，都将等比例缩小，否则项目宽度大于容器宽度，会发生溢出。如果一个项目的`flex-shrink`属性为0，其他项目都为1，则空间不足时，前者不缩小。

负值对该属性无效。

### 4.4 flex-basis 属性

`flex-basis`属性定义了在分配多余空间之前，项目占据的主轴空间（main size），即项目本身的尺寸。浏览器根据这个属性，计算主轴是否有剩余空间。它的默认值为`auto`，即项目的本来大小。

```css
.item {
  flex-basis: <length> | auto; /* default auto */
}
```

它可以设为跟`width`或`height`属性一样的值（比如350px、20%、5em等等），则项目将占据固定空间。

### 4.5 flex 属性

`flex`属性是`flex-grow`, `flex-shrink` 和 `flex-basis`的简写，默认值为`0 1 auto`。后两个属性可选。

```css
.item {
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}
```

该属性有两个快捷值：`auto` (代表`1 1 auto`) 和 none (代表`0 0 auto`)。

### 4.6 align-self 属性

`align-self`属性允许单个项目有与其他项目不一样的对齐方式，可覆盖`align-items`属性。默认值为`auto`，表示继承父元素的`align-items`属性，如果没有父元素，则等同于`stretch`。

```javascript
.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
```

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071016.png)

该属性可能取6个值，除了`auto`，其他都与`align-items`属性完全一致。

（完）

# Flex布局实例教程

[上一篇文章](http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html)，介绍了Flex布局的语法。

今天介绍，常见布局的Flex写法。你会看到，不管是什么布局，Flex往往都可以几行命令搞定。

下面的内容，我只列出代码，详细的语法解释请查阅[《Flex布局语法教程》](http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html)。

我主要参考了[Landon Schropp](http://davidwalsh.name/flexbox-dice)的文章和[Solved by Flexbox](http://philipwalton.github.io/solved-by-flexbox/)。

## 一、骰子的布局

如果不加说明，本节的HTML模板一律如下。

```html
<div class="box">
  <span class="item"></span>
</div>
```

上面代码中，div元素是Flex容器，span元素是Flex项目。如果有多个项目，就要添加多个span元素，以此类推。

### 1.1 单项目：首行左对齐

Flex布局默认就是首行左对齐，所以一行代码就够了。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071301.png)

```css
.box {
  display: flex;
}
```

### 1.2 单项目：首行居中对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071302.png)

```css
.box {
  display: flex;
  justify-content: center;
}
```

### 1.3 单项目：首行右对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071303.png)

```css
.box {
  display: flex;
  justify-content: flex-end;
}
```

### 1.4 单项目：中行左对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071304.png)

```css
.box {
  display: flex;
  align-items: center;
}
```

### 1.5 单项目：垂直居中对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071305.png)

```css
.box {
  display: flex;
  justify-content: center;
  align-items: center;
}
```

### 1.6 单项目：尾行居中对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071306.png)

```css
.box {
  display: flex;
  justify-content: center;
  align-items: flex-end;
}
```

### 1.7 单项目：尾行右对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071307.png)

```css
.box {
  display: flex;
  justify-content: flex-end;
  align-items: flex-end;
}
```

### 1.8 双项目：首行两端对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071308.png)

```css
.box {
  display: flex;
  justify-content: space-between;
}
```

### 1.9 双项目：首列两端对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071309.png)

```css
.box {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
```

### 1.10 双项目：中列两端对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071310.png)

```css
.box {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}
```

### 1.11 双项目：尾列两端对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071311.png)

```css
.box {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
}
```

### 1.12 双项目：首行左对齐，中行居中对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071312.png)

```css
.box {
  display: flex;
}

.item:nth-child(2) {
  align-self: center;
}
```

### 1.13 双项目：首行左对齐，尾行右对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071313.png)

```css
.box {
  display: flex;
  justify-content: space-between;
}

.item:nth-child(2) {
  align-self: flex-end;
}
```

### 1.14 三项目：对角线布局

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071314.png)

```css
.box {
  display: flex;
}

.item:nth-child(2) {
  align-self: center;
}

.item:nth-child(3) {
  align-self: flex-end;
}
```

### 1.15 四项目：首行排满，尾行右对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071315.png)

```css
.box {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-content: space-between;
}
```

### 1.16 四项目：四角对齐

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071316.png)

HTML代码如下。

```html
<div class="box">
  <div class="column">
    <span class="item"></span>
    <span class="item"></span>
  </div>
  <div class="column">
    <span class="item"></span>
    <span class="item"></span>
  </div>
</div>
```

CSS代码如下。

```css
.box {
  display: flex;
  flex-wrap: wrap;
  align-content: space-between;
}

.column {
  flex-basis: 100%;
  display: flex;
  justify-content: space-between;
}
```

### 1.17 六项目：首行排满，尾行排满

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071317.png)

```css
.box {
  display: flex;
  flex-wrap: wrap;
  align-content: space-between;
}
```

### 1.18 六项目：首列排满，尾列排满

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071318.png)

```css
.box {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  align-content: space-between;
}
```

### 1.19 六项目：首行两端对齐，中行居中，尾行排满

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071319.png)

HTML代码如下。

```html
<div class="box">
  <div class="row">
    <span class="item"></span>
    <span class="item"></span>
    <span class="item"></span>
  </div>
  <div class="row">
    <span class="item"></span>
  </div>
  <div class="row">
     <span class="item"></span>
     <span class="item"></span>
  </div>
</div>
```

CSS代码如下。

```css
.box {
  display: flex;
  flex-wrap: wrap;
}

.row{
  flex-basis: 100%;
  display:flex;
}

.row:nth-child(2){
  justify-content: center;
}

.row:nth-child(3){
  justify-content: space-between;
}
```

### 1.20 九项目

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071320.png)

```css
.box {
  display: flex;
  flex-wrap: wrap;
}
```

## 二、网格布局

### 2.1 基本网格布局

容器平均分配空间。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071321.png)

HTML代码如下。

```html
<div class="Grid">
  <div class="Grid-cell">…</div>
  <div class="Grid-cell">…</div>
  <div class="Grid-cell">…</div>
</div>
```

CSS代码如下。

```css
.Grid {
  display: flex;
}

.Grid-cell {
  flex: 1;
}
```

上面这样的设置，不管父容器之中有多少个项目，都会平分宽度。

遇到小屏幕时，可以使用 media query，让项目占满一行。

```css
@media (max-width: 400px) {
  .flex-grid {
    display: block;
  }
}
```

网格之间的间隔，可以像下面这样设置。

```css
.flex-grid-thirds {
  display: flex;
  justify-content: space-between;
}
.flex-grid-thirds .col {
  width: 32%;
}
```

### 2.2 百分比布局

某项目占据固定的百分比，其余项目平均分配空间。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071322.png)

HTML代码如下。

```html
<div class="Grid">
  <div class="Grid-cell u-1of4">…</div>
  <div class="Grid-cell">…</div>
  <div class="Grid-cell u-1of3">…</div>
</div>
```

```css
.Grid {
  display: flex;
}

.Grid-cell {
  flex: 1;
}

.Grid-cell.u-full {
  flex: 0 0 100%;
}

.Grid-cell.u-1of2 {
  flex: 0 0 50%;
}

.Grid-cell.u-1of3 {
  flex: 0 0 33.3333%;
}

.Grid-cell.u-1of4 {
  flex: 0 0 25%;
}
```

## 三、圣杯布局

[圣杯布局](https://en.wikipedia.org/wiki/Holy_Grail_(web_design))（Holy Grail Layout）指的是一种最常见的网站布局。页面从上到下，分成三个部分：头部（header），躯干（body），尾部（footer）。其中躯干又水平分成三栏，从左到右为：导航、主栏、副栏。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071323.png)

HTML代码如下。

```html
<body class="HolyGrail">
  <header>…</header>
  <div class="HolyGrail-body">
    <main class="HolyGrail-content">…</main>
    <nav class="HolyGrail-nav">…</nav>
    <aside class="HolyGrail-ads">…</aside>
  </div>
  <footer>…</footer>
</body>
```

CSS代码如下。

```css
.HolyGrail {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

header,
footer {
  flex: 1;
}

.HolyGrail-body {
  display: flex;
  flex: 1;
}

.HolyGrail-content {
  flex: 1;
}

.HolyGrail-nav, .HolyGrail-ads {
  /* 两个边栏的宽度设为12em */
  flex: 0 0 12em;
}

.HolyGrail-nav {
  /* 导航放到最左边 */
  order: -1;
}
```

如果是小屏幕，躯干的三栏改为垂直分布。

```css
@media (max-width: 768px) {
  .HolyGrail-body {
    flex-direction: column;
    flex: 1;
  }
  .HolyGrail-nav,
  .HolyGrail-ads,
  .HolyGrail-content {
    flex: auto;
  }
}
```

## 四、输入框的布局

常常需要在输入框的前方添加提示，后方添加按钮。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071324.png)

HTML代码如下。

```html
<div class="InputAddOn">
  <span class="InputAddOn-item">…</span>
  <input class="InputAddOn-field">
  <button class="InputAddOn-item">…</button>
</div>
```

CSS代码如下。

```css
.InputAddOn {
  display: flex;
}

.InputAddOn-field {
  flex: 1;
}
```

## 五、悬挂式布局

有时，主栏的左侧或右侧，需要添加一个图片栏。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071325.png)

HTML代码如下。

```html
<div class="Media">
  <img class="Media-figure" src="" alt="">
  <p class="Media-body">…</p>
</div>
```

CSS代码如下。

```css
.Media {
  display: flex;
  align-items: flex-start;
}

.Media-figure {
  margin-right: 1em;
}

.Media-body {
  flex: 1;
}
```

### 六、固定的底栏

有时，页面内容太少，无法占满一屏的高度，底栏就会抬高到页面的中间。

这时可以采用Flex布局，让底栏总是出现在页面的底部。

![](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071326.png)

HTML代码如下。

```html
<body class="Site">
  <header>…</header>
  <main class="Site-content">…</main>
  <footer>…</footer>
</body>
```

CSS代码如下。

```css
.Site {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

.Site-content {
  flex: 1;
}
```

## 两栏式布局

对于简单的两栏式布局，Flexbox 布局相当简单。

```css
/*
  HTML 代码如下
  <div class="wrapper">
    <section class="main-column" />
    <aside class="sidebar" />
  </div>
*/

.wrapper {
  display: flex;
}

.main-column {
  width: 70%;
  padding-right: 2rem;
}

.sidebar {
  width: 30%;
}
```

（完）

## 参考链接

- [Getting Dicey With Flexbox](http://davidwalsh.name/flexbox-dice), by Landon Schropp
- [A Complete Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
- Dimitar Stojanov, [A Visual Guide to CSS3 Flexbox Properties](https://scotch.io/tutorials/a-visual-guide-to-css3-flexbox-properties)

# float

## 简介

`float`属性指定某个元素定位在容器的左侧或右侧，文字和其他行内元素紧挨在它的旁边。

```css
h4  {
  float: left;
}
```

如果对行内元素指定`float`属性，就相当于将这个行内元素变成块级元素。

`float`属性可以取三个值。

- `left`：定位在容器的左侧
- `right`：定位在容器的右侧
- `none`：不采用浮动定位（默认值）

## 清理浮动

设置`float`属性以后，容器里面的行内元素（通常是文字）会紧贴浮动元素的边缘，进行布局。如果文字的长度小于浮动元素的高度，就会出现布局问题。

下面是一段 HTML 代码。

```html
<div>
  <h2>标题一</h2>
  Hello World
</div>
<h3>标题二</h3>
```

其中的“标题一”是浮动的。

```css
h2 {
  height: 200px;
  float: left;
}
```

由于`Hello World`的高度小于“标题一”的高度，导致紧跟在后面的“标题二”，不是出现在“标题一”的下方，而是在“标题一”的右侧。这时就需要清理浮动。

```css
h3 {
  clear: both;
}
```

`clear`属性可以取四个值。

- `left`：左侧不能有浮动元素
- `right`：右侧不能有浮动元素
- `both`：两侧不能有浮动元素
- `none`：默认值，不需要清理浮动

另一种情况是，容器内部的文本不希望紧贴浮动的元素，这时也可以使用`clear`属性进行清理浮动。

## 对容器的影响

如果一个容器里面所有子元素都是浮动的，就会导致这个容器变成一个空容器，高度缩为`0`。因为布局的时候，浮动的所有子元素是脱离父容器计算位置的，这导致渲染引擎认为这个容器是空元素。

解决这个问题，有三种方法。

（1）父容器也进行浮动。

```css
.container-with-float{
    float: left;
    width: 100%;
}
```

（2）父容器添加`overflow: hidden`

```css
.container-with-overflow{
    overflow: hidden;
}
```

添加`overflow: hidden`以后，容器计算高度的时候，就会自动将浮动的子元素考虑在内。

（3）CSS 生成内容

CSS 生成内容以后，就不会被渲染引擎当成是一个空元素。

```css
.container-with-generated-content:after{
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}
```

还有另一种写法。

```css
.grid:after {
  content: "";
  display: table;
  clear: both;
}
```

# Grid 布局

## 一、概述

网格布局（Grid）是最强大的 CSS 布局方案。

它将网页划分成一个个网格，可以任意组合不同的网格，做出各种各样的布局。以前，只能通过复杂的 CSS 框架达到的效果，现在浏览器内置了。

![](https://www.wangbase.com/blogimg/asset/201903/1_bg2019032501.png)

上图这样的布局，就是 Grid 布局的拿手好戏。

Grid 布局与 [Flex 布局](http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html)有一定的相似性，都可以指定容器内部多个项目的位置。但是，它们也存在重大区别。

Flex 布局是轴线布局，只能指定“项目”针对轴线的位置，可以看作是**一维布局**。Grid 布局则是将容器划分成“行”和“列”，产生单元格，然后指定“项目所在”的单元格，可以看作是**二维布局**。Grid 布局远比 Flex 布局强大。

## 二、基本概念

学习 Grid 布局之前，需要了解一些基本概念。

### 2.1 容器和项目

采用网格布局的区域，称为“容器”（container）。容器内部采用网格定位的子元素，称为“项目”（item）。

```html
<div>
  <div><p>1</p></div>
  <div><p>2</p></div>
  <div><p>3</p></div>
</div>
```

上面代码中，最外层的`<div>`元素就是容器，内层的三个`<div>`元素就是项目。

注意：项目只能是容器的顶层子元素，不包含项目的子元素，比如上面代码的`<p>`元素就不是项目。Grid 布局只对项目生效。

### 2.2 行和列

容器里面的水平区域称为“行”（row），垂直区域称为“列”（column）。

![](https://www.wangbase.com/blogimg/asset/201903/1_bg2019032502.png)

上图中，水平的深色区域就是“行”，垂直的深色区域就是“列”。

### 2.3 单元格

行和列的交叉区域，称为“单元格”（cell）。

正常情况下，`n`行和`m`列会产生`n x m`个单元格。比如，3行3列会产生9个单元格。

### 2.4 网格线

划分网格的线，称为“网格线”（grid line）。水平网格线划分出行，垂直网格线划分出列。

正常情况下，`n`行有`n + 1`根水平网格线，`m`列有`m + 1`根垂直网格线，比如三行就有四根水平网格线。

![](https://www.wangbase.com/blogimg/asset/201903/1_bg2019032503.png)

上图是一个 4 x 4 的网格，共有5根水平网格线和5根垂直网格线。

## 三、容器属性

Grid 布局的属性分成两类。一类定义在容器上面，称为容器属性；另一类定义在项目上面，称为项目属性。这部分先介绍容器属性。

### 3.1 display 属性

`display: grid`指定一个容器采用网格布局。

```css
div {
  display: grid;
}
```

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032504.png)

上图是`display: grid`的[效果](https://jsbin.com/guvivum/edit?html,css,output)。

默认情况下，容器元素都是块级元素，但也可以设成行内元素。

```css
div {
  display: inline-grid;
}
```

上面代码指定`div`是一个行内元素，该元素内部采用网格布局。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032505.png)

上图是`display: inline-grid`的[效果](https://jsbin.com/qatitav/edit?html,css,output)。

> 注意，设为网格布局以后，容器的`float`、`display: inline-block`、`display: table-cell`、`vertical-align`和`column-*`等设置都将失效。

### 3.2 grid-template-columns 属性，grid-template-rows 属性

容器指定了网格布局以后，接着就要划分行和列。`grid-template-columns`属性定义每一列的列宽，`grid-template-rows`属性定义每一行的行高。

```css
.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
}
```

[上面代码](https://jsbin.com/qiginur/edit?css,output)指定了一个三行三列的网格，列宽和行高都是`100px`。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032506.png)

除了使用绝对单位，也可以使用百分比。

```css
.container {
  display: grid;
  grid-template-columns: 33.33% 33.33% 33.33%;
  grid-template-rows: 33.33% 33.33% 33.33%;
}
```

**（1）repeat()**

有时候，重复写同样的值非常麻烦，尤其网格很多时。这时，可以使用`repeat()`函数，简化重复的值。上面的代码用`repeat()`改写如下。

```css
.container {
  display: grid;
  grid-template-columns: repeat(3, 33.33%);
  grid-template-rows: repeat(3, 33.33%);
}
```

`repeat()`接受两个参数，第一个参数是重复的次数（上例是3），第二个参数是所要重复的值。

`repeat()`重复某种模式也是可以的。

```css
grid-template-columns: repeat(2, 100px 20px 80px);
```

[上面代码](https://jsbin.com/cokohu/edit?css,output)定义了6列，第一列和第四列的宽度为`100px`，第二列和第五列为`20px`，第三列和第六列为`80px`。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032507.png)

**（2）auto-fill 关键字**

有时，单元格的大小是固定的，但是容器的大小不确定。如果希望每一行（或每一列）容纳尽可能多的单元格，这时可以使用`auto-fill`关键字表示自动填充。

```css
.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 100px);
}
```

[上面代码](https://jsbin.com/himoku/edit?css,output)表示每列宽度`100px`，然后自动填充，直到容器不能放置更多的列。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032508.png)

**（3）fr 关键字**

为了方便表示比例关系，网格布局提供了`fr`关键字（fraction 的缩写，意为“片段”）。如果两列的宽度分别为`1fr`和`2fr`，就表示后者是前者的两倍。

```css
.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
```

[上面代码](https://jsbin.com/hadexek/edit?html,css,output)表示两个相同宽度的列。

![](https://www.wangbase.com/blogimg/asset/201903/1_bg2019032509.png)

`fr`可以与绝对长度的单位结合使用，这时会非常方便。

```css
.container {
  display: grid;
  grid-template-columns: 150px 1fr 2fr;
}
```

[上面代码](https://jsbin.com/remowec/edit?html,css,output)表示，第一列的宽度为150像素，第二列的宽度是第三列的一半。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032510.png)

**（4）minmax()**

`minmax()`函数产生一个长度范围，表示长度就在这个范围之中。它接受两个参数，分别为最小值和最大值。

```css
grid-template-columns: 1fr 1fr minmax(100px, 1fr);
```

上面代码中，`minmax(100px, 1fr)`表示列宽不小于`100px`，不大于`1fr`。

**（5）auto 关键字**

`auto`关键字表示由浏览器自己决定长度。

```css
grid-template-columns: 100px auto 100px;
```

上面代码中，第二列的宽度，基本上等于该列单元格的最大宽度，除非单元格内容设置了`min-width`，且这个值大于最大宽度。

**（6）网格线的名称**

`grid-template-columns`属性和`grid-template-rows`属性里面，还可以使用方括号，指定每一根网格线的名字，方便以后的引用。

```css
.container {
  display: grid;
  grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
  grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
}
```

上面代码指定网格布局为3行 x 3列，因此有4根垂直网格线和4根水平网格线。方括号里面依次是这八根线的名字。

网格布局允许同一根线有多个名字，比如`[fifth-line row-5]`。

**（7）布局实例**

`grid-template-columns`属性对于网页布局非常有用。两栏式布局只需要一行代码。

```css
.wrapper {
  display: grid;
  grid-template-columns: 70% 30%;
}
```

上面代码将左边栏设为70%，右边栏设为30%。

传统的十二网格布局，写起来也很容器。

```css
grid-template-columns: repeat(12, 1fr);
```

### 3.3 grid-row-gap 属性，grid-column-gap 属性，grid-gap 属性

`grid-row-gap`属性设置行与行的间隔（行间距），`grid-column-gap`属性设置列与列的间隔（列间距）。

```css
.container {
  grid-row-gap: 20px;
  grid-column-gap: 20px;
}
```

[上面代码](https://jsbin.com/mezufab/edit?css,output)中，`grid-row-gap`用于设置行间距，`grid-column-gap`用于设置列间距。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032511.png)

`grid-gap`属性是`grid-column-gap`和`grid-row-gap`的合并简写形式，语法如下。

```css
grid-gap: <grid-row-gap> <grid-column-gap>;
```

因此，上面一段 CSS 代码等同于下面的代码。

```css
.container {
  grid-gap: 20px 20px;
}
```

如果`grid-gap`省略了第二个值，浏览器认为第二个值等于第一个值。

> 根据最新标准，上面三个属性名的`grid-`前缀已经删除，`grid-column-gap`和`grid-row-gap`写成`column-gap`和`row-gap`，`grid-gap`写成`gap`。

### 3.4 grid-template-areas 属性

网格布局允许指定“区域”（area），一个区域由单个或多个单元格组成。`grid-template-areas`属性用于定义区域。

```css
.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
  grid-template-areas: 'a b c'
                       'd e f'
                       'g h i';
}
```

上面代码先划分出9个单元格，然后将其定名为`a`到`i`的九个区域，分别对应这九个单元格。

多个单元格合并成一个区域的写法如下。

```css
grid-template-areas: 'a a a'
                     'b b b'
                     'c c c';
```

上面代码将9个单元格分成`a`、`b`、`c`三个区域。

下面是一个布局实例。

```css
grid-template-areas: "header header header"
                     "main main sidebar"
                     "footer footer footer";
```

上面代码中，顶部是页眉区域`header`，底部是页脚区域`footer`，中间部分则为`main`和`sidebar`。

如果某些区域不需要利用，则使用“点”（`.`）表示。

```css
grid-template-areas: 'a . c'
                     'd . f'
                     'g . i';
```

上面代码中，中间一列为点，表示没有用到该单元格，或者该单元格不属于任何区域。

> 注意，区域的命名会影响到网格线。每个区域的起始网格线，会自动命名为`区域名-start`，终止网格线自动命名为`区域名-end`。
> 
> 比如，区域名为`header`，则起始位置的水平网格线和垂直网格线叫做`header-start`，终止位置的水平网格线和垂直网格线叫做`header-end`。

### 3.5 grid-auto-flow 属性

划分网格以后，容器的子元素会按照顺序，自动放置在每一个网格。默认的放置顺序是“先行后列”，即先填满第一行，再开始放入第二行，即下图数字的顺序。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032506.png)

这个顺序由`grid-auto-flow`属性决定，默认值是`row`，即“先行后列”。也可以将它设成`column`，变成“先列后行”。

```css
grid-auto-flow: column;
```

[上面代码](https://jsbin.com/xutokec/edit?css,output)设置了`column`以后，放置顺序就变成了下图。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032512.png)

`grid-auto-flow`属性除了设置成`row`和`column`，还可以设成`row dense`和`column dense`。这两个值主要用于，某些项目指定位置以后，剩下的项目怎么自动放置。

[下面的例子](https://jsbin.com/wapejok/edit?css,output)让1号项目和2号项目各占据两个单元格，然后在默认的`grid-auto-flow: row`情况下，会产生下面这样的布局。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032513.png)

上图中，1号项目后面的位置是空的，这是因为3号项目默认跟着2号项目，所以会排在2号项目后面。

现在修改设置，设为`row dense`，表示“先行后列”，并且尽可能紧密填满，尽量不出现空格。

```css
grid-auto-flow: row dense;
```

[上面代码](https://jsbin.com/helewuy/edit?css,output)的效果如下。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032514.png)

上图会先填满第一行，再填满第二行，所以3号项目就会紧跟在1号项目的后面。8号项目和9号项目就会排到第四行。

如果将设置改为`column dense`，表示“先列后行”，并且尽量填满空格。

```css
grid-auto-flow: column dense;
```

[上面代码](https://jsbin.com/pupoduc/1/edit?html,css,output)的效果如下。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032515.png)

上图会先填满第一列，再填满第2列，所以3号项目在第一列，4号项目在第二列。8号项目和9号项目被挤到了第四列。

### 3.6 justify-items 属性，align-items 属性，place-items 属性

`justify-items`属性设置单元格内容的水平位置（左中右），`align-items`属性设置单元格内容的垂直位置（上中下）。

```css
.container {
  justify-items: start | end | center | stretch;
  align-items: start | end | center | stretch;
}
```

这两个属性的写法完全相同，都可以取下面这些值。

> - start：对齐单元格的起始边缘。
> - end：对齐单元格的结束边缘。
> - center：单元格内部居中。
> - stretch：拉伸，占满单元格的整个宽度（默认值）。

```css
.container {
  justify-items: start;
}
```

[上面代码](https://jsbin.com/gijeqej/edit?css,output)表示，单元格的内容左对齐，效果如下图。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032516.png)

```css
.container {
  align-items: start;
}
```

[上面代码](https://jsbin.com/tecawur/edit?css,output)表示，单元格的内容头部对齐，效果如下图。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032517.png)

`place-items`属性是`align-items`属性和`justify-items`属性的合并简写形式。

```css
place-items: <align-items> <justify-items>;
```

下面是一个例子。

```css
place-items: start end;
```

如果省略第二个值，则浏览器认为与第一个值相等。

### 3.7 justify-content 属性，align-content 属性，place-content 属性

`justify-content`属性是整个内容区域在容器里面的水平位置（左中右），`align-content`属性是整个内容区域的垂直位置（上中下）。

```css
.container {
  justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
  align-content: start | end | center | stretch | space-around | space-between | space-evenly;  
}
```

这两个属性的写法完全相同，都可以取下面这些值。（下面的图都以`justify-content`属性为例，`align-content`属性的图完全一样，只是将水平方向改成垂直方向。）

> - start - 对齐容器的起始边框。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032519.png)

> - end - 对齐容器的结束边框。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032518.png)

> - center - 容器内部居中。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032520.png)

> - stretch - 项目大小没有指定时，拉伸占据整个网格容器。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032521.png)

> - space-around - 每个项目两侧的间隔相等。所以，项目之间的间隔比项目与容器边框的间隔大一倍。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032522.png)

> - space-between - 项目与项目的间隔相等，项目与容器边框之间没有间隔。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032523.png)

>  - space-evenly - 项目与项目的间隔相等，项目与容器边框之间也是同样长度的间隔。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032524.png)

`place-content`属性是`align-content`属性和`justify-content`属性的合并简写形式。

```css
place-content: <align-content> <justify-content>
```

下面是一个例子。

```css
place-content: space-around space-evenly;
```

如果省略第二个值，浏览器就会假定第二个值等于第一个值。

### 3.8 grid-auto-columns 属性，grid-auto-rows 属性

有时候，一些项目的指定位置，在现有网格的外部。比如网格只有3列，但是某一个项目指定在第5行。这时，浏览器会自动生成多余的网格，以便放置项目。

`grid-auto-columns`属性和`grid-auto-rows`属性用来设置，浏览器自动创建的多余网格的列宽和行高。它们的写法与`grid-template-columns`和`grid-template-rows`完全相同。如果不指定这两个属性，浏览器完全根据单元格内容的大小，决定新增网格的列宽和行高。

[下面的例子](https://jsbin.com/sayuric/edit?css,output)里面，划分好的网格是3行 x 3列，但是，8号项目指定在第4行，9号项目指定在第5行。

```css
.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
  grid-auto-rows: 50px; 
}
```

上面代码指定新增的行高统一为50px（原始的行高为100px）。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032525.png)

### 3.9 grid-template 属性，grid 属性

`grid-template`属性是`grid-template-columns`、`grid-template-rows`和`grid-template-areas`这三个属性的合并简写形式。

`grid`属性是`grid-template-rows`、`grid-template-columns`、`grid-template-areas`、  `grid-auto-rows`、`grid-auto-columns`、`grid-auto-flow`这六个属性的合并简写形式。

从易读易写的角度考虑，还是建议不要合并属性，所以这里就不详细介绍这两个属性了。

## 四、项目属性

下面这些属性定义在项目上面。

### 4.1 grid-column-start 属性，grid-column-end 属性，grid-row-start 属性，grid-row-end 属性

项目的位置是可以指定的，具体方法就是指定项目的四个边框，分别定位在哪根网格线。

> - `grid-column-start`属性：左边框所在的垂直网格线
> - `grid-column-end`属性：右边框所在的垂直网格线
> - `grid-row-start`属性：上边框所在的水平网格线
> - `grid-row-end`属性：下边框所在的水平网格线

```css
.item-1 {
  grid-column-start: 2;
  grid-column-end: 4;
}
```

[上面代码](https://jsbin.com/yukobuf/edit?css,output)指定，1号项目的左边框是第二根垂直网格线，右边框是第四根垂直网格线。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032526.png)

上图中，只指定了1号项目的左右边框，没有指定上下边框，所以会采用默认位置，即上边框是第一根水平网格线，下边框是第二根水平网格线。

除了1号项目以外，其他项目都没有指定位置，由浏览器自动布局，这时它们的位置由容器的`grid-auto-flow`属性决定，这个属性的默认值是`row`，因此会“先行后列”进行排列。读者可以把这个属性的值分别改成`column`、`row dense`和`column dense`，看看其他项目的位置发生了怎样的变化。

[下面的例子](https://jsbin.com/nagobey/edit?html,css,output)是指定四个边框位置的效果。

```css
.item-1 {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 2;
  grid-row-end: 4;
}
```

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032527.png)

这四个属性的值，除了指定为第几个网格线，还可以指定为网格线的名字。

```css
.item-1 {
  grid-column-start: header-start;
  grid-column-end: header-end;
}
```

上面代码中，左边框和右边框的位置，都指定为网格线的名字。

这四个属性的值还可以使用`span`关键字，表示“跨越”，即左右边框（上下边框）之间跨越多少个网格。

```css
.item-1 {
  grid-column-start: span 2;
}
```

[上面代码](https://jsbin.com/hehumay/edit?html,css,output)表示，1号项目的左边框距离右边框跨越2个网格。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032528.png)

这与[下面的代码](https://jsbin.com/mujihib/edit?html,css,output)效果完全一样。


```css
.item-1 {
  grid-column-end: span 2;
}
```

使用这四个属性，如果产生了项目的重叠，则使用`z-index`属性指定项目的重叠顺序。

### 4.2 grid-column 属性，grid-row 属性

`grid-column`属性是`grid-column-start`和`grid-column-end`的合并简写形式，`grid-row`属性是`grid-row-start`属性和`grid-row-end`的合并简写形式。

```css
.item {
  grid-column: <start-line> / <end-line>;
  grid-row: <start-line> / <end-line>;
}
```

下面是一个例子。

```css
.item-1 {
  grid-column: 1 / 3;
  grid-row: 1 / 2;
}
/* 等同于 */
.item-1 {
  grid-column-start: 1;
  grid-column-end: 3;
  grid-row-start: 1;
  grid-row-end: 2;
}
```

上面代码中，项目`item-1`占据第一行，从第一根列线到第三根列线。

这两个属性之中，也可以使用`span`关键字，表示跨越多少个网格。

```css
.item-1 {
  background: #b03532;
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
/* 等同于 */
.item-1 {
  background: #b03532;
  grid-column: 1 / span 2;
  grid-row: 1 / span 2;
}
```

[上面代码](https://jsbin.com/volugow/edit?html,css,output)中，项目`item-1`占据的区域，包括第一行 + 第二行、第一列 + 第二列。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032529.png)

斜杠以及后面的部分可以省略，默认跨越一个网格。

```css
.item-1 {
  grid-column: 1;
  grid-row: 1;
}
```

上面代码中，项目`item-1`占据左上角第一个网格。

### 4.3 grid-area 属性

`grid-area`属性指定项目放在哪一个区域。

```css
.item-1 {
  grid-area: e;
}
```

[上面代码](https://jsbin.com/qokexob/edit?css,output)中，1号项目位于`e`区域，效果如下图。

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032530.png)

`grid-area`属性还可用作`grid-row-start`、`grid-column-start`、`grid-row-end`、`grid-column-end`的合并简写形式，直接指定项目的位置。

```css
.item {
  grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}
```

下面是一个[例子](https://jsbin.com/duyafez/edit?css,output)。

```css
.item-1 {
  grid-area: 1 / 1 / 3 / 3;
}
```

### 4.4 justify-self 属性，align-self 属性，place-self 属性

`justify-self`属性设置单元格内容的水平位置（左中右），跟`justify-items`属性的用法完全一致，但只作用于单个项目。

`align-self`属性设置单元格内容的垂直位置（上中下），跟`align-items`属性的用法完全一致，也是只作用于单个项目。

```css
.item {
  justify-self: start | end | center | stretch;
  align-self: start | end | center | stretch;
}
```

这两个属性都可以取下面四个值。

> - start：对齐单元格的起始边缘。
> - end：对齐单元格的结束边缘。
> - center：单元格内部居中。
> - stretch：拉伸，占满单元格的整个宽度（默认值）。

下面是`justify-self: start`的例子。

```css
.item-1  {
  justify-self: start;
}
```

![](https://www.wangbase.com/blogimg/asset/201903/bg2019032532.png)

`place-self`属性是`align-self`属性和`justify-self`属性的合并简写形式。

```css
place-self: <align-self> <justify-self>;
```

下面是一个例子。

```css
place-self: center center;
```

如果省略第二个值，`place-self`属性会认为这两个值相等。

## 五、参考链接

- [A Complete Guide to Grid](https://css-tricks.com/snippets/css/complete-guide-grid/), by Chris House
- [Understanding the CSS Grid Layout Module](https://webdesign.tutsplus.com/series/understanding-the-css-grid-layout-module--cms-1079), by Ian Yates
- [How to Build an Off-Canvas Navigation With CSS Grid](https://webdesign.tutsplus.com/tutorials/how-to-build-an-off-canvas-navigation-with-css-grid--cms-28191), Ian Yates
- [Introduction to the CSS Grid Layout With Examples](https://code.tutsplus.com/tutorials/introduction-to-css-grid-layout-with-examples--cms-25392), Dogacan Bilgili
- [Learn CSS Grid](http://learncssgrid.com/), Jonathan Suh
- [How I stopped using Bootstrap’s layout thanks to CSS Grid](https://blog.theodo.fr/2018/03/stop-using-bootstrap-layout-thanks-to-css-grid/), Cédric Kui

# Table 布局

CSS 可以让 HTML 元素像表格那样排列。

下面是表格的各个 HTML 标签，所对应的布局模式。

- table    { display: table }
- tr       { display: table-row }
- thead    { display: table-header-group }
- tbody    { display: table-row-group }
- tfoot    { display: table-footer-group }
- col      { display: table-column }
- colgroup { display: table-column-group }
- td, th   { display: table-cell }
- caption  { display: table-caption }

表格布局可以很简单地做到垂直居中。

```css
div {
  height: 200px;
  display:table-cell;
  vertical-align: middle;
}
```

这种写法相比下面的写法，更容易理解。

```css
div {
  height: 200px;
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}
```

表格布局的另一个用途是，让页尾总是显示在浏览器的最低部，即使页面高度不足一页。

```css
/*
  HTML 代码如下
  <body>
    <div class="main"></div>
    <div class="footer"></div>
  </body>
*/

body {
  display: table;
  width: 100%;
}

.main {
  min-height: 100%;
}

.footer {
  display: table-row;
  height:1px;
}
```

基于上面这种底部固定的技巧，可以使用表格布局，完成圣杯布局，即页面从上到下分成页首 + 内容 + 页尾三部分，其中内容部分又分成左边栏和右边栏。

```css
/*
  HTML 代码如下
<div class="wrapper">
  <div class="header">HEADER</div>
  <div class="main">
    <div class="box sidebar">Left-sidebar</div>
    <div class="box content">Main Content</div>
    <div class="box sidebar">Right-sidebar</div>
  </div>
  <div class="footer">FOOTER</div>
</div>
*/

.wrapper {
  min-height: 100%;
  display: table;
  width: 100%;
  text-align: center;
}

.header {
  display: table-row;
  height: 1px;
}

.main {
  min-height: 100%;
  display: table;
  width: 100%;
}

.box {
  display: table-cell;
}

.sidebar {
  width: 100px;
}

.footer {
  display: table-row;
  height:1px;
}
```

利用表格的不同性质的行，可以调整行的显示位置。

- `display:table-caption`使得该行显示在表格的最顶部。
- `display:table-header-group`使得该行显示在表格的头部，但是位置低于`table-caption`的行。
- `display:table-footer-group`使得该行显示在表格的底部。

## 参考链接

- Colin Toh, [The Anti-hero of CSS Layout - "display:table"](https://colintoh.com/blog/display-table-anti-hero)
- Ian Devlin, [CSS stacking with display:table](https://iandevlin.com/blog/2013/06/css/css-stacking-with-display-table/)
- Ben Frain, [CSS performance test: Flexbox v CSS Table](https://benfrain.com/css-performance-test-flexbox-v-css-table-fight/)

# 属性选择器

- `[attribute]`	匹配指定属性，不论具体值是什么
- `[attribute="value"]`	完全匹配指定属性值
- `[attribute~="value"]`	属性值是以空格分隔的多个单词，其中有一个完全匹配指定值
- `[attribute|="value"]`	属性值以value-打头
- `[attribute^="value"]`	属性值以value开头，value为完整的单词或单词的一部分
- `[attribute$="value"]`	属性值以value结尾，value为完整的单词或单词的一部分
- `[attribute*="value"]`	属性值为指定值的子字符串

## 修饰符

属性修饰器支持`i`修饰符，表示不区分大小写。

```css
[class=foo i] {
  color: red;
}
```

上面代码中，属性名`foo`后面的`i`，表示不区分`foo`的大小写，所以下面几个 class 都会匹配。

```html
<div class="foo">...</div>
<div class="Foo">...</div>
<div class="fOo">...</div>
```

这个修饰符对于匹配用户的输入，非常有用。

```css
/**
 * 匹配：
 * <input value="hello world">
 * <input value="hello World">
 * <input value="hElLo WoRlD">
 * ...
 */
[value="hello world" i] { /* ... */ }
```

## 参考链接

- [The CSS attribute selector has a case-insensitive mode](https://www.stefanjudis.com/today-i-learned/the-css-attribute-selector-has-a-case-insensitive-mode/)

# 选择器

选择器决定了样式规则对哪些元素有效。

## 基本选择器

元素名称选择器，选中所有该标签的元素。

`#elementId`选中指定`id`属性的元素。

`.elementClass`选中指定`class`属性的元素。

应该尽量使用 class 选择器，而不是 ID 选择器。

ID 选择器的样式不能在其他元素上复用（记住，在一个页面中，一个id只能出现在一个元素上）。这会导致在其他元素上重复样式，而不是通过class共享样式。

ID 选择器的特殊性比class选择器要强得多。这意味着如果要覆盖使用id选择器定义的样式，就要编写特殊性更强的CSS规则。如果数量不多，可能还不难管理。如果处理规模较大的网站，其CSS就会变得比实际所需的更长、更复杂。

## 通配符选择器

通配符`*`（星号）匹配任何元素。

因为匹配范围太广，会让浏览器加载页面变慢，因此应该谨慎使用通配符。实际适合使用通配符的情况比较少。

## :matches()

`:matches(A, B)`选择器表示匹配A或B。

```css
:matches(.foo, .bar) {
  background-color: green;
}

/* 等同于 */

.foo, .bar {
  background-color: green;
}
```

它可以简化一些选择器的写法。

```css
.syntax-highlighted .css-keyword,
.syntax-highlighted .css-tag {
  color: rgb(170, 13, 145);
}

/* 等同于 */

.syntax-highlighted :matches(.css-keyword, .css-tag) {
  color: rgb(170, 13, 145);
}
```

## :not()

`:not()`表示选中不匹配指定条件的元素。

```css
a:not(.internal) {
  color: red;
}
```

`:not()`可以采用链式写法。

```css
:not(i):not(em)

/* 等同于 */

:not(i, em)
```

## BEM命名法

BEM是Block（区块）、Element（元素）、Modifier（修饰符）三者的简称。区块是顶级组件的抽象，元素是组件的组成部分，修饰符是组件或元素的状态。区块与元素之间用两个下划线连接，元素与修饰符之间用两个连词线连接。

```css
/* Block component */
.btn {}

/* Element that depends upon the block */
.btn__price {}

/* Modifier that changes the style of the block */
.btn--orange {}
.btn--big {}
```

对应的HTML代码结构如下。

```html
<a class="btn btn--big btn--orange" href="http://css-tricks.com">
  <span class="btn__price">$9.99</span>
  <span class="btn__text">Subscribe</span>
</a>
```

BEM的重要特点就是CSS是扁平式的，不存在元素嵌套。

## target

target选择器用来匹配当前hash。

产生动画效果。

```css
#further-resources:target {
  animation: highlight .8s ease-out;
}

@keyframes highlight {
  0% { background-color: #FFFF66; }
  100% { background-color: #FFFFFF; }
}
```

弹出效果。

```css
#search-overlay {
  position: fixed;
  top: 1em;
  bottom: 1em;
  right: 1em;
  left: 1em;
  /* … */
  opacity: 0;
  transition: opacity .3s ease-in-out;
  pointer-events: none;
}

#search-overlay:target {
  opacity: 1;
  pointer-events: auto;
  transition: opacity .3s ease-in-out;
}
```

导航栏效果

```css
.main-nav {
  position: fixed;
  top: 0;
  width: 0;
  height: 100%;
  background: #3B3B3B;
  overflow-y: auto;
  transition: width 0.3s ease;
}

#main-nav:target {
  width: 20%;
}

#main-nav:target + .page-wrap {
  width: 80%;
  .open-menu {
     display: none;
  }
  .close-menu {
     display: block;
  }
  .main-header {
    width: 80%;
    left: 20%;
  }
}
```

## 伪元素（Pseudo-element）

伪元素（::before或者::after）是每个元素额外多出来的DOM节点。


```css
.pebble::before {
  ...
}
.pebble::after {
  ...
}

```

伪元素使用两个双引号标识，如果希望IE8支持，也可以使用单引号。

```css

button::after {
  content: '';
  position: absolute;
  top: -50%;
  right: -50%;
  bottom: -50%;
  left: -50%;
  background: linear-gradient(to bottom, rgba(229, 172, 142, 0.1), rgba(255,255,255,0.5) 50%, rgba(229, 172, 142, 0.1));
  transform: rotateZ(60deg) translate(-5em, 7.5em);
}

button:hover::after {
  animation: sheen 1s forwards;
}

@keyframes sheen {
  100% {
    transform: rotateZ(60deg) translate(1em, -9em);
  }
}

```

参考链接

- Donovan Hutchinson, [Animating pseudo-elements](http://cssanimation.rocks/post/pseudo-elements/)

## 伪类

- :empty：没有任何子元素
- :in-range：针对有range属性的input
- :out-of-range：针对有range属性的input
- :optional：没有required属性的input元素
- :required
- :disabled
- :fullscreen
- :not()

## 优先级

一个网页元素，可以被多个选择器匹配。如果这些选择器都设置了同样的规则，就有一个”优先级“的问题：到底哪个选择器的优先级更高？

规则是针对性越强的选择器，优先级越高。浏览器会计算每条规则的优先级值，然后采用值最高的规则，计算方法如下。

- `!important`后缀为 10000
- 行内样式为 1000
- ID 为 100
- class、伪类、属性选择器为 10
- 标签名、伪元素为 1

然后，累加得到的分数，哪条规则得分最高，就采用哪条规则。如果得分相同，就采用位置靠后的位置。

下面是一些例子。

- `<li style="color: red;">`：行内样式，得分为1000。
- `ul#nav li.active a`：一个 ID、一个 Class、三个标签名，得分为113。
- `body.ie7 .col_3 h2 ~ h3`：两个 Class，三个标签名，得分为23。
- `#footer *:not(nav) li`：一个 ID、两个标签名，得分为102。注意，`*`没有分数，而且`:not`不算伪类，只计算它括号里面的标签名。
- `ul > li ul li ol li:first-letter`：七个标签名，得分为7。注意，`:first-letter`算作伪元素。

# 伪类选择器

伪元素（pseudo-element）是 HTML 中并不存在的元素。例如，定义第一个字母或第一行文字时，并未在HTML中作相应的标记。

伪类（pseudo-class）是浏览器根据网页元素的状态，自动提供的 CSS 类，无需在 HTML 代码显式标记这些类。例如，使用:first-child可以选择某元素的第一个子元素，你就不用写成class="first-child"。更多关于伪类的内容。

伪元素有四个。

- ::first-line
- ::first-letter
- ::before
- ::after

伪类

- :first-child
- :link：新的、未访问的链接
- :visited：访问过的链接
- :focus：链接获得焦点（如通过Tab键）
- :hover：当访问者将鼠标指针停留在链接上时
- :active: 当访问者激活链接时
- :empty：空选择器

新的、未访问的链接显示为红色；访问过的链接变为橙色；

## 概述

CSS 2.1非常有限，只提供了三个伪类。

- :active
- :focus
- :hover

CSS Selector Level 3添加了更多与HTML表单相关的伪类：

- :enabled
- :disabled
- :checked
- :indeterminate

CSS Basic UI Level 3添加了许多伪类来描述窗口小部件的状态：

- :default
- :valid
- :invalid
- :in-range
- :out-of-range
- :required
- :optional
- :read-only
- :read-write

## :first-child，:last-child

`:first-child`表示一组元素的第一个元素，且该元素必须是父元素的第一个子元素。

```css
li{
  color: blue;
}

li:first-child {
  color: green;
}
```

上面代码中，第一个`li`的字体颜色为绿色，其他`li`都为蓝色。

`:last-child`表示一组元素之中的最后一个，且该元素必须是父元素的最后一个子元素。

```css
li:last-child {
  background-color: lime;
}
```

## :first-of-type，:last-of-type

`:first-of-type`选中一组元素之中的第一个元素。

```css
p:first-of-type {
  font-size: 1.25em;
}
```

上面代码中，`p:first-of-type`只会选中第一个`p`元素。

`:first-of-type`与`:first-child`的区别是，后者必须父元素的第一个子元素。

```html
<ul>
  <p>Hello World</p>
  <li>1</li>
  <li>2</li>
</ul>
```

上面这段 HTML 代码，如果想让第一个`<li>`显示为绿色，必须使用`:first-of-type`，而不能是`:first-child`。

```css
/* 正确 */
ul li:first-of-type {
  color: green;
}

/* 错误 */
ul li:first-child {
  color: green;
}
```

`:last-of-type`选中本类之中最后一个元素。

```css
p:last-of-type {
  font-size: 0.75em;
}
```

上面代码中，`p:last-of-type`只会选中最后一个`p`元素。

## :nth-child()，:nth-last-child()

`:nth-child()`选中指定位置的子元素。`:nth-last-child()`选中指定的倒数位置的子元素。

```css
p:nth-child(2) {
  color: red;
}
```

上面代码中，`:nth-child()`选中所有第二个子元素位置的`p`元素。

注意，如果元素名与`:nth-child()`之间有没有空格，含义是不一样的。没有空格时，表示匹配该种子元素；有空格时，表示匹配该元素的子元素。


```css
p :nth-child(2) {
  color: red;
}
```

上面代码表示，匹配`p`元素内部的第二个子元素，而不是匹配`p`元素本身。

`:nth-child()`和`:nth-last-child()`可以使用通配符作为参数，参见`:nth-of-type()`部分的说明。

```css
ul li:nth-child(3n+3) {
  color: #ccc;
}
```

上面代码选中3、6、9等位置的`li`元素，`n`表示整数序列0、1、2、3……。

这里还有一个技巧，如果要选中前三个元素，可以在`n`前面使用负数符号1`-`。

```css
ul li:nth-child(-n + 3) {
  color: #ccc;
}
```

上面代码只会选中前三个`li`，因为当`n`大于等于`3`时，`-n + 3`会小于等于0，而`li`的序号是从`1`开始的。

相应的，`:nth-last-child(-n + 3)`就会选中最后三个元素，而`:nth-last-child(n + 4)`会选中除了最后三个以外的其他元素。

除了使用`An + B`这种形式的表达式指定位置，还可以使用`odd`或`even`关键字，分别表示奇数位置或偶数位置的元素。

```css
ul li:nth-child(odd) {
  color: #ccc;
}
```

## :nth-of-type()，:nth-last-of-type()

`:nth-of-type()`匹配指定类型和位置的元素，`:nth-last-of-type()`匹配倒数位置的指定类型和位置的元素。

```css
p:nth-of-type(2) {
  color: red;
}
```

上面代码中，`p:nth-of-type(2)`匹配第二个`p`元素。

下面是通配符用法。

```css
li:nth-of-type(2n) {
  background: lightslategrey;
}

li:nth-of-type(3n+2) {
  background: blue;
}
```

`:nth-of-type`的参数可以是`an + b`的形式。

- “a”是一个整数
- “n”作为英文字母，总是不变的，含义是“0, 1, 2, ....”
- “+”作为一个运算符，可以是“+”，也可以是“-”
- “b”是一个整数，如果提供了运算符，就必须提供“b”

`:nth-of-type`的参数还可以是`even`或`odd`。

```css
li::nth-of-type(odd)  {
  background: lightslategrey;
}
```

## 空选择器

`:empty`是空选择器，匹配没有任何子节点的元素。

```css
p:empty {
  color: red;
}
```

下面的 HTML 代码都匹配`:empty`选择器。

```html
<!-- 闭合标签之间没有空格 -->
<p></p>

<!-- 闭合标签之间只有注释 -->
<p><!-- comment --></p>
```

下面的 HTML 代码都不匹配`:empty`选择器。

```html
<!-- 闭合标签之间有空格 -->
<p></p>

<!-- 闭合标签之间有换行符 -->
<p>
<!-- comment -->
</p>

<!-- 闭合标签有子元素 -->
<p><span></span></p>
```

这个选择器的一个作用，就是使用脚本动态添加子元素后，将该元素显示出来。

```html
<!-- No error message -->
<div class="error"></div>

<!-- Yes error message -->
<div class="error">Missing Email</div>
```

上面代码中，为`<div>`标签添加文本内容后，使用下面的 CSS 代码将其显示出来。

```css
.error:empty {
  display: none;
}

.error:before {
  color: red;
  content: "\0274c "; /* ❌ icon */
}
```

选中非空元素可以像下面这样写。

```css
.alert:not(:empty) {
  background: pink;
  padding: 10px;
}
```

## 参考链接

- [CSS :empty Selector](https://www.samanthaming.com/tidbits/72-css-empty-selector), Samantha Ming

# text-decoration

文本可以附加装饰线（比如下划线），以下属性用来设置装饰线。

## text-decoration

`text-decoration`设置文本采用哪一种装饰线，主要有下划线（underline）、上划线（overline）和删除线（line-through）等类型。

```css
h3 {
  text-decoration: underline;
}
```

该属性可以取以下值。

- none：没有任何修饰
- underline：下划线，默认线宽`1px`
- overline：上划线，默认线宽`1px`
- line-through：删除线，默认线宽`1px`
- inherit：继承父元素的设置

多种装饰线可以同时存在。

```css
.multiple {
  text-decoration: underline overline line-through;
}
```

默认情况下，装饰线的颜色与文本的`color`属性一致。`text-decoration-color`属性可以修改颜色。

`text-decoration`属性还可以用作`text-decoration-style`和`text-decoration-color`的简写形式。

```css
.fancy-underline {
  text-decoration-line: underline;
  text-decoration-style: wavy;
  text-decoration-color: red;

  /* 等同于 */
  text-decoration: underline wavy red;
}
```

## text-decoration-color

`text-decoration-color`设置文本的装饰线的颜色。

```css
a {
  text-decoration-color: #E18728;
}
```

## text-decoration-style

`text-decoration-style`设置文本的下划线（underline）、上划线（overline）和删除线（line-through）的样式。

```css
a {
  text-decoration-style: solid;
}
```

该属性支持以下样式。

- solid：单条直线
- double：双条直线
- dotted：多个点组成的直线
- dashed：多个短划线组成的直线
- wavy：波浪线

## text-decoration-line

`text-decoration-line`设置文本采用何种装饰线，与`text-decoration`单个值的写法相同。建议采用后者，因为浏览器的支持度更好。

```css
p {
  text-decoration-line: underline;
}
```

它的取值参考`text-decoration`。

## text-decoration-skip

`text-decoration-skip`设置文本的装饰线应该在哪里中断，主要用于改善文本被装饰以后的可读性。

```css
a {
  text-decoration-skip: ink;
}
```

上面代码中，下划线遇到英语字母`y`和`p`会中断，让它们较长的下划会更清晰地显示出来。

该属性可以取以下值。

- objects：默认值，装饰线遇到图片或其他`inline-block`对象时中断。
- none：装饰线不会有任何中断，包括遇到行内对象。
- spaces：装饰线在空格、断词处中断。
- ink：装饰线遇到有笔画下降或上升的字母时中断。
- edges：装饰线在内容开始后和结束前都收缩一点，主要用于多个连续的装饰线，可以看上去连成一条。
- box-decoration：装饰线在继承的 margin、border 和 padding 处中断。

## 参考链接

- [text-decoration](https://css-tricks.com/almanac/properties/t/text-decoration/), by Sara Cope
- [text-decoration-skip](https://css-tricks.com/almanac/properties/t/text-decoration-skip/), by Marie Mosley

# 文本

## direction

`direction`属性设置块级元素内部的文本阅读方向。

它的默认值是`ltr`（从左到右），适用于大部分语言。它还可以设成`rtl`（从右到左）。

```css
blockquote {
  direction: rtl;
}
```

一般采用 HTML 语言的`dir`属性控制文本阅读方向，而不是使用这个属性。

## text-align

`text-align`属性设置块级元素内部的文本对齐方式。

它可以取以下值。

- left：默认值，文本向左对齐
- right：右对齐
- center：居中对齐
- justify：两端对齐，除了最后一行是左对齐。
- inherit：继承父元素的值
- start：`direction`属性为从左到右时，为左对齐；从右到左时，为右对齐
- end：`direction`属性为从左到右时，为右对齐；从右到左时，为左对齐

## vertical-align

`vertical-align`设置一个元素与在同一条水平线上的其他元素如何对齐。这些元素需要都是`inline`。它可以取以下值。

- baseline：对齐父元素的基线，默认值
- length: 升高或降低特定的长度，可使用负值
- %：升高或降低`line-height`的百分比，不允许负值
- sub：设为下标
- super：设为上标
- top: 当前元素的顶部对齐最高元素的顶边
- text-top：当前元素的顶部对齐父元素字体的顶部
- middle：元素位于父元素的垂直中部
- bottom：当前元素的底部对齐本行最低元素的底部
- text-bottom：当前元素的底部，对齐父元素字体的底部
- initial：设置为默认值
- inherit：继承父元素的值

这个属性通常用于同一行的图标与文字的对齐。

```css
vertical-align: middle;
```

这个命令对设为`display: table-cell`的元素也有效，可以控制元素在单元格之中的垂直对齐方式。这时，一般使用`top`、`middle`和`bottom`等值。

## tab-size

`tab-size`属性设置 Tab 键的宽度，可以设置为整数（表示多少个空格），也可以设置为具体的长度单位。

```css
/* 整数植 */
tab-size: 4;
tab-size: 0;

/* 长度单位 */
tab-size: 10px;
tab-size: 2em;
```

该属性常用于`<pre>`标签之中。

```css
pre {-moz-tab-size: 16;} /* Code for Firefox */
pre {-o-tab-size: 16;} /* Code for Opera 10.6-12.1 */
pre {tab-size: 16;}
```

## word-wrap

`word-wrap`的正式名称是`overflow-wrap`，用于规定是否可以在一个词内部断行，避免溢出容器。

它可以取两个值。

- normal：只在可以断行的地方断行。
- break-word：可以在任意点断行，避免某个词过长，发生溢出。

## word-break

`word-break`用于规定是否可以在词内断行。

它可以取三个值。

- normal：使用浏览器默认的断行规则
- break-all：对于非 CJK 字符，可以任意字符之间断行。
- keep-all：对于 CJK 字符不允许换行。非 CJK 字符与`normal`相同。

## hyphens

浏览器打开连字号功能，需要两个步骤。第一个步骤是设置文本的语言。这将告诉浏览器使用哪个连字词典，正确的自动连字需要一个适合文本语言的连字词典。如果浏览器不知道文本的语言，即使打开 CSS 设置也不会自动连词。

设置网页语言，应该使用`<html>`标签的`lang`属性。

```html
<html lang="en">
```

CSS 里面使用自动连词，要开启`hyphens`属性。`hyphens`属性控制块级元素之中，文本是否显示连词线。

```css
hyphens: auto;
```

`hyphens`属性可以取以下三个值。

（1） none

`none`表示一个词不会在断行处被拆开，即断行处不会有连词线。

（2）manual

`manual`表示只有在一个词内部的字符表示可以有连词线时，才会在断行处拆开。断行处，会有连词线。

两个字符可以表示此处可以断行，并显示连词线，一个是`-`(U+2010)，表示此处可以有一个可见的断行，即使不在此处断行，这里也会有连词线显示；另一个是`U+00AD`，表示不可见的断行，HTML 文档里面可以用`&shy;`插入。

（3）auto

`auto`表示浏览器决定一个词是否可以在断行处拆开，以及是否会有连词线。

## 参考链接

- [All you need to know about hyphenation in CSS](http://clagnut.com/blog/2395), Richard Rutter

# 空格

## 一、基本规则

HTML 代码的空格通常会被浏览器忽略。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073106.jpg)

```html
<p>◡◡hello◡◡world◡◡</p>
```

上面是一行 HTML 代码，文字的前部、内部和后部各有两个空格。为了便于识别，这里使用半圆形符号`◡`表示空格。

浏览器的输出结果如下。

```html
hello world
```

可以看到，文字的前部和后部的空格都会忽略，内部的连续空格只会算作一个。这就是浏览器处理空格的基本规则。

如果希望空格原样输出，可以使用`<pre>`标签。

```html
<pre>◡◡hello◡◡world◡◡</pre>
```

另一种方法是，改用 HTML 实体`&nbsp;`表示空格。

```html
<p>&nbsp;&nbsp;hello&nbsp;&nbsp;world&nbsp;&nbsp;</p>
```

## 二、空格字符

HTML 处理空格的规则，适用于多种字符。除了普通的空格键，还包括制表符（`\t`）和换行符（`\r`和`\n`）。

浏览器会自动把这些符号转成普通的空格键。

```html
<p>hello
world</p>
```

上面代码中，文本内部包含了一个换行符，浏览器视同为空格，输出结果如下。

```html
hello world
```

所以，文本内部的换行是无效的（除非文本放在`<pre>`标签内）。

```html
<p>hello<br>world</p>
```

上面代码使用`<br>`标签显式表示换行。

## 三、CSS 的 white-space 属性

HTML 语言的空格处理，基本上就是直接过滤。这样的处理过于粗糙，完全忽视了原始文本内部的空格可能是有意义的。

CSS 提供了一个 white-space 属性，可以提供更精确一点的空格处理方式。该属性共有六个值，除了一个通用的`inherit`（继承父元素），下面依次介绍剩下的五个值。

### 3.1 white-space: normal

`white-space`属性的默认值为`normal`，表示浏览器以正常方式处理空格。

```html
<p>◡◡hellohellohello◡hello
world
</p>
```

上面代码中，文本前部有两个空格，内部有一个长单词和一个换行符。

然后，容器`<p>`指定一个比较小的宽度。为了便于识别，背景色设为红色。

```css
p {
  width: 100px;
  background: red;
}
```

显示效果如下图。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073101.png)

可以看到，文首的空格被忽略。由于容器太窄，第一个单词溢出容器，然后在后面一个空格处换行。文本内部的换行符自动转成了空格。

### 3.2 white-space: nowrap

`white-space`属性为`nowrap`时，不会因为超出容器宽度而发生换行。

```css
p {
  white-space: nowrap;
}
```

显示效果如下图。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073102.png)

所有文本显示为一行，不会换行。

### 3.3 white-space: pre

`white-space`属性为`pre`时，就按照`<pre>`标签的方式处理。

```css
p {
  white-space: pre;
}
```

显示效果如下图。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073103.png)

上面结果与原始文本完全一致，所有空格和换行符都保留了。

### 3.4 white-space: pre-wrap

`white-space`属性为`pre-wrap`时，基本还是按照`<pre>`标签的方式处理，唯一区别是超出容器宽度时，会发生换行。

```css
p {
  white-space: pre-wrap;
}
```

显示效果如下图。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073104.png)

文首的空格、内部的空格和换行符都保留了，超出容器的地方发生了折行。

### 3.5 white-space: pre-line

`white-space`属性为`pre-line`时，意为保留换行符。除了换行符会原样输出，其他都与`white-space:normal`规则一致。

```css
p {
  white-space: pre-line;
}
```

显示效果如下图。

![](https://www.wangbase.com/blogimg/asset/201807/bg2018073105.png)

除了文本内部的换行符没有转成空格，其他都与`normal`的处理规则一致。这对于诗歌类型的文本很有用。

## 四、参考链接

- [When does white space matter in HTML?](https://medium.com/@patrickbrosset/when-does-white-space-matter-in-html-b90e8a7cdd33)，by Patrick Brosset
- [white-space](https://css-tricks.com/almanac/properties/w/whitespace/)，by Sara Cope